UNPKG

ecspresso

Version:

A minimal Entity-Component-System library for typescript and javascript.

87 lines (86 loc) 4.07 kB
import type { Entity, FilteredEntity } from "./types"; import type EntityManager from "./entity-manager"; /** * Definition for a reactive query with enter/exit callbacks */ export interface ReactiveQueryDefinition<ComponentTypes extends Record<string, any>, WithComponents extends keyof ComponentTypes = keyof ComponentTypes, WithoutComponents extends keyof ComponentTypes = never, OptionalComponents extends keyof ComponentTypes = never> { /** Components the entity must have */ with: ReadonlyArray<WithComponents>; /** Components the entity must not have */ without?: ReadonlyArray<WithoutComponents>; /** Components to include in the entity type but not require for matching */ optional?: ReadonlyArray<OptionalComponents>; /** Components the entity's direct parent must have */ parentHas?: ReadonlyArray<keyof ComponentTypes>; /** Called when an entity starts matching the query */ onEnter?: (entity: FilteredEntity<ComponentTypes, WithComponents, WithoutComponents, OptionalComponents>) => void; /** Called when an entity stops matching the query (receives just the ID since entity may be gone) */ onExit?: (entityId: number) => void; } /** * Manages reactive queries that trigger callbacks when entities enter/exit query matches */ export default class ReactiveQueryManager<ComponentTypes extends Record<string, any>, QueryNames extends string = string> { private queries; private entityManager; /** Whether any registered query uses parentHas */ private _hasParentHasQueries; constructor(entityManager: EntityManager<ComponentTypes>); /** * 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<WithComponents extends keyof ComponentTypes, WithoutComponents extends keyof ComponentTypes = never, OptionalComponents extends keyof ComponentTypes = never>(name: QueryNames, definition: ReactiveQueryDefinition<ComponentTypes, WithComponents, WithoutComponents, OptionalComponents>): 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; private entityMatchesQuery; /** * 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<ComponentTypes>, _componentName: keyof ComponentTypes): void; /** * Called when a component is removed from an entity * Checks all queries for potential enter/exit events */ onComponentRemoved(entity: Entity<ComponentTypes>, _componentName: keyof ComponentTypes): 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<ComponentTypes>): 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<ComponentTypes>): 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; }