UNPKG

ecspresso

Version:

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

126 lines (125 loc) 5.7 kB
/** * Coroutine Plugin for ECSpresso * * ES6 generator-based coroutines for multi-step, frame-spanning scripted sequences. * A `coroutine` component holds a live generator. A system ticks all generators each * frame via `.next(dt)`. Helper generators (`waitSeconds`, `waitFrames`, `waitUntil`, * `waitForEvent`, `parallel`, `race`) compose via `yield*`. */ import { type BasePluginOptions } from 'ecspresso'; import type { EventsOfWorld, AnyECSpresso } from 'ecspresso'; /** * Yields void, returns void, receives deltaTime (number) via `.next(dt)`. * First `.next(dt)` initializes the generator (runs to first yield, dt discarded per JS spec). * Subsequent `.next(dt)` resume from yield with dt as the yield expression value. */ export type CoroutineGenerator = Generator<void, void, number>; export interface CoroutineEventData { entityId: number; } export interface CoroutineState { generator: CoroutineGenerator; onComplete?: (data: CoroutineEventData) => void; } export interface CoroutineComponentTypes { coroutine: CoroutineState; } export interface CoroutinePluginOptions<G extends string = 'coroutines'> extends BasePluginOptions<G> { } export interface CoroutineOptions { onComplete?: (data: CoroutineEventData) => void; } /** * Create a coroutine component for spawning or adding to an entity. * * @param generator - The generator function to drive * @param options - Optional configuration (onComplete event) * @returns Component object suitable for spreading into spawn() */ export declare function createCoroutine(generator: CoroutineGenerator, options?: CoroutineOptions): Pick<CoroutineComponentTypes, 'coroutine'>; /** * Wait for a specified number of seconds. Accumulates dt until elapsed >= seconds. * If seconds <= 0, returns immediately. */ export declare function waitSeconds(seconds: number): CoroutineGenerator; /** * Wait for a specified number of frames. Yields `frames` times. * If frames <= 0, returns immediately. */ export declare function waitFrames(frames: number): CoroutineGenerator; /** * Wait until a predicate returns true. Yields each frame until predicate is satisfied. * User closes over ecs if needed for state checks. */ export declare function waitUntil(predicate: () => boolean): CoroutineGenerator; /** * Run multiple coroutines in parallel. Completes when all finish. * Initializes all sub-generators, ticks all each frame. * Empty array = immediate return. */ export declare function parallel(...coroutines: CoroutineGenerator[]): CoroutineGenerator; /** * Run multiple coroutines, completing when the first one finishes. * Calls `.return()` on remaining generators (triggers finally blocks). * Empty array = immediate return. */ export declare function race(...coroutines: CoroutineGenerator[]): CoroutineGenerator; /** * Wait until a matching event fires on the event bus. * Subscribes via eventBus.subscribe, yields until event received, unsubscribes in finally block. * * @param eventBus - Object with subscribe method (typically ecs.eventBus) * @param eventType - Event type name to listen for * @param filter - Optional predicate to filter events */ export declare function waitForEvent<ET extends Record<string, any>, E extends keyof ET & string>(eventBus: { subscribe(type: E, cb: (data: ET[E]) => void): () => void; }, eventType: E, filter?: (data: ET[E]) => boolean): CoroutineGenerator; /** * Structural interface for ECS methods used by cancelCoroutine. */ export interface CoroutineWorld { getComponent(entityId: number, componentName: string): unknown | undefined; commands: { removeComponent(entityId: number, componentName: string): void; }; } /** * Cancel a running coroutine on an entity. Calls generator.return() (triggers finally blocks) * and queues component removal. * * @returns true if the entity had a coroutine that was cancelled, false otherwise */ export declare function cancelCoroutine(ecs: CoroutineWorld, entityId: number): boolean; /** * Type-safe coroutine helpers that validate event names against a world's event types. * Use `createCoroutineHelpers<typeof ecs>()` to get compile-time validation. */ export interface CoroutineHelpers<W extends AnyECSpresso> { createCoroutine: (generator: CoroutineGenerator, options?: { onComplete?: (data: CoroutineEventData) => void; }) => Pick<CoroutineComponentTypes, 'coroutine'>; waitForEvent: <E extends keyof EventsOfWorld<W> & string>(eventBus: { subscribe(type: E, cb: (data: EventsOfWorld<W>[E]) => void): () => void; }, eventType: E, filter?: (data: EventsOfWorld<W>[E]) => boolean) => CoroutineGenerator; } /** * Create typed coroutine helpers that validate event names at compile time. * * @example * ```typescript * const { createCoroutine, waitForEvent } = createCoroutineHelpers<typeof ecs>(); * ecs.spawn({ ...createCoroutine(myGen(), { onComplete: (data) => console.log(data.entityId) }) }); * ``` */ export declare function createCoroutineHelpers<W extends AnyECSpresso>(_world?: W): CoroutineHelpers<W>; /** * Create a coroutine plugin for ECSpresso. * * This plugin provides: * - Coroutine system that ticks all generator-based coroutines each frame * - Automatic cleanup via dispose callback (triggers generator finally blocks) * - `onComplete` callback invocation * - Component removal on completion */ export declare function createCoroutinePlugin<G extends string = 'coroutines'>(options?: CoroutinePluginOptions<G>): import("ecspresso").Plugin<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, CoroutineComponentTypes>, import("ecspresso").EmptyConfig, "coroutine-update", G, never, never>;