ecspresso 0.4.3 → 0.5.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,107 @@
1
+ /**
2
+ * Manages parent-child relationships between entities.
3
+ * Handles hierarchy storage, validation, and traversal operations.
4
+ */
5
+ export default class HierarchyManager {
6
+ /** childId -> parentId */
7
+ private parentMap;
8
+ /** parentId -> ordered childIds */
9
+ private childrenMap;
10
+ /**
11
+ * Set the parent of an entity.
12
+ * @param childId The entity to set as a child
13
+ * @param parentId The entity to set as the parent
14
+ * @throws Error if this would create a circular reference or self-parenting
15
+ */
16
+ setParent(childId: number, parentId: number): this;
17
+ /**
18
+ * Remove the parent relationship for an entity (orphan it).
19
+ * @param childId The entity to orphan
20
+ * @returns true if a parent was removed, false if entity had no parent
21
+ */
22
+ removeParent(childId: number): boolean;
23
+ /**
24
+ * Get the parent of an entity.
25
+ * @param entityId The entity to get the parent of
26
+ * @returns The parent entity ID, or null if no parent
27
+ */
28
+ getParent(entityId: number): number | null;
29
+ /**
30
+ * Get all children of an entity in insertion order.
31
+ * @param parentId The parent entity
32
+ * @returns Readonly array of child entity IDs
33
+ */
34
+ getChildren(parentId: number): readonly number[];
35
+ /**
36
+ * Get a child at a specific index.
37
+ * @param parentId The parent entity
38
+ * @param index The index of the child
39
+ * @returns The child entity ID, or null if index is out of bounds
40
+ */
41
+ getChildAt(parentId: number, index: number): number | null;
42
+ /**
43
+ * Get the index of a child within its parent's children list.
44
+ * @param parentId The parent entity
45
+ * @param childId The child entity to find
46
+ * @returns The index of the child, or -1 if not found
47
+ */
48
+ getChildIndex(parentId: number, childId: number): number;
49
+ /**
50
+ * Remove an entity from the hierarchy (called when entity is destroyed).
51
+ * Orphans any children and removes from parent's children list.
52
+ * @param entityId The entity being removed
53
+ * @returns Information about the removal (oldParent and orphanedChildren)
54
+ */
55
+ removeEntity(entityId: number): {
56
+ oldParent: number | null;
57
+ orphanedChildren: number[];
58
+ };
59
+ /**
60
+ * Get all ancestors of an entity in order [parent, grandparent, ...].
61
+ * @param entityId The entity to get ancestors of
62
+ * @returns Readonly array of ancestor entity IDs
63
+ */
64
+ getAncestors(entityId: number): readonly number[];
65
+ /**
66
+ * Get all descendants of an entity in depth-first order.
67
+ * @param entityId The entity to get descendants of
68
+ * @returns Readonly array of descendant entity IDs
69
+ */
70
+ getDescendants(entityId: number): readonly number[];
71
+ /**
72
+ * Get the root ancestor of an entity (topmost parent), or self if no parent.
73
+ * @param entityId The entity to get the root of
74
+ * @returns The root entity ID
75
+ */
76
+ getRoot(entityId: number): number;
77
+ /**
78
+ * Get siblings of an entity (other children of the same parent).
79
+ * @param entityId The entity to get siblings of
80
+ * @returns Readonly array of sibling entity IDs
81
+ */
82
+ getSiblings(entityId: number): readonly number[];
83
+ /**
84
+ * Check if an entity is a descendant of another entity.
85
+ * @param entityId The potential descendant
86
+ * @param ancestorId The potential ancestor
87
+ * @returns true if entityId is a descendant of ancestorId
88
+ */
89
+ isDescendantOf(entityId: number, ancestorId: number): boolean;
90
+ /**
91
+ * Check if an entity is an ancestor of another entity.
92
+ * @param entityId The potential ancestor
93
+ * @param descendantId The potential descendant
94
+ * @returns true if entityId is an ancestor of descendantId
95
+ */
96
+ isAncestorOf(entityId: number, descendantId: number): boolean;
97
+ /**
98
+ * Get all root entities (entities that have children but no parent).
99
+ * @returns Readonly array of root entity IDs
100
+ */
101
+ getRootEntities(): readonly number[];
102
+ /**
103
+ * Check if setting a parent would create a cycle.
104
+ * A cycle would occur if the prospective parent is a descendant of the child.
105
+ */
106
+ private wouldCreateCycle;
107
+ }
package/dist/index.d.ts CHANGED
@@ -2,13 +2,18 @@ import ECSpresso from './ecspresso';
2
2
  import { SystemBuilder } from './system-builder';
