import { Cooldown } from '@rpg/engine';
import { PLAYER_COMBAT } from './stats';
/**
* Боевое состояние героя: HP, неуязвимость, кулдауны, заряд резонанса.
* Чистая логика (без Pixi) — тестируется в Vitest.
*/
export class PlayerCombat {
hp: number;
/** Секунды удержания атаки (заряд низкого резонанса). */
chargeTime = 0;
private invulnTimer = 0;
/** Кулдаун короткого удара. */
readonly attackCd = new Cooldown(PLAYER_COMBAT.coneCooldown);
/** Кулдаун низкого резонанса. */
readonly resonanceCd = new Cooldown(PLAYER_COMBAT.resonanceCooldown);
constructor(hp: number = PLAYER_COMBAT.maxHp) {
this.hp = Math.min(hp, PLAYER_COMBAT.maxHp);
}
update(dt: number): void {
if (this.invulnTimer > 0) this.invulnTimer = Math.max(0, this.invulnTimer - dt);
this.attackCd.update(dt);
this.resonanceCd.update(dt);
if (this.isCharging()) this.chargeTime += dt;
}
/** Атака удерживается (Space зажат) — копим заряд резонанса. */
startCharge(): void {
if (this.chargeTime === 0) this.chargeTime = 0.0001; // старт отсчёта
}
isCharging(): boolean {
return this.chargeTime > 0;
}
/**
* Отпустил: если зарядился до резонанса и кулдаун готов — вернуть 'resonance',
* иначе — обычный удар (если готов). Кулдауны триггерятся здесь.
*/
release(): 'resonance' | 'attack' | null {
const charged = this.chargeTime >= PLAYER_COMBAT.resonanceCharge;
this.chargeTime = 0;
if (charged) {
if (this.resonanceCd.trigger()) return 'resonance';
return null; // резонанс ещё перезаряжается — молча
}
return this.attackCd.trigger() ? 'attack' : null;
}
/**
* Получить урон. Возвращает фактически применённый урон (0 — неуязвим).
*/
takeDamage(damage: number): number {
if (this.invulnTimer > 0 || this.hp <= 0) return 0;
const applied = Math.min(damage, this.hp);
this.hp -= applied;
this.invulnTimer = PLAYER_COMBAT.invulnTime;
return applied;
}
/** Неуязвим (мигание спрайта в этот период). */
get invuln(): boolean {
return this.invulnTimer > 0;
}
get dead(): boolean {
return this.hp <= 0;
}
/** Восстановление после смерти (респаун). */
revive(): void {
this.hp = PLAYER_COMBAT.maxHp;
this.invulnTimer = PLAYER_COMBAT.invulnTime;
this.chargeTime = 0;
this.attackCd.reset();
this.resonanceCd.reset();
}
}