UNPKG

ecspresso

Version:

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

208 lines (207 loc) 10.3 kB
import EntityManager from "./entity-manager"; import EventBus from "./event-bus"; import type { FilteredEntity, Entity } from "./types"; import type Bundle from "./bundle"; /** * Type helper to detect conflicting types between two record types. * Returns a union of keys that exist in both T and U but have incompatible types. */ type GetConflictingKeys<T, U> = { [K in keyof T & keyof U]: T[K] extends U[K] ? U[K] extends T[K] ? never : K : K; }[keyof T & keyof U]; /** * Simplified type helper to check bundle type compatibility. * Returns true if bundles can be merged without type conflicts. */ type BundlesAreCompatible<C1 extends Record<string, any>, C2 extends Record<string, any>, E1 extends Record<string, any>, E2 extends Record<string, any>, R1 extends Record<string, any>, R2 extends Record<string, any>> = [ keyof C1 ] extends [never] ? [keyof E1] extends [never] ? [keyof R1] extends [never] ? true : GetConflictingKeys<R1, R2> extends never ? true : false : GetConflictingKeys<E1, E2> extends never ? GetConflictingKeys<R1, R2> extends never ? true : false : false : GetConflictingKeys<C1, C2> extends never ? GetConflictingKeys<E1, E2> extends never ? GetConflictingKeys<R1, R2> extends never ? true : false : false : false; /** * Interface declaration for ECSpresso constructor to ensure type augmentation works properly. * This merges with the class declaration below. */ export default interface ECSpresso<ComponentTypes extends Record<string, any> = {}, EventTypes extends Record<string, any> = {}, ResourceTypes extends Record<string, any> = {}> { /** * Default constructor */ new (): ECSpresso<ComponentTypes, EventTypes, ResourceTypes>; } /** * ECSpresso is the central ECS framework class that connects all features. * It handles creation and management of entities, components, and systems, and provides lifecycle hooks. */ export default class ECSpresso<ComponentTypes extends Record<string, any> = {}, EventTypes extends Record<string, any> = {}, ResourceTypes extends Record<string, any> = {}> { /** Library version*/ static readonly VERSION: string; /** Access/modify stored components and entities*/ private _entityManager; /** Publish/subscribe to events*/ private _eventBus; /** Access/modify registered resources*/ private _resourceManager; /** Registered systems that will be updated in order*/ private _systems; /** Cached sorted systems for efficient updates */ private _sortedSystems; /** Track installed bundles to prevent duplicates*/ private _installedBundles; /** * Creates a new ECSpresso instance. */ constructor(); /** * Creates a new ECSpresso builder for type-safe bundle installation. * This is the preferred way to create an ECSpresso instance with bundles. * * @returns A builder instance for fluent method chaining * * @example * ```typescript * const ecs = ECSpresso.create<BaseComponents, BaseEvents, BaseResources>() * .withBundle(bundle1) * .withBundle(bundle2) * .build(); * ``` */ static create<C extends Record<string, any> = {}, E extends Record<string, any> = {}, R extends Record<string, any> = {}>(): ECSpressoBuilder<C, E, R>; /** * Adds a system directly to this ECSpresso instance * @param label Unique name to identify the system * @returns A SystemBuilder instance for method chaining */ addSystem(label: string): import("./system-builder").SystemBuilderWithEcspresso<ComponentTypes, EventTypes, ResourceTypes, {}>; /** * Update all systems, passing deltaTime and query results to each system's process function * @param deltaTime Time elapsed since the last update (in seconds) */ update(deltaTime: number): void; /** * Initialize all resources and systems * This method: * 1. Initializes all resources that were added as factory functions * 2. Calls the onInitialize lifecycle hook on all systems * * This is useful for game startup to ensure all resources are ready * and systems are properly initialized before the game loop begins. * * @param resourceKeys Optional array of specific resource keys to initialize * @returns Promise that resolves when everything is initialized */ initialize(): Promise<void>; /** * Initialize specific resources or all resources that were added as factory functions but haven't been initialized yet. * This is useful when you need to ensure resources are ready before proceeding. * @param keys Optional array of resource keys to initialize. If not provided, all pending resources will be initialized. * @returns Promise that resolves when the specified resources are initialized */ initializeResources<K extends keyof ResourceTypes>(...keys: K[]): Promise<void>; /** * Sort the systems array by priority (higher priority first) * Called internally when system list changes * @private */ private _sortSystems; /** * Update the priority of a system * @param label The unique label of the system to update * @param priority The new priority value (higher values execute first) * @returns true if the system was found and updated, false otherwise */ updateSystemPriority(label: string, priority: number): boolean; /** * Remove a system by its label * Calls the system's onDetach method with this ECSpresso instance if defined * @param label The unique label of the system to remove * @returns true if the system was found and removed, false otherwise */ removeSystem(label: string): boolean; /** * Check if a resource exists */ hasResource<K extends keyof ResourceTypes>(key: K): boolean; /** * Get a resource if it exists, or undefined if not */ getResource<K extends keyof ResourceTypes>(key: K): ResourceTypes[K]; /** * Add a resource to the ECS instance */ addResource<K extends keyof ResourceTypes>(key: K, resource: ResourceTypes[K] | ((ecs: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>) => ResourceTypes[K] | Promise<ResourceTypes[K]>)): this; /** * Remove a resource from the ECS instance * @param key The resource key to remove * @returns True if the resource was removed, false if it didn't exist */ removeResource<K extends keyof ResourceTypes>(key: K): boolean; /** * Update an existing resource using an updater function * @param key The resource key to update * @param updater Function that receives the current resource value and returns the new value * @returns This ECSpresso instance for chaining * @throws Error if the resource doesn't exist */ updateResource<K extends keyof ResourceTypes>(key: K, updater: (current: ResourceTypes[K]) => ResourceTypes[K]): this; /** * Get all resource keys that are currently registered * @returns Array of resource keys */ getResourceKeys(): Array<keyof ResourceTypes>; /** * Check if a resource needs initialization (was added as a factory function) * @param key The resource key to check * @returns True if the resource needs initialization */ resourceNeedsInitialization<K extends keyof ResourceTypes>(key: K): boolean; /** * Check if an entity has a component */ hasComponent<K extends keyof ComponentTypes>(entityId: number, componentName: K): boolean; /** * Create an entity and add components to it in one call * @param components Object with component names as keys and component data as values * @returns The created entity with all components added */ spawn<T extends { [K in keyof ComponentTypes]?: ComponentTypes[K]; }>(components: T & Record<Exclude<keyof T, keyof ComponentTypes>, never>): Entity<ComponentTypes>; /** * Get all entities with specific components */ getEntitiesWithQuery<WithComponents extends keyof ComponentTypes, WithoutComponents extends keyof ComponentTypes = never>(withComponents: ReadonlyArray<WithComponents>, withoutComponents?: ReadonlyArray<WithoutComponents>): Array<FilteredEntity<ComponentTypes, WithComponents, WithoutComponents>>; /** * Get all installed bundle IDs */ get installedBundles(): string[]; get entityManager(): EntityManager<ComponentTypes>; get eventBus(): EventBus<EventTypes>; /** * Internal method to install a bundle into this ECSpresso instance. * Called by the ECSpressoBuilder during the build process. * The type safety is guaranteed by the builder's type system. */ _installBundle<C extends Record<string, any>, E extends Record<string, any>, R extends Record<string, any>>(bundle: Bundle<C, E, R>): this; } /** * Builder class for ECSpresso that provides fluent type-safe bundle installation. * Handles type checking during build process to ensure type safety. */ export declare class ECSpressoBuilder<C extends Record<string, any> = {}, E extends Record<string, any> = {}, R extends Record<string, any> = {}> { /** The ECSpresso instance being built*/ private ecspresso; constructor(); /** * Add the first bundle when starting with empty types. * This overload allows any bundle to be added to an empty ECSpresso instance. */ withBundle<BC extends Record<string, any>, BE extends Record<string, any>, BR extends Record<string, any>>(this: ECSpressoBuilder<{}, {}, {}>, bundle: Bundle<BC, BE, BR>): ECSpressoBuilder<BC, BE, BR>; /** * Add a subsequent bundle with type checking. * This overload enforces bundle type compatibility. */ withBundle<BC extends Record<string, any>, BE extends Record<string, any>, BR extends Record<string, any>>(bundle: BundlesAreCompatible<C, BC, E, BE, R, BR> extends true ? Bundle<BC, BE, BR> : never): ECSpressoBuilder<C & BC, E & BE, R & BR>; /** * Complete the build process and return the built ECSpresso instance */ build(): ECSpresso<C, E, R>; } export {};