ecspresso 0.6.0 → 0.7.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.
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Bounds Bundle for ECSpresso
3
+ *
4
+ * Provides screen bounds enforcement for entities with transforms.
5
+ * Reads worldTransform for position checking; modifies localTransform for corrections.
6
+ * Supports destroy, clamp, and wrap behaviors.
7
+ */
8
+ import Bundle from '../../bundle';
9
+ import type { TransformComponentTypes } from './transform';
10
+ /**
11
+ * Component that marks an entity for destruction when outside bounds.
12
+ */
13
+ export interface DestroyOutOfBounds {
14
+ /** Extra padding beyond bounds before destruction (default: 0) */
15
+ padding?: number;
16
+ }
17
+ /**
18
+ * Component that clamps an entity's position to stay within bounds.
19
+ */
20
+ export interface ClampToBounds {
21
+ /** Margin to shrink the valid area (default: 0) */
22
+ margin?: number;
23
+ }
24
+ /**
25
+ * Component that wraps an entity's position to the opposite edge.
26
+ */
27
+ export interface WrapAtBounds {
28
+ /** Padding beyond bounds before wrapping (default: 0) */
29
+ padding?: number;
30
+ }
31
+ /**
32
+ * Component types provided by the bounds bundle.
33
+ * Extend your component types with this interface.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * interface GameComponents extends TransformComponentTypes, BoundsComponentTypes {
38
+ * sprite: Sprite;
39
+ * }
40
+ * ```
41
+ */
42
+ export interface BoundsComponentTypes {
43
+ destroyOutOfBounds: DestroyOutOfBounds;
44
+ clampToBounds: ClampToBounds;
45
+ wrapAtBounds: WrapAtBounds;
46
+ }
47
+ /**
48
+ * Bounds rectangle definition.
49
+ */
50
+ export interface BoundsRect {
51
+ /** Left edge x coordinate (default: 0) */
52
+ x?: number;
53
+ /** Top edge y coordinate (default: 0) */
54
+ y?: number;
55
+ /** Width of the bounds area */
56
+ width: number;
57
+ /** Height of the bounds area */
58
+ height: number;
59
+ }
60
+ /**
61
+ * Resource types provided by the bounds bundle.
62
+ */
63
+ export interface BoundsResourceTypes {
64
+ bounds: BoundsRect;
65
+ }
66
+ /**
67
+ * Event fired when an entity exits bounds.
68
+ */
69
+ export interface EntityOutOfBoundsEvent {
70
+ /** The entity that exited bounds */
71
+ entityId: number;
72
+ /** The edge the entity exited through */
73
+ exitEdge: 'top' | 'bottom' | 'left' | 'right';
74
+ }
75
+ /**
76
+ * Event types provided by the bounds bundle.
77
+ */
78
+ export interface BoundsEventTypes {
79
+ entityOutOfBounds: EntityOutOfBoundsEvent;
80
+ }
81
+ /**
82
+ * Configuration options for the bounds bundle.
83
+ */
84
+ export interface BoundsBundleOptions {
85
+ /** System group name (default: 'physics') */
86
+ systemGroup?: string;
87
+ /** Priority for bounds systems (default: 50) */
88
+ priority?: number;
89
+ /** Resource key for bounds rectangle (default: 'bounds') */
90
+ boundsResourceKey?: string;
91
+ /** Whether to auto-remove entities when out of bounds (default: true) */
92
+ autoRemove?: boolean;
93
+ }
94
+ /**
95
+ * Create a bounds rectangle resource.
96
+ *
97
+ * @param width The width of the bounds area
98
+ * @param height The height of the bounds area
99
+ * @param x The left edge x coordinate (default: 0)
100
+ * @param y The top edge y coordinate (default: 0)
101
+ * @returns Bounds rectangle suitable for use as a resource
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * ECSpresso.create()
106
+ * .withResource('bounds', createBounds(800, 600))
107
+ * .build();
108
+ * ```
109
+ */
110
+ export declare function createBounds(width: number, height: number, x?: number, y?: number): BoundsRect;
111
+ /**
112
+ * Create a destroyOutOfBounds component.
113
+ *
114
+ * @param padding Extra padding beyond bounds before destruction
115
+ * @returns Component object suitable for spreading into spawn()
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * ecs.spawn({
120
+ * ...createTransform(100, 200),
121
+ * ...createDestroyOutOfBounds(20),
122
+ * });
123
+ * ```
124
+ */
125
+ export declare function createDestroyOutOfBounds(padding?: number): Pick<BoundsComponentTypes, 'destroyOutOfBounds'>;
126
+ /**
127
+ * Create a clampToBounds component.
128
+ *
129
+ * @param margin Margin to shrink the valid area
130
+ * @returns Component object suitable for spreading into spawn()
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * ecs.spawn({
135
+ * ...createTransform(100, 200),
136
+ * ...createClampToBounds(30),
137
+ * });
138
+ * ```
139
+ */
140
+ export declare function createClampToBounds(margin?: number): Pick<BoundsComponentTypes, 'clampToBounds'>;
141
+ /**
142
+ * Create a wrapAtBounds component.
143
+ *
144
+ * @param padding Padding beyond bounds before wrapping
145
+ * @returns Component object suitable for spreading into spawn()
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * ecs.spawn({
150
+ * ...createTransform(100, 200),
151
+ * ...createWrapAtBounds(10),
152
+ * });
153
+ * ```
154
+ */
155
+ export declare function createWrapAtBounds(padding?: number): Pick<BoundsComponentTypes, 'wrapAtBounds'>;
156
+ type CombinedComponentTypes = BoundsComponentTypes & TransformComponentTypes;
157
+ /**
158
+ * Create a bounds bundle for ECSpresso.
159
+ *
160
+ * This bundle provides:
161
+ * - Destroy out of bounds system - removes entities that exit bounds
162
+ * - Clamp to bounds system - constrains entities within bounds
163
+ * - Wrap at bounds system - wraps entities to opposite edge
164
+ *
165
+ * Uses worldTransform for position checking (world-space) and modifies
166
+ * localTransform for corrections. Works best with entities that don't
167
+ * have parent transforms (orphan entities).
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const ecs = ECSpresso
172
+ * .create<Components, Events, Resources>()
173
+ * .withResource('bounds', createBounds(800, 600))
174
+ * .withBundle(createTransformBundle())
175
+ * .withBundle(createBoundsBundle())
176
+ * .build();
177
+ *
178
+ * // Entity that gets destroyed when leaving screen
179
+ * ecs.spawn({
180
+ * ...createTransform(100, 200),
181
+ * ...createDestroyOutOfBounds(),
182
+ * });
183
+ * ```
184
+ */
185
+ export declare function createBoundsBundle<ResourceTypes extends BoundsResourceTypes = BoundsResourceTypes>(options?: BoundsBundleOptions): Bundle<CombinedComponentTypes, BoundsEventTypes, ResourceTypes>;
186
+ export {};
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Collision Bundle for ECSpresso
3
+ *
4
+ * Provides layer-based collision detection with events.
5
+ * Uses worldTransform for position (world-space collision).
6
+ * Supports AABB and circle colliders.
7
+ */
8
+ import Bundle from '../../bundle';
9
+ import type { TransformComponentTypes } from './transform';
10
+ /**
11
+ * Axis-Aligned Bounding Box collider.
12
+ */
13
+ export interface AABBCollider {
14
+ /** Width of the bounding box */
15
+ width: number;
16
+ /** Height of the bounding box */
17
+ height: number;
18
+ /** X offset from entity position (default: 0) */
19
+ offsetX?: number;
20
+ /** Y offset from entity position (default: 0) */
21
+ offsetY?: number;
22
+ }
23
+ /**
24
+ * Circle collider.
25
+ */
26
+ export interface CircleCollider {
27
+ /** Radius of the circle */
28
+ radius: number;
29
+ /** X offset from entity position (default: 0) */
30
+ offsetX?: number;
31
+ /** Y offset from entity position (default: 0) */
32
+ offsetY?: number;
33
+ }
34
+ /**
35
+ * Collision layer configuration.
36
+ */
37
+ export interface CollisionLayer {
38
+ /** The layer this entity belongs to */
39
+ layer: string;
40
+ /** Layers this entity can collide with */
41
+ collidesWith: readonly string[];
42
+ }
43
+ /**
44
+ * Component types provided by the collision bundle.
45
+ * Extend your component types with this interface.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * interface GameComponents extends TransformComponentTypes, CollisionComponentTypes {
50
+ * sprite: Sprite;
51
+ * enemy: boolean;
52
+ * }
53
+ * ```
54
+ */
55
+ export interface CollisionComponentTypes {
56
+ aabbCollider: AABBCollider;
57
+ circleCollider: CircleCollider;
58
+ collisionLayer: CollisionLayer;
59
+ }
60
+ /**
61
+ * Event fired when two entities collide.
62
+ */
63
+ export interface CollisionEvent {
64
+ /** First entity in the collision */
65
+ entityA: number;
66
+ /** Second entity in the collision */
67
+ entityB: number;
68
+ /** Layer of the first entity */
69
+ layerA: string;
70
+ /** Layer of the second entity */
71
+ layerB: string;
72
+ }
73
+ /**
74
+ * Event types provided by the collision bundle.
75
+ */
76
+ export interface CollisionEventTypes {
77
+ collision: CollisionEvent;
78
+ }
79
+ /**
80
+ * Configuration options for the collision bundle.
81
+ */
82
+ export interface CollisionBundleOptions {
83
+ /** System group name (default: 'physics') */
84
+ systemGroup?: string;
85
+ /** Priority for collision system (default: 0) */
86
+ priority?: number;
87
+ /** Name of the collision event (default: 'collision') */
88
+ collisionEventName?: string;
89
+ }
90
+ /**
91
+ * Create an AABB collider component.
92
+ *
93
+ * @param width Width of the bounding box
94
+ * @param height Height of the bounding box
95
+ * @param offsetX X offset from entity position
96
+ * @param offsetY Y offset from entity position
97
+ * @returns Component object suitable for spreading into spawn()
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * ecs.spawn({
102
+ * ...createTransform(100, 200),
103
+ * ...createAABBCollider(50, 30),
104
+ * });
105
+ * ```
106
+ */
107
+ export declare function createAABBCollider(width: number, height: number, offsetX?: number, offsetY?: number): Pick<CollisionComponentTypes, 'aabbCollider'>;
108
+ /**
109
+ * Create a circle collider component.
110
+ *
111
+ * @param radius Radius of the circle
112
+ * @param offsetX X offset from entity position
113
+ * @param offsetY Y offset from entity position
114
+ * @returns Component object suitable for spreading into spawn()
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * ecs.spawn({
119
+ * ...createTransform(100, 200),
120
+ * ...createCircleCollider(25),
121
+ * });
122
+ * ```
123
+ */
124
+ export declare function createCircleCollider(radius: number, offsetX?: number, offsetY?: number): Pick<CollisionComponentTypes, 'circleCollider'>;
125
+ /**
126
+ * Create a collision layer component.
127
+ *
128
+ * @param layer The layer this entity belongs to
129
+ * @param collidesWith Layers this entity can collide with
130
+ * @returns Component object suitable for spreading into spawn()
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * ecs.spawn({
135
+ * ...createTransform(100, 200),
136
+ * ...createAABBCollider(50, 30),
137
+ * ...createCollisionLayer('player', ['enemy', 'obstacle']),
138
+ * });
139
+ * ```
140
+ */
141
+ export declare function createCollisionLayer(layer: string, collidesWith: readonly string[]): Pick<CollisionComponentTypes, 'collisionLayer'>;
142
+ /**
143
+ * Layer factory result from defineCollisionLayers.
144
+ */
145
+ export type LayerFactories<T extends Record<string, readonly string[]>> = {
146
+ [K in keyof T]: () => Pick<CollisionComponentTypes, 'collisionLayer'>;
147
+ };
148
+ /**
149
+ * Define collision layer relationships and get factory functions.
150
+ *
151
+ * @param rules Object mapping layer names to arrays of layers they collide with
152
+ * @returns Object with factory functions for each layer
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * const layers = defineCollisionLayers({
157
+ * player: ['enemy', 'enemyProjectile'],
158
+ * playerProjectile: ['enemy'],
159
+ * enemy: ['playerProjectile'],
160
+ * enemyProjectile: ['player'],
161
+ * });
162
+ *
163
+ * // Usage
164
+ * ecs.spawn({
165
+ * ...createTransform(100, 200),
166
+ * ...createAABBCollider(50, 30),
167
+ * ...layers.player(),
168
+ * });
169
+ * ```
170
+ */
171
+ export declare function defineCollisionLayers<T extends Record<string, readonly string[]>>(rules: T): LayerFactories<T>;
172
+ type CombinedComponentTypes = CollisionComponentTypes & TransformComponentTypes;
173
+ /**
174
+ * Create a collision bundle for ECSpresso.
175
+ *
176
+ * This bundle provides:
177
+ * - O(n²) collision detection between entities with colliders
178
+ * - AABB-AABB, circle-circle, and AABB-circle collision
179
+ * - Layer-based filtering for collision pairs
180
+ * - Deduplication of A-B / B-A collisions
181
+ *
182
+ * Uses worldTransform for position (world-space collision detection).
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * const ecs = ECSpresso
187
+ * .create<Components, Events, Resources>()
188
+ * .withBundle(createTransformBundle())
189
+ * .withBundle(createCollisionBundle())
190
+ * .build();
191
+ *
192
+ * // Entity with collision
193
+ * ecs.spawn({
194
+ * ...createTransform(100, 200),
195
+ * ...createAABBCollider(50, 30),
196
+ * ...createCollisionLayer('player', ['enemy']),
197
+ * });
198
+ * ```
199
+ */
200
+ export declare function createCollisionBundle(options?: CollisionBundleOptions): Bundle<CombinedComponentTypes, CollisionEventTypes, {}>;
201
+ export {};
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Movement Bundle for ECSpresso
3
+ *
4
+ * Provides velocity → localTransform integration for entities.
5
+ * Works with the transform bundle's localTransform/worldTransform system.
6
+ */
7
+ import Bundle from '../../bundle';
8
+ import type { TransformComponentTypes } from './transform';
9
+ /**
10
+ * Velocity component data structure.
11
+ */
12
+ export interface Velocity {
13
+ x: number;
14
+ y: number;
15
+ }
16
+ /**
17
+ * Component types provided by the movement bundle.
18
+ * Extend your component types with this interface.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * interface GameComponents extends TransformComponentTypes, MovementComponentTypes {
23
+ * sprite: Sprite;
24
+ * player: boolean;
25
+ * }
26
+ * ```
27
+ */
28
+ export interface MovementComponentTypes extends TransformComponentTypes {
29
+ velocity: Velocity;
30
+ }
31
+ /**
32
+ * Configuration options for the movement bundle.
33
+ */
34
+ export interface MovementBundleOptions {
35
+ /** System group name (default: 'physics') */
36
+ systemGroup?: string;
37
+ /** Priority for movement update system (default: 1000, runs early before transform propagation) */
38
+ priority?: number;
39
+ }
40
+ /**
41
+ * Create a velocity component.
42
+ *
43
+ * @param x The x velocity
44
+ * @param y The y velocity
45
+ * @returns Component object suitable for spreading into spawn()
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * ecs.spawn({
50
+ * ...createTransform(100, 200),
51
+ * ...createVelocity(50, -25),
52
+ * projectile: true,
53
+ * });
54
+ * ```
55
+ */
56
+ export declare function createVelocity(x: number, y: number): Pick<MovementComponentTypes, 'velocity'>;
57
+ /**
58
+ * Create a movement bundle for ECSpresso.
59
+ *
60
+ * This bundle provides:
61
+ * - Movement update system that integrates velocity into localTransform
62
+ * - Processes all entities with both localTransform and velocity components
63
+ *
64
+ * Note: This bundle modifies localTransform. The transform bundle's propagation
65
+ * system will then compute worldTransform for use by other systems.
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const ecs = ECSpresso
70
+ * .create<Components, Events, Resources>()
71
+ * .withBundle(createTransformBundle())
72
+ * .withBundle(createMovementBundle())
73
+ * .build();
74
+ *
75
+ * // Spawn entity with movement
76
+ * ecs.spawn({
77
+ * ...createTransform(100, 200),
78
+ * ...createVelocity(50, -25),
79
+ * sprite,
80
+ * });
81
+ * ```
82
+ */
83
+ export declare function createMovementBundle(options?: MovementBundleOptions): Bundle<MovementComponentTypes, {}, {}>;
@@ -5,11 +5,40 @@
5
5
  * Timers are components processed each frame, automatically cleaned up when entities are removed.