3
3
  import Bundle, { mergeBundles } from './bundle';
4
4
  export * from './types';
5
+ export * from './asset-types';
6
+ export * from './screen-types';
5
7
  export { default as EntityManager } from './entity-manager';
6
8
  export { default as EventBus } from './event-bus';
9
+ export { default as HierarchyManager } from './hierarchy-manager';
7
10
  /**
8
11
  * @internal ResourceManager is exported for testing purposes only.
9
12
  * Use ECSpresso resource methods instead: getResource(), addResource(), removeResource(), updateResource(), hasResource()
10
13
  */
11
14
  export { default as ResourceManager } from './resource-manager';
15
+ export { default as AssetManager, createAssetConfigurator } from './asset-manager';
16
+ export { default as ScreenManager, createScreenConfigurator } from './screen-manager';
12
17
  export { SystemBuilder };
13
18
  export { Bundle, mergeBundles };
14
19
  export default ECSpresso;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- class F{nextId=1;entities=new Map;componentIndices=new Map;addedCallbacks=new Map;removedCallbacks=new Map;createEntity(){let j=this.nextId++,G={id:j,components:{}};return this.entities.set(j,G),G}addComponent(j,G,H){let J=typeof j==="number"?this.entities.get(j):j;if(!J){let Y=typeof j==="number"?j:j.id;throw Error(`Cannot add component '${String(G)}': Entity with ID ${Y} does not exist`)}if(J.components[G]=H,!this.componentIndices.has(G))this.componentIndices.set(G,new Set);this.componentIndices.get(G)?.add(J.id);let X=this.addedCallbacks.get(G);if(X)for(let Y of X)Y(H,J);return this}addComponents(j,G){let H=typeof j==="number"?this.entities.get(j):j;if(!H){let J=typeof j==="number"?j:j.id;throw Error(`Cannot add components: Entity with ID ${J} does not exist`)}for(let J in G)this.addComponent(H,J,G[J]);return this}removeComponent(j,G){let H=typeof j==="number"?this.entities.get(j):j;if(!H){let Y=typeof j==="number"?j:j.id;throw Error(`Cannot remove component '${String(G)}': Entity with ID ${Y} does not exist`)}let J=H.components[G];delete H.components[G];let X=this.removedCallbacks.get(G);if(X&&J!==void 0)for(let Y of X)Y(J,H);return this.componentIndices.get(G)?.delete(H.id),this}getComponent(j,G){let H=this.entities.get(j);if(!H)throw Error(`Cannot get component '${String(G)}': Entity with ID ${j} does not exist`);return H.components[G]||null}getEntitiesWithQuery(j=[],G=[]){if(j.length===0){if(G.length===0)return Array.from(this.entities.values());return Array.from(this.entities.values()).filter((Z)=>{return G.every((_)=>!(_ in Z.components))})}let H=j.reduce((Z,_)=>{let $=this.componentIndices.get(_)?.size??0,w=this.componentIndices.get(Z)?.size??1/0;return $<w?_:Z},j[0]),J=this.componentIndices.get(H);if(!J||J.size===0)return[];let X=[],Y=G.length>0;for(let Z of J){let _=this.entities.get(Z);if(_&&j.every(($)=>($ in _.components))&&(!Y||G.every(($)=>!($ in _.components))))X.push(_)}return X}removeEntity(j){let G=typeof j==="number"?this.entities.get(j):j;if(!G)return!1;for(let H of Object.keys(G.components)){let J=G.components[H];if(J!==void 0){let X=this.removedCallbacks.get(H);if(X)for(let Y of X)Y(J,G)}this.componentIndices.get(H)?.delete(G.id)}return this.entities.delete(G.id)}getEntity(j){return this.entities.get(j)}onComponentAdded(j,G){if(!this.addedCallbacks.has(j))this.addedCallbacks.set(j,new Set);return this.addedCallbacks.get(j).add(G),this}onComponentRemoved(j,G){if(!this.removedCallbacks.has(j))this.removedCallbacks.set(j,new Set);return this.removedCallbacks.get(j).add(G),this}}class P{handlers=new Map;subscribe(j,G){return this.addHandler(j,G,!1)}once(j,G){return this.addHandler(j,G,!0)}addHandler(j,G,H){if(!this.handlers.has(j))this.handlers.set(j,[]);let J={callback:G,once:H};return this.handlers.get(j).push(J),()=>{let X=this.handlers.get(j);if(X){let Y=X.indexOf(J);if(Y!==-1)X.splice(Y,1)}}}publish(j,G){let H=this.handlers.get(j);if(!H)return;let J=[...H],X=[];for(let Y of J)if(Y.callback(G),Y.once)X.push(Y);if(X.length>0)for(let Y of X){let Z=H.indexOf(Y);if(Z!==-1)H.splice(Z,1)}}clear(){this.handlers.clear()}clearEvent(j){this.handlers.delete(j)}}class U{resources=new Map;resourceFactories=new Map;initializedResourceKeys=new Set;add(j,G){if(this._isFactoryFunction(G))this.resourceFactories.set(j,G);else this.resources.set(j,G),this.initializedResourceKeys.add(j);return this}_isFactoryFunction(j){if(typeof j!=="function")return!1;let G=j.toString();if(G.startsWith("class "))return!1;if(G.includes("[native code]"))return!1;if(j.prototype){let H=Object.getOwnPropertyNames(j.prototype);if(H.length>1||H.length===1&&H[0]!=="constructor")return!1}if(j.name&&j.name[0]===j.name[0].toUpperCase()&&j.name.length>1){if(G.includes("this.")||G.includes("new "))return!1}return!0}get(j,G){let H=this.resources.get(j);if(H!==void 0)return H;let J=this.resourceFactories.get(j);if(J===void 0)throw Error(`Resource ${String(j)} not found`);let X=J(G);if(!(X instanceof Promise))this.resources.set(j,X),this.initializedResourceKeys.add(j);return X}has(j){return this.resources.has(j)||this.resourceFactories.has(j)}remove(j){let G=this.resources.delete(j),H=this.resourceFactories.delete(j);if(this.initializedResourceKeys.has(j))this.initializedResourceKeys.delete(j);return G||H}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,G){if(!this.resourceFactories.has(j)||this.initializedResourceKeys.has(j))return;let J=await this.resourceFactories.get(j)(G);this.resources.set(j,J),this.initializedResourceKeys.add(j),this.resourceFactories.delete(j)}async initializeResources(j,...G){if(G.length===0){let H=this.getPendingInitializationKeys();await Promise.all(H.map((J)=>this.initializeResource(J,j)));return}await Promise.all(G.map((H)=>this.initializeResource(H,j)))}}class M{_label;_ecspresso;_bundle;queries={};processFunction;detachFunction;initializeFunction;eventHandlers;_priority=0;_isRegistered=!1;constructor(j,G=null,H=null){this._label=j;this._ecspresso=G;this._bundle=H}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();Q(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;return j}setPriority(j){return this._priority=j,this}addQuery(j,G){let H=this;return H.queries={...this.queries,[j]:G},H}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 G=this._createSystemObject();if(this._ecspresso)Q(G,this._ecspresso);if(j)Q(G,j);return this}}function Q(j,G){G._registerSystem(j)}function W(j,G){return new M(j,G)}function g(j,G){return new M(j,null,G)}var K="0.4.3";var z={};class A{static VERSION=K;_entityManager;_eventBus;_resourceManager;_systems=[];_sortedSystems=[];_installedBundles=new Set;constructor(){this._entityManager=new F,this._eventBus=new P,this._resourceManager=new U,this._sortedSystems=[]}static create(){return new V}addSystem(j){return W(j,this)}update(j){for(let G of this._sortedSystems){if(!G.process)continue;let H={},J=!1,X=!1;if(G.entityQueries)for(let Y in G.entityQueries){X=!0;let Z=G.entityQueries[Y];if(Z){if(H[Y]=this._entityManager.getEntitiesWithQuery(Z.with,Z.without||[]),H[Y].length)J=!0}}if(J)G.process(H,j,this);else if(!X)G.process(z,j,this)}}async initialize(){await this.initializeResources();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,G)=>{let H=j.priority??0;return(G.priority??0)-H})}updateSystemPriority(j,G){let H=this._systems.find((J)=>J.label===j);if(!H)return!1;return H.priority=G,this._sortSystems(),!0}removeSystem(j){let G=this._systems.findIndex((J)=>J.label===j);if(G===-1)return!1;let H=this._systems[G];if(!H)return!1;if(H.onDetach)H.onDetach(this);return this._systems.splice(G,1),this._sortSystems(),!0}_registerSystem(j){if(this._systems.push(j),this._sortSystems(),!j.eventHandlers)return;for(let G in j.eventHandlers){let H=j.eventHandlers[G]?.handler;if(H)this._eventBus.subscribe(G,(J)=>{H(J,this)})}}hasResource(j){return this._resourceManager.has(j)}getResource(j){let G=this._resourceManager.get(j,this);if(!G)throw Error(`Resource '${String(j)}' not found. Available resources: [${this.getResourceKeys().map((H)=>String(H)).join(", ")}]`);return G}addResource(j,G){return this._resourceManager.add(j,G),this}removeResource(j){return this._resourceManager.remove(j)}updateResource(j,G){let H=this.getResource(j),J=G(H);return this._resourceManager.add(j,J),this}getResourceKeys(){return this._resourceManager.getKeys()}resourceNeedsInitialization(j){return this._resourceManager.needsInitialization(j)}hasComponent(j,G){return this._entityManager.getComponent(j,G)!==null}spawn(j){let G=this._entityManager.createEntity();return this._entityManager.addComponents(G,j),G}getEntitiesWithQuery(j,G=[]){return this._entityManager.getEntitiesWithQuery(j,G)}get installedBundles(){return Array.from(this._installedBundles)}get entityManager(){return this._entityManager}get eventBus(){return this._eventBus}_installBundle(j){if(this._installedBundles.has(j.id))return this;this._installedBundles.add(j.id),j.registerSystemsWithEcspresso(this);let G=j.getResources();for(let[H,J]of G.entries())this._resourceManager.add(H,J);return this}}class V{ecspresso;constructor(){this.ecspresso=new A}withBundle(j){return this.ecspresso._installBundle(j),this}build(){return this.ecspresso}}function f(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class D{_systems=[];_resources=new Map;_id;constructor(j){this._id=j||f()}get id(){return this._id}set id(j){this._id=j}addSystem(j){if(typeof j==="string"){let G=g(j,this);return this._systems.push(G),G}else return this._systems.push(j),j}addResource(j,G){return this._resources.set(j,G),this}getSystems(){return this._systems.map((j)=>j.build())}registerSystemsWithEcspresso(j){for(let G of this._systems)G.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,...G){if(G.length===0)return new D(j);let H=new D(j);for(let J of G){for(let X of J.getSystemBuilders())H.addSystem(X);for(let[X,Y]of J.getResources().entries())H.addResource(X,Y)}return H}function p(j){return j}var a=A;export{E as mergeBundles,a as default,p as createQueryDefinition,M as SystemBuilder,U as ResourceManager,P as EventBus,F as EntityManager,D as Bundle};
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};
2
2
 
3
- //# debugId=179E5389134FBDFC64756E2164756E21
3
+ //# debugId=B0736F43983D1AE364756E2164756E21
4
4
  //# sourceMappingURL=index.js.map