87 lines
2.4 KiB
JavaScript
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();
|
|
}
|
|
}
|