6
6
  */
7
7
  import Bundle from '../../bundle';
8
+ /**
9
+ * Data structure published when a timer completes.
10
+ * Use this type when defining timer completion events in your EventTypes interface.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * interface Events {
15
+ * hideMessage: TimerEventData;
16
+ * spawnWave: TimerEventData;
17
+ * }
18
+ * ```
19
+ */
20
+ export interface TimerEventData {
21
+ /** The entity ID that the timer belongs to */
22
+ entityId: number;
23
+ /** The timer's configured duration in seconds */
24
+ duration: number;
25
+ /** The actual elapsed time (may exceed duration slightly) */
26
+ elapsed: number;
27
+ }
28
+ /**
29
+ * Extracts event names from EventTypes that have TimerEventData as their payload.
30
+ * This ensures only compatible events can be used with timer.onComplete.
31
+ */
32
+ export type TimerEventName<EventTypes extends Record<string, any>> = {
33
+ [K in keyof EventTypes]: EventTypes[K] extends TimerEventData ? K : never;
34
+ }[keyof EventTypes];
8
35
  /**
9
36
  * Timer component data structure.
10
37
  * Use `justFinished` to detect timer completion in your systems.
38
+ *
39
+ * @template EventTypes The event types from your ECS
11
40
  */
12
- export interface Timer {
41
+ export interface Timer<EventTypes extends Record<string, any>> {
13
42
  /** Time accumulated so far (seconds) */
14
43
  elapsed: number;
15
44
  /** Target duration (seconds) */
@@ -20,21 +49,25 @@ export interface Timer {
20
49
  active: boolean;
21
50
  /** True for one frame after timer completes */
22
51
  justFinished: boolean;
52
+ /** Optional event name to publish when timer completes. Must be an event with TimerEventData payload. */
53
+ onComplete?: TimerEventName<EventTypes>;
23
54
  }
24
55
  /**
25
56
  * Component types provided by the timer bundle.
26
57
  * Extend your component types with this interface.
27
58
  *
59
+ * @template EventTypes The event types from your ECS
60
+ *
28
61
  * @example
29
62
  * ```typescript
30
- * interface GameComponents extends TimerComponentTypes {
63
+ * interface GameComponents extends TimerComponentTypes<GameEvents> {
31
64
  * velocity: { x: number; y: number };
32
65
  * player: true;
33
66
  * }
34
67
  * ```
35
68
  */
36
- export interface TimerComponentTypes {
37
- timer: Timer;
69
+ export interface TimerComponentTypes<EventTypes extends Record<string, any>> {
70
+ timer: Timer<EventTypes>;
38
71
  }
39
72
  /**
40
73
  * Configuration options for the timer bundle.
@@ -45,38 +78,61 @@ export interface TimerBundleOptions {
45
78
  /** Priority for timer update system (default: 0) */
46
79
  priority?: number;
47
80
  }
81
+ /**
82
+ * Options for timer creation
83
+ *
84
+ * @template EventTypes The event types from your ECS
85
+ */
86
+ export interface TimerOptions<EventTypes extends Record<string, any>> {
87
+ /** Event name to publish when timer completes. Must be an event with TimerEventData payload. */
88
+ onComplete?: TimerEventName<EventTypes>;
89
+ }
48
90
  /**
49
91
  * Create a one-shot timer that fires once after the specified duration.
50
92
  *
93
+ * @template EventTypes The event types from your ECS (must be explicitly provided)
51
94
  * @param duration Duration in seconds until the timer completes
95
+ * @param options Optional configuration including event name
52
96
  * @returns Component object suitable for spreading into spawn()
53
97
  *
54
98
  * @example
55
99
  * ```typescript
56
- * // Timer that triggers after 2 seconds
100
+ * // Timer without event
57
101
  * ecs.spawn({
58
- * ...createTimer(2),
102
+ * ...createTimer<GameEvents>(2),
59
103
  * explosion: true,
60
104
  * });
105
+ *
106
+ * // Timer that publishes an event on completion
107
+ * ecs.spawn({
108
+ * ...createTimer<GameEvents>(1.5, { onComplete: 'hideMessage' }),
109
+ * });
61
110
  * ```
62
111
  */
63
- export declare function createTimer(duration: number): Pick<TimerComponentTypes, 'timer'>;
112
+ export declare function createTimer<EventTypes extends Record<string, any>>(duration: number, options?: TimerOptions<EventTypes>): Pick<TimerComponentTypes<EventTypes>, 'timer'>;
64
113
  /**
65
114
  * Create a repeating timer that fires every `duration` seconds.
66
115
  *
116
+ * @template EventTypes The event types from your ECS (must be explicitly provided)
67
117
  * @param duration Duration in seconds between each timer completion
118
+ * @param options Optional configuration including event name
68
119
  * @returns Component object suitable for spreading into spawn()
69
120
  *
70
121
  * @example
71
122
  * ```typescript
72
- * // Timer that triggers every 5 seconds
123
+ * // Timer without event
73
124
  * ecs.spawn({
74
- * ...createRepeatingTimer(5),
125
+ * ...createRepeatingTimer<GameEvents>(5),
75
126
  * spawner: true,
76
127
  * });
128
+ *
129
+ * // Repeating timer that publishes an event each cycle
130
+ * ecs.spawn({
131
+ * ...createRepeatingTimer<GameEvents>(3, { onComplete: 'spawnWave' }),
132
+ * });
77
133
  * ```
78
134
  */
79
- export declare function createRepeatingTimer(duration: number): Pick<TimerComponentTypes, 'timer'>;
135
+ export declare function createRepeatingTimer<EventTypes extends Record<string, any>>(duration: number, options?: TimerOptions<EventTypes>): Pick<TimerComponentTypes<EventTypes>, 'timer'>;
80
136
  /**
81
137
  * Create a timer bundle for ECSpresso.
82
138
  *
@@ -110,4 +166,4 @@ export declare function createRepeatingTimer(duration: number): Pick<TimerCompon
110
166
  * });
111
167
  * ```
112
168
  */
113
- export declare function createTimerBundle(options?: TimerBundleOptions): Bundle<TimerComponentTypes, {}, {}>;
169
+ export declare function createTimerBundle<EventTypes extends Record<string, any>>(options?: TimerBundleOptions): Bundle<TimerComponentTypes<EventTypes>, EventTypes, {}>;
@@ -1,4 +1,4 @@
1
- var Z=Object.create;var{getPrototypeOf:_,defineProperty:V,getOwnPropertyNames:$}=Object;var C=Object.prototype.hasOwnProperty;var q=(j,x,F)=>{F=j!=null?Z(_(j)):{};let J=x||!j||!j.__esModule?V(F,"default",{value:j,enumerable:!0}):F;for(let H of $(j))if(!C.call(J,H))V(J,H,{get:()=>j[H],enumerable:!0});return J};var z=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(x,F)=>(typeof require<"u"?require:x)[F]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});class U{_label;_ecspresso;_bundle;queries={};processFunction;detachFunction;initializeFunction;eventHandlers;_priority=0;_isRegistered=!1;_groups=[];_inScreens;_excludeScreens;_requiredAssets;constructor(j,x=null,F=null){this._label=j;this._ecspresso=x;this._bundle=F}get label(){return this._label}get bundle(){return this._bundle}get ecspresso(){return this._ecspresso}_autoRegister(){if(this._isRegistered||!this._ecspresso)return;let j=this._buildSystemObject();R(j,this._ecspresso),this._isRegistered=!0}_buildSystemObject(){return this._createSystemObject()}_createSystemObject(){let j={label:this._label,entityQueries:this.queries,priority:this._priority};if(this.processFunction)j.process=this.processFunction;if(this.detachFunction)j.onDetach=this.detachFunction;if(this.initializeFunction)j.onInitialize=this.initializeFunction;if(this.eventHandlers)j.eventHandlers=this.eventHandlers;if(this._groups.length>0)j.groups=[...this._groups];if(this._inScreens)j.inScreens=this._inScreens;if(this._excludeScreens)j.excludeScreens=this._excludeScreens;if(this._requiredAssets)j.requiredAssets=this._requiredAssets;return j}setPriority(j){return this._priority=j,this}inGroup(j){if(!this._groups.includes(j))this._groups.push(j);return this}inScreens(j){return this._inScreens=[...j],this}excludeScreens(j){return this._excludeScreens=[...j],this}requiresAssets(j){return this._requiredAssets=[...j],this}addQuery(j,x){let F=this;return F.queries={...this.queries,[j]:x},F}setProcess(j){return this.processFunction=j,this}registerAndContinue(){if(!this._ecspresso)throw Error(`Cannot register system '${this._label}': SystemBuilder is not attached to an ECSpresso instance. Use Bundle.addSystem() or ECSpresso.addSystem() instead.`);return this._autoRegister(),this._ecspresso}and(){if(this._ecspresso)return this._autoRegister(),this._ecspresso;if(this._bundle)return this._bundle;throw Error(`Cannot use and() on system '${this._label}': not attached to ECSpresso or Bundle.`)}setOnDetach(j){return this.detachFunction=j,this}setOnInitialize(j){return this.initializeFunction=j,this}setEventHandlers(j){return this.eventHandlers=j,this}build(j){let x=this._createSystemObject();if(this._ecspresso)R(x,this._ecspresso);if(j)R(x,j);return this}}function R(j,x){x._registerSystem(j)}function P(j,x){return new U(j,x)}function X(j,x){return new U(j,null,x)}function G(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class Q{_systems=[];_resources=new Map;_assets=new Map;_assetGroups=new Map;_screens=new Map;_id;constructor(j){this._id=j||G()}get id(){return this._id}set id(j){this._id=j}addSystem(j){if(typeof j==="string"){let x=X(j,this);return this._systems.push(x),x}else return this._systems.push(j),j}addResource(j,x){return this._resources.set(j,x),this}addAsset(j,x,F){return this._assets.set(j,{loader:x,eager:F?.eager??!0,group:F?.group}),this}addAssetGroup(j,x){let F=new Map;for(let[J,H]of Object.entries(x))F.set(J,H),this._assets.set(J,{loader:H,eager:!1,group:j});return this._assetGroups.set(j,F),this}addScreen(j,x){return this._screens.set(j,x),this}getAssets(){return new Map(this._assets)}getScreens(){return new Map(this._screens)}_setResource(j,x){this._resources.set(j,x)}_setAsset(j,x){this._assets.set(j,x)}_setScreen(j,x){this._screens.set(j,x)}getSystems(){return this._systems.map((j)=>j.build())}registerSystemsWithEcspresso(j){for(let x of this._systems)x.build(j)}getResources(){return new Map(this._resources)}getResource(j){return this._resources.get(j)}getSystemBuilders(){return[...this._systems]}hasResource(j){return this._resources.has(j)}}function E(j,...x){if(x.length===0)return new Q(j);let F=new Q(j);for(let J of x){for(let H of J.getSystemBuilders())F.addSystem(H);for(let[H,M]of J.getResources().entries())F._setResource(H,M);for(let[H,M]of J.getAssets().entries())F._setAsset(H,M);for(let[H,M]of J.getScreens().entries())F._setScreen(H,M)}return F}function A(j){return{timer:{elapsed:0,duration:j,repeat:!1,active:!0,justFinished:!1}}}function L(j){return{timer:{elapsed:0,duration:j,repeat:!0,active:!0,justFinished:!1}}}function N(j){let{systemGroup:x="timers",priority:F=0}=j??{},J=new Q("timers");return J.addSystem("timer-update").setPriority(F).inGroup(x).addQuery("timers",{with:["timer"]}).setProcess((H,M)=>{for(let Y of H.timers){let{timer:K}=Y.components;if(K.justFinished=!1,!K.active)continue;if(K.elapsed+=M,K.elapsed>=K.duration)if(K.justFinished=!0,K.repeat)K.elapsed-=K.duration;else K.active=!1}}).and(),J}export{N as createTimerBundle,A as createTimer,L as createRepeatingTimer};
1
+ var $=Object.create;var{getPrototypeOf:G,defineProperty:Z,getOwnPropertyNames:q}=Object;var z=Object.prototype.hasOwnProperty;var P=(j,x,F)=>{F=j!=null?$(G(j)):{};let K=x||!j||!j.__esModule?Z(F,"default",{value:j,enumerable:!0}):F;for(let H of q(j))if(!z.call(K,H))Z(K,H,{get:()=>j[H],enumerable:!0});return K};var W=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(x,F)=>(typeof require<"u"?require:x)[F]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});class Y{_label;_ecspresso;_bundle;queries={};processFunction;detachFunction;initializeFunction;eventHandlers;_priority=0;_isRegistered=!1;_groups=[];_inScreens;_excludeScreens;_requiredAssets;constructor(j,x=null,F=null){this._label=j;this._ecspresso=x;this._bundle=F}get label(){return this._label}get bundle(){return this._bundle}get ecspresso(){return this._ecspresso}_autoRegister(){if(this._isRegistered||!this._ecspresso)return;let j=this._buildSystemObject();X(j,this._ecspresso),this._isRegistered=!0}_buildSystemObject(){return this._createSystemObject()}_createSystemObject(){let j={label:this._label,entityQueries:this.queries,priority:this._priority};if(this.processFunction)j.process=this.processFunction;if(this.detachFunction)j.onDetach=this.detachFunction;if(this.initializeFunction)j.onInitialize=this.initializeFunction;if(this.eventHandlers)j.eventHandlers=this.eventHandlers;if(this._groups.length>0)j.groups=[...this._groups];if(this._inScreens)j.inScreens=this._inScreens;if(this._excludeScreens)j.excludeScreens=this._excludeScreens;if(this._requiredAssets)j.requiredAssets=this._requiredAssets;return j}setPriority(j){return this._priority=j,this}inGroup(j){if(!this._groups.includes(j))this._groups.push(j);return this}inScreens(j){return this._inScreens=[...j],this}excludeScreens(j){return this._excludeScreens=[...j],this}requiresAssets(j){return this._requiredAssets=[...j],this}addQuery(j,x){let F=this;return F.queries={...this.queries,[j]:x},F}setProcess(j){return this.processFunction=j,this}registerAndContinue(){if(!this._ecspresso)throw Error(`Cannot register system '${this._label}': SystemBuilder is not attached to an ECSpresso instance. Use Bundle.addSystem() or ECSpresso.addSystem() instead.`);return this._autoRegister(),this._ecspresso}and(){if(this._ecspresso)return this._autoRegister(),this._ecspresso;if(this._bundle)return this._bundle;throw Error(`Cannot use and() on system '${this._label}': not attached to ECSpresso or Bundle.`)}setOnDetach(j){return this.detachFunction=j,this}setOnInitialize(j){return this.initializeFunction=j,this}setEventHandlers(j){return this.eventHandlers=j,this}build(j){let x=this._createSystemObject();if(this._ecspresso)X(x,this._ecspresso);if(j)X(x,j);return this}}function X(j,x){x._registerSystem(j)}function D(j,x){return new Y(j,x)}function _(j,x){return new Y(j,null,x)}function C(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class U{_systems=[];_resources=new Map;_assets=new Map;_assetGroups=new Map;_screens=new Map;_id;constructor(j){this._id=j||C()}get id(){return this._id}set id(j){this._id=j}addSystem(j){if(typeof j==="string"){let x=_(j,this);return this._systems.push(x),x}else return this._systems.push(j),j}addResource(j,x){return this._resources.set(j,x),this}addAsset(j,x,F){return this._assets.set(j,{loader:x,eager:F?.eager??!0,group:F?.group}),this}addAssetGroup(j,x){let F=new Map;for(let[K,H]of Object.entries(x))F.set(K,H),this._assets.set(K,{loader:H,eager:!1,group:j});return this._assetGroups.set(j,F),this}addScreen(j,x){return this._screens.set(j,x),this}getAssets(){return new Map(this._assets)}getScreens(){return new Map(this._screens)}_setResource(j,x){this._resources.set(j,x)}_setAsset(j,x){this._assets.set(j,x)}_setScreen(j,x){this._screens.set(j,x)}getSystems(){return this._systems.map((j)=>j.build())}registerSystemsWithEcspresso(j){for(let x of this._systems)x.build(j)}getResources(){return new Map(this._resources)}getResource(j){return this._resources.get(j)}getSystemBuilders(){return[...this._systems]}hasResource(j){return this._resources.has(j)}}function O(j,...x){if(x.length===0)return new U(j);let F=new U(j);for(let K of x){for(let H of K.getSystemBuilders())F.addSystem(H);for(let[H,M]of K.getResources().entries())F._setResource(H,M);for(let[H,M]of K.getAssets().entries())F._setAsset(H,M);for(let[H,M]of K.getScreens().entries())F._setScreen(H,M)}return F}function T(j,x){return{timer:{elapsed:0,duration:j,repeat:!1,active:!0,justFinished:!1,onComplete:x?.onComplete}}}function S(j,x){return{timer:{elapsed:0,duration:j,repeat:!0,active:!0,justFinished:!1,onComplete:x?.onComplete}}}function k(j){let{systemGroup:x="timers",priority:F=0}=j??{},K=new U("timers");K.addSystem("timer-update").setPriority(F).inGroup(x).addQuery("timers",{with:["timer"]}).setProcess((M,V,Q)=>{for(let R of M.timers){let{timer:J}=R.components;if(J.justFinished=!1,!J.active)continue;if(J.elapsed+=V,J.elapsed<J.duration)continue;if(J.repeat)while(J.elapsed>=J.duration)J.justFinished=!0,H(Q,R.id,J),J.elapsed-=J.duration;else J.justFinished=!0,H(Q,R.id,J),J.active=!1,Q.commands.removeEntity(R.id)}}).and();function H(M,V,Q){if(!Q.onComplete)return;let R={entityId:V,duration:Q.duration,elapsed:Q.elapsed};M.eventBus.publish(Q.onComplete,R)}return K}export{k as createTimerBundle,T as createTimer,S as createRepeatingTimer};
2
2
 
3
- //# debugId=13DBE68984ED14A764756E2164756E21
3
+ //# debugId=8A5D1E808447563C64756E2164756E21
4
4
  //# sourceMappingURL=timers.js.map