overrun/js/systems/BonusManager.js

120 lines
3.3 KiB
JavaScript

const BONUS_LIFETIME_MS = 15000;
const BLINK_START_MS = 10000; // blink when < 5s left (at 10s elapsed of 15s)
const COOLDOWN_PICKUP = 10000;
const COOLDOWN_EXPIRE = 10000;
const FIRST_SPAWN_MS = 20000;
const PICKUP_RADIUS = 32;
const BONUS_TYPES = ['medpack', 'shotgun', 'rocket', 'fourway', 'stimulant'];
const FRAME_INDEX = { medpack: 0, shotgun: 1, rocket: 2, fourway: 3, stimulant: 4 };
const GLOW_COLOR = { medpack: 0x44ff44, shotgun: 0xff9900, rocket: 0xff3300, fourway: 0x00ffff, stimulant: 0xaa44ff };
export class BonusManager {
constructor(scene, player) {
this._scene = scene;
this._player = player;
this._countdown = FIRST_SPAWN_MS;
this._active = null; // { sprite, glow, glowTween, blinkTween, type, elapsed }
}
update(delta) {
if (!this._active) {
if (this._player.weaponMode === 'default') {
this._countdown -= delta;
if (this._countdown <= 0) this._spawn();
}
return;
}
const a = this._active;
a.elapsed += delta;
// Pickup check
const dist = Phaser.Math.Distance.Between(
a.sprite.x, a.sprite.y, this._player.x, this._player.y
);
if (dist < PICKUP_RADIUS) {
this._player.applyBonus(a.type);
this._clear();
this._countdown = COOLDOWN_PICKUP;
return;
}
// Start blinking for last 5 seconds
if (a.elapsed >= BLINK_START_MS && !a.blinkTween) {
a.blinkTween = this._scene.tweens.add({
targets: [a.sprite, a.glow],
alpha: 0,
duration: 200,
yoyo: true,
repeat: -1,
});
}
// Expiry
if (a.elapsed >= BONUS_LIFETIME_MS) {
this._clear();
this._countdown = COOLDOWN_EXPIRE;
}
}
_spawn() {
const type = BONUS_TYPES[Phaser.Math.Between(0, BONUS_TYPES.length - 1)];
const cell = this._findSpawnCell();
if (!cell) {
// No valid cell found — retry in 2 seconds
this._countdown = 2000;
return;
}
const { cx, cy } = cell;
const glow = this._scene.add.circle(cx, cy, 24, GLOW_COLOR[type], 0.4).setDepth(4);
const glowTween = this._scene.tweens.add({
targets: glow,
scaleX: 1.4, scaleY: 1.4,
alpha: 0.1,
duration: 700,
yoyo: true,
repeat: -1,
});
const sprite = this._scene.add.image(cx, cy, 'bonus', FRAME_INDEX[type]).setDepth(5);
this._active = { sprite, glow, glowTween, blinkTween: null, type, elapsed: 0 };
}
_findSpawnCell() {
const W = this._scene.scale.width;
const H = this._scene.scale.height;
const COLS = Math.floor(W / 80);
const ROWS = Math.floor(H / 80);
const candidates = [];
for (let col = 1; col < COLS - 1; col++) {
for (let row = 1; row < ROWS - 1; row++) {
const cx = col * 80 + 40;
const cy = row * 80 + 40;
if (this._scene.barrierManager?.isPointBlocked(cx, cy)) continue;
if (Phaser.Math.Distance.Between(cx, cy, this._player.x, this._player.y) < 300) continue;
candidates.push({ cx, cy });
}
}
return candidates.length ? Phaser.Utils.Array.GetRandom(candidates) : null;
}
_clear() {
if (!this._active) return;
const a = this._active;
a.blinkTween?.stop();
a.glowTween?.stop();
a.sprite?.destroy();
a.glow?.destroy();
this._active = null;
}
destroy() {
this._clear();
}
}