import type { FogBounds } from "./fog.js"; /** Numeric code for a cell no group member has seen, or has fully forgotten. */ export declare const VISIBILITY_HIDDEN = 0; /** Numeric code for a cell seen before but not currently in view (last-known). */ export declare const VISIBILITY_REMEMBERED = 1; /** Numeric code for a cell a group currently observes. */ export declare const VISIBILITY_OBSERVED = 2; /** One viewer group's knowledge of a cell: never/forgotten, last-known, or live. */ export type VisibilityState = "hidden" | "remembered" | "observed"; /** * How a cell decays after a group stops observing it — the terrain-memory policy. * `permanent` keeps last-known terrain forever (RTS explored map); `none` drops * it straight back to hidden the moment it leaves view (pure line-of-sight * stealth); `decay` remembers it for `updates` observe cycles, then hides it. * Day/night, height, sensor, and stealth rules are NOT encoded here — the caller * bakes those into which cells it reports as observed each update. */ export type VisibilityMemory = { readonly kind: "permanent"; } | { readonly kind: "none"; } | { readonly kind: "decay"; readonly updates: number; }; /** Construction options for {@link createVisibilityField}. */ export interface VisibilityFieldConfig { /** World-space rectangle the grid covers (reused from `world/fog`). */ readonly bounds: FogBounds; /** World units per grid cell. Must be positive. */ readonly cellSize: number; /** Default memory policy applied to every group. Default `{ kind: "permanent" }`. */ readonly memory?: VisibilityMemory; /** Rehydrate from a previously serialized field (see {@link VisibilityField.toState}). */ readonly restore?: VisibilityFieldState; } /** * The cells that changed state for one group in a single {@link VisibilityField.observe} * call — a pure incremental readout for rendering, minimaps, AI, UI, and * authoritative replication. Never lists unchanged cells, so it stays bounded by * how much the group's view actually moved. */ export interface VisibilityDelta { readonly group: string; /** Cells that entered observation this update. */ readonly observed: readonly number[]; /** Cells that left observation into remembered (last-known) state. */ readonly remembered: readonly number[]; /** Cells that fell out of memory entirely (decay expiry or `none` policy). */ readonly hidden: readonly number[]; } /** Sparse serialized knowledge for one group — only non-hidden cells are stored. */ export interface VisibilityGroupState { /** Currently observed cell indices. */ readonly observed: readonly number[]; /** Remembered cells as `[cellIndex, lastObservedTick]` pairs. */ readonly remembered: readonly (readonly [number, number])[]; /** Monotonic update counter, used to age remembered cells under a decay policy. */ readonly tick: number; /** Per-group memory override, when it differs from the field default. */ readonly memory?: VisibilityMemory; } /** Serializable snapshot of a whole field — JSON round-trips losslessly. */ export interface VisibilityFieldState { readonly cols: number; readonly rows: number; readonly minX: number; readonly minZ: number; readonly cellSize: number; readonly memory: VisibilityMemory; readonly groups: Readonly>; } /** Dense per-cell readout for one group, shaped for minimap/overlay drawing. */ export interface VisibilityCells { readonly cols: number; readonly rows: number; readonly minX: number; readonly minZ: number; readonly cellSize: number; /** Row-major `VISIBILITY_*` codes; index = row * cols + col. */ readonly codes: Uint8Array; } /** * A gameplay (fog-of-war) knowledge field: per viewer group, every grid cell is * hidden, remembered, or observed. Distinct from render culling in * `core/visibility` — this models what a team KNOWS, not what a camera can draw. * It generalizes `world/fog`'s single-group reveal-once model to many groups * with configurable terrain memory, and keeps each group's knowledge isolated so * one group's hidden state can never be derived from another's. */ export interface VisibilityField { readonly cols: number; readonly rows: number; cellCount(): number; colOf(x: number): number; rowOf(z: number): number; /** Row-major cell index for a world point, clamped into the grid. */ cellIndex(x: number, z: number): number; /** Register a viewer group, optionally with its own memory policy. Idempotent. */ ensureGroup(group: string, memory?: VisibilityMemory): void; hasGroup(group: string): boolean; groups(): string[]; /** Forget everything for a group and drop it. */ removeGroup(group: string): void; /** * Cell indices whose centers fall within `radius` of a world point (bounded * disc), appended into `out` when supplied. Feed these into {@link observe}; * union several sources' discs before observing to model overlapping observers. */ cellsInRadius(x: number, z: number, radius: number, out?: number[]): number[]; /** * Replace a group's observed set for this update. Cells newly covered become * observed; cells that dropped out become remembered (or hidden under `none`); * decayed memory expires. Work is bounded by the observed + remembered working * sets — it never scans the whole grid. Returns the resulting {@link VisibilityDelta}. */ observe(group: string, cells: Iterable): VisibilityDelta; /** Convenience {@link observe} over a single disc around a source. */ observeCircle(group: string, x: number, z: number, radius: number): VisibilityDelta; stateAtCell(group: string, cell: number): VisibilityState; stateAt(group: string, x: number, z: number): VisibilityState; isObserved(group: string, x: number, z: number): boolean; isRemembered(group: string, x: number, z: number): boolean; /** Terrain disclosure: observed OR remembered (anything the group has ever mapped). */ isKnown(group: string, x: number, z: number): boolean; observedCount(group: string): number; knownCount(group: string): number; /** * Authoritative entity-disclosure filter: keep only entities standing in a cell * the group currently OBSERVES. Remembered and hidden cells never disclose live * entities, so a unit that moved away or was destroyed re-hides. Reads only this * group's state, so it is the seam that stops one group's hidden entities from * leaking into another group's replicated view. */ visibleTo(group: string, entities: readonly T[], positionOf: (entity: T) => { readonly x: number; readonly z: number; }): T[]; /** Dense per-cell codes for a group, for minimap/overlay rendering. */ cells(group: string): VisibilityCells; /** Sparse serializable snapshot; round-trips through {@link createVisibilityField}'s `restore`. */ toState(): VisibilityFieldState; /** Forget one group's knowledge, or every group when `group` is omitted. */ reset(group?: string): void; } /** * Build a per-group gameplay visibility field. First adopters: an RTS/stealth * team whose scout observes terrain (which stays remembered after it leaves) * while enemy units re-hide once no scout observes their cell, and any * authoritative host that must filter hidden entities out of a client's replica. * * @capability visibility-field per-viewer-group observed/remembered/hidden fog-of-war knowledge with terrain memory and authoritative entity disclosure */ export declare function createVisibilityField(config: VisibilityFieldConfig): VisibilityField;