slop game oder so
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Abwurfobjekte der Orbital-Befehle: Verteidigungsgeschütz und Nachschubkapsel.
|
||||
* @module entities/Deployables
|
||||
*/
|
||||
|
||||
import { GAME } from '../config.js';
|
||||
import { angleTo, distance } from '../logic/math.js';
|
||||
import { TEXTURE_KEYS } from '../systems/Placeholders.js';
|
||||
|
||||
/** Letzte Sekunden, in denen ein auslaufendes Objekt blinkt. */
|
||||
const EXPIRY_WARNING_MS = 3000;
|
||||
|
||||
/**
|
||||
* Geschütz »Wachsamer Nachbar«: zielt automatisch auf den nächsten Gegner in Reichweite.
|
||||
*/
|
||||
export class Turret extends Phaser.GameObjects.Sprite {
|
||||
/**
|
||||
* @param {Phaser.Scene} scene
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {{durationMs: number, fireIntervalMs: number, range: number, bulletDamage: number, bulletSpeed: number}} cfg
|
||||
* @param {number} now
|
||||
*/
|
||||
constructor(scene, x, y, cfg, now) {
|
||||
super(scene, x, y, TEXTURE_KEYS.turret);
|
||||
scene.add.existing(this);
|
||||
this.setDepth(GAME.depth.pickups);
|
||||
this.cfg = cfg;
|
||||
this.expiresAt = now + cfg.durationMs;
|
||||
this.nextShotAt = now;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} now
|
||||
* @param {Iterable<import('./Enemy.js').Enemy>} enemies
|
||||
* @param {(x: number, y: number, angle: number) => void} fire
|
||||
* @returns {boolean} false, wenn die Einsatzzeit abgelaufen ist.
|
||||
*/
|
||||
step(now, enemies, fire) {
|
||||
if (now >= this.expiresAt) return false;
|
||||
let target = null;
|
||||
let best = this.cfg.range;
|
||||
for (const enemy of enemies) {
|
||||
if (!enemy.alive) continue;
|
||||
const d = distance(this, enemy);
|
||||
if (d < best) {
|
||||
best = d;
|
||||
target = enemy;
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
const angle = angleTo(this, target);
|
||||
this.setRotation(angle);
|
||||
if (now >= this.nextShotAt) {
|
||||
this.nextShotAt = now + this.cfg.fireIntervalMs;
|
||||
fire(this.x + Math.cos(angle) * 24, this.y + Math.sin(angle) * 24, angle);
|
||||
}
|
||||
}
|
||||
// Kurz vor Ablauf blinken, damit der Spieler nicht überrascht wird.
|
||||
const left = this.expiresAt - now;
|
||||
this.setAlpha(left < EXPIRY_WARNING_MS && Math.floor(now / 150) % 2 === 0 ? 0.4 : 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nachschubkapsel »Fürsorgepaket«: Beim Berühren gibt es Leben und Munition.
|
||||
*/
|
||||
export class SupplyPod extends Phaser.Physics.Arcade.Sprite {
|
||||
/**
|
||||
* @param {Phaser.Scene} scene
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {{lifetimeMs: number, pickupRadius: number, heal: number}} cfg
|
||||
* @param {number} now
|
||||
*/
|
||||
constructor(scene, x, y, cfg, now) {
|
||||
super(scene, x, y, TEXTURE_KEYS.supplyPod);
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this);
|
||||
this.setDepth(GAME.depth.pickups);
|
||||
this.cfg = cfg;
|
||||
this.expiresAt = now + cfg.lifetimeMs;
|
||||
this.collected = false;
|
||||
}
|
||||
|
||||
/** Nach dem Einfügen in die Gruppe: runden Aufnahmebereich setzen. */
|
||||
initBody() {
|
||||
const r = this.cfg.pickupRadius;
|
||||
this.body.setCircle(r, this.width / 2 - r, this.height / 2 - r);
|
||||
// Liegt fest am Boden: kein Wegschieben durch Physik.
|
||||
this.body.setImmovable(true);
|
||||
this.body.moves = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} now
|
||||
* @returns {boolean} false, wenn die Kapsel abgelaufen ist.
|
||||
*/
|
||||
tick(now) {
|
||||
const left = this.expiresAt - now;
|
||||
if (left <= 0) return false;
|
||||
this.setAlpha(left < EXPIRY_WARNING_MS && Math.floor(now / 150) % 2 === 0 ? 0.4 : 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user