import type { NavGrid, NavPoint } from "../nav/navGrid.js"; /** * Deterministic 2D offset uniformly within a disc of `radius`, stable per `id` — so a crowd converging * on one target (a flank point, a rally banner, a boss) fans out to distinct spots instead of all * stacking on the same coordinate. Same `id` always yields the same offset, no per-entity state to store. * @internal */ export declare function spreadOffset(id: string, radius: number): readonly [number, number]; export interface FlowFieldOptions { clearance?: number; congestion?: (col: number, row: number) => number; } export interface FlowField { readonly cols: number; readonly rows: number; cost(col: number, row: number): number; reachable(point: NavPoint): boolean; direction(point: NavPoint): NavPoint; next(point: NavPoint): NavPoint | null; } /** @internal */ export declare function computeFlowField(grid: NavGrid, goals: readonly NavPoint[], options?: FlowFieldOptions): FlowField; export interface CrowdField { enter(point: NavPoint): void; leave(point: NavPoint): void; count(point: NavPoint): number; cellCount(col: number, row: number): number; penalty(weight?: number): (col: number, row: number) => number; reset(): void; } /** @internal */ export declare function createCrowdField(grid: NavGrid): CrowdField; export interface Poi { id: string; point: NavPoint; appeal?: number; capacity?: number; } export interface SelectPoiOptions { roll: number; occupancy?: (id: string) => number; distanceBias?: number; distance?: (from: NavPoint, poi: Poi) => number; } /** @internal */ export declare function selectPoi(pois: readonly Poi[], from: NavPoint, options: SelectPoiOptions): Poi | null; /** A many-agent visitor's current step of the seek→travel→arrive→dwell→depart loop. */ export type VisitorPhase = "seeking" | "traveling" | "dwelling" | "departing" | "done"; /** One tick's result from a {@link VisitorLoop}: current phase, where to steer, and which POI it concerns. */ export interface VisitorStep { phase: VisitorPhase; /** Where to steer this tick (feed `scene.entity.moveToward`/`moveTowardCommit`), or `null` once `done` or stuck `seeking` with no eligible POI. */ target: NavPoint | null; poiId: string | null; } /** Config for {@link createVisitorLoop}: the POI catalog, dwell duration, and exit point every agent shares. */ export interface VisitorLoopOptions { pois: readonly Poi[]; /** How long (ms, sim time) a visitor lingers once it arrives at a POI. */ dwellMs: (poi: Poi) => number; /** Where a visitor heads once its dwell ends — a fixed exit/despawn point, or one resolved per agent (multiple gates). */ exitPoint: NavPoint | ((agentId: string) => NavPoint); /** Distance under which "traveling"/"departing" counts as arrived. Default 1. */ arriveRadius?: number; occupancy?: (poiId: string) => number; distanceBias?: number; distance?: (from: NavPoint, poi: Poi) => number; /** Fires once, on the traveling→dwelling transition (e.g. `crowdField.enter(poi.point)`). */ onArrive?: (agentId: string, poiId: string) => void; /** Fires once, on the dwelling→departing transition (e.g. `crowdField.leave(poi.point)`). */ onDepart?: (agentId: string, poiId: string) => void; } /** Handle returned by {@link createVisitorLoop}: per-agent seek/travel/dwell/depart state machine. */ export interface VisitorLoop { /** Registers `agentId` in the `"seeking"` phase; `tick` also auto-spawns on first call. */ spawn(agentId: string): VisitorStep; /** Advances one agent by `dtMs` from its current `position`; `roll()` feeds `selectPoi`'s weighted pick. */ tick(agentId: string, position: NavPoint, dtMs: number, roll: () => number): VisitorStep; get(agentId: string): VisitorStep | null; remove(agentId: string): void; clear(): void; } /** * A many-agent seek→travel→arrive→dwell→depart loop over weighted points of interest (park visitors, * shoppers, tavern patrons) — `ai/crowd`'s `selectPoi` covers only the weighted-pick step; this owns * the per-agent phase machine around it so the caller only supplies positions and drives movement. * * @capability visitor-loop many-agent seek/travel/dwell/depart state machine over weighted POIs * @internal */ export declare function createVisitorLoop(options: VisitorLoopOptions): VisitorLoop;