import type { EntityPosition } from "./entityStore.js"; import { type ColliderPurpose, type EntityColliderSet } from "./colliders.js"; import type { SceneObject } from "./objectStore.js"; import type { WorldSolids } from "../world/worldSolids.js"; export type SceneRaycastTargetKind = "entity" | "object" | "terrain" | "wall"; export interface SceneRaycastHit { targetKind: SceneRaycastTargetKind; instanceId: string; catalogId?: string; colliderName: string; purpose: ColliderPurpose; damageEligible: boolean; blocks: boolean; distance: number; point: EntityPosition; normal: EntityPosition; } export interface SceneRaycastFilter { entities?: boolean; objects?: boolean; terrain?: boolean; /** Wall segments and indexed world solids. */ walls?: boolean; } export interface SceneRaycastInput { /** Finite world coordinates. */ origin: EntityPosition; /** Finite direction and magnitude, normalized by the query. Zero direction returns no hits. */ direction: EntityPosition; /** Finite, nonnegative segment length in world units. */ maxDistance: number; excludeInstanceIds?: ReadonlySet | readonly string[]; filter?: SceneRaycastFilter; /** When set, only hits satisfying the predicate are kept (applied after geometry). */ accept?: (hit: SceneRaycastHit) => boolean; } export interface SceneEntityQuerySource { list(): readonly { id: string; position: EntityPosition; rotationY: number; name?: string; }[]; collidersOf?(instanceId: string): EntityColliderSet | null | undefined; inRadius?(center: EntityPosition, radius: number): readonly string[]; get?(instanceId: string): { id: string; position: EntityPosition; rotationY: number; name?: string; } | null; } export interface SceneObjectQuerySource { list(): readonly SceneObject[]; inBox?(min: EntityPosition, max: EntityPosition): readonly SceneObject[]; collidersOf?(instanceId: string): EntityColliderSet | null | undefined; halfExtentsOf?(catalogId: string): EntityPosition | null | undefined; } export interface TerrainRaycastSource { sampleHeight(x: number, z: number): number; } export interface WallSegment { id: string; /** Segment endpoints on the XZ plane; extruded by halfHeight along Y around yCenter. */ a: readonly [number, number]; b: readonly [number, number]; yCenter?: number; halfHeight?: number; thickness?: number; } export interface SceneRaycastDeps { entities?: SceneEntityQuerySource; objects?: SceneObjectQuerySource; terrain?: TerrainRaycastSource; walls?: readonly WallSegment[]; /** Indexed geometry returns physical, non-damage wall hits with instance id `world-solid`. */ solids?: Pick; } export interface SceneRaycastApi { raycast(input: SceneRaycastInput): SceneRaycastHit | null; raycastAll(input: SceneRaycastInput): SceneRaycastHit[]; } /** First impact: nearest hit that blocks, or nearest hit if none block. */ export declare function firstImpact(hits: readonly SceneRaycastHit[]): SceneRaycastHit | null; /** Hits up to and including the first blocking collider (damage hitboxes before a wall stay). */ export declare function hitsUntilBlocked(hits: readonly SceneRaycastHit[]): SceneRaycastHit[]; export declare function createSceneRaycast(deps: SceneRaycastDeps): SceneRaycastApi;