ecspresso 0.5.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.
- package/README.md +379 -9
- package/dist/bundle.d.ts +5 -2
- package/dist/bundles/renderers/pixi.d.ts +235 -0
- package/dist/bundles/renderers/pixi.js +4 -0
- package/dist/bundles/renderers/pixi.js.map +13 -0
- package/dist/bundles/utils/bounds.d.ts +186 -0
- package/dist/bundles/utils/collision.d.ts +201 -0
- package/dist/bundles/utils/movement.d.ts +83 -0
- package/dist/bundles/utils/timers.d.ts +169 -0
- package/dist/bundles/utils/timers.js +4 -0
- package/dist/bundles/utils/timers.js.map +12 -0
- package/dist/bundles/utils/transform.d.ts +148 -0
- package/dist/command-buffer.d.ts +90 -0
- package/dist/ecspresso.d.ts +137 -3
- package/dist/entity-manager.d.ts +19 -3
- package/dist/hierarchy-manager.d.ts +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +13 -11
- package/dist/reactive-query-manager.d.ts +59 -0
- package/dist/resource-manager.d.ts +37 -5
- package/dist/system-builder.d.ts +8 -0
- package/dist/types.d.ts +22 -0
- package/package.json +23 -3
package/dist/ecspresso.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ import EntityManager from "./entity-manager";
|
|
|
2
2
|
import EventBus from "./event-bus";
|
|
3
3
|
import AssetManager from "./asset-manager";
|
|
4
4
|
import ScreenManager from "./screen-manager";
|
|
5
|
-
import
|
|
5
|
+
import { type ReactiveQueryDefinition } from "./reactive-query-manager";
|
|
6
|
+
import CommandBuffer from "./command-buffer";
|
|
7
|
+
import type { System, FilteredEntity, Entity, RemoveEntityOptions, HierarchyEntry, HierarchyIteratorOptions } from "./types";
|
|
6
8
|
import type Bundle from "./bundle";
|
|
7
9
|
import type { BundlesAreCompatible } from "./type-utils";
|
|
8
10
|
import type { AssetHandle, AssetConfigurator } from "./asset-types";
|
|
@@ -30,22 +32,33 @@ export default class ECSpresso<ComponentTypes extends Record<string, any> = {},
|
|
|
30
32
|
private _eventBus;
|
|
31
33
|
/** Access/modify registered resources*/
|
|
32
34
|
private _resourceManager;
|
|
35
|
+
/** Command buffer for deferred structural changes */
|
|
36
|
+
private _commandBuffer;
|
|
33
37
|
/** Registered systems that will be updated in order*/
|
|
34
38
|
private _systems;
|
|
35
39
|
/** Cached sorted systems for efficient updates */
|
|
36
40
|
private _sortedSystems;
|
|
37
41
|
/** Track installed bundles to prevent duplicates*/
|
|
38
42
|
private _installedBundles;
|
|
43
|
+
/** Disabled system groups */
|
|
44
|
+
private _disabledGroups;
|
|
39
45
|
/** Asset manager for loading and accessing assets */
|
|
40
46
|
private _assetManager;
|
|
41
47
|
/** Screen manager for state/screen transitions */
|
|
42
48
|
private _screenManager;
|
|
49
|
+
/** Reactive query manager for enter/exit callbacks */
|
|
50
|
+
private _reactiveQueryManager;
|
|
43
51
|
/** Post-update hooks to be called after all systems in update() */
|
|
44
52
|
private _postUpdateHooks;
|
|
45
53
|
/**
|
|
46
54
|
* Creates a new ECSpresso instance.
|
|
47
55
|
*/
|
|
48
56
|
constructor();
|
|
57
|
+
/**
|
|
58
|
+
* Sets up component lifecycle hooks for reactive query tracking
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
private _setupReactiveQueryHooks;
|
|
49
62
|
/**
|
|
50
63
|
* Creates a new ECSpresso builder for type-safe bundle installation.
|
|
51
64
|
* This is the preferred way to create an ECSpresso instance with bundles.
|
|
@@ -107,6 +120,28 @@ export default class ECSpresso<ComponentTypes extends Record<string, any> = {},
|
|
|
107
120
|
* @returns true if the system was found and updated, false otherwise
|
|
108
121
|
*/
|
|
109
122
|
updateSystemPriority(label: string, priority: number): boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Disable a system group. Systems in this group will be skipped during update().
|
|
125
|
+
* @param groupName The name of the group to disable
|
|
126
|
+
*/
|
|
127
|
+
disableSystemGroup(groupName: string): void;
|
|
128
|
+
/**
|
|
129
|
+
* Enable a system group. Systems in this group will run during update().
|
|
130
|
+
* @param groupName The name of the group to enable
|
|
131
|
+
*/
|
|
132
|
+
enableSystemGroup(groupName: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Check if a system group is enabled.
|
|
135
|
+
* @param groupName The name of the group to check
|
|
136
|
+
* @returns true if the group is enabled (or doesn't exist), false if disabled
|
|
137
|
+
*/
|
|
138
|
+
isSystemGroupEnabled(groupName: string): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Get all system labels that belong to a specific group.
|
|
141
|
+
* @param groupName The name of the group
|
|
142
|
+
* @returns Array of system labels in the group
|
|
143
|
+
*/
|
|
144
|
+
getSystemsInGroup(groupName: string): string[];
|
|
110
145
|
/**
|
|
111
146
|
* Remove a system by its label
|
|
112
147
|
* Calls the system's onDetach method with this ECSpresso instance if defined
|
|
@@ -130,13 +165,29 @@ export default class ECSpresso<ComponentTypes extends Record<string, any> = {},
|
|
|
130
165
|
/**
|
|
131
166
|
* Add a resource to the ECS instance
|
|
132
167
|
*/
|
|
133
|
-
addResource<K extends keyof ResourceTypes>(key: K, resource: ResourceTypes[K] | ((ecs: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>) => ResourceTypes[K] | Promise<ResourceTypes[K]>)
|
|
168
|
+
addResource<K extends keyof ResourceTypes>(key: K, resource: ResourceTypes[K] | ((ecs: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>) => ResourceTypes[K] | Promise<ResourceTypes[K]>) | {
|
|
169
|
+
dependsOn?: readonly string[];
|
|
170
|
+
factory: (ecs: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>) => ResourceTypes[K] | Promise<ResourceTypes[K]>;
|
|
171
|
+
onDispose?: (resource: ResourceTypes[K], ecs?: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>) => void | Promise<void>;
|
|
172
|
+
}): this;
|
|
134
173
|
/**
|
|
135
|
-
* Remove a resource from the ECS instance
|
|
174
|
+
* Remove a resource from the ECS instance (without calling onDispose)
|
|
136
175
|
* @param key The resource key to remove
|
|
137
176
|
* @returns True if the resource was removed, false if it didn't exist
|
|
138
177
|
*/
|
|
139
178
|
removeResource<K extends keyof ResourceTypes>(key: K): boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Dispose a single resource, calling its onDispose callback if defined
|
|
181
|
+
* @param key The resource key to dispose
|
|
182
|
+
* @returns True if the resource existed and was disposed, false if it didn't exist
|
|
183
|
+
*/
|
|
184
|
+
disposeResource<K extends keyof ResourceTypes>(key: K): Promise<boolean>;
|
|
185
|
+
/**
|
|
186
|
+
* Dispose all initialized resources in reverse dependency order.
|
|
187
|
+
* Resources that depend on others are disposed first.
|
|
188
|
+
* Calls each resource's onDispose callback if defined.
|
|
189
|
+
*/
|
|
190
|
+
disposeResources(): Promise<void>;
|
|
140
191
|
/**
|
|
141
192
|
* Update an existing resource using an updater function
|
|
142
193
|
* @param key The resource key to update
|
|
@@ -269,6 +320,20 @@ export default class ECSpresso<ComponentTypes extends Record<string, any> = {},
|
|
|
269
320
|
* @returns Readonly array of root entity IDs
|
|
270
321
|
*/
|
|
271
322
|
getRootEntities(): readonly number[];
|
|
323
|
+
/**
|
|
324
|
+
* Traverse the hierarchy in parent-first (breadth-first) order.
|
|
325
|
+
* Parents are guaranteed to be visited before their children.
|
|
326
|
+
* @param callback Function called for each entity with (entityId, parentId, depth)
|
|
327
|
+
* @param options Optional traversal options (roots to filter to specific subtrees)
|
|
328
|
+
*/
|
|
329
|
+
forEachInHierarchy(callback: (entityId: number, parentId: number | null, depth: number) => void, options?: HierarchyIteratorOptions): void;
|
|
330
|
+
/**
|
|
331
|
+
* Generator-based hierarchy traversal in parent-first (breadth-first) order.
|
|
332
|
+
* Supports early termination via break.
|
|
333
|
+
* @param options Optional traversal options (roots to filter to specific subtrees)
|
|
334
|
+
* @yields HierarchyEntry for each entity in parent-first order
|
|
335
|
+
*/
|
|
336
|
+
hierarchyIterator(options?: HierarchyIteratorOptions): Generator<HierarchyEntry, void, unknown>;
|
|
272
337
|
/**
|
|
273
338
|
* Emit a hierarchy changed event
|
|
274
339
|
* @internal
|
|
@@ -280,6 +345,44 @@ export default class ECSpresso<ComponentTypes extends Record<string, any> = {},
|
|
|
280
345
|
get installedBundles(): string[];
|
|
281
346
|
get entityManager(): EntityManager<ComponentTypes>;
|
|
282
347
|
get eventBus(): EventBus<EventTypes>;
|
|
348
|
+
/**
|
|
349
|
+
* Command buffer for queuing deferred structural changes.
|
|
350
|
+
* Commands are executed automatically at the end of each update() cycle.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```typescript
|
|
354
|
+
* // In a system or event handler
|
|
355
|
+
* ecs.commands.removeEntity(entityId);
|
|
356
|
+
* ecs.commands.spawn({ position: { x: 0, y: 0 } });
|
|
357
|
+
* ```
|
|
358
|
+
*/
|
|
359
|
+
get commands(): CommandBuffer<ComponentTypes, EventTypes, ResourceTypes>;
|
|
360
|
+
/**
|
|
361
|
+
* Register a callback when a specific component is added to any entity
|
|
362
|
+
* @param componentName The component key
|
|
363
|
+
* @param handler Function receiving the new component value and the entity
|
|
364
|
+
* @returns Unsubscribe function to remove the callback
|
|
365
|
+
*/
|
|
366
|
+
onComponentAdded<K extends keyof ComponentTypes>(componentName: K, handler: (value: ComponentTypes[K], entity: Entity<ComponentTypes>) => void): () => void;
|
|
367
|
+
/**
|
|
368
|
+
* Register a callback when a specific component is removed from any entity
|
|
369
|
+
* @param componentName The component key
|
|
370
|
+
* @param handler Function receiving the old component value and the entity
|
|
371
|
+
* @returns Unsubscribe function to remove the callback
|
|
372
|
+
*/
|
|
373
|
+
onComponentRemoved<K extends keyof ComponentTypes>(componentName: K, handler: (oldValue: ComponentTypes[K], entity: Entity<ComponentTypes>) => void): () => void;
|
|
374
|
+
/**
|
|
375
|
+
* Add a reactive query that triggers callbacks when entities enter/exit the query match.
|
|
376
|
+
* @param name Unique name for the query
|
|
377
|
+
* @param definition Query definition with with/without arrays and onEnter/onExit callbacks
|
|
378
|
+
*/
|
|
379
|
+
addReactiveQuery<WithComponents extends keyof ComponentTypes, WithoutComponents extends keyof ComponentTypes = never>(name: string, definition: ReactiveQueryDefinition<ComponentTypes, WithComponents, WithoutComponents>): void;
|
|
380
|
+
/**
|
|
381
|
+
* Remove a reactive query by name.
|
|
382
|
+
* @param name Name of the query to remove
|
|
383
|
+
* @returns true if the query existed and was removed, false otherwise
|
|
384
|
+
*/
|
|
385
|
+
removeReactiveQuery(name: string): boolean;
|
|
283
386
|
/**
|
|
284
387
|
* Subscribe to an event (convenience wrapper for eventBus.subscribe)
|
|
285
388
|
* @param eventType The event type to subscribe to
|
|
@@ -397,6 +500,14 @@ export default class ECSpresso<ComponentTypes extends Record<string, any> = {},
|
|
|
397
500
|
*/
|
|
398
501
|
_installBundle<C extends Record<string, any>, E extends Record<string, any>, R extends Record<string, any>, A extends Record<string, unknown> = {}, S extends Record<string, ScreenDefinition<any, any>> = {}>(bundle: Bundle<C, E, R, A, S>): this;
|
|
399
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* Resource factory with optional dependencies and disposal callback
|
|
505
|
+
*/
|
|
506
|
+
type ResourceFactoryWithDeps<T> = {
|
|
507
|
+
dependsOn?: readonly string[];
|
|
508
|
+
factory: (context?: any) => T | Promise<T>;
|
|
509
|
+
onDispose?: (resource: T, context?: any) => void | Promise<void>;
|
|
510
|
+
};
|
|
400
511
|
/**
|
|
401
512
|
* Builder class for ECSpresso that provides fluent type-safe bundle installation.
|
|
402
513
|
* Handles type checking during build process to ensure type safety.
|
|
@@ -408,6 +519,8 @@ export declare class ECSpressoBuilder<C extends Record<string, any> = {}, E exte
|
|
|
408
519
|
private assetConfigurator;
|
|
409
520
|
/** Screen configurator for collecting screen definitions */
|
|
410
521
|
private screenConfigurator;
|
|
522
|
+
/** Pending resources to add during build */
|
|
523
|
+
private pendingResources;
|
|
411
524
|
constructor();
|
|
412
525
|
/**
|
|
413
526
|
* Add the first bundle when starting with empty types.
|
|
@@ -419,6 +532,26 @@ export declare class ECSpressoBuilder<C extends Record<string, any> = {}, E exte
|
|
|
419
532
|
* This overload enforces bundle type compatibility.
|
|
420
533
|
*/
|
|
421
534
|
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, A, S>;
|
|
535
|
+
/**
|
|
536
|
+
* Add a resource during ECSpresso construction
|
|
537
|
+
* @param key The resource key
|
|
538
|
+
* @param resource The resource value, factory function, or factory with dependencies/disposal
|
|
539
|
+
* @returns This builder with updated resource types
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```typescript
|
|
543
|
+
* ECSpresso.create<Components, Events, Resources>()
|
|
544
|
+
* .withResource('config', { debug: true })
|
|
545
|
+
* .withResource('counter', () => 42)
|
|
546
|
+
* .withResource('derived', {
|
|
547
|
+
* dependsOn: ['base'],
|
|
548
|
+
* factory: (ecs) => ecs.getResource('base') * 2,
|
|
549
|
+
* onDispose: (value) => console.log('Disposed:', value)
|
|
550
|
+
* })
|
|
551
|
+
* .build();
|
|
552
|
+
* ```
|
|
553
|
+
*/
|
|
554
|
+
withResource<K extends string, V>(key: K, resource: V | ((context?: any) => V | Promise<V>) | ResourceFactoryWithDeps<V>): ECSpressoBuilder<C, E, R & Record<K, V>, A, S>;
|
|
422
555
|
/**
|
|
423
556
|
* Configure assets for this ECSpresso instance
|
|
424
557
|
* @param configurator Function that receives an AssetConfigurator and returns it after adding assets
|
|
@@ -464,3 +597,4 @@ export declare class ECSpressoBuilder<C extends Record<string, any> = {}, E exte
|
|
|
464
597
|
*/
|
|
465
598
|
build(): ECSpresso<C, E, R, A, S>;
|
|
466
599
|
}
|
|
600
|
+
export {};
|
package/dist/entity-manager.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Entity, FilteredEntity, RemoveEntityOptions } from "./types";
|
|
1
|
+
import type { Entity, FilteredEntity, RemoveEntityOptions, HierarchyEntry, HierarchyIteratorOptions } from "./types";
|
|
2
2
|
export default class EntityManager<ComponentTypes> {
|
|
3
3
|
private nextId;
|
|
4
4
|
private entities;
|
|
@@ -38,14 +38,16 @@ export default class EntityManager<ComponentTypes> {
|
|
|
38
38
|
* Register a callback when a specific component is added to any entity
|
|
39
39
|
* @param componentName The component key
|
|
40
40
|
* @param handler Function receiving the new component value and the entity
|
|
41
|
+
* @returns Unsubscribe function to remove the callback
|
|
41
42
|
*/
|
|
42
|
-
onComponentAdded<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (value: ComponentTypes[ComponentName], entity: Entity<ComponentTypes>) => void):
|
|
43
|
+
onComponentAdded<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (value: ComponentTypes[ComponentName], entity: Entity<ComponentTypes>) => void): () => void;
|
|
43
44
|
/**
|
|
44
45
|
* Register a callback when a specific component is removed from any entity
|
|
45
46
|
* @param componentName The component key
|
|
46
47
|
* @param handler Function receiving the old component value and the entity
|
|
48
|
+
* @returns Unsubscribe function to remove the callback
|
|
47
49
|
*/
|
|
48
|
-
onComponentRemoved<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (oldValue: ComponentTypes[ComponentName], entity: Entity<ComponentTypes>) => void):
|
|
50
|
+
onComponentRemoved<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (oldValue: ComponentTypes[ComponentName], entity: Entity<ComponentTypes>) => void): () => void;
|
|
49
51
|
/**
|
|
50
52
|
* Create an entity as a child of another entity with initial components
|
|
51
53
|
* @param parentId The parent entity ID
|
|
@@ -136,4 +138,18 @@ export default class EntityManager<ComponentTypes> {
|
|
|
136
138
|
* @returns Readonly array of root entity IDs
|
|
137
139
|
*/
|
|
138
140
|
getRootEntities(): readonly number[];
|
|
141
|
+
/**
|
|
142
|
+
* Traverse the hierarchy in parent-first (breadth-first) order.
|
|
143
|
+
* Parents are guaranteed to be visited before their children.
|
|
144
|
+
* @param callback Function called for each entity with (entityId, parentId, depth)
|
|
145
|
+
* @param options Optional traversal options (roots to filter to specific subtrees)
|
|
146
|
+
*/
|
|
147
|
+
forEachInHierarchy(callback: (entityId: number, parentId: number | null, depth: number) => void, options?: HierarchyIteratorOptions): void;
|
|
148
|
+
/**
|
|
149
|
+
* Generator-based hierarchy traversal in parent-first (breadth-first) order.
|
|
150
|
+
* Supports early termination via break.
|
|
151
|
+
* @param options Optional traversal options (roots to filter to specific subtrees)
|
|
152
|
+
* @yields HierarchyEntry for each entity in parent-first order
|
|
153
|
+
*/
|
|
154
|
+
hierarchyIterator(options?: HierarchyIteratorOptions): Generator<HierarchyEntry, void, unknown>;
|
|
139
155
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { HierarchyEntry, HierarchyIteratorOptions } from "./types";
|
|
1
2
|
/**
|
|
2
3
|
* Manages parent-child relationships between entities.
|
|
3
4
|
* Handles hierarchy storage, validation, and traversal operations.
|
|
@@ -104,4 +105,18 @@ export default class HierarchyManager {
|
|
|
104
105
|
* A cycle would occur if the prospective parent is a descendant of the child.
|
|
105
106
|
*/
|
|
106
107
|
private wouldCreateCycle;
|
|
108
|
+
/**
|
|
109
|
+
* Traverse the hierarchy in parent-first (breadth-first) order.
|
|
110
|
+
* Parents are guaranteed to be visited before their children.
|
|
111
|
+
* @param callback Function called for each entity with (entityId, parentId, depth)
|
|
112
|
+
* @param options Optional traversal options (roots to filter to specific subtrees)
|
|
113
|
+
*/
|
|
114
|
+
forEachInHierarchy(callback: (entityId: number, parentId: number | null, depth: number) => void, options?: HierarchyIteratorOptions): void;
|
|
115
|
+
/**
|
|
116
|
+
* Generator-based hierarchy traversal in parent-first (breadth-first) order.
|
|
117
|
+
* Supports early termination via break.
|
|
118
|
+
* @param options Optional traversal options (roots to filter to specific subtrees)
|
|
119
|
+
* @yields HierarchyEntry for each entity in parent-first order
|
|
120
|
+
*/
|
|
121
|
+
hierarchyIterator(options?: HierarchyIteratorOptions): Generator<HierarchyEntry, void, unknown>;
|
|
107
122
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,10 @@ import Bundle, { mergeBundles } from './bundle';
|
|
|
4
4
|
export * from './types';
|
|
5
5
|
export * from './asset-types';
|
|
6
6
|
export * from './screen-types';
|
|
7
|
+
export type { ReactiveQueryDefinition } from './reactive-query-manager';
|
|
7
8
|
export { default as EntityManager } from './entity-manager';
|
|
8
9
|
export { default as EventBus } from './event-bus';
|
|
10
|
+
export { default as CommandBuffer } from './command-buffer';
|
|
9
11
|
export { default as HierarchyManager } from './hierarchy-manager';
|
|
10
12
|
/**
|
|
11
13
|
* @internal ResourceManager is exported for testing purposes only.
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class H{parentMap=new Map;childrenMap=new Map;setParent(j,F){if(j===F)throw Error(`Cannot set entity ${j} as its own parent`);if(this.wouldCreateCycle(j,F))throw Error("Cannot set parent: would create circular reference");let J=this.parentMap.get(j);if(J!==void 0){let Y=this.childrenMap.get(J);if(Y){let Z=Y.indexOf(j);if(Z!==-1)Y.splice(Z,1)}}this.parentMap.set(j,F);let X=this.childrenMap.get(F);if(X)X.push(j);else this.childrenMap.set(F,[j]);return this}removeParent(j){let F=this.parentMap.get(j);if(F===void 0)return!1;let J=this.childrenMap.get(F);if(J){let X=J.indexOf(j);if(X!==-1)J.splice(X,1)}return this.parentMap.delete(j),!0}getParent(j){return this.parentMap.get(j)??null}getChildren(j){let F=this.childrenMap.get(j);return F?[...F]:[]}getChildAt(j,F){if(F<0)return null;let J=this.childrenMap.get(j);if(!J||F>=J.length)return null;return J[F]??null}getChildIndex(j,F){let J=this.childrenMap.get(j);if(!J)return-1;return J.indexOf(F)}removeEntity(j){let F=this.parentMap.get(j)??null;if(F!==null){let Y=this.childrenMap.get(F);if(Y){let Z=Y.indexOf(j);if(Z!==-1)Y.splice(Z,1)}}this.parentMap.delete(j);let J=this.childrenMap.get(j)??[],X=[...J];for(let Y of J)this.parentMap.delete(Y);return this.childrenMap.delete(j),{oldParent:F,orphanedChildren:X}}getAncestors(j){let F=[],J=this.parentMap.get(j);while(J!==void 0)F.push(J),J=this.parentMap.get(J);return F}getDescendants(j){let F=[],J=[...this.childrenMap.get(j)??[]];while(J.length>0){let X=J.shift();if(X===void 0)continue;F.push(X);let Y=this.childrenMap.get(X);if(Y)J.unshift(...Y)}return F}getRoot(j){let F=j,J=this.parentMap.get(F);while(J!==void 0)F=J,J=this.parentMap.get(F);return F}getSiblings(j){let F=this.parentMap.get(j);if(F===void 0)return[];let J=this.childrenMap.get(F);if(!J)return[];return J.filter((X)=>X!==j)}isDescendantOf(j,F){if(j===F)return!1;let J=this.parentMap.get(j);while(J!==void 0){if(J===F)return!0;J=this.parentMap.get(J)}return!1}isAncestorOf(j,F){return this.isDescendantOf(F,j)}getRootEntities(){let j=[];for(let F of this.childrenMap.keys())if(!this.parentMap.has(F))j.push(F);return j}wouldCreateCycle(j,F){let J=F;while(J!==void 0){if(J===j)return!0;J=this.parentMap.get(J)}return!1}}class U{nextId=1;entities=new Map;componentIndices=new Map;addedCallbacks=new Map;removedCallbacks=new Map;hierarchyManager=new H;createEntity(){let j=this.nextId++,F={id:j,components:{}};return this.entities.set(j,F),F}addComponent(j,F,J){let X=typeof j==="number"?this.entities.get(j):j;if(!X){let Z=typeof j==="number"?j:j.id;throw Error(`Cannot add component '${String(F)}': Entity with ID ${Z} does not exist`)}if(X.components[F]=J,!this.componentIndices.has(F))this.componentIndices.set(F,new Set);this.componentIndices.get(F)?.add(X.id);let Y=this.addedCallbacks.get(F);if(Y)for(let Z of Y)Z(J,X);return this}addComponents(j,F){let J=typeof j==="number"?this.entities.get(j):j;if(!J){let X=typeof j==="number"?j:j.id;throw Error(`Cannot add components: Entity with ID ${X} does not exist`)}for(let X in F)this.addComponent(J,X,F[X]);return this}removeComponent(j,F){let J=typeof j==="number"?this.entities.get(j):j;if(!J){let Z=typeof j==="number"?j:j.id;throw Error(`Cannot remove component '${String(F)}': Entity with ID ${Z} does not exist`)}let X=J.components[F];delete J.components[F];let Y=this.removedCallbacks.get(F);if(Y&&X!==void 0)for(let Z of Y)Z(X,J);return this.componentIndices.get(F)?.delete(J.id),this}getComponent(j,F){let J=this.entities.get(j);if(!J)throw Error(`Cannot get component '${String(F)}': Entity with ID ${j} does not exist`);return J.components[F]||null}getEntitiesWithQuery(j=[],F=[]){if(j.length===0){if(F.length===0)return Array.from(this.entities.values());return Array.from(this.entities.values()).filter((_)=>{return F.every(($)=>!($ in _.components))})}let J=j.reduce((_,$)=>{let G=this.componentIndices.get($)?.size??0,C=this.componentIndices.get(_)?.size??1/0;return G<C?$:_},j[0]),X=this.componentIndices.get(J);if(!X||X.size===0)return[];let Y=[],Z=F.length>0;for(let _ of X){let $=this.entities.get(_);if($&&j.every((G)=>(G in $.components))&&(!Z||F.every((G)=>!(G in $.components))))Y.push($)}return Y}removeEntity(j,F){let J=typeof j==="number"?this.entities.get(j):j;if(!J)return!1;if(F?.cascade??!0){let Y=this.hierarchyManager.getDescendants(J.id);for(let Z of[...Y].reverse())this.removeEntityInternal(Z)}return this.removeEntityInternal(J.id)}removeEntityInternal(j){let F=this.entities.get(j);if(!F)return!1;this.hierarchyManager.removeEntity(j);for(let J of Object.keys(F.components)){let X=F.components[J];if(X!==void 0){let Y=this.removedCallbacks.get(J);if(Y)for(let Z of Y)Z(X,F)}this.componentIndices.get(J)?.delete(F.id)}return this.entities.delete(F.id)}getEntity(j){return this.entities.get(j)}onComponentAdded(j,F){if(!this.addedCallbacks.has(j))this.addedCallbacks.set(j,new Set);return this.addedCallbacks.get(j).add(F),this}onComponentRemoved(j,F){if(!this.removedCallbacks.has(j))this.removedCallbacks.set(j,new Set);return this.removedCallbacks.get(j).add(F),this}spawnChild(j,F){let J=this.createEntity();return this.addComponents(J,F),this.setParent(J.id,j),J}setParent(j,F){return this.hierarchyManager.setParent(j,F),this}removeParent(j){return this.hierarchyManager.removeParent(j)}getParent(j){return this.hierarchyManager.getParent(j)}getChildren(j){return this.hierarchyManager.getChildren(j)}getChildAt(j,F){return this.hierarchyManager.getChildAt(j,F)}getChildIndex(j,F){return this.hierarchyManager.getChildIndex(j,F)}getAncestors(j){return this.hierarchyManager.getAncestors(j)}getDescendants(j){return this.hierarchyManager.getDescendants(j)}getRoot(j){return this.hierarchyManager.getRoot(j)}getSiblings(j){return this.hierarchyManager.getSiblings(j)}isDescendantOf(j,F){return this.hierarchyManager.isDescendantOf(j,F)}isAncestorOf(j,F){return this.hierarchyManager.isAncestorOf(j,F)}getRootEntities(){return this.hierarchyManager.getRootEntities()}}class D{handlers=new Map;subscribe(j,F){return this.addHandler(j,F,!1)}once(j,F){return this.addHandler(j,F,!0)}unsubscribe(j,F){let J=this.handlers.get(j);if(!J)return!1;let X=J.findIndex((Y)=>Y.callback===F);if(X===-1)return!1;return J.splice(X,1),!0}addHandler(j,F,J){if(!this.handlers.has(j))this.handlers.set(j,[]);let X={callback:F,once:J};return this.handlers.get(j).push(X),()=>{let Y=this.handlers.get(j);if(Y){let Z=Y.indexOf(X);if(Z!==-1)Y.splice(Z,1)}}}publish(j,F){let J=this.handlers.get(j);if(!J)return;let X=[...J],Y=[];for(let Z of X)if(Z.callback(F),Z.once)Y.push(Z);if(Y.length>0)for(let Z of Y){let _=J.indexOf(Z);if(_!==-1)J.splice(_,1)}}clear(){this.handlers.clear()}clearEvent(j){this.handlers.delete(j)}}class Q{resources=new Map;resourceFactories=new Map;initializedResourceKeys=new Set;add(j,F){if(this._isFactoryFunction(F))this.resourceFactories.set(j,F);else this.resources.set(j,F),this.initializedResourceKeys.add(j);return this}_isFactoryFunction(j){if(typeof j!=="function")return!1;let F=j.toString();if(F.startsWith("class "))return!1;if(F.includes("[native code]"))return!1;if(j.prototype){let J=Object.getOwnPropertyNames(j.prototype);if(J.length>1||J.length===1&&J[0]!=="constructor")return!1}if(j.name&&j.name[0]===j.name[0].toUpperCase()&&j.name.length>1){if(F.includes("this.")||F.includes("new "))return!1}return!0}get(j,F){let J=this.resources.get(j);if(J!==void 0)return J;let X=this.resourceFactories.get(j);if(X===void 0)throw Error(`Resource ${String(j)} not found`);let Y=X(F);if(!(Y instanceof Promise))this.resources.set(j,Y),this.initializedResourceKeys.add(j);return Y}has(j){return this.resources.has(j)||this.resourceFactories.has(j)}remove(j){let F=this.resources.delete(j),J=this.resourceFactories.delete(j);if(this.initializedResourceKeys.has(j))this.initializedResourceKeys.delete(j);return F||J}getKeys(){let j=new Set([...this.resources.keys(),...this.resourceFactories.keys()]);return Array.from(j)}needsInitialization(j){return this.resourceFactories.has(j)&&!this.initializedResourceKeys.has(j)}getPendingInitializationKeys(){return Array.from(this.resourceFactories.keys()).filter((j)=>!this.initializedResourceKeys.has(j))}async initializeResource(j,F){if(!this.resourceFactories.has(j)||this.initializedResourceKeys.has(j))return;let X=await this.resourceFactories.get(j)(F);this.resources.set(j,X),this.initializedResourceKeys.add(j),this.resourceFactories.delete(j)}async initializeResources(j,...F){if(F.length===0){let J=this.getPendingInitializationKeys();await Promise.all(J.map((X)=>this.initializeResource(X,j)));return}await Promise.all(F.map((J)=>this.initializeResource(J,j)))}}class L{assets=new Map;groups=new Map;eventBus=null;setEventBus(j){this.eventBus=j}register(j,F){if(this.assets.set(j,{definition:F,status:"pending"}),F.group){let J=this.groups.get(F.group)??new Set;J.add(j),this.groups.set(F.group,J)}}async loadEagerAssets(){let j=[];for(let[F,J]of this.assets)if(J.definition.eager&&J.status==="pending")j.push(F);await Promise.all(j.map((F)=>this.loadAsset(F)))}async loadAsset(j){let F=j,J=this.assets.get(F);if(!J)throw Error(`Asset '${F}' not found`);if(J.status==="loaded"&&J.value!==void 0)return J.value;if(J.status==="loading"&&J.loadPromise)return J.loadPromise;if(J.status==="failed")J.status="pending";J.status="loading",J.loadPromise=J.definition.loader();try{let X=await J.loadPromise;return J.value=X,J.status="loaded",J.loadPromise=void 0,this.eventBus?.publish("assetLoaded",{key:F}),this.checkGroupProgress(J.definition.group),X}catch(X){let Y=X instanceof Error?X:Error(String(X));throw J.status="failed",J.error=Y,J.loadPromise=void 0,this.eventBus?.publish("assetFailed",{key:F,error:Y}),Y}}async loadAssetGroup(j){let F=this.groups.get(j);if(!F||F.size===0)throw Error(`Asset group '${j}' not found or empty`);await Promise.all(Array.from(F).map((J)=>this.loadAsset(J)))}get(j){let F=j,J=this.assets.get(F);if(!J)throw Error(`Asset '${F}' not found`);if(J.status!=="loaded"||J.value===void 0)throw Error(`Asset '${F}' is not loaded (status: ${J.status})`);return J.value}getOrUndefined(j){let F=j,J=this.assets.get(F);if(!J||J.status!=="loaded")return;return J.value}getHandle(j){let F=j,J=this.assets.get(F);if(!J)throw Error(`Asset '${F}' not found`);let X=this;return{get status(){return J.status},get isLoaded(){return J.status==="loaded"},get(){return X.get(j)},getOrUndefined(){return X.getOrUndefined(j)}}}getStatus(j){let F=j,J=this.assets.get(F);if(!J)throw Error(`Asset '${F}' not found`);return J.status}isLoaded(j){let F=j;return this.assets.get(F)?.status==="loaded"}isGroupLoaded(j){let F=this.groups.get(j);if(!F||F.size===0)return!1;for(let J of F){let X=this.assets.get(J);if(!X||X.status!=="loaded")return!1}return!0}getGroupProgress(j){let F=this.groups.get(j);if(!F||F.size===0)return 0;let J=0;for(let X of F)if(this.assets.get(X)?.status==="loaded")J++;return J/F.size}getGroupProgressDetails(j){let F=this.groups.get(j);if(!F||F.size===0)return{loaded:0,total:0,progress:0};let J=0;for(let Y of F)if(this.assets.get(Y)?.status==="loaded")J++;let X=F.size;return{loaded:J,total:X,progress:J/X}}checkGroupProgress(j){if(!j||!this.eventBus)return;let F=this.getGroupProgressDetails(j);if(this.eventBus.publish("assetGroupProgress",{group:j,...F}),F.loaded===F.total)this.eventBus.publish("assetGroupLoaded",{group:j})}createResource(){let j=this;return{getStatus(F){return j.getStatus(F)},isLoaded(F){return j.isLoaded(F)},isGroupLoaded(F){return j.isGroupLoaded(F)},getGroupProgress(F){return j.getGroupProgress(F)},get(F){return j.get(F)},getOrUndefined(F){return j.getOrUndefined(F)},getHandle(F){return j.getHandle(F)}}}getKeys(){return Array.from(this.assets.keys())}getGroupNames(){return Array.from(this.groups.keys())}getGroupKeys(j){let F=this.groups.get(j);return F?Array.from(F):[]}}class A{manager;constructor(j){this.manager=j}add(j,F){return this.manager.register(j,{loader:F,eager:!0}),this}addWithConfig(j,F){return this.manager.register(j,F),this}addGroup(j,F){for(let[J,X]of Object.entries(F))this.manager.register(J,{loader:X,eager:!1,group:j});return this}getManager(){return this.manager}}function w(j){return new A(j??new L)}class z{screens=new Map;currentScreen=null;screenStack=[];eventBus=null;assetManager=null;ecs=null;setDependencies(j,F,J){this.eventBus=j,this.assetManager=F,this.ecs=J}register(j,F){this.screens.set(j,{definition:F})}async setScreen(j,F){let J=j,X=this.screens.get(J);if(!X)throw Error(`Screen '${J}' not found`);await this.verifyRequiredAssets(X.definition);while(this.screenStack.length>0){let Z=this.screenStack.pop();if(Z)await this.exitScreen(Z.name)}if(this.currentScreen)await this.exitScreen(this.currentScreen.name);let Y=X.definition.initialState(F);this.currentScreen={name:j,config:F,state:Y},await X.definition.onEnter?.(F,this.ecs),this.eventBus?.publish("screenEnter",{screen:J,config:F})}async pushScreen(j,F){let J=j,X=this.screens.get(J);if(!X)throw Error(`Screen '${J}' not found`);if(await this.verifyRequiredAssets(X.definition),this.currentScreen)this.screenStack.push(this.currentScreen);let Y=X.definition.initialState(F);this.currentScreen={name:j,config:F,state:Y},await X.definition.onEnter?.(F,this.ecs),this.eventBus?.publish("screenPush",{screen:J,config:F})}async popScreen(){if(this.screenStack.length===0)throw Error("Cannot pop screen: stack is empty");if(this.currentScreen){let j=this.currentScreen.name;await this.exitScreen(j),this.eventBus?.publish("screenPop",{screen:j})}this.currentScreen=this.screenStack.pop()??null}async exitScreen(j){let F=this.screens.get(j);if(F?.definition.onExit)await F.definition.onExit(this.ecs);this.eventBus?.publish("screenExit",{screen:j})}async verifyRequiredAssets(j){if(!this.assetManager)return;if(j.requiredAssets){for(let F of j.requiredAssets)if(!this.assetManager.isLoaded(F))await this.assetManager.loadAsset(F)}if(j.requiredAssetGroups){for(let F of j.requiredAssetGroups)if(!this.assetManager.isGroupLoaded(F))await this.assetManager.loadAssetGroup(F)}}getCurrentScreen(){return this.currentScreen?.name??null}getConfig(){if(!this.currentScreen)throw Error("No current screen");return this.currentScreen.config}getConfigOrNull(){return this.currentScreen?.config??null}getState(){if(!this.currentScreen)throw Error("No current screen");return this.currentScreen.state}getStateOrNull(){return this.currentScreen?.state??null}updateState(j){if(!this.currentScreen)throw Error("No current screen");let F=typeof j==="function"?j(this.currentScreen.state):j;this.currentScreen.state={...this.currentScreen.state,...F}}getStackDepth(){return this.screenStack.length}isOverlay(){return this.screenStack.length>0}isActive(j){if(this.currentScreen?.name===j)return!0;return this.screenStack.some((F)=>F.name===j)}isCurrent(j){return this.currentScreen?.name===j}createResource(){let j=this;return{get current(){return j.getCurrentScreen()},get config(){return j.getConfigOrNull()},get state(){return j.getStateOrNull()},set state(F){if(j.currentScreen)j.currentScreen.state=F},get stack(){return j.screenStack},get isOverlay(){return j.isOverlay()},get stackDepth(){return j.getStackDepth()},isActive(F){return j.isActive(F)},isCurrent(F){return j.isCurrent(F)}}}getScreenNames(){return Array.from(this.screens.keys())}hasScreen(j){return this.screens.has(j)}}class q{manager;constructor(j){this.manager=j}add(j,F){return this.manager.register(j,F),this}getManager(){return this.manager}}function E(j){return new q(j??new z)}class M{_label;_ecspresso;_bundle;queries={};processFunction;detachFunction;initializeFunction;eventHandlers;_priority=0;_isRegistered=!1;_inScreens;_excludeScreens;_requiredAssets;constructor(j,F=null,J=null){this._label=j;this._ecspresso=F;this._bundle=J}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();P(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._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}inScreens(j){return this._inScreens=[...j],this}excludeScreens(j){return this._excludeScreens=[...j],this}requiresAssets(j){return this._requiredAssets=[...j],this}addQuery(j,F){let J=this;return J.queries={...this.queries,[j]:F},J}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 F=this._createSystemObject();if(this._ecspresso)P(F,this._ecspresso);if(j)P(F,j);return this}}function P(j,F){F._registerSystem(j)}function K(j,F){return new M(j,F)}function B(j,F){return new M(j,null,F)}var R="0.5.0";var O={};class W{static VERSION=R;_entityManager;_eventBus;_resourceManager;_systems=[];_sortedSystems=[];_installedBundles=new Set;_assetManager=null;_screenManager=null;_postUpdateHooks=[];constructor(){this._entityManager=new U,this._eventBus=new D,this._resourceManager=new Q,this._sortedSystems=[]}static create(){return new T}addSystem(j){return K(j,this)}update(j){let F=this._screenManager?.getCurrentScreen()??null;for(let J of this._sortedSystems){if(!J.process)continue;if(J.inScreens?.length){if(F===null||!J.inScreens.includes(F))continue}if(J.excludeScreens?.length){if(F!==null&&J.excludeScreens.includes(F))continue}if(J.requiredAssets?.length&&this._assetManager){let _=!0;for(let $ of J.requiredAssets)if(!this._assetManager.isLoaded($)){_=!1;break}if(!_)continue}let X={},Y=!1,Z=!1;if(J.entityQueries)for(let _ in J.entityQueries){Z=!0;let $=J.entityQueries[_];if($){if(X[_]=this._entityManager.getEntitiesWithQuery($.with,$.without||[]),X[_].length)Y=!0}}if(Y)J.process(X,j,this);else if(!Z)J.process(O,j,this)}for(let J of this._postUpdateHooks)J(this,j)}async initialize(){if(await this.initializeResources(),this._assetManager)this._assetManager.setEventBus(this._eventBus),await this._assetManager.loadEagerAssets(),this._resourceManager.add("$assets",this._assetManager.createResource());if(this._screenManager)this._screenManager.setDependencies(this._eventBus,this._assetManager,this),this._resourceManager.add("$screen",this._screenManager.createResource());for(let j of this._systems)await j.onInitialize?.(this)}async initializeResources(...j){await this._resourceManager.initializeResources(this,...j)}_sortSystems(){this._sortedSystems=[...this._systems].sort((j,F)=>{let J=j.priority??0;return(F.priority??0)-J})}updateSystemPriority(j,F){let J=this._systems.find((X)=>X.label===j);if(!J)return!1;return J.priority=F,this._sortSystems(),!0}removeSystem(j){let F=this._systems.findIndex((X)=>X.label===j);if(F===-1)return!1;let J=this._systems[F];if(!J)return!1;if(J.onDetach)J.onDetach(this);return this._systems.splice(F,1),this._sortSystems(),!0}_registerSystem(j){if(this._systems.push(j),this._sortSystems(),!j.eventHandlers)return;for(let F in j.eventHandlers){let J=j.eventHandlers[F]?.handler;if(J)this._eventBus.subscribe(F,(X)=>{J(X,this)})}}hasResource(j){return this._resourceManager.has(j)}getResource(j){let F=this._resourceManager.get(j,this);if(!F)throw Error(`Resource '${String(j)}' not found. Available resources: [${this.getResourceKeys().map((J)=>String(J)).join(", ")}]`);return F}addResource(j,F){return this._resourceManager.add(j,F),this}removeResource(j){return this._resourceManager.remove(j)}updateResource(j,F){let J=this.getResource(j),X=F(J);return this._resourceManager.add(j,X),this}getResourceKeys(){return this._resourceManager.getKeys()}resourceNeedsInitialization(j){return this._resourceManager.needsInitialization(j)}hasComponent(j,F){return this._entityManager.getComponent(j,F)!==null}spawn(j){let F=this._entityManager.createEntity();return this._entityManager.addComponents(F,j),F}getEntitiesWithQuery(j,F=[]){return this._entityManager.getEntitiesWithQuery(j,F)}removeEntity(j,F){return this._entityManager.removeEntity(j,F)}spawnChild(j,F){let J=this._entityManager.spawnChild(j,F);return this._emitHierarchyChanged(J.id,null,j),J}setParent(j,F){let J=this._entityManager.getParent(j);return this._entityManager.setParent(j,F),this._emitHierarchyChanged(j,J,F),this}removeParent(j){let F=this._entityManager.getParent(j),J=this._entityManager.removeParent(j);if(J)this._emitHierarchyChanged(j,F,null);return J}getParent(j){return this._entityManager.getParent(j)}getChildren(j){return this._entityManager.getChildren(j)}getChildAt(j,F){return this._entityManager.getChildAt(j,F)}getChildIndex(j,F){return this._entityManager.getChildIndex(j,F)}getAncestors(j){return this._entityManager.getAncestors(j)}getDescendants(j){return this._entityManager.getDescendants(j)}getRoot(j){return this._entityManager.getRoot(j)}getSiblings(j){return this._entityManager.getSiblings(j)}isDescendantOf(j,F){return this._entityManager.isDescendantOf(j,F)}isAncestorOf(j,F){return this._entityManager.isAncestorOf(j,F)}getRootEntities(){return this._entityManager.getRootEntities()}_emitHierarchyChanged(j,F,J){this._eventBus.publish("hierarchyChanged",{entityId:j,oldParent:F,newParent:J})}get installedBundles(){return Array.from(this._installedBundles)}get entityManager(){return this._entityManager}get eventBus(){return this._eventBus}on(j,F){return this._eventBus.subscribe(j,F)}off(j,F){return this._eventBus.unsubscribe(j,F)}onPostUpdate(j){return this._postUpdateHooks.push(j),()=>{let F=this._postUpdateHooks.indexOf(j);if(F!==-1)this._postUpdateHooks.splice(F,1)}}getAsset(j){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager.get(j)}getAssetOrUndefined(j){return this._assetManager?.getOrUndefined(j)}getAssetHandle(j){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager.getHandle(j)}isAssetLoaded(j){return this._assetManager?.isLoaded(j)??!1}async loadAsset(j){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager.loadAsset(j)}async loadAssetGroup(j){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager.loadAssetGroup(j)}isAssetGroupLoaded(j){return this._assetManager?.isGroupLoaded(j)??!1}getAssetGroupProgress(j){return this._assetManager?.getGroupProgress(j)??0}async setScreen(j,F){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.setScreen(j,F)}async pushScreen(j,F){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.pushScreen(j,F)}async popScreen(){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.popScreen()}getCurrentScreen(){return this._screenManager?.getCurrentScreen()??null}getScreenConfig(){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.getConfig()}getScreenConfigOrNull(){return this._screenManager?.getConfigOrNull()??null}getScreenState(){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.getState()}getScreenStateOrNull(){return this._screenManager?.getStateOrNull()??null}updateScreenState(j){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");this._screenManager.updateState(j)}isCurrentScreen(j){return this._screenManager?.isCurrent(j)??!1}isScreenActive(j){return this._screenManager?.isActive(j)??!1}getScreenStackDepth(){return this._screenManager?.getStackDepth()??0}_setAssetManager(j){this._assetManager=j}_setScreenManager(j){this._screenManager=j}_installBundle(j){if(this._installedBundles.has(j.id))return this;this._installedBundles.add(j.id),j.registerSystemsWithEcspresso(this);let F=j.getResources();for(let[J,X]of F.entries())this._resourceManager.add(J,X);if(this._assetManager){let J=j.getAssets();for(let[X,Y]of J.entries())this._assetManager.register(X,Y)}if(this._screenManager){let J=j.getScreens();for(let[X,Y]of J.entries())this._screenManager.register(X,Y)}return this}}class T{ecspresso;assetConfigurator=null;screenConfigurator=null;constructor(){this.ecspresso=new W}withBundle(j){return this.ecspresso._installBundle(j),this}withAssets(j){let F=w();return j(F),this.assetConfigurator=F,this}withScreens(j){let F=E();return j(F),this.screenConfigurator=F,this}build(){if(this.assetConfigurator)this.ecspresso._setAssetManager(this.assetConfigurator.getManager());if(this.screenConfigurator)this.ecspresso._setScreenManager(this.screenConfigurator.getManager());return this.ecspresso}}function v(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class V{_systems=[];_resources=new Map;_assets=new Map;_assetGroups=new Map;_screens=new Map;_id;constructor(j){this._id=j||v()}get id(){return this._id}set id(j){this._id=j}addSystem(j){if(typeof j==="string"){let F=B(j,this);return this._systems.push(F),F}else return this._systems.push(j),j}addResource(j,F){return this._resources.set(j,F),this}addAsset(j,F,J){return this._assets.set(j,{loader:F,eager:J?.eager??!0,group:J?.group}),this}addAssetGroup(j,F){let J=new Map;for(let[X,Y]of Object.entries(F))J.set(X,Y),this._assets.set(X,{loader:Y,eager:!1,group:j});return this._assetGroups.set(j,J),this}addScreen(j,F){return this._screens.set(j,F),this}getAssets(){return new Map(this._assets)}getScreens(){return new Map(this._screens)}_setResource(j,F){this._resources.set(j,F)}_setAsset(j,F){this._assets.set(j,F)}_setScreen(j,F){this._screens.set(j,F)}getSystems(){return this._systems.map((j)=>j.build())}registerSystemsWithEcspresso(j){for(let F of this._systems)F.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 S(j,...F){if(F.length===0)return new V(j);let J=new V(j);for(let X of F){for(let Y of X.getSystemBuilders())J.addSystem(Y);for(let[Y,Z]of X.getResources().entries())J._setResource(Y,Z);for(let[Y,Z]of X.getAssets().entries())J._setAsset(Y,Z);for(let[Y,Z]of X.getScreens().entries())J._setScreen(Y,Z)}return J}function J0(j){return j}var M0=W;export{S as mergeBundles,M0 as default,E as createScreenConfigurator,J0 as createQueryDefinition,w as createAssetConfigurator,M as SystemBuilder,z as ScreenManager,Q as ResourceManager,H as HierarchyManager,D as EventBus,U as EntityManager,V as Bundle,L as AssetManager};
|
|
1
|
+
var k=Object.create;var{getPrototypeOf:g,defineProperty:C,getOwnPropertyNames:I}=Object;var p=Object.prototype.hasOwnProperty;var y=(j,J,X)=>{X=j!=null?k(g(j)):{};let Y=J||!j||!j.__esModule?C(X,"default",{value:j,enumerable:!0}):X;for(let Z of I(j))if(!p.call(Y,Z))C(Y,Z,{get:()=>j[Z],enumerable:!0});return Y};var o=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(J,X)=>(typeof require<"u"?require:J)[X]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});class Q{parentMap=new Map;childrenMap=new Map;setParent(j,J){if(j===J)throw Error(`Cannot set entity ${j} as its own parent`);if(this.wouldCreateCycle(j,J))throw Error("Cannot set parent: would create circular reference");let X=this.parentMap.get(j);if(X!==void 0){let Z=this.childrenMap.get(X);if(Z){let _=Z.indexOf(j);if(_!==-1)Z.splice(_,1)}}this.parentMap.set(j,J);let Y=this.childrenMap.get(J);if(Y)Y.push(j);else this.childrenMap.set(J,[j]);return this}removeParent(j){let J=this.parentMap.get(j);if(J===void 0)return!1;let X=this.childrenMap.get(J);if(X){let Y=X.indexOf(j);if(Y!==-1)X.splice(Y,1)}return this.parentMap.delete(j),!0}getParent(j){return this.parentMap.get(j)??null}getChildren(j){let J=this.childrenMap.get(j);return J?[...J]:[]}getChildAt(j,J){if(J<0)return null;let X=this.childrenMap.get(j);if(!X||J>=X.length)return null;return X[J]??null}getChildIndex(j,J){let X=this.childrenMap.get(j);if(!X)return-1;return X.indexOf(J)}removeEntity(j){let J=this.parentMap.get(j)??null;if(J!==null){let Z=this.childrenMap.get(J);if(Z){let _=Z.indexOf(j);if(_!==-1)Z.splice(_,1)}}this.parentMap.delete(j);let X=this.childrenMap.get(j)??[],Y=[...X];for(let Z of X)this.parentMap.delete(Z);return this.childrenMap.delete(j),{oldParent:J,orphanedChildren:Y}}getAncestors(j){let J=[],X=this.parentMap.get(j);while(X!==void 0)J.push(X),X=this.parentMap.get(X);return J}getDescendants(j){let J=[],X=[...this.childrenMap.get(j)??[]];while(X.length>0){let Y=X.shift();if(Y===void 0)continue;J.push(Y);let Z=this.childrenMap.get(Y);if(Z)X.unshift(...Z)}return J}getRoot(j){let J=j,X=this.parentMap.get(J);while(X!==void 0)J=X,X=this.parentMap.get(J);return J}getSiblings(j){let J=this.parentMap.get(j);if(J===void 0)return[];let X=this.childrenMap.get(J);if(!X)return[];return X.filter((Y)=>Y!==j)}isDescendantOf(j,J){if(j===J)return!1;let X=this.parentMap.get(j);while(X!==void 0){if(X===J)return!0;X=this.parentMap.get(X)}return!1}isAncestorOf(j,J){return this.isDescendantOf(J,j)}getRootEntities(){let j=[];for(let J of this.childrenMap.keys())if(!this.parentMap.has(J))j.push(J);return j}wouldCreateCycle(j,J){let X=J;while(X!==void 0){if(X===j)return!0;X=this.parentMap.get(X)}return!1}forEachInHierarchy(j,J){let X=J?.roots??this.getRootEntities(),Y=[];for(let Z of X)Y.push({entityId:Z,parentId:null,depth:0});while(Y.length>0){let Z=Y.shift();if(!Z)break;j(Z.entityId,Z.parentId,Z.depth);let _=this.childrenMap.get(Z.entityId);if(_)for(let F of _)Y.push({entityId:F,parentId:Z.entityId,depth:Z.depth+1})}}*hierarchyIterator(j){let J=j?.roots??this.getRootEntities(),X=[];for(let Y of J)X.push({entityId:Y,parentId:null,depth:0});while(X.length>0){let Y=X.shift();if(!Y)break;yield Y;let Z=this.childrenMap.get(Y.entityId);if(Z)for(let _ of Z)X.push({entityId:_,parentId:Y.entityId,depth:Y.depth+1})}}}class V{nextId=1;entities=new Map;componentIndices=new Map;addedCallbacks=new Map;removedCallbacks=new Map;hierarchyManager=new Q;createEntity(){let j=this.nextId++,J={id:j,components:{}};return this.entities.set(j,J),J}addComponent(j,J,X){let Y=typeof j==="number"?this.entities.get(j):j;if(!Y){let _=typeof j==="number"?j:j.id;throw Error(`Cannot add component '${String(J)}': Entity with ID ${_} does not exist`)}if(Y.components[J]=X,!this.componentIndices.has(J))this.componentIndices.set(J,new Set);this.componentIndices.get(J)?.add(Y.id);let Z=this.addedCallbacks.get(J);if(Z)for(let _ of[...Z])_(X,Y);return this}addComponents(j,J){let X=typeof j==="number"?this.entities.get(j):j;if(!X){let Y=typeof j==="number"?j:j.id;throw Error(`Cannot add components: Entity with ID ${Y} does not exist`)}for(let Y in J)this.addComponent(X,Y,J[Y]);return this}removeComponent(j,J){let X=typeof j==="number"?this.entities.get(j):j;if(!X){let _=typeof j==="number"?j:j.id;throw Error(`Cannot remove component '${String(J)}': Entity with ID ${_} does not exist`)}let Y=X.components[J];delete X.components[J];let Z=this.removedCallbacks.get(J);if(Z&&Y!==void 0)for(let _ of[...Z])_(Y,X);return this.componentIndices.get(J)?.delete(X.id),this}getComponent(j,J){let X=this.entities.get(j);if(!X)throw Error(`Cannot get component '${String(J)}': Entity with ID ${j} does not exist`);return X.components[J]||null}getEntitiesWithQuery(j=[],J=[]){if(j.length===0){if(J.length===0)return Array.from(this.entities.values());return Array.from(this.entities.values()).filter((F)=>{return J.every(($)=>!($ in F.components))})}let X=j.reduce((F,$)=>{let U=this.componentIndices.get($)?.size??0,G=this.componentIndices.get(F)?.size??1/0;return U<G?$:F},j[0]),Y=this.componentIndices.get(X);if(!Y||Y.size===0)return[];let Z=[],_=J.length>0;for(let F of Y){let $=this.entities.get(F);if($&&j.every((U)=>(U in $.components))&&(!_||J.every((U)=>!(U in $.components))))Z.push($)}return Z}removeEntity(j,J){let X=typeof j==="number"?this.entities.get(j):j;if(!X)return!1;if(J?.cascade??!0){let Z=this.hierarchyManager.getDescendants(X.id);for(let _ of[...Z].reverse())this.removeEntityInternal(_)}return this.removeEntityInternal(X.id)}removeEntityInternal(j){let J=this.entities.get(j);if(!J)return!1;this.hierarchyManager.removeEntity(j);for(let X of Object.keys(J.components)){let Y=J.components[X];if(Y!==void 0){let Z=this.removedCallbacks.get(X);if(Z)for(let _ of[...Z])_(Y,J)}this.componentIndices.get(X)?.delete(J.id)}return this.entities.delete(J.id)}getEntity(j){return this.entities.get(j)}onComponentAdded(j,J){if(!this.addedCallbacks.has(j))this.addedCallbacks.set(j,new Set);return this.addedCallbacks.get(j).add(J),()=>{this.addedCallbacks.get(j)?.delete(J)}}onComponentRemoved(j,J){if(!this.removedCallbacks.has(j))this.removedCallbacks.set(j,new Set);return this.removedCallbacks.get(j).add(J),()=>{this.removedCallbacks.get(j)?.delete(J)}}spawnChild(j,J){let X=this.createEntity();return this.addComponents(X,J),this.setParent(X.id,j),X}setParent(j,J){return this.hierarchyManager.setParent(j,J),this}removeParent(j){return this.hierarchyManager.removeParent(j)}getParent(j){return this.hierarchyManager.getParent(j)}getChildren(j){return this.hierarchyManager.getChildren(j)}getChildAt(j,J){return this.hierarchyManager.getChildAt(j,J)}getChildIndex(j,J){return this.hierarchyManager.getChildIndex(j,J)}getAncestors(j){return this.hierarchyManager.getAncestors(j)}getDescendants(j){return this.hierarchyManager.getDescendants(j)}getRoot(j){return this.hierarchyManager.getRoot(j)}getSiblings(j){return this.hierarchyManager.getSiblings(j)}isDescendantOf(j,J){return this.hierarchyManager.isDescendantOf(j,J)}isAncestorOf(j,J){return this.hierarchyManager.isAncestorOf(j,J)}getRootEntities(){return this.hierarchyManager.getRootEntities()}forEachInHierarchy(j,J){this.hierarchyManager.forEachInHierarchy(j,J)}hierarchyIterator(j){return this.hierarchyManager.hierarchyIterator(j)}}class L{handlers=new Map;subscribe(j,J){return this.addHandler(j,J,!1)}once(j,J){return this.addHandler(j,J,!0)}unsubscribe(j,J){let X=this.handlers.get(j);if(!X)return!1;let Y=X.findIndex((Z)=>Z.callback===J);if(Y===-1)return!1;return X.splice(Y,1),!0}addHandler(j,J,X){if(!this.handlers.has(j))this.handlers.set(j,[]);let Y={callback:J,once:X};return this.handlers.get(j).push(Y),()=>{let Z=this.handlers.get(j);if(Z){let _=Z.indexOf(Y);if(_!==-1)Z.splice(_,1)}}}publish(j,J){let X=this.handlers.get(j);if(!X)return;let Y=[...X],Z=[];for(let _ of Y)if(_.callback(J),_.once)Z.push(_);if(Z.length>0)for(let _ of Z){let F=X.indexOf(_);if(F!==-1)X.splice(F,1)}}clear(){this.handlers.clear()}clearEvent(j){this.handlers.delete(j)}}function u(j){return typeof j==="object"&&j!==null&&"factory"in j&&typeof j.factory==="function"}function q(j,J){let X=[],Y=new Set,Z=new Set;function _(F,$=[]){if(Y.has(F))return;if(Z.has(F))throw Error(`Circular resource dependency: ${[...$,F].join(" -> ")}`);Z.add(F);for(let U of J(F))if(j.includes(U))_(U,[...$,F]);Z.delete(F),Y.add(F),X.push(F)}for(let F of j)_(F);return X}class z{resources=new Map;resourceFactories=new Map;resourceDependencies=new Map;resourceDisposers=new Map;initializedResourceKeys=new Set;add(j,J){if(u(J)){if(this.resourceFactories.set(j,J.factory),this.resourceDependencies.set(j,J.dependsOn??[]),J.onDispose)this.resourceDisposers.set(j,J.onDispose)}else if(this._isFactoryFunction(J))this.resourceFactories.set(j,J),this.resourceDependencies.set(j,[]);else this.resources.set(j,J),this.initializedResourceKeys.add(j),this.resourceDependencies.set(j,[]);return this}_isFactoryFunction(j){if(typeof j!=="function")return!1;let J=j.toString();if(J.startsWith("class "))return!1;if(J.includes("[native code]"))return!1;if(j.prototype){let X=Object.getOwnPropertyNames(j.prototype);if(X.length>1||X.length===1&&X[0]!=="constructor")return!1}if(j.name&&j.name[0]===j.name[0].toUpperCase()&&j.name.length>1){if(J.includes("this.")||J.includes("new "))return!1}return!0}get(j,J){let X=this.resources.get(j);if(X!==void 0)return X;let Y=this.resourceFactories.get(j);if(Y===void 0)throw Error(`Resource ${String(j)} not found`);let Z=Y(J);if(!(Z instanceof Promise))this.resources.set(j,Z),this.initializedResourceKeys.add(j);return Z}has(j){return this.resources.has(j)||this.resourceFactories.has(j)}remove(j){let J=this.resources.delete(j),X=this.resourceFactories.delete(j);return this.resourceDependencies.delete(j),this.resourceDisposers.delete(j),this.initializedResourceKeys.delete(j),J||X}getKeys(){let j=new Set([...this.resources.keys(),...this.resourceFactories.keys()]);return Array.from(j)}needsInitialization(j){return this.resourceFactories.has(j)&&!this.initializedResourceKeys.has(j)}getPendingInitializationKeys(){return Array.from(this.resourceFactories.keys()).filter((j)=>!this.initializedResourceKeys.has(j))}async initializeResource(j,J){if(!this.resourceFactories.has(j)||this.initializedResourceKeys.has(j))return;let Y=await this.resourceFactories.get(j)(J);this.resources.set(j,Y),this.initializedResourceKeys.add(j),this.resourceFactories.delete(j)}async initializeResources(j,...J){let X=J.length===0?this.getPendingInitializationKeys():J.map((Z)=>Z);if(X.length===0)return;let Y=q(X,(Z)=>this.resourceDependencies.get(Z)??[]);for(let Z of Y)await this.initializeResource(Z,j)}getDependencies(j){return this.resourceDependencies.get(j)??[]}async disposeResource(j,J){let X=j;if(!this.resources.has(X)&&!this.resourceFactories.has(X))return!1;if(this.initializedResourceKeys.has(X)){let Y=this.resourceDisposers.get(X),Z=this.resources.get(X);if(Y&&Z!==void 0)await Y(Z,J)}return this.resources.delete(X),this.resourceFactories.delete(X),this.resourceDependencies.delete(X),this.resourceDisposers.delete(X),this.initializedResourceKeys.delete(X),!0}async disposeResources(j){let J=Array.from(this.initializedResourceKeys);if(J.length===0)return;let X=q(J,(Y)=>this.resourceDependencies.get(Y)??[]).reverse();for(let Y of X)await this.disposeResource(Y,j)}}class w{assets=new Map;groups=new Map;eventBus=null;setEventBus(j){this.eventBus=j}register(j,J){if(this.assets.set(j,{definition:J,status:"pending"}),J.group){let X=this.groups.get(J.group)??new Set;X.add(j),this.groups.set(J.group,X)}}async loadEagerAssets(){let j=[];for(let[J,X]of this.assets)if(X.definition.eager&&X.status==="pending")j.push(J);await Promise.all(j.map((J)=>this.loadAsset(J)))}async loadAsset(j){let J=j,X=this.assets.get(J);if(!X)throw Error(`Asset '${J}' not found`);if(X.status==="loaded"&&X.value!==void 0)return X.value;if(X.status==="loading"&&X.loadPromise)return X.loadPromise;if(X.status==="failed")X.status="pending";X.status="loading",X.loadPromise=X.definition.loader();try{let Y=await X.loadPromise;return X.value=Y,X.status="loaded",X.loadPromise=void 0,this.eventBus?.publish("assetLoaded",{key:J}),this.checkGroupProgress(X.definition.group),Y}catch(Y){let Z=Y instanceof Error?Y:Error(String(Y));throw X.status="failed",X.error=Z,X.loadPromise=void 0,this.eventBus?.publish("assetFailed",{key:J,error:Z}),Z}}async loadAssetGroup(j){let J=this.groups.get(j);if(!J||J.size===0)throw Error(`Asset group '${j}' not found or empty`);await Promise.all(Array.from(J).map((X)=>this.loadAsset(X)))}get(j){let J=j,X=this.assets.get(J);if(!X)throw Error(`Asset '${J}' not found`);if(X.status!=="loaded"||X.value===void 0)throw Error(`Asset '${J}' is not loaded (status: ${X.status})`);return X.value}getOrUndefined(j){let J=j,X=this.assets.get(J);if(!X||X.status!=="loaded")return;return X.value}getHandle(j){let J=j,X=this.assets.get(J);if(!X)throw Error(`Asset '${J}' not found`);let Y=this;return{get status(){return X.status},get isLoaded(){return X.status==="loaded"},get(){return Y.get(j)},getOrUndefined(){return Y.getOrUndefined(j)}}}getStatus(j){let J=j,X=this.assets.get(J);if(!X)throw Error(`Asset '${J}' not found`);return X.status}isLoaded(j){let J=j;return this.assets.get(J)?.status==="loaded"}isGroupLoaded(j){let J=this.groups.get(j);if(!J||J.size===0)return!1;for(let X of J){let Y=this.assets.get(X);if(!Y||Y.status!=="loaded")return!1}return!0}getGroupProgress(j){let J=this.groups.get(j);if(!J||J.size===0)return 0;let X=0;for(let Y of J)if(this.assets.get(Y)?.status==="loaded")X++;return X/J.size}getGroupProgressDetails(j){let J=this.groups.get(j);if(!J||J.size===0)return{loaded:0,total:0,progress:0};let X=0;for(let Z of J)if(this.assets.get(Z)?.status==="loaded")X++;let Y=J.size;return{loaded:X,total:Y,progress:X/Y}}checkGroupProgress(j){if(!j||!this.eventBus)return;let J=this.getGroupProgressDetails(j);if(this.eventBus.publish("assetGroupProgress",{group:j,...J}),J.loaded===J.total)this.eventBus.publish("assetGroupLoaded",{group:j})}createResource(){let j=this;return{getStatus(J){return j.getStatus(J)},isLoaded(J){return j.isLoaded(J)},isGroupLoaded(J){return j.isGroupLoaded(J)},getGroupProgress(J){return j.getGroupProgress(J)},get(J){return j.get(J)},getOrUndefined(J){return j.getOrUndefined(J)},getHandle(J){return j.getHandle(J)}}}getKeys(){return Array.from(this.assets.keys())}getGroupNames(){return Array.from(this.groups.keys())}getGroupKeys(j){let J=this.groups.get(j);return J?Array.from(J):[]}}class S{manager;constructor(j){this.manager=j}add(j,J){return this.manager.register(j,{loader:J,eager:!0}),this}addWithConfig(j,J){return this.manager.register(j,J),this}addGroup(j,J){for(let[X,Y]of Object.entries(J))this.manager.register(X,{loader:Y,eager:!1,group:j});return this}getManager(){return this.manager}}function R(j){return new S(j??new w)}class M{screens=new Map;currentScreen=null;screenStack=[];eventBus=null;assetManager=null;ecs=null;setDependencies(j,J,X){this.eventBus=j,this.assetManager=J,this.ecs=X}register(j,J){this.screens.set(j,{definition:J})}async setScreen(j,J){let X=j,Y=this.screens.get(X);if(!Y)throw Error(`Screen '${X}' not found`);await this.verifyRequiredAssets(Y.definition);while(this.screenStack.length>0){let _=this.screenStack.pop();if(_)await this.exitScreen(_.name)}if(this.currentScreen)await this.exitScreen(this.currentScreen.name);let Z=Y.definition.initialState(J);this.currentScreen={name:j,config:J,state:Z},await Y.definition.onEnter?.(J,this.ecs),this.eventBus?.publish("screenEnter",{screen:X,config:J})}async pushScreen(j,J){let X=j,Y=this.screens.get(X);if(!Y)throw Error(`Screen '${X}' not found`);if(await this.verifyRequiredAssets(Y.definition),this.currentScreen)this.screenStack.push(this.currentScreen);let Z=Y.definition.initialState(J);this.currentScreen={name:j,config:J,state:Z},await Y.definition.onEnter?.(J,this.ecs),this.eventBus?.publish("screenPush",{screen:X,config:J})}async popScreen(){if(this.screenStack.length===0)throw Error("Cannot pop screen: stack is empty");if(this.currentScreen){let j=this.currentScreen.name;await this.exitScreen(j),this.eventBus?.publish("screenPop",{screen:j})}this.currentScreen=this.screenStack.pop()??null}async exitScreen(j){let J=this.screens.get(j);if(J?.definition.onExit)await J.definition.onExit(this.ecs);this.eventBus?.publish("screenExit",{screen:j})}async verifyRequiredAssets(j){if(!this.assetManager)return;if(j.requiredAssets){for(let J of j.requiredAssets)if(!this.assetManager.isLoaded(J))await this.assetManager.loadAsset(J)}if(j.requiredAssetGroups){for(let J of j.requiredAssetGroups)if(!this.assetManager.isGroupLoaded(J))await this.assetManager.loadAssetGroup(J)}}getCurrentScreen(){return this.currentScreen?.name??null}getConfig(){if(!this.currentScreen)throw Error("No current screen");return this.currentScreen.config}getConfigOrNull(){return this.currentScreen?.config??null}getState(){if(!this.currentScreen)throw Error("No current screen");return this.currentScreen.state}getStateOrNull(){return this.currentScreen?.state??null}updateState(j){if(!this.currentScreen)throw Error("No current screen");let J=typeof j==="function"?j(this.currentScreen.state):j;this.currentScreen.state={...this.currentScreen.state,...J}}getStackDepth(){return this.screenStack.length}isOverlay(){return this.screenStack.length>0}isActive(j){if(this.currentScreen?.name===j)return!0;return this.screenStack.some((J)=>J.name===j)}isCurrent(j){return this.currentScreen?.name===j}createResource(){let j=this;return{get current(){return j.getCurrentScreen()},get config(){return j.getConfigOrNull()},get state(){return j.getStateOrNull()},set state(J){if(j.currentScreen)j.currentScreen.state=J},get stack(){return j.screenStack},get isOverlay(){return j.isOverlay()},get stackDepth(){return j.getStackDepth()},isActive(J){return j.isActive(J)},isCurrent(J){return j.isCurrent(J)}}}getScreenNames(){return Array.from(this.screens.keys())}hasScreen(j){return this.screens.has(j)}}class v{manager;constructor(j){this.manager=j}add(j,J){return this.manager.register(j,J),this}getManager(){return this.manager}}function x(j){return new v(j??new M)}class K{queries=new Map;entityManager;constructor(j){this.entityManager=j}addQuery(j,J){let X={definition:J,matchingEntities:new Set};this.queries.set(j,X);let Y=this.entityManager.getEntitiesWithQuery(J.with,J.without??[]);for(let Z of Y)X.matchingEntities.add(Z.id),J.onEnter?.(Z)}removeQuery(j){return this.queries.delete(j)}entityMatchesQuery(j,J){for(let X of J.with)if(!(X in j.components))return!1;if(J.without){for(let X of J.without)if(X in j.components)return!1}return!0}onComponentAdded(j,J){for(let[X,Y]of this.queries){let Z=Y.matchingEntities.has(j.id),_=this.entityMatchesQuery(j,Y.definition);if(!Z&&_)Y.matchingEntities.add(j.id),Y.definition.onEnter?.(j);else if(Z&&!_)Y.matchingEntities.delete(j.id),Y.definition.onExit?.(j.id)}}onComponentRemoved(j,J){for(let[X,Y]of this.queries){let Z=Y.matchingEntities.has(j.id),_=this.entityMatchesQuery(j,Y.definition);if(Z&&!_)Y.matchingEntities.delete(j.id),Y.definition.onExit?.(j.id);else if(!Z&&_)Y.matchingEntities.add(j.id),Y.definition.onEnter?.(j)}}onEntityRemoved(j){for(let[J,X]of this.queries)if(X.matchingEntities.has(j))X.matchingEntities.delete(j),X.definition.onExit?.(j)}recheckEntity(j){for(let[J,X]of this.queries){let Y=X.matchingEntities.has(j.id),Z=this.entityMatchesQuery(j,X.definition);if(!Y&&Z)X.matchingEntities.add(j.id),X.definition.onEnter?.(j);else if(Y&&!Z)X.matchingEntities.delete(j.id),X.definition.onExit?.(j.id)}}}class D{commands=[];removeEntity(j,J){this.commands.push((X)=>{X.removeEntity(j,J)})}addComponent(j,J,X){this.commands.push((Y)=>{Y.entityManager.addComponent(j,J,X)})}removeComponent(j,J){this.commands.push((X)=>{X.entityManager.removeComponent(j,J)})}spawn(j){this.commands.push((J)=>{J.spawn(j)})}spawnChild(j,J){this.commands.push((X)=>{X.spawnChild(j,J)})}addComponents(j,J){this.commands.push((X)=>{X.entityManager.addComponents(j,J)})}setParent(j,J){this.commands.push((X)=>{X.setParent(j,J)})}removeParent(j){this.commands.push((J)=>{J.removeParent(j)})}playback(j){for(let J of this.commands)try{J(j)}catch(X){console.warn("CommandBuffer: Command failed during playback:",X)}this.commands=[]}clear(){this.commands=[]}get length(){return this.commands.length}}class B{_label;_ecspresso;_bundle;queries={};processFunction;detachFunction;initializeFunction;eventHandlers;_priority=0;_isRegistered=!1;_groups=[];_inScreens;_excludeScreens;_requiredAssets;constructor(j,J=null,X=null){this._label=j;this._ecspresso=J;this._bundle=X}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();T(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,J){let X=this;return X.queries={...this.queries,[j]:J},X}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 J=this._createSystemObject();if(this._ecspresso)T(J,this._ecspresso);if(j)T(J,j);return this}}function T(j,J){J._registerSystem(j)}function O(j,J){return new B(j,J)}function f(j,J){return new B(j,null,J)}var N="0.7.0";var l={};class P{static VERSION=N;_entityManager;_eventBus;_resourceManager;_commandBuffer;_systems=[];_sortedSystems=[];_installedBundles=new Set;_disabledGroups=new Set;_assetManager=null;_screenManager=null;_reactiveQueryManager;_postUpdateHooks=[];constructor(){this._entityManager=new V,this._eventBus=new L,this._resourceManager=new z,this._reactiveQueryManager=new K(this._entityManager),this._commandBuffer=new D,this._sortedSystems=[],this._setupReactiveQueryHooks()}_setupReactiveQueryHooks(){let j=0,J=new Set,X=()=>{for(let $ of J){let U=this._entityManager.getEntity($);if(U)this._reactiveQueryManager.recheckEntity(U)}J.clear()},Y=this._entityManager.addComponent.bind(this._entityManager);this._entityManager.addComponent=($,U,G)=>{let H=Y($,U,G),W=typeof $==="number"?$:$.id;if(j>0)J.add(W);else{let E=this._entityManager.getEntity(W);if(E)this._reactiveQueryManager.onComponentAdded(E,U)}return H};let Z=this._entityManager.addComponents.bind(this._entityManager);this._entityManager.addComponents=($,U)=>{j++;let G=Z($,U);if(j--,j===0)X();return G};let _=this._entityManager.removeComponent.bind(this._entityManager);this._entityManager.removeComponent=($,U)=>{let G=typeof $==="number"?$:$.id,H=this._entityManager.getEntity(G),W=_($,U);if(H)this._reactiveQueryManager.onComponentRemoved(H,U);return W};let F=this._entityManager.removeEntity.bind(this._entityManager);this._entityManager.removeEntity=($,U)=>{let G=typeof $==="number"?$:$.id;if(this._entityManager.getEntity(G)){if(U?.cascade??!0){let E=this._entityManager.getDescendants(G);for(let h of E)this._reactiveQueryManager.onEntityRemoved(h)}this._reactiveQueryManager.onEntityRemoved(G)}return F($,U)}}static create(){return new b}addSystem(j){return O(j,this)}update(j){let J=this._screenManager?.getCurrentScreen()??null;for(let X of this._sortedSystems){if(!X.process)continue;if(X.groups?.length){let F=!1;for(let $ of X.groups)if(this._disabledGroups.has($)){F=!0;break}if(F)continue}if(X.inScreens?.length){if(J===null||!X.inScreens.includes(J))continue}if(X.excludeScreens?.length){if(J!==null&&X.excludeScreens.includes(J))continue}if(X.requiredAssets?.length&&this._assetManager){let F=!0;for(let $ of X.requiredAssets)if(!this._assetManager.isLoaded($)){F=!1;break}if(!F)continue}let Y={},Z=!1,_=!1;if(X.entityQueries)for(let F in X.entityQueries){_=!0;let $=X.entityQueries[F];if($){if(Y[F]=this._entityManager.getEntitiesWithQuery($.with,$.without||[]),Y[F].length)Z=!0}}if(Z)X.process(Y,j,this);else if(!_)X.process(l,j,this)}for(let X of this._postUpdateHooks)X(this,j);this._commandBuffer.playback(this)}async initialize(){if(await this.initializeResources(),this._assetManager)this._assetManager.setEventBus(this._eventBus),await this._assetManager.loadEagerAssets(),this._resourceManager.add("$assets",this._assetManager.createResource());if(this._screenManager)this._screenManager.setDependencies(this._eventBus,this._assetManager,this),this._resourceManager.add("$screen",this._screenManager.createResource());for(let j of this._systems)await j.onInitialize?.(this)}async initializeResources(...j){await this._resourceManager.initializeResources(this,...j)}_sortSystems(){this._sortedSystems=[...this._systems].sort((j,J)=>{let X=j.priority??0;return(J.priority??0)-X})}updateSystemPriority(j,J){let X=this._systems.find((Y)=>Y.label===j);if(!X)return!1;return X.priority=J,this._sortSystems(),!0}disableSystemGroup(j){this._disabledGroups.add(j)}enableSystemGroup(j){this._disabledGroups.delete(j)}isSystemGroupEnabled(j){return!this._disabledGroups.has(j)}getSystemsInGroup(j){return this._systems.filter((J)=>J.groups?.includes(j)).map((J)=>J.label)}removeSystem(j){let J=this._systems.findIndex((Y)=>Y.label===j);if(J===-1)return!1;let X=this._systems[J];if(!X)return!1;if(X.onDetach)X.onDetach(this);return this._systems.splice(J,1),this._sortSystems(),!0}_registerSystem(j){if(this._systems.push(j),this._sortSystems(),!j.eventHandlers)return;for(let J in j.eventHandlers){let X=j.eventHandlers[J]?.handler;if(X)this._eventBus.subscribe(J,(Y)=>{X(Y,this)})}}hasResource(j){return this._resourceManager.has(j)}getResource(j){let J=this._resourceManager.get(j,this);if(!J)throw Error(`Resource '${String(j)}' not found. Available resources: [${this.getResourceKeys().map((X)=>String(X)).join(", ")}]`);return J}addResource(j,J){return this._resourceManager.add(j,J),this}removeResource(j){return this._resourceManager.remove(j)}async disposeResource(j){return this._resourceManager.disposeResource(j,this)}async disposeResources(){return this._resourceManager.disposeResources(this)}updateResource(j,J){let X=this.getResource(j),Y=J(X);return this._resourceManager.add(j,Y),this}getResourceKeys(){return this._resourceManager.getKeys()}resourceNeedsInitialization(j){return this._resourceManager.needsInitialization(j)}hasComponent(j,J){return this._entityManager.getComponent(j,J)!==null}spawn(j){let J=this._entityManager.createEntity();return this._entityManager.addComponents(J,j),J}getEntitiesWithQuery(j,J=[]){return this._entityManager.getEntitiesWithQuery(j,J)}removeEntity(j,J){return this._entityManager.removeEntity(j,J)}spawnChild(j,J){let X=this._entityManager.spawnChild(j,J);return this._emitHierarchyChanged(X.id,null,j),X}setParent(j,J){let X=this._entityManager.getParent(j);return this._entityManager.setParent(j,J),this._emitHierarchyChanged(j,X,J),this}removeParent(j){let J=this._entityManager.getParent(j),X=this._entityManager.removeParent(j);if(X)this._emitHierarchyChanged(j,J,null);return X}getParent(j){return this._entityManager.getParent(j)}getChildren(j){return this._entityManager.getChildren(j)}getChildAt(j,J){return this._entityManager.getChildAt(j,J)}getChildIndex(j,J){return this._entityManager.getChildIndex(j,J)}getAncestors(j){return this._entityManager.getAncestors(j)}getDescendants(j){return this._entityManager.getDescendants(j)}getRoot(j){return this._entityManager.getRoot(j)}getSiblings(j){return this._entityManager.getSiblings(j)}isDescendantOf(j,J){return this._entityManager.isDescendantOf(j,J)}isAncestorOf(j,J){return this._entityManager.isAncestorOf(j,J)}getRootEntities(){return this._entityManager.getRootEntities()}forEachInHierarchy(j,J){this._entityManager.forEachInHierarchy(j,J)}hierarchyIterator(j){return this._entityManager.hierarchyIterator(j)}_emitHierarchyChanged(j,J,X){this._eventBus.publish("hierarchyChanged",{entityId:j,oldParent:J,newParent:X})}get installedBundles(){return Array.from(this._installedBundles)}get entityManager(){return this._entityManager}get eventBus(){return this._eventBus}get commands(){return this._commandBuffer}onComponentAdded(j,J){return this._entityManager.onComponentAdded(j,J)}onComponentRemoved(j,J){return this._entityManager.onComponentRemoved(j,J)}addReactiveQuery(j,J){this._reactiveQueryManager.addQuery(j,J)}removeReactiveQuery(j){return this._reactiveQueryManager.removeQuery(j)}on(j,J){return this._eventBus.subscribe(j,J)}off(j,J){return this._eventBus.unsubscribe(j,J)}onPostUpdate(j){return this._postUpdateHooks.push(j),()=>{let J=this._postUpdateHooks.indexOf(j);if(J!==-1)this._postUpdateHooks.splice(J,1)}}getAsset(j){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager.get(j)}getAssetOrUndefined(j){return this._assetManager?.getOrUndefined(j)}getAssetHandle(j){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager.getHandle(j)}isAssetLoaded(j){return this._assetManager?.isLoaded(j)??!1}async loadAsset(j){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager.loadAsset(j)}async loadAssetGroup(j){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager.loadAssetGroup(j)}isAssetGroupLoaded(j){return this._assetManager?.isGroupLoaded(j)??!1}getAssetGroupProgress(j){return this._assetManager?.getGroupProgress(j)??0}async setScreen(j,J){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.setScreen(j,J)}async pushScreen(j,J){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.pushScreen(j,J)}async popScreen(){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.popScreen()}getCurrentScreen(){return this._screenManager?.getCurrentScreen()??null}getScreenConfig(){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.getConfig()}getScreenConfigOrNull(){return this._screenManager?.getConfigOrNull()??null}getScreenState(){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager.getState()}getScreenStateOrNull(){return this._screenManager?.getStateOrNull()??null}updateScreenState(j){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");this._screenManager.updateState(j)}isCurrentScreen(j){return this._screenManager?.isCurrent(j)??!1}isScreenActive(j){return this._screenManager?.isActive(j)??!1}getScreenStackDepth(){return this._screenManager?.getStackDepth()??0}_setAssetManager(j){this._assetManager=j}_setScreenManager(j){this._screenManager=j}_installBundle(j){if(this._installedBundles.has(j.id))return this;this._installedBundles.add(j.id),j.registerSystemsWithEcspresso(this);let J=j.getResources();for(let[X,Y]of J.entries())this._resourceManager.add(X,Y);if(this._assetManager){let X=j.getAssets();for(let[Y,Z]of X.entries())this._assetManager.register(Y,Z)}if(this._screenManager){let X=j.getScreens();for(let[Y,Z]of X.entries())this._screenManager.register(Y,Z)}return this}}class b{ecspresso;assetConfigurator=null;screenConfigurator=null;pendingResources=[];constructor(){this.ecspresso=new P}withBundle(j){return this.ecspresso._installBundle(j),this}withResource(j,J){return this.pendingResources.push({key:j,value:J}),this}withAssets(j){let J=R();return j(J),this.assetConfigurator=J,this}withScreens(j){let J=x();return j(J),this.screenConfigurator=J,this}build(){for(let{key:j,value:J}of this.pendingResources)this.ecspresso.addResource(j,J);if(this.assetConfigurator)this.ecspresso._setAssetManager(this.assetConfigurator.getManager());if(this.screenConfigurator)this.ecspresso._setScreenManager(this.screenConfigurator.getManager());return this.ecspresso}}function c(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class A{_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 J=f(j,this);return this._systems.push(J),J}else return this._systems.push(j),j}addResource(j,J){return this._resources.set(j,J),this}addAsset(j,J,X){return this._assets.set(j,{loader:J,eager:X?.eager??!0,group:X?.group}),this}addAssetGroup(j,J){let X=new Map;for(let[Y,Z]of Object.entries(J))X.set(Y,Z),this._assets.set(Y,{loader:Z,eager:!1,group:j});return this._assetGroups.set(j,X),this}addScreen(j,J){return this._screens.set(j,J),this}getAssets(){return new Map(this._assets)}getScreens(){return new Map(this._screens)}_setResource(j,J){this._resources.set(j,J)}_setAsset(j,J){this._assets.set(j,J)}_setScreen(j,J){this._screens.set(j,J)}getSystems(){return this._systems.map((j)=>j.build())}registerSystemsWithEcspresso(j){for(let J of this._systems)J.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 s(j,...J){if(J.length===0)return new A(j);let X=new A(j);for(let Y of J){for(let Z of Y.getSystemBuilders())X.addSystem(Z);for(let[Z,_]of Y.getResources().entries())X._setResource(Z,_);for(let[Z,_]of Y.getAssets().entries())X._setAsset(Z,_);for(let[Z,_]of Y.getScreens().entries())X._setScreen(Z,_)}return X}function Kj(j){return j}var Ij=P;export{s as mergeBundles,Ij as default,x as createScreenConfigurator,Kj as createQueryDefinition,R as createAssetConfigurator,B as SystemBuilder,M as ScreenManager,z as ResourceManager,Q as HierarchyManager,L as EventBus,V as EntityManager,D as CommandBuffer,A as Bundle,w as AssetManager};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=358E7564D517B85664756E2164756E21
|
|
4
4
|
//# sourceMappingURL=index.js.map
|