import type { PerceptionService } from "../../sensor/perception.js"; import type { Vec3 } from "../../world/geometry.js"; /** A unit that could hear a noise: its id and where it stands when the noise lands. */ export interface SoundHunter { id: string; position: Vec3; } /** One noise in the world, as the game produces it. */ export interface NoiseEvent { /** Who made the noise — the hearing owner records the memory against this id. */ sourceId: string; position: Vec3; /** Seconds on the game clock, the same clock the perception service is given. */ at: number; /** Multiplies `hearingRange` exactly as `PerceptionStimulus.loudness` does. Default `1`. */ loudness?: number; } /** A hunter still working a noise, as the squad layer reads it this tick. */ export interface Investigation { hunterId: string; sourceId: string; /** Where the noise came from: the hearing owner's remembered position. */ position: Vec3; /** Confidence left in the memory, `0..1`; it falls as the memory decays and ends the hunt at 0. */ confidence: number; /** Seconds since the noise landed. */ ageSeconds: number; } /** One noise and the hunters sent to it. Serializable, so a hunt survives a save. */ export interface SoundHuntEntry { noise: NoiseEvent; hunters: string[]; } /** Serializable book-keeping: which noises are still being worked, and by whom. */ export interface SoundHuntSnapshot { entries: SoundHuntEntry[]; } /** Everything the hunt reads and its bounds. */ export interface SoundHuntConfig { perception: PerceptionService; /** Candidates near a noise, bounded by the caller's roster. Called once per noise, not per tick. */ hunters: (position: Vec3, hearingRange: number) => readonly SoundHunter[]; /** The same hearing range given to `createPerception`. */ hearingRange: number; /** Most hunters one noise sends. Default `3`. */ maxInvestigators?: number; /** Confidence at or below which a hunter gives up and returns to its post. Default `0.15`. */ giveUpConfidence?: number; /** Longest one hunt survives, seconds. Default `20`. */ maxSeconds?: number; /** Most noises remembered at once; the oldest is dropped first. Default `16`. */ maxNoises?: number; } /** Sound hunting: raise a noise, get the bounded set of enemies that go and look. */ export interface SoundHunt { /** * Record one noise in the hearing owner's stimulus stream and admit the nearest hunters to it. * Returns the admitted hunter ids so the caller can issue its own move order now; the decay and * the give-up are read back through {@link SoundHunt.investigations}. */ noise(event: NoiseEvent): readonly string[]; /** Hunters still working their noise at `nowMs`, with the memory's remaining confidence. */ investigations(nowMs: number): Investigation[]; snapshot(): SoundHuntSnapshot; restore(snapshot: SoundHuntSnapshot): void; } /** * Make enemies investigate a noise, reusing the hearing owner. * * The noise goes into `PerceptionService.pushStimulus` as a `sound` stimulus, so the same range, * loudness and linear memory decay that every other hearing consumer gets apply here; this layer * adds only the two things a squad needs: a *bound* on how many enemies chase one noise, and a * *give-up* rule, both read back from that memory instead of a second copy of it. A sighting of the * source during the hunt raises the same memory's confidence, so an investigation escalates into a * pursuit without any bookkeeping here. * * @capability sound-hunting send a bounded set of nearby enemies to investigate a noise with decaying memory */ export declare function createSoundHunt(config: SoundHuntConfig): SoundHunt;