import { Mask } from './mask.cjs'; type QueryId = string; declare class Query { id: QueryId; private _aspects; private _includeMask; private _excludeMask; /** * Flat array of entities matched by this query. Deletions tombstone the * slot (set to null) and `compact` repacks during flushQuery, so iteration * order is stable insertion order and the array tolerates concurrent * removals (e.g. an entity destroying itself mid-forEach). */ private _entityList; private _entityListIndex; private _holes; /** * If non-null, parallel arrays of component instances kept in sync with * _entityList so forEachWith can hand component refs to the callback * without an entity.get() per iteration. _boundComponentArrays[k][i] is * the instance of _boundComponents[k] on _entityList[i] (null when the * slot is tombstoned). */ private _boundComponents; private _boundComponentArrays; /** * Used by World.refreshQueriesForEntity to dedupe per-entity work without * allocating a Set on every entity build. World stamps a fresh tick into * this field; queries skip themselves when stamped. */ _visitedTick: number; private currentChangeSet; private changeSets; constructor(aspects: Aspect[]); get nextChangeSetIndex(): 0 | 1; get added(): Entity[]; get removed(): Entity[]; get nextAdded(): Entity[]; get nextRemoved(): Entity[]; get current(): Entity[]; get aspects(): Aspect[]; get includeMask(): Mask; get excludeMask(): Mask; /** * Snapshot of currently-matched entities as a Map. Built on demand from * the internal flat list — held only for backward compat with consumers * that want Map iteration; prefer `current` (Array) or `forEach` for hot * paths. */ get entities(): Map; initializeMasks(): void; /** * True for queries built only from Without aspects (no Has). The world * needs to route entity changes to these queries via a separate path, * because they don't appear in queryRegistry under any component. */ get hasOnlyExclusiveAspects(): boolean; checkIncludeMask: (entity: Entity) => boolean; checkExcludeMask: (entity: Entity) => boolean; /** * Check whether an entity is currently being tracked by this query * @param entity Entity to check * @returns whether the entity is in the query's entity list */ hasEntity(entity: Entity): boolean; /** * Adds an entity to this query's entity track list * @param entity Entity to add */ registerEntity(entity: Entity): void; /** * Remove an entity from this query's entity track list * @param entity Entity to remove */ unregisterEntity(entity: Entity): void; shouldRegisterEntity(entity: Entity): boolean; updateRegistry: (entity: Entity) => void; /** * Iterate the entities currently matching this query. Re-reads list length * each iteration so entities registered mid-forEach are visited (matching * Map.forEach semantics); tombstoned slots from concurrent removals are * skipped. */ forEach(callbackfn: (entity: Entity) => void): void; /** * Repack _entityList (and any bound component arrays) in place so * tombstoned slots don't accumulate. Called from flushQuery — at most once * per world tick — keeping per-iteration null-check overhead bounded. */ private compact; /** * Bind a fixed list of components to this query so forEachWith can deliver * them directly to the callback. Call once before the query starts being * used; subsequent calls throw. Components don't have to overlap with the * query's aspects — but the entities must actually have them, otherwise * the callback receives null in that slot. */ bindComponents(components: ComponentConstructor[]): this; /** * Iterate, handing each entity its bound component instances directly — * no entity.get() per element. Specialized fast paths for 1/2/3 * components cover the typical system-loop shapes; longer tuples fall * through to a generic loop. */ forEachWith(callback: (entity: Entity, ...components: any[]) => void): void; flushQuery: () => void; } type SystemId = string; declare abstract class System { order?: number; id: SystemId; world: World; private _queries; constructor(); initialize(): void; abstract run(delta: number): void; get queries(): Query[]; protected query(aspects: Aspect[]): Query; /** * Convenience for `this.query(aspects).bindComponents(components)` — * returns a Query whose forEachWith hands the listed components straight * to the callback. */ protected queryWith(aspects: Aspect[], components: ComponentConstructor[]): Query; protected spawnEntity(opts?: EntityOpts): EntityBuilder; } type SystemAndProps = { system: T; props: ConstructorParameters; }; declare class World { private systems; private queryRegistry; private allQueries; /** * Queries that have only Without aspects don't appear in queryRegistry * under any component, so we keep them in a flat list and check them * on every entity-component change. */ private exclusiveOnlyQueries; /** * Monotonic tick stamped onto Query._visitedTick during * refreshQueriesForEntity. Lets us dedupe per-entity work across multiple * components without allocating a Set per build. */ private _refreshTick; private entities; private deadEntities; private entitiesToBePurged; private initialized; private onRunCallbacks; private constructor(); static create(): World; destroy(): void; getEntityById(id: EntityId): Entity | undefined; /** * Keeping track of which queries are interested in which components, * in order to reduce the amount of iterations when updating entity lists */ addToQueryRegistry(key: ComponentConstructor, query: Query, registry: Map): void; mapAspects(system: System): void; addSystem(systemClass: T, ...args: ConstructorParameters): this; /** * Adds a callback that is called on each world.run */ onRun: (cb: (delta: number) => void) => void; /** * Run all of the world's systems * @param delta Optional, defaults to 1 */ run: (delta?: number) => void; mapEntity(entity: Entity): void; updateRegistry(component: ComponentConstructor, entity: Entity): void; /** * Refresh every query that cares about any of the given components, exactly * once per query. Used by EntityBuilder.build so that queries spanning * multiple just-added components don't get re-checked per component. */ refreshQueriesForEntity(entity: Entity, components: ComponentConstructor[]): void; spawnEntity(opts?: EntityOpts): EntityBuilder; processGraveyard(): void; markDeadEntity(entity: Entity): void; /** * Used for testing */ getSystem(systemClass: T): InstanceType; } type EntityId = string; type IfEquals = (() => G extends T ? 1 : 2) extends () => G extends U ? 1 : 2 ? Y : N; type KeysOfType = { [K in keyof T]: T[K] extends U ? K : never; }[keyof T]; type RequiredKeys = Exclude>, undefined>; type ExcludeOptionalProps = Pick>; /** * A base class from which all game entities derive, supports adding and removing {@link Component | Components} */ declare class Entity { id: EntityId; name: string; alive: boolean; private world; private _componentMask; private cleanupCallbacks; private components; private currentQueries; constructor(world: World, opts?: EntityOpts); addComponent(component: { new (): T; }, args?: ComponentArgs): void; /** * Internal: add a component without notifying the world's query registry. * Caller MUST follow up with World.refreshQueriesForEntity (or addComponent * for a single component) so queries see the new state. Used by * EntityBuilder.build to dedupe per-query work across many components. */ _addComponent(component: { new (): T; }, args?: ComponentArgs): void; /** * Alias for {@link upsertComponent} */ upsert(component: { new (): T; }, args?: ComponentArgs): void; /** * Add or update a given component * @param component Component to update * @param args Values to set on component */ upsertComponent(component: { new (): T; }, args?: ComponentArgs): void; /** * Alias for {@link getComponent} */ get(componentClass: T | Component): InstanceType; /** * Gets a component from the entity by class * @param componentClass Class of component to get * @returns Component instance */ getComponent(componentClass: T | Component): InstanceType; /** * @deprecated Class names may be mangled by minifiers/obfuscators. * Iterate components directly or work with class references instead. */ getComponentNames(): ComponentName[]; get componentMask(): Mask; /** * Alias for {@link hasComponent} */ has(componentClass: T | Component): boolean; /** * Check if an entity has a component. * @param componentClass Component class to check * @returns boolean true/false depending on whether the component is on the entity */ hasComponent(componentClass: T | Component): boolean; /** * Alias for {@link removeComponent} */ remove(componentClass: T): boolean; /** * Removes a component from entity by class. Returns true if the component * was present and removed, false if the entity did not have it. * @param componentClass Component class to remove */ removeComponent(componentClass: T): boolean; /** * @deprecated Class names may be mangled by minifiers/obfuscators. * Use {@link removeComponent} with a class reference instead. */ removeComponentByName(componentName: ComponentName): boolean; /** * Check whether this entity is the same as passed entity */ equals(other: Entity): boolean; registerQuery(query: Query): void; unregisterQuery(query: Query): void; addCleanupCallback(callback: (entity: Entity) => void): void; destroy(): void; purge(): void; } interface IEntityBuilder { build(): Entity; } type EntityOpts = { id?: string; name?: string; }; declare class EntityBuilder implements IEntityBuilder { private world; private opts; private componentRecipes; static create(world: World, opts?: EntityOpts): EntityBuilder; constructor(world: World, opts?: EntityOpts); with(componentConstructor: { new (): T; }, args?: ComponentArgs): EntityBuilder; build(): Entity; } type ComponentId = number; type ComponentName = string; type ComponentField = { fieldName: string; defaultValue?: unknown; }; /** * Component that can be attached to entities. */ declare abstract class Component { /** * Bitflag id assigned to a component class at registration time. * Keyed by the class reference itself, not by class name, so the ECS * survives identifier mangling by minifiers and obfuscators. */ static ComponentIdMap: Map; static ComponentFieldMap: Map>; static ComponentFieldInitializeMap: Map>>; static maxId: number; componentId: ComponentId; private entity; setValues(values: Record): void; setEntity(entity: Entity): void; getEntity(): Entity; onComponentRemoved(): void; toString(): string; } type NonFunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]; type ComponentArgs = { [Property in Exclude, keyof Component>]: C[Property]; }; type ComponentConstructor = { new (...args: any[]): Component; }; declare const registerComponentWithSpecificId: (constructor: T, id: number) => void; declare function RegisterComponent(value: T, _context: ClassDecoratorContext): void; declare class Aspect { bitFlag: number; component: ComponentConstructor; constructor(component: { new (...args: never): Component; }); } declare class HasAspect extends Aspect { } declare class WithoutAspect extends Aspect { } declare const Has: Component>(component: T) => Aspect; declare const Without: Component>(component: T) => Aspect; export { Aspect as A, Component as C, Entity as E, Has as H, type IEntityBuilder as I, type KeysOfType as K, Query as Q, RegisterComponent as R, System as S, Without as W, type ComponentField as a, EntityBuilder as b, World as c, type QueryId as d, type SystemId as e, type SystemAndProps as f, type EntityId as g, type EntityOpts as h, type ExcludeOptionalProps as i, type IfEquals as j, type RequiredKeys as k, type ComponentArgs as l, type ComponentConstructor as m, type ComponentId as n, type ComponentName as o, HasAspect as p, WithoutAspect as q, registerComponentWithSpecificId as r };