Zum Inhalt

src/engine/modes/combat-director.ts

// CombatDirector — seeded spawn cadence for combat actors. Reads threat each frame,
// spawns/retires the antagonist + CORP support units; the actors self-manage motion +
// fire and emit events on the bus. Cinematic, no damage model.
import { mkRng } from '../engine/rng';
import type { EventBus } from '../events/bus';
import { AntagonistDrone, SupportUnit, type Actor } from './actors';

// Disabled for now (2026-06-05 feedback): the drones read as glitches, not aircraft,
// and the tracers aren't legible ("taking fire" with nothing visible). The whole
// actor/event infrastructure stays; flip this on once actors look like real craft.
const COMBAT_ACTORS_ENABLED = false;

export interface ActorHost {
  add(a: Actor): void;
  /** current accent / enemy colour hex (actors build their own materials from these). */
  accentHex(): number;
  enemyHex(): number;
}

export class CombatDirector {
  private rng: () => number;
  private antagonist: Actor | null = null;
  private support: Actor[] = [];
  private nextSupportT = 10;

  constructor(private seed: number, private bus: EventBus, private host: ActorHost) {
    this.rng = mkRng((seed ^ 0xc0ffee) >>> 0);
  }

  update(t: number, _dt: number, threat: number): void {
    if (!COMBAT_ACTORS_ENABLED) return;
    // antagonist: appears once threat is up; the actor self-retires at threat<0.05.
    if (threat > 0.35 && (!this.antagonist || !this.antagonist.alive)) {
      const a = new AntagonistDrone(this.host.enemyHex(), this.seed,
        () => this.bus.emit({ kind: 'incomingFire', intensity: threat }));
      this.antagonist = a;
      this.host.add(a);
    }
    // CORP support: seeded beats during high threat, ≤2 concurrent.
    this.support = this.support.filter((s) => s.alive);
    if (threat > 0.4 && this.support.length < 2 && t > this.nextSupportT) {
      this.nextSupportT = t + 9 + this.rng() * 13;
      if (this.rng() < 0.6) {
        const crash = this.rng() < 0.5;
        const s = new SupportUnit(this.host.accentHex(), this.seed + Math.floor(t),
          crash, () => this.bus.emit({ kind: 'unitCrash' }));
        this.support.push(s);
        this.host.add(s);
        this.bus.emit({ kind: 'unitArrive' });
      }
    }
  }
}