ecspresso 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -17
- package/dist/asset-manager.d.ts +15 -15
- package/dist/asset-types.d.ts +16 -14
- package/dist/bundle.d.ts +66 -16
- package/dist/bundles/audio.d.ts +293 -0
- package/dist/bundles/{utils/bounds.d.ts → bounds.d.ts} +9 -7
- package/dist/bundles/camera.d.ts +89 -0
- package/dist/bundles/collision.d.ts +289 -0
- package/dist/bundles/diagnostics.d.ts +48 -0
- package/dist/bundles/{utils/input.d.ts → input.d.ts} +16 -17
- package/dist/bundles/physics2D.d.ts +159 -0
- package/dist/bundles/renderers/renderer2D.d.ts +65 -24
- package/dist/bundles/spatial-index.d.ts +57 -0
- package/dist/bundles/state-machine.d.ts +298 -0
- package/dist/bundles/{utils/timers.d.ts → timers.d.ts} +9 -8
- package/dist/bundles/{utils/transform.d.ts → transform.d.ts} +10 -10
- package/dist/bundles/tween.d.ts +197 -0
- package/dist/command-buffer.d.ts +20 -20
- package/dist/ecspresso-builder.d.ts +165 -0
- package/dist/ecspresso.d.ts +157 -178
- package/dist/entity-manager.d.ts +76 -40
- package/dist/event-bus.d.ts +6 -1
- package/dist/index.d.ts +1 -9
- package/dist/reactive-query-manager.d.ts +14 -3
- package/dist/resource-manager.d.ts +35 -19
- package/dist/screen-manager.d.ts +4 -4
- package/dist/screen-types.d.ts +12 -11
- package/dist/src/bundles/audio.js +4 -0
- package/dist/src/bundles/audio.js.map +10 -0
- package/dist/src/bundles/bounds.js +4 -0
- package/dist/src/bundles/bounds.js.map +10 -0
- package/dist/src/bundles/camera.js +4 -0
- package/dist/src/bundles/camera.js.map +10 -0
- package/dist/src/bundles/collision.js +4 -0
- package/dist/src/bundles/collision.js.map +11 -0
- package/dist/src/bundles/diagnostics.js +5 -0
- package/dist/src/bundles/diagnostics.js.map +10 -0
- package/dist/src/bundles/input.js +4 -0
- package/dist/src/bundles/input.js.map +10 -0
- package/dist/src/bundles/physics2D.js +4 -0
- package/dist/src/bundles/physics2D.js.map +11 -0
- package/dist/src/bundles/renderers/renderer2D.js +4 -0
- package/dist/src/bundles/renderers/renderer2D.js.map +10 -0
- package/dist/src/bundles/spatial-index.js +4 -0
- package/dist/src/bundles/spatial-index.js.map +11 -0
- package/dist/src/bundles/state-machine.js +4 -0
- package/dist/src/bundles/state-machine.js.map +10 -0
- package/dist/src/bundles/timers.js +4 -0
- package/dist/src/bundles/timers.js.map +10 -0
- package/dist/src/bundles/transform.js +4 -0
- package/dist/src/bundles/transform.js.map +10 -0
- package/dist/src/bundles/tween.js +4 -0
- package/dist/src/bundles/tween.js.map +11 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +25 -0
- package/dist/system-builder.d.ts +36 -42
- package/dist/type-utils.d.ts +52 -3
- package/dist/types.d.ts +10 -19
- package/dist/utils/check-required-cycle.d.ts +12 -0
- package/dist/utils/easing.d.ts +71 -0
- package/dist/utils/math.d.ts +67 -0
- package/dist/utils/narrowphase.d.ts +63 -0
- package/dist/utils/spatial-hash.d.ts +53 -0
- package/package.json +50 -20
- package/dist/bundles/renderers/renderer2D.js +0 -4
- package/dist/bundles/renderers/renderer2D.js.map +0 -10
- package/dist/bundles/utils/bounds.js +0 -4
- package/dist/bundles/utils/bounds.js.map +0 -10
- package/dist/bundles/utils/collision.d.ts +0 -204
- package/dist/bundles/utils/collision.js +0 -4
- package/dist/bundles/utils/collision.js.map +0 -10
- package/dist/bundles/utils/input.js +0 -4
- package/dist/bundles/utils/input.js.map +0 -10
- package/dist/bundles/utils/movement.d.ts +0 -86
- package/dist/bundles/utils/movement.js +0 -4
- package/dist/bundles/utils/movement.js.map +0 -10
- package/dist/bundles/utils/timers.js +0 -4
- package/dist/bundles/utils/timers.js.map +0 -10
- package/dist/bundles/utils/transform.js +0 -4
- package/dist/bundles/utils/transform.js.map +0 -10
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -22
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Application, ApplicationOptions, Container, Sprite, Graphics } from 'pixi.js';
|
|
10
10
|
import { Bundle } from 'ecspresso';
|
|
11
|
-
import { type LocalTransform, type WorldTransform, type TransformComponentTypes, type TransformBundleOptions } from 'ecspresso/bundles/
|
|
12
|
-
import { type BoundsRect } from 'ecspresso/bundles/
|
|
11
|
+
import { type LocalTransform, type WorldTransform, type TransformComponentTypes, type TransformBundleOptions } from 'ecspresso/bundles/transform';
|
|
12
|
+
import { type BoundsRect } from 'ecspresso/bundles/bounds';
|
|
13
|
+
import type { CameraResourceTypes } from 'ecspresso/bundles/camera';
|
|
13
14
|
export type { LocalTransform, WorldTransform, TransformComponentTypes };
|
|
14
15
|
export type { BoundsRect };
|
|
15
|
-
export { createTransform, createLocalTransform, createWorldTransform } from 'ecspresso/bundles/
|
|
16
|
+
export { createTransform, createLocalTransform, createWorldTransform, DEFAULT_LOCAL_TRANSFORM, DEFAULT_WORLD_TRANSFORM } from 'ecspresso/bundles/transform';
|
|
16
17
|
/**
|
|
17
18
|
* Visibility and alpha component
|
|
18
19
|
*/
|
|
@@ -22,14 +23,14 @@ export interface Visible {
|
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Aggregate component types for the 2D renderer bundle.
|
|
25
|
-
*
|
|
26
|
+
* Included automatically via `.withBundle(createRenderer2DBundle({ ... }))`.
|
|
26
27
|
*
|
|
27
28
|
* @example
|
|
28
29
|
* ```typescript
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* player: true
|
|
32
|
-
*
|
|
30
|
+
* const ecs = ECSpresso.create()
|
|
31
|
+
* .withBundle(createRenderer2DBundle({ ... }))
|
|
32
|
+
* .withComponentTypes<{ velocity: { x: number; y: number }; player: true }>()
|
|
33
|
+
* .build();
|
|
33
34
|
* ```
|
|
34
35
|
*/
|
|
35
36
|
export interface Renderer2DComponentTypes extends TransformComponentTypes {
|
|
@@ -59,14 +60,33 @@ export interface Renderer2DResourceTypes {
|
|
|
59
60
|
/** Screen bounds derived from PixiJS screen dimensions, updated on resize */
|
|
60
61
|
bounds: BoundsRect;
|
|
61
62
|
}
|
|
63
|
+
export type ScaleMode = 'fit' | 'cover' | 'stretch';
|
|
64
|
+
export interface ScreenScaleOptions {
|
|
65
|
+
readonly width: number;
|
|
66
|
+
readonly height: number;
|
|
67
|
+
readonly mode?: ScaleMode;
|
|
68
|
+
}
|
|
69
|
+
export interface ViewportScale {
|
|
70
|
+
scaleX: number;
|
|
71
|
+
scaleY: number;
|
|
72
|
+
offsetX: number;
|
|
73
|
+
offsetY: number;
|
|
74
|
+
physicalWidth: number;
|
|
75
|
+
physicalHeight: number;
|
|
76
|
+
readonly designWidth: number;
|
|
77
|
+
readonly designHeight: number;
|
|
78
|
+
}
|
|
79
|
+
export interface ViewportScaleResourceTypes {
|
|
80
|
+
viewportScale: ViewportScale;
|
|
81
|
+
}
|
|
62
82
|
/**
|
|
63
83
|
* Common options shared between both initialization modes
|
|
64
84
|
*/
|
|
65
|
-
interface Renderer2DBundleCommonOptions {
|
|
85
|
+
interface Renderer2DBundleCommonOptions<G extends string = 'renderer2d'> {
|
|
66
86
|
/** Optional custom root container (defaults to app.stage) */
|
|
67
87
|
rootContainer?: Container;
|
|
68
88
|
/** System group name (default: 'renderer2d') */
|
|
69
|
-
systemGroup?:
|
|
89
|
+
systemGroup?: G;
|
|
70
90
|
/** Priority for render sync system (default: 500) */
|
|
71
91
|
renderSyncPriority?: number;
|
|
72
92
|
/** Options for the included transform bundle */
|
|
@@ -75,11 +95,17 @@ interface Renderer2DBundleCommonOptions {
|
|
|
75
95
|
startLoop?: boolean;
|
|
76
96
|
/** Ordered render layer names (back-to-front). Entities with a renderLayer component are placed in the corresponding container. */
|
|
77
97
|
renderLayers?: string[];
|
|
98
|
+
/** Automatically apply cameraState resource to rootContainer each frame.
|
|
99
|
+
* Requires the camera bundle to be installed. (default: false) */
|
|
100
|
+
camera?: boolean;
|
|
101
|
+
/** Enforce a logical design resolution with automatic aspect-ratio-aware scaling.
|
|
102
|
+
* When set, systems work in design-resolution coordinate space. */
|
|
103
|
+
screenScale?: ScreenScaleOptions;
|
|
78
104
|
}
|
|
79
105
|
/**
|
|
80
106
|
* Options when providing a pre-initialized PixiJS Application
|
|
81
107
|
*/
|
|
82
|
-
export interface Renderer2DBundleAppOptions extends Renderer2DBundleCommonOptions {
|
|
108
|
+
export interface Renderer2DBundleAppOptions<G extends string = 'renderer2d'> extends Renderer2DBundleCommonOptions<G> {
|
|
83
109
|
/** The PixiJS Application instance (already initialized) */
|
|
84
110
|
app: Application;
|
|
85
111
|
init?: never;
|
|
@@ -88,7 +114,7 @@ export interface Renderer2DBundleAppOptions extends Renderer2DBundleCommonOption
|
|
|
88
114
|
/**
|
|
89
115
|
* Options when letting the bundle create and manage the PixiJS Application
|
|
90
116
|
*/
|
|
91
|
-
export interface Renderer2DBundleManagedOptions extends Renderer2DBundleCommonOptions {
|
|
117
|
+
export interface Renderer2DBundleManagedOptions<G extends string = 'renderer2d'> extends Renderer2DBundleCommonOptions<G> {
|
|
92
118
|
app?: never;
|
|
93
119
|
/** PixiJS ApplicationOptions - bundle will create and initialize the Application */
|
|
94
120
|
init: Partial<ApplicationOptions>;
|
|
@@ -108,31 +134,25 @@ export interface Renderer2DBundleManagedOptions extends Renderer2DBundleCommonOp
|
|
|
108
134
|
* ```typescript
|
|
109
135
|
* const app = new Application();
|
|
110
136
|
* await app.init({ resizeTo: window });
|
|
111
|
-
* const ecs = ECSpresso.create
|
|
137
|
+
* const ecs = ECSpresso.create()
|
|
112
138
|
* .withBundle(createRenderer2DBundle({ app }))
|
|
139
|
+
* .withComponentTypes<{ player: true }>()
|
|
113
140
|
* .build();
|
|
114
141
|
* ```
|
|
115
142
|
*
|
|
116
143
|
* @example Managed mode (convenience)
|
|
117
144
|
* ```typescript
|
|
118
|
-
* const ecs = ECSpresso.create
|
|
145
|
+
* const ecs = ECSpresso.create()
|
|
119
146
|
* .withBundle(createRenderer2DBundle({
|
|
120
147
|
* init: { background: '#1099bb', resizeTo: window },
|
|
121
148
|
* container: document.body,
|
|
122
149
|
* }))
|
|
150
|
+
* .withComponentTypes<{ player: true }>()
|
|
123
151
|
* .build();
|
|
124
152
|
* await ecs.initialize(); // Application created here
|
|
125
153
|
* ```
|
|
126
154
|
*/
|
|
127
|
-
export type Renderer2DBundleOptions = Renderer2DBundleAppOptions | Renderer2DBundleManagedOptions
|
|
128
|
-
/**
|
|
129
|
-
* Default local transform values
|
|
130
|
-
*/
|
|
131
|
-
export declare const DEFAULT_LOCAL_TRANSFORM: Readonly<LocalTransform>;
|
|
132
|
-
/**
|
|
133
|
-
* Default world transform values
|
|
134
|
-
*/
|
|
135
|
-
export declare const DEFAULT_WORLD_TRANSFORM: Readonly<WorldTransform>;
|
|
155
|
+
export type Renderer2DBundleOptions<G extends string = 'renderer2d'> = Renderer2DBundleAppOptions<G> | Renderer2DBundleManagedOptions<G>;
|
|
136
156
|
interface PositionOption {
|
|
137
157
|
x?: number;
|
|
138
158
|
y?: number;
|
|
@@ -188,6 +208,15 @@ export declare function createGraphicsComponents(graphics: Graphics, position?:
|
|
|
188
208
|
* ```
|
|
189
209
|
*/
|
|
190
210
|
export declare function createContainerComponents(container: Container, position?: PositionOption, options?: TransformOptions): Pick<Renderer2DComponentTypes, 'container' | 'localTransform' | 'worldTransform' | 'visible'>;
|
|
211
|
+
export declare function computeViewportScale(physicalW: number, physicalH: number, designW: number, designH: number, mode: ScaleMode): ViewportScale;
|
|
212
|
+
/**
|
|
213
|
+
* Convert physical canvas pixel coordinates to design-resolution (logical) coordinates.
|
|
214
|
+
* Compose with camera `screenToWorld()` for full physical→world conversion.
|
|
215
|
+
*/
|
|
216
|
+
export declare function physicalToLogical(physicalX: number, physicalY: number, viewport: ViewportScale): {
|
|
217
|
+
x: number;
|
|
218
|
+
y: number;
|
|
219
|
+
};
|
|
191
220
|
/**
|
|
192
221
|
* Create a 2D rendering bundle for ECSpresso.
|
|
193
222
|
*
|
|
@@ -217,4 +246,16 @@ export declare function createContainerComponents(container: Container, position
|
|
|
217
246
|
* await ecs.initialize();
|
|
218
247
|
* ```
|
|
219
248
|
*/
|
|
220
|
-
|
|
249
|
+
type Renderer2DLabels = 'renderer2d-sync' | 'renderer2d-scene-graph' | 'renderer2d-camera-sync' | 'transform-propagation';
|
|
250
|
+
type Renderer2DReactiveQueryNames = 'renderer2d-sprites' | 'renderer2d-graphics' | 'renderer2d-containers';
|
|
251
|
+
export declare function createRenderer2DBundle<G extends string = 'renderer2d'>(options: Renderer2DBundleOptions<G> & {
|
|
252
|
+
screenScale: ScreenScaleOptions;
|
|
253
|
+
camera: true;
|
|
254
|
+
}): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes & CameraResourceTypes, {}, {}, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;
|
|
255
|
+
export declare function createRenderer2DBundle<G extends string = 'renderer2d'>(options: Renderer2DBundleOptions<G> & {
|
|
256
|
+
screenScale: ScreenScaleOptions;
|
|
257
|
+
}): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes, {}, {}, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;
|
|
258
|
+
export declare function createRenderer2DBundle<G extends string = 'renderer2d'>(options: Renderer2DBundleOptions<G> & {
|
|
259
|
+
camera: true;
|
|
260
|
+
}): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & CameraResourceTypes, {}, {}, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;
|
|
261
|
+
export declare function createRenderer2DBundle<G extends string = 'renderer2d'>(options: Renderer2DBundleOptions<G>): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes, {}, {}, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spatial Index Bundle for ECSpresso
|
|
3
|
+
*
|
|
4
|
+
* Provides a uniform-grid spatial hash for broadphase collision detection
|
|
5
|
+
* and proximity queries. Replaces O(n²) brute-force with O(n·d) where
|
|
6
|
+
* d = local density.
|
|
7
|
+
*
|
|
8
|
+
* Standalone usage: queryRect / queryRadius for proximity queries.
|
|
9
|
+
* Automatic acceleration: collision and physics2D bundles detect the
|
|
10
|
+
* spatialIndex resource at runtime and use it for broadphase when present.
|
|
11
|
+
*/
|
|
12
|
+
import { Bundle } from 'ecspresso';
|
|
13
|
+
import type { TransformComponentTypes } from './transform';
|
|
14
|
+
import type { CollisionComponentTypes } from './collision';
|
|
15
|
+
import { type SpatialIndex } from '../utils/spatial-hash';
|
|
16
|
+
export interface SpatialIndexResourceTypes {
|
|
17
|
+
spatialIndex: SpatialIndex;
|
|
18
|
+
}
|
|
19
|
+
type SpatialIndexComponentTypes = TransformComponentTypes & Pick<CollisionComponentTypes<string>, 'aabbCollider' | 'circleCollider'>;
|
|
20
|
+
export type SpatialIndexPhase = 'fixedUpdate' | 'postUpdate';
|
|
21
|
+
type SpatialIndexLabel = `spatial-index-rebuild-${SpatialIndexPhase}`;
|
|
22
|
+
export interface SpatialIndexBundleOptions<G extends string = 'spatialIndex'> {
|
|
23
|
+
/** Cell size for the spatial hash grid (default: 64) */
|
|
24
|
+
cellSize?: number;
|
|
25
|
+
/** System group name (default: 'spatialIndex') */
|
|
26
|
+
systemGroup?: G;
|
|
27
|
+
/** Priority for rebuild systems (default: 2000, before collision) */
|
|
28
|
+
priority?: number;
|
|
29
|
+
/** Phases to register rebuild systems in (default: ['fixedUpdate', 'postUpdate']) */
|
|
30
|
+
phases?: ReadonlyArray<SpatialIndexPhase>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a spatial index bundle for ECSpresso.
|
|
34
|
+
*
|
|
35
|
+
* Provides a uniform-grid spatial hash that accelerates collision detection.
|
|
36
|
+
* When installed alongside the collision or physics2D bundles, they
|
|
37
|
+
* automatically use the spatial index for broadphase instead of O(n²)
|
|
38
|
+
* brute-force.
|
|
39
|
+
*
|
|
40
|
+
* Also provides proximity query methods for game logic (e.g. "find all
|
|
41
|
+
* enemies within 200 units").
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const ecs = ECSpresso.create()
|
|
46
|
+
* .withBundle(createTransformBundle())
|
|
47
|
+
* .withBundle(createCollisionBundle({ layers }))
|
|
48
|
+
* .withBundle(createSpatialIndexBundle({ cellSize: 128 }))
|
|
49
|
+
* .build();
|
|
50
|
+
*
|
|
51
|
+
* // Proximity query in a system:
|
|
52
|
+
* const si = ecs.getResource('spatialIndex');
|
|
53
|
+
* const nearby = si.queryRadius(playerX, playerY, 200);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function createSpatialIndexBundle<G extends string = 'spatialIndex'>(options?: SpatialIndexBundleOptions<G>): Bundle<SpatialIndexComponentTypes, {}, SpatialIndexResourceTypes, {}, {}, SpatialIndexLabel, G>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Machine Bundle for ECSpresso
|
|
3
|
+
*
|
|
4
|
+
* Provides ECS-native finite state machines with guard-based transitions,
|
|
5
|
+
* event-driven transitions, and lifecycle hooks (onEnter, onExit, onUpdate).
|
|
6
|
+
*
|
|
7
|
+
* Each entity gets a `stateMachine` component referencing a shared definition.
|
|
8
|
+
* One system processes all state machine entities each tick.
|
|
9
|
+
*/
|
|
10
|
+
import { Bundle } from 'ecspresso';
|
|
11
|
+
import type { SystemPhase } from 'ecspresso';
|
|
12
|
+
/**
|
|
13
|
+
* Structural interface for ECS methods available inside state machine hooks.
|
|
14
|
+
* Uses method syntax for bivariant parameter checking under strictFunctionTypes,
|
|
15
|
+
* allowing users to annotate hooks with their concrete ECSpresso type.
|
|
16
|
+
*/
|
|
17
|
+
export interface StateMachineWorld {
|
|
18
|
+
entityManager: {
|
|
19
|
+
getComponent(entityId: number, componentName: string): unknown | undefined;
|
|
20
|
+
};
|
|
21
|
+
eventBus: {
|
|
22
|
+
publish(eventType: string, data: unknown): void;
|
|
23
|
+
};
|
|
24
|
+
spawn(components: Record<string, unknown>): {
|
|
25
|
+
id: number;
|
|
26
|
+
};
|
|
27
|
+
removeEntity(entityOrId: number): boolean;
|
|
28
|
+
hasComponent(entityId: number, componentName: string): boolean;
|
|
29
|
+
getResource(key: string): unknown;
|
|
30
|
+
hasResource(key: string): boolean;
|
|
31
|
+
markChanged(entityId: number, componentName: string): void;
|
|
32
|
+
commands: {
|
|
33
|
+
spawn(components: Record<string, unknown>): void;
|
|
34
|
+
removeEntity(entityId: number): void;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Configuration for a single state in a state machine definition.
|
|
39
|
+
*
|
|
40
|
+
* @template S - Union of state name strings
|
|
41
|
+
* @template W - World interface type for hooks/guards (default: StateMachineWorld)
|
|
42
|
+
*/
|
|
43
|
+
export interface StateConfig<S extends string, W extends StateMachineWorld = StateMachineWorld> {
|
|
44
|
+
/** Called when entering this state */
|
|
45
|
+
onEnter?(ecs: W, entityId: number): void;
|
|
46
|
+
/** Called when exiting this state */
|
|
47
|
+
onExit?(ecs: W, entityId: number): void;
|
|
48
|
+
/** Called each tick while in this state */
|
|
49
|
+
onUpdate?(ecs: W, entityId: number, deltaTime: number): void;
|
|
50
|
+
/** Guard-based transitions evaluated each tick. First passing guard wins. */
|
|
51
|
+
transitions?: ReadonlyArray<{
|
|
52
|
+
target: S;
|
|
53
|
+
guard(ecs: W, entityId: number): boolean;
|
|
54
|
+
}>;
|
|
55
|
+
/** Event-based transition map: eventName → target state or guarded transition */
|
|
56
|
+
on?: Record<string, S | {
|
|
57
|
+
target: S;
|
|
58
|
+
guard(ecs: W, entityId: number): boolean;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Immutable definition of a state machine. Shared across entities.
|
|
63
|
+
*
|
|
64
|
+
* @template S - Union of state name strings
|
|
65
|
+
*/
|
|
66
|
+
export interface StateMachineDefinition<S extends string> {
|
|
67
|
+
readonly id: string;
|
|
68
|
+
readonly initial: S;
|
|
69
|
+
readonly states: {
|
|
70
|
+
readonly [K in S]: StateConfig<S>;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Runtime state machine component stored on each entity.
|
|
75
|
+
*
|
|
76
|
+
* @template S - Union of state name strings (default: string)
|
|
77
|
+
*/
|
|
78
|
+
export interface StateMachine<S extends string = string> {
|
|
79
|
+
readonly definition: StateMachineDefinition<string>;
|
|
80
|
+
current: S;
|
|
81
|
+
previous: S | null;
|
|
82
|
+
stateTime: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Component types provided by the state machine bundle.
|
|
86
|
+
*
|
|
87
|
+
* @template S - Union of state name strings (default: string)
|
|
88
|
+
*/
|
|
89
|
+
export interface StateMachineComponentTypes<S extends string = string> {
|
|
90
|
+
stateMachine: StateMachine<S>;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Event published on every state transition.
|
|
94
|
+
*
|
|
95
|
+
* @template S - Union of state name strings (default: string)
|
|
96
|
+
*/
|
|
97
|
+
export interface StateTransitionEvent<S extends string = string> {
|
|
98
|
+
entityId: number;
|
|
99
|
+
from: S;
|
|
100
|
+
to: S;
|
|
101
|
+
definitionId: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Event types provided by the state machine bundle.
|
|
105
|
+
*
|
|
106
|
+
* @template S - Union of state name strings (default: string)
|
|
107
|
+
*/
|
|
108
|
+
export interface StateMachineEventTypes<S extends string = string> {
|
|
109
|
+
stateTransition: StateTransitionEvent<S>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Extract the state name union from a StateMachineDefinition.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const enemyFSM = defineStateMachine('enemy', { initial: 'idle', states: { idle: {}, chase: {} } });
|
|
117
|
+
* type EnemyStates = StatesOf<typeof enemyFSM>; // 'idle' | 'chase'
|
|
118
|
+
* type AllStates = StatesOf<typeof enemyFSM> | StatesOf<typeof playerFSM>;
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export type StatesOf<D> = D extends StateMachineDefinition<infer S> ? S : never;
|
|
122
|
+
/**
|
|
123
|
+
* Configuration options for the state machine bundle.
|
|
124
|
+
*/
|
|
125
|
+
export interface StateMachineBundleOptions<G extends string = 'stateMachine'> {
|
|
126
|
+
/** System group name (default: 'stateMachine') */
|
|
127
|
+
systemGroup?: G;
|
|
128
|
+
/** Priority for state machine system (default: 0) */
|
|
129
|
+
priority?: number;
|
|
130
|
+
/** Execution phase (default: 'update') */
|
|
131
|
+
phase?: SystemPhase;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Define a state machine with type-safe state names.
|
|
135
|
+
*
|
|
136
|
+
* @template S - Union of state name strings, inferred from `states` keys
|
|
137
|
+
* @param id - Unique identifier for this definition
|
|
138
|
+
* @param config - Initial state and state configurations
|
|
139
|
+
* @returns A frozen StateMachineDefinition
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const enemyFSM = defineStateMachine('enemy', {
|
|
144
|
+
* initial: 'idle',
|
|
145
|
+
* states: {
|
|
146
|
+
* idle: {
|
|
147
|
+
* onEnter: (ecs, id) => { ... },
|
|
148
|
+
* transitions: [{ target: 'chase', guard: (ecs, id) => playerNearby(ecs, id) }],
|
|
149
|
+
* },
|
|
150
|
+
* chase: {
|
|
151
|
+
* onUpdate: (ecs, id, dt) => { ... },
|
|
152
|
+
* on: { playerLost: 'idle' },
|
|
153
|
+
* },
|
|
154
|
+
* },
|
|
155
|
+
* });
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export declare function defineStateMachine<S extends string>(id: string, config: {
|
|
159
|
+
initial: NoInfer<S>;
|
|
160
|
+
states: Record<S, StateConfig<NoInfer<S>>>;
|
|
161
|
+
}): StateMachineDefinition<S>;
|
|
162
|
+
/**
|
|
163
|
+
* Create a stateMachine component from a definition.
|
|
164
|
+
*
|
|
165
|
+
* @param definition - The state machine definition to use
|
|
166
|
+
* @param options - Optional overrides (e.g., initial state)
|
|
167
|
+
* @returns Component object suitable for spreading into spawn()
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* ecs.spawn({
|
|
172
|
+
* ...createStateMachine(enemyFSM),
|
|
173
|
+
* position: { x: 100, y: 200 },
|
|
174
|
+
* });
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export declare function createStateMachine<S extends string>(definition: StateMachineDefinition<S>, options?: {
|
|
178
|
+
initial?: S;
|
|
179
|
+
}): Pick<StateMachineComponentTypes<S>, 'stateMachine'>;
|
|
180
|
+
/**
|
|
181
|
+
* Directly transition an entity's state machine to a target state.
|
|
182
|
+
* Fires onExit, onEnter hooks and publishes stateTransition event.
|
|
183
|
+
*
|
|
184
|
+
* @param ecs - ECS instance (structural typing)
|
|
185
|
+
* @param entityId - Entity to transition
|
|
186
|
+
* @param targetState - State to transition to
|
|
187
|
+
* @returns true if transition succeeded, false if entity has no stateMachine or target state doesn't exist
|
|
188
|
+
*/
|
|
189
|
+
export declare function transitionTo(ecs: StateMachineWorld, entityId: number, targetState: string): boolean;
|
|
190
|
+
/**
|
|
191
|
+
* Send a named event to an entity's state machine.
|
|
192
|
+
* Checks the current state's `on` handlers for a matching event.
|
|
193
|
+
*
|
|
194
|
+
* @param ecs - ECS instance (structural typing)
|
|
195
|
+
* @param entityId - Entity to send event to
|
|
196
|
+
* @param eventName - Event name to match against `on` handlers
|
|
197
|
+
* @returns true if a transition occurred, false otherwise
|
|
198
|
+
*/
|
|
199
|
+
export declare function sendEvent(ecs: StateMachineWorld, entityId: number, eventName: string): boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Get the current state of an entity's state machine.
|
|
202
|
+
*
|
|
203
|
+
* @param ecs - ECS instance (structural typing)
|
|
204
|
+
* @param entityId - Entity to query
|
|
205
|
+
* @returns The current state string, or undefined if entity has no stateMachine
|
|
206
|
+
*/
|
|
207
|
+
export declare function getStateMachineState(ecs: StateMachineWorld, entityId: number): string | undefined;
|
|
208
|
+
/**
|
|
209
|
+
* A typed kit that captures the world type W once, providing helpers
|
|
210
|
+
* where hooks/guards contextually receive W instead of StateMachineWorld.
|
|
211
|
+
*
|
|
212
|
+
* @template W - Concrete ECS world type
|
|
213
|
+
*/
|
|
214
|
+
export interface StateMachineKit<W extends StateMachineWorld, S extends string = string> {
|
|
215
|
+
bundle: Bundle<StateMachineComponentTypes<S>, StateMachineEventTypes<S>, {}, {}, {}, 'state-machine-update', 'stateMachine'>;
|
|
216
|
+
defineStateMachine: <DS extends S>(id: string, config: {
|
|
217
|
+
initial: NoInfer<DS>;
|
|
218
|
+
states: Record<DS, StateConfig<NoInfer<DS>, W>>;
|
|
219
|
+
}) => StateMachineDefinition<DS>;
|
|
220
|
+
createStateMachine: <DS extends S>(definition: StateMachineDefinition<DS>, options?: {
|
|
221
|
+
initial?: DS;
|
|
222
|
+
}) => Pick<StateMachineComponentTypes<S>, 'stateMachine'>;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Create a typed state machine kit that captures the world type W.
|
|
226
|
+
*
|
|
227
|
+
* Hooks and guards in definitions created via the kit's `defineStateMachine`
|
|
228
|
+
* contextually receive W as their `ecs` parameter — no manual annotations needed.
|
|
229
|
+
*
|
|
230
|
+
* @template W - Concrete ECS world type
|
|
231
|
+
* @param options - Optional bundle configuration (same as createStateMachineBundle)
|
|
232
|
+
* @returns A kit object with bundle, defineStateMachine, createStateMachine, transitionTo, sendEvent, getStateMachineState
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```typescript
|
|
236
|
+
* const ecs = ECSpresso.create()
|
|
237
|
+
* .withBundle(createStateMachineBundle())
|
|
238
|
+
* .withComponentTypes<{ enemy: true }>()
|
|
239
|
+
* .build();
|
|
240
|
+
*
|
|
241
|
+
* type ECS = typeof ecs;
|
|
242
|
+
* const { bundle, defineStateMachine, createStateMachine } =
|
|
243
|
+
* createStateMachineKit<ECS>();
|
|
244
|
+
*
|
|
245
|
+
* const enemyFSM = defineStateMachine('enemy', {
|
|
246
|
+
* initial: 'patrol',
|
|
247
|
+
* states: {
|
|
248
|
+
* patrol: {
|
|
249
|
+
* onEnter(ecs, entityId) {
|
|
250
|
+
* ecs.getResource('bounds'); // fully typed
|
|
251
|
+
* },
|
|
252
|
+
* transitions: [{
|
|
253
|
+
* target: 'chase',
|
|
254
|
+
* guard: (ecs, entityId) => distanceToPlayer(ecs, entityId) < 180,
|
|
255
|
+
* }],
|
|
256
|
+
* },
|
|
257
|
+
* chase: {},
|
|
258
|
+
* },
|
|
259
|
+
* });
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
export declare function createStateMachineKit<W extends StateMachineWorld = StateMachineWorld, S extends string = string>(options?: StateMachineBundleOptions): StateMachineKit<W, S>;
|
|
263
|
+
/**
|
|
264
|
+
* Create a state machine bundle for ECSpresso.
|
|
265
|
+
*
|
|
266
|
+
* Provides:
|
|
267
|
+
* - Lifecycle hooks (onEnter, onExit, onUpdate) per state
|
|
268
|
+
* - Guard-based automatic transitions evaluated each tick
|
|
269
|
+
* - Event-based transitions via `sendEvent()`
|
|
270
|
+
* - Direct transitions via `transitionTo()`
|
|
271
|
+
* - stateTransition events published on every transition
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* const ecs = ECSpresso.create()
|
|
276
|
+
* .withBundle(createStateMachineBundle())
|
|
277
|
+
* .build();
|
|
278
|
+
*
|
|
279
|
+
* const fsm = defineStateMachine('enemy', {
|
|
280
|
+
* initial: 'idle',
|
|
281
|
+
* states: {
|
|
282
|
+
* idle: {
|
|
283
|
+
* transitions: [{ target: 'chase', guard: (ecs, id) => playerNearby(ecs, id) }],
|
|
284
|
+
* },
|
|
285
|
+
* chase: {
|
|
286
|
+
* onUpdate: (ecs, id, dt) => moveTowardPlayer(ecs, id, dt),
|
|
287
|
+
* on: { playerLost: 'idle' },
|
|
288
|
+
* },
|
|
289
|
+
* },
|
|
290
|
+
* });
|
|
291
|
+
*
|
|
292
|
+
* ecs.spawn({
|
|
293
|
+
* ...createStateMachine(fsm),
|
|
294
|
+
* position: { x: 0, y: 0 },
|
|
295
|
+
* });
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
export declare function createStateMachineBundle<S extends string = string, G extends string = 'stateMachine'>(options?: StateMachineBundleOptions<G>): Bundle<StateMachineComponentTypes<S>, StateMachineEventTypes<S>, {}, {}, {}, 'state-machine-update', G>;
|
|
@@ -55,16 +55,17 @@ export interface Timer<EventTypes extends Record<string, any>> {
|
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
57
|
* Component types provided by the timer bundle.
|
|
58
|
-
*
|
|
58
|
+
* Included automatically via `.withBundle(createTimerBundle<Events>())`.
|
|
59
|
+
* The EventTypes generic constrains which events can be used with `onComplete`.
|
|
59
60
|
*
|
|
60
61
|
* @template EventTypes The event types from your ECS
|
|
61
62
|
*
|
|
62
63
|
* @example
|
|
63
64
|
* ```typescript
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* player: true
|
|
67
|
-
*
|
|
65
|
+
* const ecs = ECSpresso.create()
|
|
66
|
+
* .withBundle(createTimerBundle<{ respawn: TimerEventData }>())
|
|
67
|
+
* .withComponentTypes<{ velocity: { x: number; y: number }; player: true }>()
|
|
68
|
+
* .build();
|
|
68
69
|
* ```
|
|
69
70
|
*/
|
|
70
71
|
export interface TimerComponentTypes<EventTypes extends Record<string, any>> {
|
|
@@ -73,9 +74,9 @@ export interface TimerComponentTypes<EventTypes extends Record<string, any>> {
|
|
|
73
74
|
/**
|
|
74
75
|
* Configuration options for the timer bundle.
|
|
75
76
|
*/
|
|
76
|
-
export interface TimerBundleOptions {
|
|
77
|
+
export interface TimerBundleOptions<G extends string = 'timers'> {
|
|
77
78
|
/** System group name (default: 'timers') */
|
|
78
|
-
systemGroup?:
|
|
79
|
+
systemGroup?: G;
|
|
79
80
|
/** Priority for timer update system (default: 0) */
|
|
80
81
|
priority?: number;
|
|
81
82
|
/** Execution phase (default: 'preUpdate') */
|
|
@@ -169,4 +170,4 @@ export declare function createRepeatingTimer<EventTypes extends Record<string, a
|
|
|
169
170
|
* });
|
|
170
171
|
* ```
|
|
171
172
|
*/
|
|
172
|
-
export declare function createTimerBundle<EventTypes extends Record<string, any
|
|
173
|
+
export declare function createTimerBundle<EventTypes extends Record<string, any>, G extends string = 'timers'>(options?: TimerBundleOptions<G>): Bundle<TimerComponentTypes<EventTypes>, EventTypes, {}, {}, {}, 'timer-update', G>;
|
|
@@ -32,14 +32,14 @@ export interface WorldTransform {
|
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Component types provided by the transform bundle.
|
|
35
|
-
*
|
|
35
|
+
* Included automatically via `.withBundle(createTransformBundle())`.
|
|
36
36
|
*
|
|
37
37
|
* @example
|
|
38
38
|
* ```typescript
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* velocity: { x: number; y: number }
|
|
42
|
-
*
|
|
39
|
+
* const ecs = ECSpresso.create()
|
|
40
|
+
* .withBundle(createTransformBundle())
|
|
41
|
+
* .withComponentTypes<{ sprite: Sprite; velocity: { x: number; y: number } }>()
|
|
42
|
+
* .build();
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
export interface TransformComponentTypes {
|
|
@@ -49,10 +49,10 @@ export interface TransformComponentTypes {
|
|
|
49
49
|
/**
|
|
50
50
|
* Configuration options for the transform bundle.
|
|
51
51
|
*/
|
|
52
|
-
export interface TransformBundleOptions {
|
|
52
|
+
export interface TransformBundleOptions<G extends string = 'transform'> {
|
|
53
53
|
/** System group name (default: 'transform') */
|
|
54
|
-
systemGroup?:
|
|
55
|
-
/** Priority for transform propagation (default: 500, runs after physics
|
|
54
|
+
systemGroup?: G;
|
|
55
|
+
/** Priority for transform propagation (default: 500, runs after physics) */
|
|
56
56
|
priority?: number;
|
|
57
57
|
/** Execution phase (default: 'postUpdate') */
|
|
58
58
|
phase?: SystemPhase;
|
|
@@ -138,7 +138,7 @@ export declare function createTransform(x: number, y: number, options?: Transfor
|
|
|
138
138
|
* const ecs = ECSpresso
|
|
139
139
|
* .create<Components, Events, Resources>()
|
|
140
140
|
* .withBundle(createTransformBundle())
|
|
141
|
-
* .withBundle(
|
|
141
|
+
* .withBundle(createPhysics2DBundle())
|
|
142
142
|
* .build();
|
|
143
143
|
*
|
|
144
144
|
* // Spawn entity with transform
|
|
@@ -148,4 +148,4 @@ export declare function createTransform(x: number, y: number, options?: Transfor
|
|
|
148
148
|
* });
|
|
149
149
|
* ```
|
|
150
150
|
*/
|
|
151
|
-
export declare function createTransformBundle(options?: TransformBundleOptions): Bundle<TransformComponentTypes, {}, {}>;
|
|
151
|
+
export declare function createTransformBundle<G extends string = 'transform'>(options?: TransformBundleOptions<G>): Bundle<TransformComponentTypes, {}, {}, {}, {}, 'transform-propagation', G>;
|