overrun/js/entities/enemies/BaseEnemy.js

87 lines
2.4 KiB
JavaScript

const HP_BAR_WIDTH = 30;
const HP_BAR_HEIGHT = 4;
const HP_BAR_OFFSET_Y = -20;
export class BaseEnemy {
/**
* @param {Phaser.Scene} scene
* @param {number} x
* @param {number} y
* @param {object} player - Player instance
* @param {object} config - { color, radius, hp, speed, xp, contactDamage }
*/
constructor(scene, x, y, player, config) {
this.scene = scene;
this.player = player;
this.maxHp = config.hp;
this.hp = config.hp;
this.speed = config.speed;
this.xp = config.xp;
this.contactDamage = config.contactDamage ?? 10;
this.radius = config.radius ?? 14;
this.body = scene.add.circle(x, y, this.radius, config.color ?? 0xff4444);
scene.physics.add.existing(this.body);
this.body.body.setCircle(this.radius);
// HP bar
this._hpBg = scene.add.rectangle(x, y + HP_BAR_OFFSET_Y, HP_BAR_WIDTH, HP_BAR_HEIGHT, 0x333333);
this._hpFill = scene.add.rectangle(x, y + HP_BAR_OFFSET_Y, HP_BAR_WIDTH, HP_BAR_HEIGHT, 0x00ff44);
this._contactTimer = 0;
}
get x() { return this.body.x; }
get y() { return this.body.y; }
get active() { return this.body?.active ?? false; }
takeDamage(amount) {
this.hp -= amount;
this._updateHpBar();
if (this.hp <= 0) this._die();
}
_updateHpBar() {
const ratio = Math.max(0, this.hp / this.maxHp);
this._hpFill.width = HP_BAR_WIDTH * ratio;
this._hpFill.x = this.x - (HP_BAR_WIDTH * (1 - ratio)) / 2;
}
_syncBarPosition() {
this._hpBg.setPosition(this.x, this.y + HP_BAR_OFFSET_Y);
this._hpFill.setPosition(
this.x - (HP_BAR_WIDTH * (1 - Math.max(0, this.hp / this.maxHp))) / 2,
this.y + HP_BAR_OFFSET_Y
);
}
_die() {
this.scene.events.emit('enemy-killed', { xp: this.xp, x: this.x, y: this.y });
this.destroy();
}
/** Check contact damage with player. Call from subclass update(). */
_checkContact(delta) {
this._contactTimer -= delta;
if (this._contactTimer > 0) return;
const dist = Phaser.Math.Distance.Between(this.x, this.y, this.player.x, this.player.y);
if (dist < this.radius + 16) {
this.player.takeDamage(this.contactDamage);
this._contactTimer = 800; // ms between contact hits
}
}
update(delta) {
this._syncBarPosition();
this._checkContact(delta);
}
destroy() {
this.body?.destroy();
this._hpBg?.destroy();
this._hpFill?.destroy();
}
}