import type { Entity, FilteredEntity } from "./types"; import type EntityManager from "./entity-manager"; import type ECSpresso from "./ecspresso"; import type { WorldConfig } from "./type-utils"; type ComponentKey = keyof Cfg['components']; export type ReactiveQueryEnterContext = ComponentKey, WithoutComponents extends ComponentKey = never, OptionalComponents extends ComponentKey = never> = { entity: FilteredEntity; ecs: ECSpresso; }; export type ReactiveQueryExitContext = { entityId: number; ecs: ECSpresso; }; /** * Definition for a reactive query with enter/exit callbacks */ export interface ReactiveQueryDefinition = ComponentKey, WithoutComponents extends ComponentKey = never, OptionalComponents extends ComponentKey = never> { /** Components the entity must have */ with: ReadonlyArray; /** Components the entity must not have */ without?: ReadonlyArray; /** Components to include in the entity type but not require for matching */ optional?: ReadonlyArray; /** Components the entity's direct parent must have */ parentHas?: ReadonlyArray>; /** Called when an entity starts matching the query */ onEnter?: (ctx: ReactiveQueryEnterContext) => void; /** Called when an entity stops matching the query (receives just the ID since entity may be gone) */ onExit?: (ctx: ReactiveQueryExitContext) => void; } /** * Manages reactive queries that trigger callbacks when entities enter/exit query matches */ export default class ReactiveQueryManager { private queries; private entityManager; private ecs; /** Whether any registered query uses parentHas */ private _hasParentHasQueries; constructor(entityManager: EntityManager, ecs: ECSpresso); /** * Whether any registered reactive query uses parentHas filters */ get hasParentHasQueries(): boolean; /** * Add a reactive query * @param name Unique name for the query * @param definition Query definition with callbacks */ addQuery, WithoutComponents extends ComponentKey = never, OptionalComponents extends ComponentKey = never>(name: QueryNames, definition: ReactiveQueryDefinition): void; /** * Remove a reactive query * @param name Name of the query to remove * @returns true if the query existed and was removed */ removeQuery(name: QueryNames): boolean; /** @internal Release query definitions and their entity tracking. */ clear(): void; private entityMatchesQuery; private _fireEnter; private _fireExit; /** * Apply enter/exit transitions for a single query against an entity. * Fires onEnter when entity starts matching, onExit when it stops. */ private _applyQueryTransition; /** * Called when a component is added to an entity * Checks all queries for potential enter/exit events */ onComponentAdded(entity: Entity, _componentName: keyof Cfg['components']): void; /** * Called when a component is removed from an entity * Checks all queries for potential enter/exit events */ onComponentRemoved(entity: Entity, _componentName: keyof Cfg['components']): void; /** * Called when an entity is removed * Triggers onExit for all queries the entity was matching */ onEntityRemoved(entityId: number): void; /** * Recheck an entity against all queries (used after batch component additions) * Fires enter/exit callbacks as appropriate based on current state vs tracked state */ recheckEntity(entity: Entity): void; /** * Recheck an entity and its children against all queries. * Used after component mutations to handle both the entity's own queries * and parentHas queries on its children. */ recheckEntityAndChildren(entity: Entity): void; /** * Recheck all children of a parent entity against parentHas queries. * Called when a component is added/removed from a parent entity. */ private _recheckChildren; /** * Recalculate the _hasParentHasQueries flag from all registered queries */ private _recalcParentHasFlag; } export {};