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(); } }