UNPKG

miniplex

Version:

A developer-friendly entity management system for games and similarly demanding applications, based on ECS architecture.

225 lines (224 loc) 9.35 kB
import { Bucket } from "@miniplex/bucket"; export * from "@miniplex/bucket"; export type Predicate<E, D extends E> = ((v: E) => v is D) | ((entity: E) => boolean); /** * A utility type that marks the specified properties as required. */ export type With<E, P extends keyof E> = E & Required<Pick<E, P>>; export type Without<E, P extends keyof E> = Omit<E, P>; /** * A utility type that removes all optional properties. */ export type Strict<T> = WithoutOptional<T>; type OptionalKeys<T> = { [K in keyof T]-?: undefined extends T[K] ? K : never; }; type WithoutOptional<T> = Pick<T, Exclude<keyof T, OptionalKeys<T>[keyof T]>>; type QueryConfiguration = { with: any[]; without: any[]; predicates: Function[]; }; interface IQueryableBucket<E> { /** * Queries for entities that have all of the given components. If this is called on * an existing query, the query will be extended to include this new criterion. * * @param components The components to query for. */ with<C extends keyof E>(...components: C[]): Query<With<E, C>>; /** * Queries for entities that have none of the given components. If this is called on * an existing query, the query will be extended to include this new criterion. * * @param components The components to query for. */ without<C extends keyof E>(...components: C[]): Query<Without<E, C>>; /** * Queries for entities that match the given predicate. If this is called on * an existing query, the query will be extended to include this new criterion. * * @param predicate The predicate to query for. */ where<D extends E>(predicate: Predicate<E, D>): Query<D>; } export declare class World<E extends {} = any> extends Bucket<E> implements IQueryableBucket<E> { constructor(entities?: E[]); update(entity: E): E; update<C extends keyof E>(entity: E, component: C, value: E[C]): E; update(entity: E, update: Partial<E>): E; update(entity: E, fun: (entity: E) => Partial<E> | void): E; /** * Adds a component to an entity. If the entity already has the component, the * existing component will not be overwritten. * * After the component was added, the entity will be reindexed, causing it to be * added to or removed from any queries depending on their criteria. * * @param entity The entity to modify. * @param component The name of the component to add. * @param value The value of the component to add. */ addComponent<C extends keyof E>(entity: E, component: C, value: E[C]): void; /** * Removes a component from an entity. If the entity does not have the component, * this function does nothing. * * After the component was removed, the entity will be reindexed, causing it to be * added to or removed from any queries depending on their criteria. * * @param entity The entity to modify. * @param component The name of the component to remove. */ removeComponent(entity: E, component: keyof E): void; protected queries: Set<Query<any>>; /** * Creates (or reuses) a query that matches the given configuration. * * @param config The query configuration. * @returns A query that matches the given configuration. */ query<D>(config: QueryConfiguration): Query<D>; /** * Creates (or reuses) a query that holds entities that have all of the specified * components. * * @param components One or more component names to query for. * @returns A query that holds entities that have all of the given components. */ with<C extends keyof E>(...components: C[]): Query<With<E, C>>; /** * Creates (or reuses) a query that holds entities that do not have any of the * specified components. * * @param components One or more component names to query for. * @returns A query that holds entities that do not have any of the given components. */ without<C extends keyof E>(...components: C[]): Query<Without<E, C>>; /** * Creates (or reuses) a query that holds entities that match the given predicate. * Please note that as soon as you are building queries that use predicates, you * will need to explicitly reindex entities when their properties change. * * @param predicate The predicate that entities must match. * @returns A query that holds entities that match the given predicate. */ where<D extends E>(predicate: Predicate<E, D>): Query<D>; /** * Reindexes the specified entity. This will iteratere over all registered queries * and ask them to reevaluate the entity. * * If the `future` parameter is specified, * it will be used in the evaluation instead of the entity itself. This is useful * if you are about to perform a destructive change on the entity (like removing * a component), but want emitted events to still have access to the unmodified entity * before the change. * * @param entity The entity to reindex. * @param future The entity that the entity will become in the future. */ reindex(entity: E, future?: E): void; private entityToId; private idToEntity; private nextId; /** * Generate and return a numerical identifier for the given entity. The ID can later * be used to retrieve the entity again through the `entity(id)` method. * * @param entity The entity to get the ID for. * @returns An ID for the entity, or undefined if the entity is not in the world. */ id(entity: E): number | undefined; /** * Given an entity ID that was previously generated through the `id(entity)` function, * returns the entity matching that ID, or undefined if no such entity exists. * * @param id The ID of the entity to retrieve. * @returns The entity with the given ID, or undefined if no such entity exists. */ entity(id: number): E | undefined; } export declare class Query<E> extends Bucket<E> implements IQueryableBucket<E> { world: World; config: QueryConfiguration; protected _isConnected: boolean; /** * True if this query is connected to the world, and will automatically * re-evaluate when entities are added or removed. */ get isConnected(): boolean; /** * A unique, string-based key for this query, based on its configuration. */ readonly key: string; constructor(world: World, config: QueryConfiguration); /** * An array containing all entities that match this query. For iteration, it * is recommended to use the `for (const entity of query) {}` syntax instead. */ get entities(): E[]; [Symbol.iterator](): { next: () => { value: E; done: boolean; }; }; /** * Connects this query to the world. While connected, it will automatically * re-evaluate when entities are added or removed, and store those that match * its query configuration. * * @returns The query object. */ connect(): this; /** * Disconnects this query from the world. This essentially stops the query from * automatically receiving entities. */ disconnect(): this; /** * Returns a new query that extends this query and also matches entities that * have all of the components specified. * * @param components The components that entities must have. * @returns A new query representing the extended query configuration. */ with<C extends keyof E>(...components: C[]): Query<With<E, C>>; /** * Returns a new query that extends this query and also matches entities that * have none of the components specified. * * @param components The components that entities must not have. * @returns A new query representing the extended query configuration. */ without<C extends keyof E>(...components: C[]): Query<Without<E, C>>; /** * Returns a new query that extends this query and also matches entities that * match the given predicate. * * @param predicate The predicate that entities must match. * @returns A new query representing the extended query configuration. */ where<D extends E>(predicate: Predicate<E, D>): Query<D>; /** * Checks the given entity against this query's configuration, and returns * true if the entity matches the query, false otherwise. * * @param entity The entity to check. * @returns True if the entity matches this query, false otherwise. */ want(entity: E): boolean; /** * Evaluate the given entity against this query's configuration, and add or * remove it from the query if necessary. * * If `future` is specified, the entity will be evaluated against that entity * instead. This is useful for checking if an entity will match the query * after some potentially destructive change has been made to it, before * actually applying that change to the entity itself. * * @param entity The entity to evaluate. * @param future The entity to evaluate against. If not specified, the entity will be evaluated against itself. */ evaluate(entity: any, future?: any): void; }