ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
87 lines (86 loc) • 4.07 kB
TypeScript
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;
}