86 lines
2.6 KiB
JavaScript
86 lines
2.6 KiB
JavaScript
/**
|
||
* Bodenmarkierung für verzögerte Einschläge (Orbital-Befehle, Mörser).
|
||
*
|
||
* Die Markierung zeigt den tatsächlichen Wirkungsradius und füllt sich bis zum
|
||
* Einschlag – so kann der Spieler Gefahr erkennen und ausweichen.
|
||
* @module systems/GroundMarker
|
||
*/
|
||
|
||
import { GAME } from '../config.js';
|
||
import { UI } from '../ui/theme.js';
|
||
|
||
export class GroundMarker {
|
||
/**
|
||
* @param {Phaser.Scene} scene
|
||
* @param {number} x
|
||
* @param {number} y
|
||
* @param {number} radius
|
||
* @param {number} color
|
||
* @param {number} startAt Spielzeit bei Erstellung.
|
||
* @param {number} impactAt Spielzeit des Einschlags.
|
||
* @param {{label?: string, beam?: boolean}} [options]
|
||
*/
|
||
constructor(scene, x, y, radius, color, startAt, impactAt, options = {}) {
|
||
this.scene = scene;
|
||
this.x = x;
|
||
this.y = y;
|
||
this.radius = radius;
|
||
this.color = color;
|
||
this.startAt = startAt;
|
||
this.impactAt = impactAt;
|
||
this.beam = options.beam ?? false;
|
||
this.graphics = scene.add.graphics().setDepth(GAME.depth.markers);
|
||
this.label = options.label
|
||
? scene.add
|
||
.text(x, y, '', {
|
||
fontFamily: UI.uiFont,
|
||
fontStyle: 'bold',
|
||
fontSize: '16px',
|
||
color: '#f4ecd8',
|
||
stroke: '#0d1321',
|
||
strokeThickness: 4,
|
||
})
|
||
.setOrigin(0.5)
|
||
.setDepth(GAME.depth.markers + 1)
|
||
: null;
|
||
this.labelText = options.label ?? '';
|
||
}
|
||
|
||
/**
|
||
* Zeichnet den aktuellen Zustand.
|
||
* @param {number} now
|
||
*/
|
||
update(now) {
|
||
const total = Math.max(1, this.impactAt - this.startAt);
|
||
const progress = Math.min(1, Math.max(0, (now - this.startAt) / total));
|
||
// Puls wird zum Einschlag hin schneller – intuitive Warnung ohne Text.
|
||
const pulse = 0.5 + 0.5 * Math.sin(now / (120 - 80 * progress));
|
||
const g = this.graphics;
|
||
g.clear();
|
||
g.fillStyle(this.color, 0.12 + 0.1 * pulse);
|
||
g.fillCircle(this.x, this.y, this.radius);
|
||
g.fillStyle(this.color, 0.35);
|
||
g.fillCircle(this.x, this.y, this.radius * progress);
|
||
g.lineStyle(3, this.color, 0.6 + 0.4 * pulse);
|
||
g.strokeCircle(this.x, this.y, this.radius);
|
||
if (this.beam) {
|
||
g.lineStyle(2 + 6 * progress, this.color, 0.25 + 0.5 * progress);
|
||
g.lineBetween(this.x, this.y - 900, this.x, this.y);
|
||
}
|
||
if (this.label) {
|
||
const seconds = Math.max(0, (this.impactAt - now) / 1000).toFixed(1).replace('.', ',');
|
||
this.label.setText(`${this.labelText} ${seconds}`);
|
||
}
|
||
}
|
||
|
||
/** @param {number} now @returns {boolean} */
|
||
isDue(now) {
|
||
return now >= this.impactAt;
|
||
}
|
||
|
||
destroy() {
|
||
this.graphics.destroy();
|
||
this.label?.destroy();
|
||
}
|
||
}
|