import { type RandomSeed } from "../random/rng.js"; /** A world-space `[x, z]` used for the heat system's spawn ring and witness proximity checks. */ export type HeatPoint = readonly [number, number]; /** One escalation tier — the heat threshold it begins at and the pursuer count it wants active. */ export interface HeatLevelDef { /** Ascending level number (1, 2, 3, ...); level 0 is implicit below the first threshold. */ level: number; /** Cumulative heat at which this level begins. */ threshold: number; /** Active pursuers this level wants once reached. */ pursuerBudget: number; } /** Tuning for {@link createHeatState}/{@link advanceHeat} — levels, decay, and pursuit-spawn ring. */ export interface HeatConfig { /** Sorted ascending by `threshold`. */ levels: readonly HeatLevelDef[]; maxHeat?: number; /** Heat bled per second once decay is unblocked. */ decayPerSecond: number; /** Seconds after the last witnessed gain before decay starts (a beat of "still hot" after the last crime). */ decayDelaySeconds?: number; /** Seconds at level 0 with pursuers still alive before {@link HeatStep.standDown} fires. */ standDownSeconds?: number; /** `[min, max]` radius for {@link HeatStep.spawnPoints}; omit to skip ring generation. */ spawnRingRadius?: readonly [number, number]; seed?: number; } /** Serializable heat-system state — round-trips through `createHeatState`/`advanceHeat` each tick. */ export interface HeatState { heat: number; level: number; sinceGain: number; standDownElapsed: number; /** Opaque PRNG cursor — round-trip it as part of the state, never read or set it directly. */ rng: RandomSeed; } /** One crime tick's contribution — only `witnessed` gains raise heat (unseen crimes are free, GTA-style). */ export interface HeatGain { amount: number; witnessed: boolean; } /** Per-tick world facts {@link advanceHeat} needs but can't derive itself — witness proximity, pursuer count, origin. */ export interface HeatTickContext { /** True blocks decay this tick (a witness/pursuer is still close enough to keep the heat hot). */ nearWitness: boolean; activePursuers: number; around: HeatPoint; } /** One `advanceHeat` tick's result — updated state plus what the caller should spawn/despawn. */ export interface HeatStep { state: HeatState; levelChanged: boolean; pursuerBudget: number; /** `max(0, pursuerBudget - activePursuers)` — how many more pursuers to spawn this tick. */ wantSpawns: number; /** `wantSpawns` points on the configured ring around `ctx.around`; empty without `spawnRingRadius`. */ spawnPoints: HeatPoint[]; /** True exactly the tick pursuit should fully clear — despawn every remaining pursuer. */ standDown: boolean; } /** * Escalating crime/heat/pursuit state machine (#533.4) — the star-meter every open-world crime sandbox * hand-rolls: witness-scoped gain (unseen crimes don't raise heat), proximity-gated decay (still hot * while a witness or pursuer is close), tiered pursuer budgets, ring-shaped spawn points around the * player, and a stand-down countdown once heat clears so pursuit doesn't vanish instantly. Pure and * seeded like `ai/spawnDirector` — the caller owns spawning/despawning the actual entities. * @internal */ export declare function createHeatState(config: HeatConfig): HeatState; /** * Advances {@link HeatState} by one tick: sums this tick's witnessed gains, bleeds heat once clear of * witnesses past `decayDelaySeconds`, resolves the current {@link HeatLevelDef}, and reports how many * pursuers to spawn (with ring points) or whether to stand pursuit down entirely. * @internal */ export declare function advanceHeat(config: HeatConfig, state: HeatState, dt: number, gains: readonly HeatGain[], ctx: HeatTickContext): HeatStep;