/** A world position used by perception, in `[x, y, z]` order. */ export type PerceptionPosition = readonly [number, number, number]; /** The kinds of stimulus the perception service can retain. */ export type PerceptionStimulusKind = "sight" | "sound" | "damage"; /** An event that can seed an observer's memory. */ export interface PerceptionStimulus { kind: PerceptionStimulusKind; sourceId: string; position: PerceptionPosition; /** Multiplies `hearingRange` for this sound: a footstep might be `0.3`, a gunshot `4`. Default `1`. */ loudness?: number; at: number; } /** The observer pose used for a sight check. */ export interface PerceptionObserver { id: string; position: PerceptionPosition; yaw: number; } /** A candidate target considered by a sight check. */ export interface PerceptionCandidate { id: string; position: PerceptionPosition; } /** A remembered target and its linearly decaying confidence. */ export interface PerceptionMemory { targetId: string; /** When the fact was observed, on the same clock `observe` is given; its age is `now − lastSeenAt`. */ lastSeenAt: number; lastKnownPos: PerceptionPosition; /** 1 at the moment of the sighting, falling to 0 across `memorySeconds`. */ confidence: number; /** Whose senses produced the fact: this observer's id for its own sighting, a squadmate's for a shared one. */ sourceId: string; /** Observe pass that recorded the fact, so a shared sighting is attributable to the tick it arrived. */ sourceTick: number; } /** Configuration for sight, hearing, memory decay, squad sharing and optional occlusion. */ export interface PerceptionConfig { sightRange: number; sightConeDeg: number; hearingRange: number; memorySeconds: number; /** Most stimuli retained at once; the oldest are dropped first. Default 256. */ maxStimuli?: number; /** * Cheap "nothing blocks the segment from `from` to `to`" test, supplied by the game's collision * layer. Only called for pairs already inside range and cone, so the costly test sees the pruned * set. Omit for open ground. */ isSegmentClear?: (from: PerceptionPosition, to: PerceptionPosition) => boolean; /** * The other members of an observer's squad, by id. A sighting one member makes is written into * each mate's memory with the seeing member's id and the pass it happened on. Omit for lone * observers. Keep the returned set small — the roster is the caller's, so the caller bounds it. */ squadMates?: (observerId: string) => Iterable; /** Most sighting shares one pass may write, so a large squad cannot blow the tick budget. Default 64. */ maxSharesPerTick?: number; /** Register a devtools probe under this name so the overlay shows perception work and memory. */ devtoolsName?: string; } /** Counters a perf test or a devtools probe reads. Reset with {@link PerceptionService.resetStats}. */ export interface PerceptionStats { /** Observe passes run. Keeps counting across `resetStats`, since it is also the recorded tick. */ passes: number; /** `(observer, candidate)` pairs examined — one check per pair per pass. */ pairChecks: number; /** Pairs rejected by distance before any cone or occlusion work. */ rangeCulled: number; /** Pairs inside sight range, so they paid the cone test. */ coneTests: number; /** Calls into the injected `isSegmentClear`. */ occlusionChecks: number; /** Pairs that produced the observer's own sighting. */ sightings: number; /** Sound and damage stimuli an observer resolved inside its hearing range. */ heard: number; /** Sightings written into a squadmate's memory. */ shares: number; /** Sightings the per-pass share cap kept from being broadcast. */ shareDropped: number; } /** Serializable state for a perception service. */ export interface PerceptionSnapshot { memories: Array<{ observerId: string; memory: PerceptionMemory; }>; stimuli: Array; nowMs: number; /** Per observer, the last stimulus sequence number it has already judged. Absent in older saves. */ heard?: Record; nextSeq?: number; /** Observe passes recorded, which is also the tick a memory carries. Absent in older saves. */ passes?: number; } /** Stateful perception API with retuning and save/restore support. */ export interface PerceptionService { pushStimulus(stimulus: PerceptionStimulus): void; observe(observer: PerceptionObserver, candidates: readonly PerceptionCandidate[], nowMs: number): void; memory(observerId: string): PerceptionMemory[]; /** One remembered target with its confidence now, or `null` when never seen or already faded. */ memoryOf(observerId: string, targetId: string): PerceptionMemory | null; /** Drop one remembered target, or everything an observer remembers (despawn, reset, disguise). */ forget(observerId: string, targetId?: string): void; stats(): PerceptionStats; /** Zero the work counters, keeping the pass count that memories are stamped with. */ resetStats(): void; retune(patch: Partial>): void; snapshot(): PerceptionSnapshot; restore(snapshot: PerceptionSnapshot): void; /** Drop the devtools probe registered by `devtoolsName`. Safe to call twice. */ dispose(): void; } /** * Creates a deterministic sight, sound, and damage perception service with expiring target memory. * Signals are judged in the order the caller supplies them, never from the wall clock. * * A sighting needs range, cone and a clear line; a sound carries `hearingRange × loudness` and * fades with the distance it crossed, so a distant footstep is inaudible while a near gunshot is * not. Memory is the only record of "seen recently": each fact fades linearly over `memorySeconds` * and is then gone. With `squadMates` injected, a sighting one member makes lands in every mate's * memory stamped with the seeing member and the pass, which is what lets a squad react to one pair * of eyes without each member re-seeing anything. * * @capability perception-service retain sightings, sounds and damage with cone, range, occlusion, decaying memory and shared squad knowledge */ export declare function createPerception(initial: PerceptionConfig): PerceptionService;