ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
102 lines (101 loc) • 4.77 kB
TypeScript
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<Cfg extends WorldConfig> = keyof Cfg['components'];
export type ReactiveQueryEnterContext<Cfg extends WorldConfig, WithComponents extends ComponentKey<Cfg> = ComponentKey<Cfg>, WithoutComponents extends ComponentKey<Cfg> = never, OptionalComponents extends ComponentKey<Cfg> = never> = {
entity: FilteredEntity<Cfg['components'], WithComponents, WithoutComponents, OptionalComponents>;
ecs: ECSpresso<Cfg>;
};
export type ReactiveQueryExitContext<Cfg extends WorldConfig> = {
entityId: number;
ecs: ECSpresso<Cfg>;
};
/**
* Definition for a reactive query with enter/exit callbacks
*/
export interface ReactiveQueryDefinition<Cfg extends WorldConfig, WithComponents extends ComponentKey<Cfg> = ComponentKey<Cfg>, WithoutComponents extends ComponentKey<Cfg> = never, OptionalComponents extends ComponentKey<Cfg> = 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<ComponentKey<Cfg>>;
/** Called when an entity starts matching the query */
onEnter?: (ctx: ReactiveQueryEnterContext<Cfg, WithComponents, WithoutComponents, OptionalComponents>) => void;
/** Called when an entity stops matching the query (receives just the ID since entity may be gone) */
onExit?: (ctx: ReactiveQueryExitContext<Cfg>) => void;
}
/**
* Manages reactive queries that trigger callbacks when entities enter/exit query matches
*/
export default class ReactiveQueryManager<Cfg extends WorldConfig, QueryNames extends string = string> {
private queries;
private entityManager;
private ecs;
/** Whether any registered query uses parentHas */
private _hasParentHasQueries;
constructor(entityManager: EntityManager<Cfg['components']>, ecs: ECSpresso<Cfg>);
/**
* 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 ComponentKey<Cfg>, WithoutComponents extends ComponentKey<Cfg> = never, OptionalComponents extends ComponentKey<Cfg> = never>(name: QueryNames, definition: ReactiveQueryDefinition<Cfg, 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;
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<Cfg['components']>, _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<Cfg['components']>, _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<Cfg['components']>): 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<Cfg['components']>): 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 {};