ecsjs 1.4.0-beta.5 → 1.4.0-beta.6

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 1.4.0-beta.6
2
+
3
+ - Hardened component registration and error handling
4
+ - `ecs.register()` now throws `ComponentAlreadyRegistered` if a component is already registered.
5
+ - `ecs.register()` now throws `ComponentTypeKeyMissing` for anonymous classes (classes with no name).
6
+ - Standardized `has`, `hasAll`, and `hasAny` to throw `ComponentNotRegistered` when any of the specified component(s) are not registered.
7
+
1
8
  # 1.4.0-beta.5
2
9
 
3
10
  - Added entity id reclaiming to [EntityMap](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html)
package/README.md CHANGED
@@ -6,16 +6,31 @@
6
6
 
7
7
  [![BuyMeACoffee](https://www.buymeacoffee.com/assets/img/custom_images/purple_img.png)](https://www.buymeacoffee.com/peterf)
8
8
 
9
- An entity component system library for JavaScript
9
+ An entity component system library for JavaScript.
10
10
 
11
- ## Install
11
+ ## Table of Contents
12
12
 
13
- `npm install --save ecsjs`
13
+ - [Features](#features)
14
+ - [Install](#install)
15
+ - [Usage](#usage)
16
+ - [API](#api)
17
+ - [Examples](#examples)
18
+ - [Cheat Sheet](https://gitlab.com/ecsjs/ecs/-/blob/master/docs/cheat-sheet.md)
19
+ - [Developer Documentation](./docs/development.md)
20
+ - [License](#license)
14
21
 
15
- ## Documentation
22
+ ## Features
16
23
 
17
- - [Cheat Sheet](https://gitlab.com/ecsjs/ecs/-/blob/master/docs/cheat-sheet.md)
18
- - [Reference Documentation](https://ecsjs.gitlab.io/ecs)
24
+ - **Simple, ID-based Entities:** Entities are treated as lightweight numeric identifiers, making them easy to track and manage without the overhead of complex objects.
25
+ - **Component-Driven Data:** Define your data using simple classes that can be dynamically attached to, or removed from, any entity at runtime.
26
+ - **Flexible Entity Management:** Use the built-in global instance (`ecs`) or create isolated `EntityMap` managers, providing `get`, `set`, `remove` and [more api functions](#api).
27
+ - **Iterable Queries:** Retrieve and filter sets of entities by primary component keys and relationships.
28
+ - **Logic-Data Decoupling:** Designed to support independent systems that process data in bulk, keeping your logic clean and separate from your state.
29
+ - **High-Performance Backbone:** A minimalist, zero-dependency core optimized for high-frequency updates and large-scale entity management.
30
+
31
+ ## Install
32
+
33
+ `npm install --save ecsjs`
19
34
 
20
35
  ## Examples
21
36
 
@@ -24,6 +39,8 @@ An entity component system library for JavaScript
24
39
  - [1943](https://gitlab.com/ecsjs/example-1943)
25
40
  - [Misc Examples](https://gitlab.com/ecsjs/ecs-examples)
26
41
 
42
+ ## Usage
43
+
27
44
  #### Browser
28
45
 
29
46
  ```html
@@ -71,8 +88,105 @@ ecs.set(entityId, new Position(25, 25))
71
88
  ```
72
89
 
73
90
 
91
+ ## API
92
+
93
+ The primary interface for managing entities and components is the `EntityMap` class. A global instance `ecs` is exported for convenience, but you can also create your own instances.
94
+
95
+ [Full Api Reference Documentation](https://ecsjs.gitlab.io/ecs)
96
+
97
+ ### `ecs.register(...ComponentClasses)`
98
+ Registers one or more component classes. Components **must** be registered before they can be used with an entity.
99
+ - **Throws:** `ComponentTypeKeyMissing` if a class name is missing (e.g., anonymous classes).
100
+ - **Throws:** `ComponentAlreadyRegistered` if a class is already registered.
101
+ - **Example:** `ecs.register(Position, Velocity)`
102
+
103
+ ### `ecs.getNextId()`
104
+ Generates a unique entity identifier (a simple number). It reclaims IDs from destroyed entities when possible.
105
+ - **Returns:** `number`
106
+
107
+ ### `ecs.set(entityId, ...componentInstances)`
108
+ Adds or updates one or more component instances for a specific entity.
109
+ - **Example:** `ecs.set(entityId, new Position(10, 20))`
110
+
111
+ ### `ecs.getMap(ComponentClass)`
112
+ Returns the underlying `ComponentMap` for a specific component class.
113
+ - **Returns:** `ComponentMap<T> | undefined`
114
+
115
+ ### `ecs.get(entityId, ...ComponentClasses)`
116
+ Retrieves component data for an entity.
117
+ - If one class is provided, returns the instance.
118
+ - If multiple classes are provided, returns an array of instances.
119
+ - If no classes are provided, returns all components for that entity.
120
+
121
+ ### `ecs.firstKey(KeyComponent, ...RelatedComponents)`
122
+ Returns the entity ID for the first entity that matches the `KeyComponent`. If related components are provided, returns `[id, ...relatedData]`.
123
+
124
+ ### `ecs.firstValue(KeyComponent, ...RelatedComponents)`
125
+ Returns the component data for the first entity that matches the `KeyComponent`.
126
+ - **Example:** `const [player, pos] = ecs.firstValue(Player, Position) ?? []`
127
+
128
+ ### `ecs.firstEntry(KeyComponent, ...RelatedComponents)`
129
+ Similar to `firstValue`, but also returns the `entityId` as the first element in the array.
130
+ - **Example:** `const [id, player, pos] = ecs.firstEntry(Player, Position) ?? []`
131
+
132
+ ### `ecs.firstEntity(KeyComponent)`
133
+ Returns an array of all components for the first entity associated with the `KeyComponent`.
134
+
135
+ ### `ecs.entityValues(KeyComponent)`
136
+ Returns an array of component arrays (all data) for every entity associated with the `KeyComponent`.
137
+
138
+ ### `ecs.has(entityId, ComponentClass)`
139
+ Checks if an entity has a specific component.
140
+ - **Throws:** `ComponentNotRegistered` if the component class is not registered.
141
+ - **Returns:** `boolean`
142
+
143
+ ### `ecs.hasAll(entityId, ...ComponentClasses)`
144
+ Checks if an entity has **all** of the specified components.
145
+ - **Throws:** `ComponentNotRegistered` if any of the component classes are not registered.
146
+
147
+ ### `ecs.hasAny(entityId, ...ComponentClasses)`
148
+ Checks if an entity has **at least one** of the specified components.
149
+ - **Throws:** `ComponentNotRegistered` if any of the component classes are not registered.
150
+
151
+ ### `ecs.remove(entityId, ...ComponentClasses)`
152
+ Removes one or more components from an entity.
153
+
154
+ ### `ecs.destroyEntity(...entityIds)`
155
+ Removes all components from the specified entities and reclaims their IDs for future use.
156
+
157
+ ### `ecs.clearComponents()`
158
+ Removes all entity data and reclaims all IDs, but keeps the registered component classes.
159
+
160
+ ### `ecs.clear()`
161
+ Resets everything, including removing all registered component classes.
162
+
163
+ ### `ecs.iterator(KeyComponent, ...RelatedComponents)`
164
+ Returns an iterator for entities matching the `KeyComponent`.
165
+ - **Example:**
166
+ ```js
167
+ for (const [id, pos, vel] of ecs.iterator(Position, Velocity)) {
168
+ // process
169
+ }
170
+ ```
171
+
172
+ ### `ecs.query(KeyComponent, ...RelatedComponents)`
173
+ Creates a reusable query for entities that possess at least the `KeyComponent`.
174
+ - **Returns:** A `ComponentQuery` object that can be iterated or used to fetch specific results.
175
+
176
+ ### `ecs.printTable(components?, properties?)`
177
+ Prints component maps to the console in a tabular format for debugging.
178
+
179
+ ### `ecs.printEntity(entityId, properties?)`
180
+ Prints all component data for a specific entity to the console in a tabular format.
181
+
182
+ ### `EntityMap.parse(json)` (static)
183
+ Restores an `EntityMap` instance from a JSON string.
184
+
185
+ ### `EntityMap.createWithTracing(funcFilter?)` (static)
186
+ Creates a new `EntityMap` wrapped in a Proxy that logs all method calls to the console for debugging.
187
+
74
188
  ## License
75
189
 
76
190
  Licensed under GNU GPL v3
77
191
 
78
- Copyright &copy; 2013+ [contributors](https://gitlab.com/ecsjs/ecs/-/graphs/master)
192
+ Copyright &copy; 2013+ [contributors](https://gitlab.com/ecsjs/ecs/-/graphs/master)
package/dist/ecs.js CHANGED
@@ -1,2 +1,2 @@
1
- var l="ComponentMap",y="ComponentClassesMap";function T(o){let e=0;for(let t=0;t<o.length;t++){let n=o.charCodeAt(t);e=(e<<5)-e+n,e|=0}return e}var m=class{hashIdToName=new Map;hashIdToIndex=new Map;indexToHashId=[];maps=[];constructor(e){if(e)for(let t=0;t<e.length;t++){let n=e[t];this.setByHashId(n[0],n[1])}}get size(){return this.maps.length}clear(){this.maps.length=0,this.indexToHashId.length=0,this.hashIdToIndex.clear()}get(e){let t=this.hashIdToIndex.get(e.hashId);return t!==void 0?this.maps[t]:void 0}set(e,t){return e.hashId===void 0&&(e.hashId=T(e.name)),this.hashIdToName.set(e.hashId,e.name),this.setByHashId(e.hashId,t)}setByHashId(e,t){let n=this.hashIdToIndex.get(e);return n!==void 0?this.maps[n]=t:(this.hashIdToIndex.set(e,this.maps.length),this.indexToHashId.push(e),this.maps.push(t)),this}delete(e){let t=e.hashId,n=this.hashIdToIndex.get(t);if(n===void 0)return!1;let s=this.maps.length-1,r=this.indexToHashId[s];return this.maps[n]=this.maps[s],this.indexToHashId[n]=r,this.hashIdToIndex.set(r,n),this.maps.pop(),this.indexToHashId.pop(),this.hashIdToIndex.delete(t),!0}forEach(e){for(let t=0;t<this.maps.length;t++)e(this.maps[t],this.hashIdToName.get(this.indexToHashId[t]))}values(){return this.maps.values()}entries(){let e=0,t=this.maps,n=this.indexToHashId;return{next(){if(e<t.length){let s=[n[e],t[e]];return e++,{value:s,done:!1}}return{value:void 0,done:!0}},reset(){e=0},[Symbol.iterator](){return this}}}toJSON(){return{[y]:1,iterable:[...this.entries()]}}};var u=class{indices=[];entities=[];instances=[];constructor(e){if(e)for(let t=0;t<e.length;t++){let n=e[t];this.set(n[0],n[1])}}get size(){return this.instances.length}clear(){this.indices=[],this.entities.length=0,this.instances.length=0}set(e,t){if(this.has(e)){this.instances[this.indices[e]]=t;return}this.indices[e]=this.instances.length,this.entities.push(e),this.instances.push(t)}get(e){let t=this.indices[e];return t!==void 0&&this.entities[t]===e?this.instances[t]:void 0}delete(e){let t=this.indices[e];if(t===void 0||t===-1||this.entities[t]!==e)return!1;let n=this.instances.length-1,s=this.entities[n];return this.instances[t]=this.instances[n],this.entities[t]=s,this.indices[s]=t,this.instances.pop(),this.entities.pop(),this.indices[e]=-1,!0}has(e){let t=this.indices[e];return t!==void 0&&t!==-1&&this.entities[t]===e}forEach(e){for(let t=0;t<this.instances.length;t++)e(this.instances[t],this.entities[t])}firstEntry(){return this.entities.length===0?void 0:[this.entities[0],this.instances[0]]}firstKey(){return this.entities.length===0?void 0:this.entities[0]}firstValue(){return this.instances.length===0?void 0:this.instances[0]}[Symbol.iterator](){return this.entries()}keys(){return this.entities.values()}values(){return this.instances.values()}entries(){let e=0,t=this.entities,n=this.instances;return{next(){if(e<n.length){let s={value:[t[e],n[e]],done:!1};return e++,s}return{value:void 0,done:!0}},reset(){e=0},[Symbol.iterator](){return this}}}toJSON(){return{[l]:1,iterable:[...this.entries()]}}toTable(){let e=[];for(let[t,n]of this.entries()){let s=n,r={};r["entity.key"]=t,r["entity.type"]=s.constructor.name,e.push({...r,...s})}return e}printTable(e=[]){return console.table(this.toTable(),e),this}};var f=class extends Error{constructor(){super("Component type is missing the 'name' parameter. i.e. a constructor name")}},a=class extends Error{constructor(t){super(`Component map does not exist for '${t}'`);this.componentName=t}};var p=class{keyMap;componentMaps=[];entries;constructor(e){this.keyMap=e.keyMap,this.componentMaps=e.componentMaps,this.entries=this.keyMap.entries()}next(){let e=this.entries.next(),{value:t,done:n}=e;if(n)return{value:t,done:n};let s=t[0];for(let r=0;r<this.componentMaps.length;r++){let i=this.componentMaps[r];t.push(i.get(s))}return{value:t,done:!1}}reset(){this.entries=this.keyMap.entries()}[Symbol.iterator](){return this}};var h=class{constructor(e,...t){this.ecs=e;this.keyMap=this.ecs.getMap(t[0]);for(let n=1;n<t.length;n++)this.componentMaps.push(this.ecs.getMap(t[n]))}keyMap;componentMaps=[];firstEntry(){if(this.componentMaps.length===0)return this.keyMap.firstEntry();let[e,t]=this.keyMap.firstEntry()??[];if(e===void 0)return;let n=[e,t];for(let s=0;s<this.componentMaps.length;s++)n.push(this.componentMaps[s].get(e));return n}firstKey(){let e=this.keyMap.firstKey();if(this.componentMaps.length===0)return e;if(e===void 0)return;let t=[e];for(let n=0;n<this.componentMaps.length;n++)t.push(this.componentMaps[n].get(e));return t}firstValue(){if(this.componentMaps.length===0)return this.keyMap.firstValue();let[e,t]=this.keyMap.firstEntry()??[];if(e===void 0)return;let n=[t];for(let s=0;s<this.componentMaps.length;s++)n.push(this.componentMaps[s].get(e));return n}get entityCount(){return this.keyMap.size}destroyEntities(){return this.ecs.destroyEntity(...this.keyMap.keys())}keys(){return this.keyMap.keys()}values(){let e=this.keyMap,t=this.componentMaps,n=e.keys();return{next(){let s=n.next();if(s.done)return{value:void 0,done:!0};let r=s.value,C=[e.get(r)];for(let c=0;c<t.length;c++){let I=t[c];C.push(I.get(r))}return{value:C,done:!1}},reset(){n=e.keys()},[Symbol.iterator](){return this}}}entries(){return this[Symbol.iterator]()}[Symbol.iterator](){return new p(this)}};var d=class o{components=new m;nextId=0;freeIds=[];register(...e){for(let t of e){if(t.name===void 0)throw new f;this.components.set(t,new u)}return this}getMap(e){let t=this.components.get(e);if(t===void 0)throw new a(e.name);return t}first(e){return this.firstEntity(e)}firstEntity(e){let t=this.getMap(e)?.firstKey();if(t!==void 0)return this.get(t)}entityValues(e){let t=this.getMap(e)?.keys();if(t!==void 0)return[...t].map(n=>this.get(n))}firstEntry(e,...t){return t.length===0?this.getMap(e)?.firstEntry():this.firstKey(e,e,...t)}firstKey(e,...t){let n=this.getMap(e)?.firstKey();if(arguments.length===1)return n;if(n!==void 0)return[n,...t.map(s=>this.getEntity(n,s))]}firstValue(e,...t){if(arguments.length===1)return this.getMap(e)?.firstValue();let[n,s]=this.getMap(e)?.firstEntry()??[];if(n!==void 0)return[s,...t.map(r=>this.getEntity(n,r))]}getEntity(e,t){let n=this.components.get(t);if(n===void 0)throw new a(t.name);return n.get(e)}get(e,...t){return t.length===1?this.getEntity(e,t[0]):t.length>1?t.map(n=>this.getEntity(e,n)):[...this.components.values()].filter(n=>n.has(e)).map(n=>n.get(e))}has(e,t){let n=this.components.get(t);return n===void 0?!1:n.has(e)}hasAll(e,...t){for(let n=0;n<t.length;n++){let s=t[n],r=this.components.get(s);if(r===void 0||r.has(e)===!1)return!1}return!0}hasAny(e,...t){for(let n=0;n<t.length;n++){let s=t[n],r=this.components.get(s);if(r!==void 0&&r.has(e))return!0}return!1}setEntity(e,t){let n=this.components.get(t.constructor);if(n===void 0)throw new a(t.constructor.name);return n.set(e,t),t}set(e,...t){return t.length>1?t.map(n=>this.setEntity(e,n)):this.setEntity(e,t[0])}remove(e,...t){for(let n of t)this.removeByKey(e,n)}removeByKey(e,t){let n=this.components.get(t);if(n===void 0)throw new a(t.name);return n.get(e)===void 0?!1:n.delete(e)}destroyEntity(...e){let t=0;for(let n=0;n<e.length;n++){let s=e[n],r=!1;for(let i of this.components.values())i.has(s)&&(i.delete(s),t++,r=!0);r&&this.freeIds.push(s)}return t}getNextId(){let e=this.freeIds.pop();return e!==void 0?e:(this.nextId++,this.nextId)}clear(){return this.components.clear(),this.nextId=0,this.freeIds.length=0,this}clearComponents(){return this.components.forEach(e=>e.clear()),this.nextId=0,this.freeIds.length=0,this}iterator(e,...t){return new p(new h(this,e,...t))}query(e,...t){return new h(this,e,...t)}printTable(e=[],t=[]){return this.components.forEach((n,s)=>{(e.length===0||e.includes(s))&&console.table(n.toTable(),t)}),this}printEntity(e,t=[]){for(let n of this.components.values()){if(n.has(e)===!1)continue;let s=n.get(e),r={"entity.type":s.constructor.name,...s};console.table({[e]:r},t)}return this}static parse(e){return JSON.parse(e,function(n,s){return s.hasOwnProperty("components")?(Reflect.setPrototypeOf(s,o.prototype),s):s.hasOwnProperty("nextId")||s.hasOwnProperty("freeIds")?s:s.hasOwnProperty(l)?new u(s.iterable):s.hasOwnProperty(y)?new m(s.iterable):this[n]})}static createWithTracing(e=[]){let t={get(n,s){let r=n[s];return typeof r=="function"&&(e.length===0||e.includes(s))?function(...i){return console.groupCollapsed("ecs trace",s,i),console.trace(),console.groupEnd(),r.apply(this,i)}:r}};return new Proxy(new o,t)}};var g=new d;typeof window<"u"?window.ecs=g:typeof module<"u"&&module!==null&&(module.exports={ecs:g});export{m as ComponentClassesMap,p as ComponentIterator,u as ComponentMap,h as ComponentQuery,d as EntityMap,g as ecs};
1
+ var l="ComponentMap",y="ComponentClassesMap";function f(o){let e=0;for(let t=0;t<o.length;t++){let n=o.charCodeAt(t);e=(e<<5)-e+n,e|=0}return e}var h=class{hashIdToName=new Map;hashIdToIndex=new Map;indexToHashId=[];maps=[];constructor(e){if(e)for(let t=0;t<e.length;t++){let n=e[t];this.setByHashId(n[0],n[1])}}get size(){return this.maps.length}clear(){this.maps.length=0,this.indexToHashId.length=0,this.hashIdToIndex.clear()}get(e){let t=this.hashIdToIndex.get(e.hashId);return t!==void 0?this.maps[t]:void 0}has(e){return e.hashId===void 0?!1:this.hashIdToIndex.has(e.hashId)}set(e,t){return e.hashId===void 0&&(e.hashId=f(e.name)),this.hashIdToName.set(e.hashId,e.name),this.setByHashId(e.hashId,t)}setByHashId(e,t){let n=this.hashIdToIndex.get(e);return n!==void 0?this.maps[n]=t:(this.hashIdToIndex.set(e,this.maps.length),this.indexToHashId.push(e),this.maps.push(t)),this}delete(e){let t=e.hashId,n=this.hashIdToIndex.get(t);if(n===void 0)return!1;let s=this.maps.length-1,r=this.indexToHashId[s];return this.maps[n]=this.maps[s],this.indexToHashId[n]=r,this.hashIdToIndex.set(r,n),this.maps.pop(),this.indexToHashId.pop(),this.hashIdToIndex.delete(t),!0}forEach(e){for(let t=0;t<this.maps.length;t++)e(this.maps[t],this.hashIdToName.get(this.indexToHashId[t]))}values(){return this.maps.values()}entries(){let e=0,t=this.maps,n=this.indexToHashId;return{next(){if(e<t.length){let s=[n[e],t[e]];return e++,{value:s,done:!1}}return{value:void 0,done:!0}},reset(){e=0},[Symbol.iterator](){return this}}}toJSON(){return{[y]:1,iterable:[...this.entries()]}}};var m=class{indices=[];entities=[];instances=[];constructor(e){if(e)for(let t=0;t<e.length;t++){let n=e[t];this.set(n[0],n[1])}}get size(){return this.instances.length}clear(){this.indices=[],this.entities.length=0,this.instances.length=0}set(e,t){if(this.has(e)){this.instances[this.indices[e]]=t;return}this.indices[e]=this.instances.length,this.entities.push(e),this.instances.push(t)}get(e){let t=this.indices[e];return t!==void 0&&this.entities[t]===e?this.instances[t]:void 0}delete(e){let t=this.indices[e];if(t===void 0||t===-1||this.entities[t]!==e)return!1;let n=this.instances.length-1,s=this.entities[n];return this.instances[t]=this.instances[n],this.entities[t]=s,this.indices[s]=t,this.instances.pop(),this.entities.pop(),this.indices[e]=-1,!0}has(e){let t=this.indices[e];return t!==void 0&&t!==-1&&this.entities[t]===e}forEach(e){for(let t=0;t<this.instances.length;t++)e(this.instances[t],this.entities[t])}firstEntry(){return this.entities.length===0?void 0:[this.entities[0],this.instances[0]]}firstKey(){return this.entities.length===0?void 0:this.entities[0]}firstValue(){return this.instances.length===0?void 0:this.instances[0]}[Symbol.iterator](){return this.entries()}keys(){return this.entities.values()}values(){return this.instances.values()}entries(){let e=0,t=this.entities,n=this.instances;return{next(){if(e<n.length){let s={value:[t[e],n[e]],done:!1};return e++,s}return{value:void 0,done:!0}},reset(){e=0},[Symbol.iterator](){return this}}}toJSON(){return{[l]:1,iterable:[...this.entries()]}}toTable(){let e=[];for(let[t,n]of this.entries()){let s=n,r={};r["entity.key"]=t,r["entity.type"]=s.constructor.name,e.push({...r,...s})}return e}printTable(e=[]){return console.table(this.toTable(),e),this}};var c=class extends Error{constructor(){super("Component type is missing the 'name' parameter. i.e. a constructor name")}},i=class extends Error{constructor(t){super(`Component map does not exist for '${t}'`);this.componentName=t}},C=class extends Error{constructor(t){super(`Component '${t}' is already registered`);this.componentName=t}};var p=class{keyMap;componentMaps=[];entries;constructor(e){this.keyMap=e.keyMap,this.componentMaps=e.componentMaps,this.entries=this.keyMap.entries()}next(){let e=this.entries.next(),{value:t,done:n}=e;if(n)return{value:t,done:n};let s=t[0];for(let r=0;r<this.componentMaps.length;r++){let a=this.componentMaps[r];t.push(a.get(s))}return{value:t,done:!1}}reset(){this.entries=this.keyMap.entries()}[Symbol.iterator](){return this}};var u=class{constructor(e,...t){this.ecs=e;this.keyMap=this.ecs.getMap(t[0]);for(let n=1;n<t.length;n++)this.componentMaps.push(this.ecs.getMap(t[n]))}keyMap;componentMaps=[];firstEntry(){if(this.componentMaps.length===0)return this.keyMap.firstEntry();let[e,t]=this.keyMap.firstEntry()??[];if(e===void 0)return;let n=[e,t];for(let s=0;s<this.componentMaps.length;s++)n.push(this.componentMaps[s].get(e));return n}firstKey(){let e=this.keyMap.firstKey();if(this.componentMaps.length===0)return e;if(e===void 0)return;let t=[e];for(let n=0;n<this.componentMaps.length;n++)t.push(this.componentMaps[n].get(e));return t}firstValue(){if(this.componentMaps.length===0)return this.keyMap.firstValue();let[e,t]=this.keyMap.firstEntry()??[];if(e===void 0)return;let n=[t];for(let s=0;s<this.componentMaps.length;s++)n.push(this.componentMaps[s].get(e));return n}get entityCount(){return this.keyMap.size}destroyEntities(){return this.ecs.destroyEntity(...this.keyMap.keys())}keys(){return this.keyMap.keys()}values(){let e=this.keyMap,t=this.componentMaps,n=e.keys();return{next(){let s=n.next();if(s.done)return{value:void 0,done:!0};let r=s.value,g=[e.get(r)];for(let T=0;T<t.length;T++){let x=t[T];g.push(x.get(r))}return{value:g,done:!1}},reset(){n=e.keys()},[Symbol.iterator](){return this}}}entries(){return this[Symbol.iterator]()}[Symbol.iterator](){return new p(this)}};var d=class o{components=new h;nextId=0;freeIds=[];register(...e){for(let t of e){let n=t.name;if(n===void 0||n==="")throw new c;if(t.hashId===void 0&&(t.hashId=f(n)),this.components.has(t))throw new C(n);this.components.set(t,new m)}return this}getMap(e){let t=this.components.get(e);if(t===void 0)throw new i(e.name);return t}first(e){return this.firstEntity(e)}firstEntity(e){let t=this.getMap(e)?.firstKey();if(t!==void 0)return this.get(t)}entityValues(e){let t=this.getMap(e)?.keys();if(t!==void 0)return[...t].map(n=>this.get(n))}firstEntry(e,...t){return t.length===0?this.getMap(e)?.firstEntry():this.firstKey(e,e,...t)}firstKey(e,...t){let n=this.getMap(e)?.firstKey();if(arguments.length===1)return n;if(n!==void 0)return[n,...t.map(s=>this.getEntity(n,s))]}firstValue(e,...t){if(arguments.length===1)return this.getMap(e)?.firstValue();let[n,s]=this.getMap(e)?.firstEntry()??[];if(n!==void 0)return[s,...t.map(r=>this.getEntity(n,r))]}getEntity(e,t){let n=this.components.get(t);if(n===void 0)throw new i(t.name);return n.get(e)}get(e,...t){return t.length===1?this.getEntity(e,t[0]):t.length>1?t.map(n=>this.getEntity(e,n)):[...this.components.values()].filter(n=>n.has(e)).map(n=>n.get(e))}has(e,t){let n=this.components.get(t);if(n===void 0)throw new i(t.name);return n.has(e)}hasAll(e,...t){for(let n=0;n<t.length;n++){let s=t[n],r=this.components.get(s);if(r===void 0)throw new i(s.name);if(r.has(e)===!1)return!1}return!0}hasAny(e,...t){for(let n=0;n<t.length;n++){let s=t[n],r=this.components.get(s);if(r===void 0)throw new i(s.name);if(r.has(e))return!0}return!1}setEntity(e,t){let n=this.components.get(t.constructor);if(n===void 0)throw new i(t.constructor.name);return n.set(e,t),t}set(e,...t){return t.length>1?t.map(n=>this.setEntity(e,n)):this.setEntity(e,t[0])}remove(e,...t){for(let n of t)this.removeByKey(e,n)}removeByKey(e,t){let n=this.components.get(t);if(n===void 0)throw new i(t.name);return n.get(e)===void 0?!1:n.delete(e)}destroyEntity(...e){let t=0;for(let n=0;n<e.length;n++){let s=e[n],r=!1;for(let a of this.components.values())a.has(s)&&(a.delete(s),t++,r=!0);r&&this.freeIds.push(s)}return t}getNextId(){let e=this.freeIds.pop();return e!==void 0?e:(this.nextId++,this.nextId)}clear(){return this.components.clear(),this.nextId=0,this.freeIds.length=0,this}clearComponents(){return this.components.forEach(e=>e.clear()),this.nextId=0,this.freeIds.length=0,this}iterator(e,...t){return new p(new u(this,e,...t))}query(e,...t){return new u(this,e,...t)}printTable(e=[],t=[]){return this.components.forEach((n,s)=>{(e.length===0||e.includes(s))&&console.table(n.toTable(),t)}),this}printEntity(e,t=[]){for(let n of this.components.values()){if(n.has(e)===!1)continue;let s=n.get(e),r={"entity.type":s.constructor.name,...s};console.table({[e]:r},t)}return this}static parse(e){return JSON.parse(e,function(n,s){return s.hasOwnProperty("components")?(Reflect.setPrototypeOf(s,o.prototype),s):s.hasOwnProperty("nextId")||s.hasOwnProperty("freeIds")?s:s.hasOwnProperty(l)?new m(s.iterable):s.hasOwnProperty(y)?new h(s.iterable):this[n]})}static createWithTracing(e=[]){let t={get(n,s){let r=n[s];return typeof r=="function"&&(e.length===0||e.includes(s))?function(...a){return console.groupCollapsed("ecs trace",s,a),console.trace(),console.groupEnd(),r.apply(this,a)}:r}};return new Proxy(new o,t)}};var I=new d;typeof window<"u"?window.ecs=I:typeof module<"u"&&module!==null&&(module.exports={ecs:I});export{h as ComponentClassesMap,p as ComponentIterator,m as ComponentMap,u as ComponentQuery,d as EntityMap,I as ecs};
2
2
  //# sourceMappingURL=ecs.js.map
package/dist/ecs.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/types.ts", "../src/utils.ts", "../src/component-classes.ts", "../src/component-map.ts", "../src/errors.ts", "../src/iterator.ts", "../src/query.ts", "../src/entity-map.ts", "../src/ecs.ts"],
4
- "sourcesContent": ["/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/**\n * Property key used for ComponentMap identification during serialization\n * @category Constants\n */\nexport const ComponentMapKey = \"ComponentMap\"\n\n/**\n * Property key used for ComponentClassesMap identification during serialization\n * @category Constants\n */\nexport const ComponentClassesMapKey = \"ComponentClassesMap\"\n\n/**\n * Variadic type helper that preserves the strongly typed parameter interfaces used by the compiler and intellisense.\n * Works with single returned types or spread array returned types.\n * @category Types\n */\nexport type SingleOrArray<T extends any[]> =\n // return a single variadic item when only 1 item is specified\n T['length'] extends 1 ? T[0]\n // otherwise return an array of variadic types\n : { [Index in keyof T]: T[Index] }\n\n/**\n * Used to return component instance data.\n * \n * Note: This is not used for registering components or extending component classes\n * @category Types\n */\nexport interface Component { }\n\n/**\n * @category Types\n * @example\n * \n * class PositionComponent {\n * constructor(x, y) {\n * this.x = x;\n * this.y = y;\n * }\n * }\n */\nexport type ComponentClass<ComponentInstance> = (new (...args: any[]) => ComponentInstance) & {\n // static property\n hashId?: number;\n};\n\n/**\n* Used for inferring generic type spread for classes\n* @category Types\n*/\nexport type ComponentClasses<T extends Component[]> =\n { [Index in keyof T]: ComponentClass<T[Index]> }\n\n/**\n * Custom iterator interface that includes a reset function\n * @category Types\n */\nexport type Iterator<T> = {\n /**\n * Gets the next result in the iteration\n */\n next: () => IteratorResult<T>\n /**\n * Resets the iterator back to the beginning\n */\n reset: () => void\n /**\n * Returns the iterator instance\n */\n [Symbol.iterator]: () => Iterator<T>\n}\n\n/**\n * Helper type for late-bound iterator assignment with IntelliSense support\n * @category Types\n * @example\n * \n * let someIterator: IComponentIterator<[Player, Position]>\n * \n * // assignment made somewhere else in the code\n * const [player, position] = someIterator(Player, Position) ?? []\n * position?.x = 123 // position will have IntelliSense\n */\nexport type IComponentIterator<T extends Component[]>\n = Iterator<ComponentClasses<[number, ...T]>>", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/**\n * Generates a 32-bit integer hash from a string\n * @param str - The string to hash\n * @returns A 32-bit integer hash\n */\nexport function hashString(str: string) {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash |= 0; // Convert to 32bit\n }\n return hash;\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport type { ComponentMap } from './component-map.js';\nimport {\n ComponentClassesMapKey,\n type ComponentClass,\n type Iterator,\n} from './types.js';\nimport { hashString } from './utils.js';\n\n/**\n * Component class map for storing registered component maps\n * @category Maps\n */\nexport class ComponentClassesMap {\n\n private hashIdToName: Map<number, string> = new Map<number, string>()\n private hashIdToIndex = new Map<number, number>();\n private indexToHashId: number[] = [];\n private maps: ComponentMap<any>[] = [];\n\n /**\n * Creates a new component classes map\n * @param entries - Optional initial data for the map\n */\n constructor(entries?: readonly (readonly [number, ComponentMap<any>])[] | null) {\n if (entries) {\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n this.setByHashId(entry[0], entry[1]);\n }\n }\n }\n\n /**\n * Returns the number of registered component maps\n */\n get size() {\n return this.maps.length;\n }\n\n /**\n * Clears all maps and internal indices while preserving array references\n */\n clear(): void {\n this.maps.length = 0;\n this.indexToHashId.length = 0;\n this.hashIdToIndex.clear();\n }\n\n /**\n * Retrieves a component map using the component class\n * @param componentClass - The component class to get the map for\n * @returns The {@link ComponentMap} for the specified class, or undefined if not found\n */\n get<T>(componentClass: ComponentClass<T>): ComponentMap<T> | undefined {\n const index = this.hashIdToIndex.get(componentClass.hashId!);\n return index !== undefined ? this.maps[index] : undefined;\n }\n\n /**\n * Registers a component map. Automatically generates a hashId from the class name if missing\n * @param componentClass - The component class to register\n * @param map - The {@link ComponentMap} instance to store\n * @returns The {@link ComponentClassesMap} instance for chaining\n */\n set<T>(componentClass: ComponentClass<T>, map: ComponentMap<T>): this {\n if (componentClass.hashId === undefined) {\n componentClass.hashId = hashString(componentClass.name);\n }\n\n this.hashIdToName.set(componentClass.hashId, componentClass.name);\n return this.setByHashId(componentClass.hashId!, map);\n }\n\n /**\n * Maps a specific numeric hash id to a component map instance.\n * @param hash - The numeric hash id\n * @param map - The {@link ComponentMap} instance to store\n * @returns The {@link ComponentClassesMap} instance for chaining\n */\n setByHashId(hash: number, map: ComponentMap<any>): this {\n const existingIndex = this.hashIdToIndex.get(hash);\n\n if (existingIndex !== undefined) {\n this.maps[existingIndex] = map;\n } else {\n this.hashIdToIndex.set(hash, this.maps.length);\n this.indexToHashId.push(hash);\n this.maps.push(map);\n }\n\n return this;\n }\n\n /**\n * Removes a component map and re-orders internal storage to maintain density\n * @param componentClass - The component class to remove the map for\n * @returns True if the map was removed, otherwise false\n */\n delete(componentClass: ComponentClass<any>): boolean {\n const hashId = componentClass.hashId!;\n const index = this.hashIdToIndex.get(hashId);\n if (index === undefined) return false;\n\n const lastIndex = this.maps.length - 1;\n const lastHash = this.indexToHashId[lastIndex];\n\n // Swap\n this.maps[index] = this.maps[lastIndex];\n this.indexToHashId[index] = lastHash;\n\n // Update map pointer\n this.hashIdToIndex.set(lastHash, index);\n\n // Pop\n this.maps.pop();\n this.indexToHashId.pop();\n this.hashIdToIndex.delete(hashId);\n return true;\n }\n\n /**\n * Executes a callback for every map, providing the instance and its registered name\n * @param callback - The callback to execute\n */\n forEach(callback: (value: ComponentMap<any>, name: string) => void) {\n for (let i = 0; i < this.maps.length; i++) {\n callback(this.maps[i], this.hashIdToName.get(this.indexToHashId[i])!);\n }\n }\n\n /**\n * Returns an iterator of all component map instances \n * @returns An {@link ArrayIterator} of {@link ComponentMap} instances\n */\n values(): ArrayIterator<ComponentMap<any>> { return this.maps.values() }\n\n /**\n * Returns an iterator of [hashId, componentMap] pairs\n * @returns An {@link Iterator} instance\n */\n entries(): Iterator<[number, ComponentMap<any>]> {\n let index = 0;\n const instances = this.maps;\n const hashes = this.indexToHashId;\n\n return {\n next(): IteratorResult<[number, ComponentMap<any>]> {\n if (index < instances.length) {\n const value: [number, ComponentMap<any>] = [hashes[index], instances[index]];\n index++;\n return { value, done: false };\n }\n return { value: undefined as any, done: true };\n },\n reset() { index = 0; },\n [Symbol.iterator]() { return this; }\n };\n }\n\n /**\n * Called when using JSON.stringify\n * @returns An object representation for serialization\n */\n toJSON() {\n return { [ComponentClassesMapKey]: 1, iterable: [...this.entries()] };\n }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport {\n ComponentMapKey,\n type Iterator\n} from './types.js';\n\n/**\n * Component map for storing entity ids and related component data\n * @category Maps\n */\nexport class ComponentMap<TComponentInstance> implements Iterable<[number, TComponentInstance]> {\n\n private indices: number[] = [];\n\n private entities: number[] = [];\n\n private instances: TComponentInstance[] = [];\n\n /**\n * Creates a new component map\n * @param entries - Optional initial data for the map\n */\n constructor(entries?: readonly (readonly [number, TComponentInstance])[] | null) {\n if (entries) {\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n this.set(entry[0], entry[1]);\n }\n }\n }\n\n /**\n * Returns the number of entities in the map\n */\n get size() {\n return this.instances.length;\n }\n\n /**\n * Clears all component data from the map\n */\n clear() {\n this.indices = [];\n this.entities.length = 0;\n this.instances.length = 0;\n }\n\n /**\n * Adds or updates component data for an entity\n * @param entityId - The entity id to set\n * @param component - The component data to set\n */\n set(entityId: number, component: TComponentInstance) {\n if (this.has(entityId)) {\n this.instances[this.indices[entityId]] = component;\n return;\n }\n this.indices[entityId] = this.instances.length;\n this.entities.push(entityId);\n this.instances.push(component);\n }\n\n /**\n * Gets the component data for an entity\n * @param entityId - The entity id to get\n * @returns The component data, or undefined if not found\n */\n get(entityId: number) {\n const index = this.indices[entityId];\n return index !== undefined && this.entities[index] === entityId\n ? this.instances[index]\n : undefined;\n }\n\n /**\n * Removes component data for an entity\n * @param entityId - The entity id to remove\n * @returns True if the component was removed, otherwise false\n */\n delete(entityId: number) {\n const index = this.indices[entityId];\n if (index === undefined || index === -1 || this.entities[index] !== entityId) return false;\n\n // swap and pop with last element to keep dense\n const lastIdx = this.instances.length - 1;\n const lastEntity = this.entities[lastIdx];\n this.instances[index] = this.instances[lastIdx];\n this.entities[index] = lastEntity;\n this.indices[lastEntity] = index;\n this.instances.pop();\n this.entities.pop();\n\n // invalidate index\n this.indices[entityId] = -1;\n\n return true;\n }\n\n /**\n * Checks if an entity has a component in this map\n * @param entityId - The entity id to check\n * @returns True if the entity has a component, otherwise false\n */\n has(entityId: number) {\n const index = this.indices[entityId];\n return index !== undefined && index !== -1 && this.entities[index] === entityId;\n }\n\n /**\n * Executes a callback for every map, providing the component data and its entity id\n * @param callback - The callback to execute\n */\n forEach(callback: (value: TComponentInstance, key: number) => void) {\n for (let i = 0; i < this.instances.length; i++) {\n callback(this.instances[i], this.entities[i]);\n }\n }\n\n /**\n * Returns the first entry\n * @returns A tuple of [entityId, componentData], or undefined if empty\n */\n firstEntry(): [entityId: number, value: TComponentInstance] | undefined {\n return this.entities.length === 0\n ? undefined\n : [this.entities[0], this.instances[0]];\n }\n\n /**\n * Returns the first entity id\n * @returns The first entity id, or undefined if empty\n */\n firstKey(): number | undefined {\n return this.entities.length === 0\n ? undefined\n : this.entities[0];\n }\n\n /**\n * Returns the first entity data\n * @returns The first component data, or undefined if empty\n */\n firstValue(): TComponentInstance | undefined {\n return this.instances.length === 0\n ? undefined\n : this.instances[0];\n }\n\n /**\n * Returns an iterator of [entityId, componentData] pairs\n * @returns An {@link Iterator} instance\n */\n [Symbol.iterator](): Iterator<[number, TComponentInstance]> {\n return this.entries();\n }\n\n /**\n * Returns an iterator of all entity ids\n * @returns An {@link ArrayIterator} of entity ids\n */\n keys(): ArrayIterator<number> {\n return this.entities.values();\n }\n\n /**\n * Returns an iterator of all component data\n * @returns An {@link ArrayIterator} of component data\n */\n values(): ArrayIterator<TComponentInstance> {\n return this.instances.values();\n }\n\n /**\n * Returns an iterator of [entityId, componentData] pairs\n * @returns An {@link Iterator} instance\n */\n entries(): Iterator<[number, TComponentInstance]> {\n let index = 0;\n const entities = this.entities;\n const instances = this.instances;\n\n return {\n next(): IteratorResult<[number, TComponentInstance]> {\n if (index < instances.length) {\n const result: IteratorResult<[number, TComponentInstance]> = {\n value: [entities[index], instances[index]],\n done: false\n };\n index++;\n return result;\n }\n return { value: <any>undefined, done: true };\n },\n reset() {\n index = 0;\n },\n [Symbol.iterator]() {\n return this;\n },\n };\n }\n\n /**\n * Called when using JSON.stringify\n * @returns An object representation for serialization\n */\n toJSON() {\n return { [ComponentMapKey]: 1, iterable: [...this.entries()] };\n }\n\n /**\n * Returns an array of objects for tabular display\n * @returns An array of objects formatted for {@link console.table}\n */\n toTable(): any[] {\n const table = []\n for (const [key, value] of this.entries()) {\n const entity = value as { new(): TComponentInstance };\n const meta: Record<string, any> = {};\n meta[\"entity.key\"] = key;\n meta[\"entity.type\"] = entity.constructor.name;\n table.push({ ...meta, ...entity });\n }\n return table;\n }\n\n /**\n * Prints entity data in a tabular format to the console\n * @param properties - Optional filter. Specifies which property columns to display in the tables.\n * @returns The {@link ComponentMap} instance for chaining\n */\n printTable(properties: string[] = []): this {\n console.table(this.toTable(), properties);\n return this;\n }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/**\n * Thrown when trying to register a component that is missing a 'name' parameter\n * @category Errors\n */\nexport class ComponentTypeKeyMissing extends Error {\n constructor() {\n super(`Component type is missing the 'name' parameter. i.e. a constructor name`);\n }\n}\n\n/**\n * Thrown when trying to access a component that has not been registered\n * @param componentName - The name of the unregistered component\n * @category Errors\n */\nexport class ComponentNotRegistered extends Error {\n constructor(public componentName: string) {\n super(`Component map does not exist for '${componentName}'`);\n }\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport type { ComponentMap } from './component-map.js';\nimport type { ComponentQuery } from './query.js';\n\n/**\n * An iterator for yielding entity ids and their associated component data\n * @category Iterators\n * @example\n * // construct an instance\n * const iterator = new ComponentIterator(new ComponentQuery(entityMap, Player, Position))\n * \n * // iterate component data that is related to the Player\n * for(const [entityId, player, position] of iterator) { }\n * \n * // you can also reset the iterator back to the start\n * // without having to create a new ComponentIterator instance\n * iterator.reset()\n * for(const [entityId, player, position] of iterator) { }\n */\nexport class ComponentIterator<TKey, TRelated extends any[]> {\n private keyMap: ComponentMap<TKey>;\n private componentMaps: ComponentMap<any>[] = [];\n // iteration state\n private entries: Iterator<[number, TKey]>;\n\n /**\n * Creates a new component iterator\n * @param query - The {@link ComponentQuery} to iterate over\n */\n constructor(query: ComponentQuery<TKey, TRelated>) {\n // @ts-ignore internal private var accessor\n this.keyMap = query.keyMap\n // @ts-ignore internal private var accessor\n this.componentMaps = query.componentMaps\n this.entries = this.keyMap.entries();\n }\n\n /**\n * Gets the next iterator value\n * @returns An {@link IteratorResult} containing the next [entityId, keyComponentData, ...relatedComponentData] tuple\n */\n next(): IteratorResult<[number, TKey, ...TRelated]> {\n const entry = this.entries.next();\n const { value, done } = entry;\n if (done) return { value, done };\n\n const entityId = value[0];\n\n // append the related components\n for (let i = 0; i < this.componentMaps.length; i++) {\n const map = this.componentMaps[i];\n value.push(map.get(entityId));\n }\n\n return { value: value as any, done: false };\n }\n\n /**\n * Resets the iterator back to the first entry\n */\n reset() {\n this.entries = this.keyMap.entries();\n }\n\n /**\n * Returns this iterator instance\n */\n [Symbol.iterator]() { return this; }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport type { ComponentMap } from './component-map.js';\nimport type { EntityMap } from './entity-map.js';\nimport { ComponentIterator } from './iterator.js';\nimport type { ComponentClass, ComponentClasses, Iterator, SingleOrArray } from './types.js';\n\n/**\n * A reusable query for retrieving entities and their components\n * @category Queries\n * @example\n * const query = ecs.query(Player, Position)\n * \n * // get the first entry\n * const [playerId, player, position] = query.firstEntry() ?? []\n * \n * // get the first key\n * const [playerId, position] = query.firstKey() ?? []\n * \n * // get the first value\n * const [player, position] = query.firstValue() ?? []\n * \n * // iterate\n * for (const [playerId, player, position] of query) {\n * \n * }\n */\nexport class ComponentQuery<TKey, TRelated extends any[]> {\n private keyMap: ComponentMap<TKey>\n private componentMaps: ComponentMap<any>[] = []\n\n /**\n * Creates a new component query\n * @param ecs - The {@link EntityMap} instance to query\n * @param components - The first component class <TKey> is the primary component. Optional additional component classes <TRelated> to retrieve for each entity.\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n */\n constructor(\n public ecs: EntityMap,\n ...components: [ComponentClass<TKey>, ...ComponentClasses<TRelated>]\n ) {\n this.keyMap = this.ecs.getMap(components[0])!;\n for (let i = 1; i < components.length; i++) {\n this.componentMaps.push(this.ecs.getMap(components[i])!)\n }\n }\n\n /**\n * Gets the first entry matching the query.\n * \n * Includes the entity id, the key component data, and any related component data.\n * @returns A tuple of `[entityId, keyComponentData, ...relatedComponentData]`, or `undefined` if no entities match.\n */\n firstEntry(): [number, TKey, ...TRelated] | undefined;\n firstEntry() {\n if (this.componentMaps.length === 0) return this.keyMap.firstEntry()\n\n const [entityId, entryValue] = this.keyMap.firstEntry() ?? []\n if (entityId === undefined) return undefined;\n\n const results: any[] = [entityId, entryValue]\n for (let i = 0; i < this.componentMaps.length; i++) {\n results.push(this.componentMaps[i].get(entityId));\n }\n\n return results\n }\n\n /**\n * Gets the first key matching the query.\n * \n * @returns The entity id if no related components were specified in the constructor.\n * \n * Otherwise, a tuple of `[entityId, ...relatedComponentData]`.\n * \n * Returns `undefined` if no entities match.\n */\n firstKey(): SingleOrArray<[number, ...TRelated]> | undefined;\n firstKey() {\n const entityId = this.keyMap.firstKey()\n if (this.componentMaps.length === 0) return entityId;\n if (entityId === undefined) return undefined;\n\n const results: any[] = [entityId]\n for (let i = 0; i < this.componentMaps.length; i++) {\n results.push(this.componentMaps[i].get(entityId));\n }\n\n return results\n }\n\n /**\n * Get the first component data matching the query.\n * \n * @returns The key component data if no related components were specified in the constructor.\n * \n * Otherwise, a tuple of `[keyComponentData, ...relatedComponentData]`.\n * \n * Returns `undefined` if no entities match.\n */\n firstValue(): SingleOrArray<[TKey, ...TRelated]> | undefined;\n firstValue() {\n if (this.componentMaps.length === 0) return this.keyMap.firstValue()\n\n const [entityId, keyValue] = this.keyMap.firstEntry() ?? []\n if (entityId === undefined) return undefined;\n\n const results = [keyValue]\n for (let i = 0; i < this.componentMaps.length; i++) {\n results.push(this.componentMaps[i].get(entityId));\n }\n\n return results\n }\n\n /**\n * Returns the count of entities matching the query\n * @returns The number of entities that have the key component\n */\n get entityCount() {\n return this.keyMap.size\n }\n\n /**\n * Destroys all entities matching the query\n * @returns The total number of components destroyed across all affected entities\n * @example\n * const destroyedCount = query.destroyEntities()\n */\n destroyEntities(): number {\n return this.ecs.destroyEntity(...this.keyMap.keys())\n }\n\n /**\n * Gets all entity ids matching the query.\n * \n * @returns An {@link ArrayIterator} of entity ids.\n */\n keys(): ArrayIterator<number> { return this.keyMap.keys() }\n\n /**\n * Gets all component data matching the query.\n *\n * @returns An {@link Iterator} of `[keyComponentData, ...relatedComponentData]` tuples containing component data\n * @example\n * const query = ecs.query(Player, Position)\n * for (const [player, position] of query.values()) { }\n */\n values(): Iterator<[TKey, ...TRelated]> {\n const keyMap = this.keyMap;\n const componentMaps = this.componentMaps;\n let keysIterator = keyMap.keys();\n\n return {\n next(): IteratorResult<[TKey, ...TRelated]> {\n const key = keysIterator.next();\n if (key.done) return { value: <any>undefined, done: true };\n\n const entityId = key.value;\n const keyComponent = keyMap.get(entityId);\n\n // append the related components\n const value = [keyComponent];\n for (let i = 0; i < componentMaps.length; i++) {\n const map = componentMaps[i];\n value.push(map.get(entityId));\n }\n\n return { value: value as any, done: false };\n },\n reset() { keysIterator = keyMap.keys(); },\n [Symbol.iterator]() { return this; },\n };\n }\n\n /**\n * Gets all component data entries matching the query\n * \n * @returns A {@link ComponentIterator} instance that yields `[entityId, keyComponentData, ...relatedComponentData]`\n * @example\n * const query = ecs.query(Player, Position)\n * for (const [entityId, player, position] of query.entries()) { }\n */\n entries() { return this[Symbol.iterator](); }\n\n /**\n * @returns A {@link ComponentIterator} instance that yields `[entityId, keyComponentData, ...relatedComponentData]`\n */\n [Symbol.iterator]() {\n return new ComponentIterator(this);\n }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { ComponentClassesMap } from './component-classes.js';\nimport { ComponentMap } from './component-map.js';\nimport { ComponentNotRegistered, ComponentTypeKeyMissing } from './errors.js';\nimport { ComponentIterator } from './iterator.js';\nimport { ComponentQuery } from './query.js';\nimport {\n ComponentClassesMapKey,\n ComponentMapKey,\n type Component,\n type ComponentClass,\n type ComponentClasses,\n type SingleOrArray\n} from './types.js';\n\n/**\n * Class for storing entities and their relationships.\n * @category Maps\n */\nexport class EntityMap {\n /**\n * Registered component classes that contain the component data\n */\n public components = new ComponentClassesMap()\n\n private nextId: number = 0\n\n private freeIds: number[] = []\n\n /**\n * Registers component classes with the {@link EntityMap}\n * @param componentClasses - One or more component classes to register\n * @returns The {@link EntityMap} instance for chaining\n * @throws {@link ComponentTypeKeyMissing} when the specified component type is missing a 'name' parameter\n * @example\n * // component class\n * class MyComponent {\n * constructor(x) {\n * this.x = x;\n * }\n * }\n *\n * ecs.register(MyComponent);\n * // or multiple\n * ecs.register(MyComponent1, MyComponent2);\n */\n register<TComponentClasses extends ComponentClass<any>[]>(...componentClasses: TComponentClasses) {\n for (const componentClass of componentClasses) {\n const componentName = componentClass.name;\n if (componentName === undefined) throw new ComponentTypeKeyMissing();\n\n // create the component map\n // const componentDataMap: ComponentMap<any> = new ComponentMap();\n this.components.set(componentClass, new ComponentMap());\n }\n\n // chain\n return this;\n }\n\n /**\n * Gets a component class map\n * @param component - The component class to get the map for\n * @returns The {@link ComponentMap} for the specified component, or undefined if not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * const positionMap = ecs.getMap(Position)\n * for(const [entityId, position] of positionMap) {\n * position.x += 1\n * }\n */\n getMap<T>(component: ComponentClass<T>): ComponentMap<T> | undefined {\n const map = this.components.get(component);\n if (map === undefined) throw new ComponentNotRegistered(component.name)\n return map\n }\n\n /**\n * @deprecated Use {@link EntityMap.firstEntity} instead\n * \n * Returns an array of component data for the first entity associated with the keyComponent\n * @param keyComponent - The component class used to find the first entity\n * @returns An array of components for the first entity, or undefined if not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * // get the first entity\n * const playerEntity = ecs.first(Player) ?? []\n */\n first(keyComponent: ComponentClass<any>): Component[] | undefined { return this.firstEntity(keyComponent); }\n\n /**\n * Returns an array of component data for the first entity associated with the keyComponent\n * @param keyComponent - The component class used to find the first entity\n * @returns An array of components for the first entity, or undefined if not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * // get the first entity\n * const playerEntity = ecs.firstEntity(Player) ?? []\n */\n firstEntity(keyComponent: ComponentClass<any>): Component[] | undefined {\n const entityId = this.getMap(keyComponent)?.firstKey();\n if (entityId === undefined) return undefined;\n\n return this.get(entityId);\n }\n\n /**\n * Returns an array of component data arrays associated with the keyComponent\n * @param keyComponent - The component class used to find the entities\n * @returns An array of component arrays, or undefined if the key component map is not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * // get an array of component arrays: e.g. [[Player, Position, Velocity], [Player, Position, Velocity], ...]\n * const entities = ecs.entityValues(Player) ?? []\n */\n entityValues(keyComponent: ComponentClass<any>): Array<Component[]> | undefined {\n const entities = this.getMap(keyComponent)?.keys();\n if (entities === undefined) return undefined;\n\n return [...entities].map(x => this.get(x)!)\n }\n\n /**\n * Gets the first entity entry for a component class\n * @param keyComponent - The component class to find the first entry for\n * @param components - Additional component classes to retrieve for the same entity\n * @returns A tuple containing the entity id followed by the component data, or undefined if not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * // return the first entry\n * const [entityId, player] = ecs.firstEntry(Player) ?? []\n * \n * // or return multiple related components in addition to the first entry\n * const [entityId, player, position, direction] = ecs.firstEntry(\n * Player,\n * Position,\n * Direction\n * ) ?? []\n */\n firstEntry<TKey, TRelated extends any[]>(\n keyComponent: ComponentClass<TKey>,\n ...components: ComponentClasses<TRelated>\n ): [number, TKey, ...TRelated] | undefined;\n firstEntry(keyComponent: ComponentClass<any>, ...components: ComponentClass<any>[]) {\n if (components.length === 0) return this.getMap(keyComponent)?.firstEntry()\n\n return this.firstKey(keyComponent, keyComponent, ...components);\n }\n\n /**\n * Gets the first entity id for a component class \n * and optionally any related component data\n * @param keyComponent - The component class used to find the first entity\n * @param components - Additional component classes to retrieve for the same entity\n * @returns If only keyComponent is provided, returns the entity id. \n * \n * If related components are provided, returns a tuple with the id and component data. \n * \n * Returns undefined if not found.\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * // return the first entity id\n * const entityId = ecs.firstKey(Player)\n * \n * // or return multiple related component in addition to entity id\n * const [entityId, position, direction] = ecs.firstKey(\n * Player,\n * Position,\n * Direction\n * ) ?? []\n */\n firstKey<TKey, TRelated extends any[]>(\n keyComponent: TKey,\n ...components: ComponentClasses<TRelated>\n ): SingleOrArray<[number, ...TRelated]> | undefined;\n firstKey(keyComponent: ComponentClass<any>, ...components: ComponentClass<any>[]) {\n const entityId = this.getMap(keyComponent)?.firstKey();\n\n // single component key\n if (arguments.length === 1) return entityId;\n if (entityId === undefined) return undefined;\n\n // attach related component data\n return [entityId, ...components.map(x => this.getEntity(entityId, x))];\n }\n\n /**\n * Gets the first entity component data for a component class\n * and optionally any related component data\n * @param keyComponent - The component class used to find the first entity\n * @param components - Additional component classes to retrieve for the same entity\n * @returns If only keyComponent is provided, returns the component data. \n * \n * If related components are provided, returns a tuple with all component data. \n * \n * Returns undefined if not found.\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * // return the first component data\n * const player = ecs.firstValue(Player)\n * \n * // or multiple related data in addition to the first component\n * const [player, position, direction] = ecs.firstValue(\n * Player,\n * Position,\n * Direction\n * ) ?? []\n */\n firstValue<TKey, TRelated extends any[]>(\n keyComponent: ComponentClass<TKey>,\n ...components: ComponentClasses<TRelated>\n ): SingleOrArray<[TKey, ...TRelated]> | undefined;\n firstValue(keyComponent: ComponentClass<any>, ...components: ComponentClass<any>[]) {\n // single component\n if (arguments.length === 1) return this.getMap(keyComponent)?.firstValue();\n\n // get the first entry\n const [entityId, keyValue] = this.getMap(keyComponent)?.firstEntry() ?? [];\n if (entityId === undefined) return undefined;\n\n // attach related components\n return [keyValue, ...components.map(x => this.getEntity(entityId, x))]\n }\n\n /**\n * Internal helper to get component data for an entity\n * @param entityId - The entity id to get the component for\n * @param component - The component class to get the data of\n * @returns The component data if it exists, otherwise undefined\n * @throws {@link ComponentNotRegistered} when the specified component is not registered \n */\n private getEntity<T>(entityId: number, component: ComponentClass<T>): T | undefined {\n const map = this.components.get(component);\n if (map === undefined) throw new ComponentNotRegistered(component.name)\n\n return map.get(entityId);\n }\n\n /**\n * Gets component data related to an entity id\n * @param entityId - The entity id to get component(s) for\n * @param components - One or more component classes to retrieve. If none provided, retrieves all components for the entity.\n * @returns Depending on parameters: a single component, an array of specified components, or an array of all components for the entity.\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * // get one by id\n * const player = ecs.get(entityId, Player)\n * \n * // get multiple by id\n * const [player, position] = ecs.get(entityId, Player, Position) ?? []\n * \n * // get all by id\n * const playerEntity = ecs.get(entityId) ?? []\n */\n get(entityId: number): Component[] | undefined;\n get<T>(entityId: number, component: ComponentClass<T>): T | undefined;\n get<T extends any[]>(entityId: number, ...components: ComponentClasses<T>): SingleOrArray<T> | undefined;\n get(entityId: number, ...components: ComponentClasses<any>): Component | Component[] | undefined {\n // return a single component\n if (components.length === 1) return this.getEntity(entityId, components[0]);\n // return filtered components\n if (components.length > 1) return components.map(x => this.getEntity(entityId, x));\n // return all components\n return [...this.components.values()]\n .filter(v => v.has(entityId))\n .map(v => v.get(entityId));\n }\n\n /**\n * Check if a component exists for an entity\n * @param entityId - The entity id to check for the component\n * @param component - The component class to check\n * @returns True if the entity has the component, otherwise false\n * @example\n * const exists = ecs.has(entityId, Position)\n */\n has<T>(entityId: number, component: ComponentClass<T>): boolean {\n // get the component map\n const map = this.components.get(component);\n if (map === undefined) return false\n\n return map.has(entityId);\n }\n\n /**\n * Checks if all of the specified components exist for an entity\n * @param entityId - The entity id to check\n * @param components - One or more component classes to check for\n * @returns True if the entity has ALL specified components, otherwise false\n * @example\n * const hasAll = ecs.hasAll(entityId, Position, Velocity)\n */\n hasAll<T extends ComponentClass<any>[]>(entityId: number, ...components: T): boolean {\n for (let index = 0; index < components.length; index++) {\n const component = components[index];\n const map = this.components.get(component);\n if (map === undefined) return false;\n if (map.has(entityId) === false) return false;\n }\n return true\n }\n\n /**\n * Checks if any of the specified components exist for an entity\n * @param entityId - The entity id to check\n * @param components - One or more component classes to check for\n * @returns True if the entity has ANY of the specified components, otherwise false\n * @example\n * const hasAny = ecs.hasAny(entityId, Position, Velocity)\n */\n hasAny<T extends ComponentClass<any>[]>(entityId: number, ...components: T): boolean {\n for (let index = 0; index < components.length; index++) {\n const component = components[index];\n const map = this.components.get(component);\n if (map === undefined) continue;\n if (map.has(entityId)) return true;\n }\n return false\n }\n\n /**\n * Internal helper to set component data for an entity\n * @param entityId - The entity id to set the component for\n * @param componentData - The component data to set\n * @returns The component data that was set\n * @throws {@link ComponentNotRegistered} when the component class of the instance is not registered\n */\n private setEntity<T extends Component>(entityId: number, componentData: T): T {\n // get the component map\n const map = this.components.get((<ComponentClass<any>>componentData.constructor));\n if (map === undefined) throw new ComponentNotRegistered(componentData.constructor.name)\n\n // set the entity on the entity map\n map.set(entityId, componentData);\n\n // return instance\n return componentData;\n }\n\n /**\n * Add or update multiple component data for an entity\n * @param entityId - The entity id to set components for\n * @param component - A single component data instance to set\n * @param components - Multiple component data instances to set\n * @returns The single component data or an array of component data that were set\n * @throws {@link ComponentNotRegistered} when any of the component classes are not registered\n * @example\n * // set one\n * const player = ecs.set(entityId, new Player());\n * \n * // or set multiple\n * const [player, position] = ecs.set(\n * entityId,\n * new Player(),\n * new Position()\n * );\n */\n set<T extends Component>(entityId: number, component: T): T;\n set<T extends Component[]>(entityId: number, ...components: T): T;\n set(entityId: number, ...components: Component[]): Component | Component[] {\n if (components.length > 1) return components.map(x => this.setEntity(entityId, x))\n\n // set and return a single component\n return this.setEntity(entityId, components[0])\n }\n\n /**\n * Removes the specified component(s) from an entity\n * @param entityId - The entity id to remove components from\n * @param components - One or more component classes to remove\n * @throws {@link ComponentNotRegistered} when any of the specified component(s) are not registered\n * @example\n * ecs.remove(entityId, Position);\n */\n remove<T extends ComponentClass<any>[]>(entityId: number, ...components: T) {\n for (const component of components) {\n this.removeByKey(entityId, component)\n }\n }\n\n /**\n * Removes the specified component from an entity\n * @param entityId - The entity id to remove the component from\n * @param component - The component class to remove\n * @returns True if the component was successfully removed, otherwise false\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * ecs.removeByKey(entityId, Position);\n */\n removeByKey(entityId: number, component: ComponentClass<any>) {\n // get the entity map\n const entityMap = this.components.get(component);\n\n // ensure the map is defined\n if (entityMap === undefined) throw new ComponentNotRegistered(component.name);\n\n // get the entity\n const entity = entityMap.get(entityId);\n if (entity === undefined) return false;\n\n // remove the entity from the entity map\n return entityMap.delete(entityId);\n }\n\n /**\n * Deletes all components from an entity and reclaims the id for reuse\n * @param entityIds - One or more entity ids to destroy\n * @returns The total number of components destroyed\n * @example\n * const destroyedCount = ecs.destroyEntity(entityId1)\n * \n * // or multiple\n * const destroyedCount = ecs.destroyEntity(entityId1, entityId2)\n */\n destroyEntity(...entityIds: number[]): number {\n let deletedCount = 0;\n for (let index = 0; index < entityIds.length; index++) {\n const entityId = entityIds[index];\n let found = false;\n for (const map of this.components.values()) {\n if (map.has(entityId)) {\n map.delete(entityId);\n deletedCount++;\n found = true;\n }\n }\n if (found) {\n this.freeIds.push(entityId);\n }\n }\n return deletedCount;\n }\n\n /**\n * Creates a new entity id for the EntityMap.\n * \n * Reuses Ids from destroyed entities otherwise increments the Id counter.\n * @returns A new unique entity id\n * @example\n * const newEntityId = ecs.getNextId()\n * ecs.set(newEntityId, new Player())\n */\n getNextId(): number {\n const reclaimedId = this.freeIds.pop();\n if (reclaimedId !== undefined) return reclaimedId;\n\n this.nextId++;\n return this.nextId;\n }\n\n /**\n * Clears all registered component classes, all entity data and all reclaimed IDs\n * @returns The {@link EntityMap} instance for chaining\n * @example\n * ecs.clear()\n */\n clear() {\n this.components.clear();\n this.nextId = 0;\n this.freeIds.length = 0;\n return this;\n }\n\n /**\n * Clears all component data and reclaimed IDs while keeping the registered component classes\n * @returns The {@link EntityMap} instance for chaining\n * @example\n * ecs.clearComponents()\n */\n clearComponents() {\n this.components.forEach(x => x.clear())\n this.nextId = 0\n this.freeIds.length = 0;\n return this;\n }\n\n /**\n * Iterates over each entity that contains the specified key component\n * @param keyComponent - The primary component class used to filter entities\n * @param components - Additional component classes to retrieve for each entity\n * @returns A {@link ComponentIterator} that yields tuples of [entityId, keyComponentData, ...relatedComponentData]\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * // iterate each component data that is related to the Player entity\n * const iterator = ecs.iterator(Player, Position)\n * \n * for(const [playerId, player, position] of iterator) { }\n * \n * // you can also declare the type of iterator before it's assigned\n * // using the ComponentIterator type\n * let iterator: ComponentIterator<[Player, Position]>\n * \n * // then with late bound assignment (keeping the iterator intellisense)\n * iterator = ecs.iterator(Player, Position)\n * \n * for(const [playerId, player, position] of iterator) {\n * const moving = player.isMoving\n * }\n */\n iterator<TKey, TRelated extends any[]>(\n keyComponent: ComponentClass<TKey>,\n ...components: ComponentClasses<TRelated>\n ) {\n return new ComponentIterator(new ComponentQuery(this, keyComponent, ...components));\n }\n\n /**\n * Creates a query that can be stored and reused\n * @param keyComponent - The primary component class used to filter entities\n * @param components - Additional component classes to retrieve for each entity\n * @returns A {@link ComponentQuery} instance\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * const query = ecs.query(Player, Position)\n * \n * // get the first entry\n * const [playerId, player, position] = query.firstEntry() ?? []\n * \n * // get the first key\n * const [playerId, position] = query.firstKey() ?? []\n * \n * // get the first value\n * const [player, position] = query.firstValue() ?? []\n * \n * // iterate\n * for (const [playerId, player, position] of query) {\n * \n * }\n */\n query<TKey, TRelated extends any[]>(\n keyComponent: ComponentClass<TKey>,\n ...components: ComponentClasses<TRelated>\n ) {\n return new ComponentQuery(this, keyComponent, ...components)\n }\n\n /**\n * Prints all component maps in a tabular format to the console.\n * \n * Additional generated columns are:\n * \n * 'Entity.Key' is the entity id\n * \n * 'Entity.Type' is the component name\n * \n * @param components - Optional filter. Only includes specific component class names.\n * @param properties - Optional filter. Specifies which property columns to display in the tables.\n * @returns The {@link EntityMap} instance for chaining\n */\n printTable(components?: string[]): this;\n printTable(components?: string[], properties?: string[]): this;\n printTable(components: string[] = [], properties: string[] = []) {\n this.components.forEach((map, key) => {\n if (components.length === 0 || components.includes(key)) {\n console.table(map.toTable(), properties);\n }\n });\n return this;\n }\n\n /**\n * Prints all component data for the specified entity id in a tabular format to the console\n * \n * Additional generated columns are:\n * \n * 'Entity.Type' is the component name\n * \n * @param entityId - The entity id to print\n * @param properties - Optional filter. Specifies which property columns to display in the tables.\n * @returns The {@link EntityMap} instance for chaining\n */\n printEntity(entityId: number, properties: string[] = []) {\n for (const map of this.components.values()) {\n if (map.has(entityId) === false) continue;\n\n const data = map.get(entityId);\n const columns = {\n 'entity.type': data.constructor.name,\n ...data\n };\n console.table({ [entityId]: columns }, properties);\n }\n\n return this;\n }\n\n /**\n * Parses the JSON and returns an {@link EntityMap} object\n * @param json - The JSON string representing an {@link EntityMap}\n * @returns A restored {@link EntityMap} instance\n * @example\n * const json = JSON.stringify(ecs);\n * const restoredMap = EntityMap.parse(json);\n */\n static parse(json: string): EntityMap {\n const restored = JSON.parse(json, function (key: string, value: any) {\n if (value.hasOwnProperty('components')) {\n Reflect.setPrototypeOf(value, EntityMap.prototype);\n return value;\n }\n if (value.hasOwnProperty('nextId')) return value;\n if (value.hasOwnProperty('freeIds')) return value;\n if (value.hasOwnProperty(ComponentMapKey)) return new ComponentMap(value.iterable);\n if (value.hasOwnProperty(ComponentClassesMapKey)) return new ComponentClassesMap(value.iterable);\n return this[key];\n });\n return restored;\n }\n\n /**\n * A tracing method used for debugging.\n * Intercepts all functions specified and logs each call to the console.\n * @param funcFilter - A list of function names you want to intercept. If no function names are specified then will log all functions called.\n * @returns A new proxy of an {@link EntityMap} with tracing enabled\n * @example\n * // trace all method calls\n * const ecs = EntityMap.createWithTracing();\n * \n * // trace only 'set' and 'remove' calls\n * const ecs = EntityMap.createWithTracing(['set', 'remove']);\n */\n static createWithTracing(funcFilter: string[] = []) {\n const traceHandler = {\n get(target: any, propKey: string) {\n const targetValue = target[propKey]\n\n if (typeof targetValue === 'function' && (funcFilter.length === 0 || funcFilter.includes(propKey))) {\n return function (this: any, ...args: any[]) {\n console.groupCollapsed('ecs trace', propKey, args);\n console.trace();\n console.groupEnd();\n return targetValue.apply(this, args);\n }\n }\n\n return targetValue;\n }\n }\n\n return new Proxy(new EntityMap(), traceHandler)\n }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**\n * An entity component system library for JavaScript\n * @showCategories\n * @module ecsjs\n */\nimport { EntityMap } from './entity-map.js'\nexport { ComponentClassesMap } from './component-classes.js'\nexport { ComponentMap } from './component-map.js'\nexport { EntityMap } from './entity-map.js'\nexport type { ComponentNotRegistered, ComponentTypeKeyMissing } from './errors.js'\nexport { ComponentIterator } from './iterator.js'\nexport { ComponentQuery } from './query.js'\nexport type { ComponentClass, IComponentIterator } from './types.js'\n\n/**\n * A global default instance of an {@link EntityMap}\n * \n * Provides a convenient way to use the ECS without managing multiple maps.\n * \n * See the [cheat sheet](https://gitlab.com/ecsjs/ecs/-/blob/master/docs/cheat-sheet.md) for more examples.\n * @category Constants\n * @example\n * // register component(s)\n * ecs.register(Player, Position)\n * \n * // create an entity and set its components\n * const [player, position] = ecs.set(ecs.getNextId(), new Player(), new Position(10, 40))\n */\nexport const ecs = new EntityMap()\n\nif (typeof window !== 'undefined') {\n // @ts-ignore: exports to window\n window.ecs = ecs\n} else if (typeof module !== 'undefined' && module !== null) {\n // exports to nodejs\n module.exports = { ecs };\n}"],
5
- "mappings": "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",
6
- "names": ["ComponentMapKey", "ComponentClassesMapKey", "hashString", "str", "hash", "i", "char", "ComponentClassesMap", "entries", "i", "entry", "componentClass", "index", "map", "hashString", "hash", "existingIndex", "hashId", "lastIndex", "lastHash", "callback", "instances", "hashes", "value", "ComponentClassesMapKey", "ComponentMap", "entries", "i", "entry", "entityId", "component", "index", "lastIdx", "lastEntity", "callback", "entities", "instances", "result", "ComponentMapKey", "table", "key", "value", "entity", "meta", "properties", "ComponentTypeKeyMissing", "ComponentNotRegistered", "componentName", "ComponentIterator", "query", "entry", "value", "done", "entityId", "i", "map", "ComponentQuery", "ecs", "components", "i", "entityId", "entryValue", "results", "keyValue", "keyMap", "componentMaps", "keysIterator", "key", "value", "map", "ComponentIterator", "EntityMap", "_EntityMap", "ComponentClassesMap", "componentClasses", "componentClass", "ComponentTypeKeyMissing", "ComponentMap", "component", "map", "ComponentNotRegistered", "keyComponent", "entityId", "entities", "x", "components", "keyValue", "v", "index", "componentData", "entityMap", "entityIds", "deletedCount", "found", "reclaimedId", "ComponentIterator", "ComponentQuery", "properties", "key", "data", "columns", "json", "value", "ComponentMapKey", "ComponentClassesMapKey", "funcFilter", "traceHandler", "target", "propKey", "targetValue", "args", "ecs", "EntityMap"]
4
+ "sourcesContent": ["/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/**\n * Property key used for ComponentMap identification during serialization\n * @category Constants\n */\nexport const ComponentMapKey = \"ComponentMap\"\n\n/**\n * Property key used for ComponentClassesMap identification during serialization\n * @category Constants\n */\nexport const ComponentClassesMapKey = \"ComponentClassesMap\"\n\n/**\n * Variadic type helper that preserves the strongly typed parameter interfaces used by the compiler and intellisense.\n * Works with single returned types or spread array returned types.\n * @category Types\n */\nexport type SingleOrArray<T extends any[]> =\n // return a single variadic item when only 1 item is specified\n T['length'] extends 1 ? T[0]\n // otherwise return an array of variadic types\n : { [Index in keyof T]: T[Index] }\n\n/**\n * Used to return component instance data.\n * \n * Note: This is not used for registering components or extending component classes\n * @category Types\n */\nexport interface Component { }\n\n/**\n * @category Types\n * @example\n * \n * class PositionComponent {\n * constructor(x, y) {\n * this.x = x;\n * this.y = y;\n * }\n * }\n */\nexport type ComponentClass<ComponentInstance> = (new (...args: any[]) => ComponentInstance) & {\n // static property\n hashId?: number;\n};\n\n/**\n* Used for inferring generic type spread for classes\n* @category Types\n*/\nexport type ComponentClasses<T extends Component[]> =\n { [Index in keyof T]: ComponentClass<T[Index]> }\n\n/**\n * Custom iterator interface that includes a reset function\n * @category Types\n */\nexport type Iterator<T> = {\n /**\n * Gets the next result in the iteration\n */\n next: () => IteratorResult<T>\n /**\n * Resets the iterator back to the beginning\n */\n reset: () => void\n /**\n * Returns the iterator instance\n */\n [Symbol.iterator]: () => Iterator<T>\n}\n\n/**\n * Helper type for late-bound iterator assignment with IntelliSense support\n * @category Types\n * @example\n * \n * let someIterator: IComponentIterator<[Player, Position]>\n * \n * // assignment made somewhere else in the code\n * const [player, position] = someIterator(Player, Position) ?? []\n * position?.x = 123 // position will have IntelliSense\n */\nexport type IComponentIterator<T extends Component[]>\n = Iterator<ComponentClasses<[number, ...T]>>", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/**\n * Generates a 32-bit integer hash from a string\n * @param str - The string to hash\n * @returns A 32-bit integer hash\n */\nexport function hashString(str: string) {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash |= 0; // Convert to 32bit\n }\n return hash;\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport type { ComponentMap } from './component-map.js';\nimport {\n ComponentClassesMapKey,\n type ComponentClass,\n type Iterator,\n} from './types.js';\nimport { hashString } from './utils.js';\n\n/**\n * Component class map for storing registered component maps\n * @category Maps\n */\nexport class ComponentClassesMap {\n\n private hashIdToName: Map<number, string> = new Map<number, string>()\n private hashIdToIndex = new Map<number, number>();\n private indexToHashId: number[] = [];\n private maps: ComponentMap<any>[] = [];\n\n /**\n * Creates a new component classes map\n * @param entries - Optional initial data for the map\n */\n constructor(entries?: readonly (readonly [number, ComponentMap<any>])[] | null) {\n if (entries) {\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n this.setByHashId(entry[0], entry[1]);\n }\n }\n }\n\n /**\n * Returns the number of registered component maps\n */\n get size() {\n return this.maps.length;\n }\n\n /**\n * Clears all maps and internal indices while preserving array references\n */\n clear(): void {\n this.maps.length = 0;\n this.indexToHashId.length = 0;\n this.hashIdToIndex.clear();\n }\n\n /**\n * Retrieves a component map using the component class\n * @param componentClass - The component class to get the map for\n * @returns The {@link ComponentMap} for the specified class, or undefined if not found\n */\n get<T>(componentClass: ComponentClass<T>): ComponentMap<T> | undefined {\n const index = this.hashIdToIndex.get(componentClass.hashId!);\n return index !== undefined ? this.maps[index] : undefined;\n }\n\n /**\n * Checks if a component class map is already registered\n * @param componentClass - The component class to check\n * @returns True if the map is registered, otherwise false\n */\n has<T>(componentClass: ComponentClass<T>): boolean {\n if (componentClass.hashId === undefined) return false;\n return this.hashIdToIndex.has(componentClass.hashId);\n }\n\n /**\n * Registers a component map. Automatically generates a hashId from the class name if missing\n * @param componentClass - The component class to register\n * @param map - The {@link ComponentMap} instance to store\n * @returns The {@link ComponentClassesMap} instance for chaining\n */\n set<T>(componentClass: ComponentClass<T>, map: ComponentMap<T>): this {\n if (componentClass.hashId === undefined) {\n componentClass.hashId = hashString(componentClass.name);\n }\n\n this.hashIdToName.set(componentClass.hashId, componentClass.name);\n return this.setByHashId(componentClass.hashId!, map);\n }\n\n /**\n * Maps a specific numeric hash id to a component map instance.\n * @param hash - The numeric hash id\n * @param map - The {@link ComponentMap} instance to store\n * @returns The {@link ComponentClassesMap} instance for chaining\n */\n setByHashId(hash: number, map: ComponentMap<any>): this {\n const existingIndex = this.hashIdToIndex.get(hash);\n\n if (existingIndex !== undefined) {\n this.maps[existingIndex] = map;\n } else {\n this.hashIdToIndex.set(hash, this.maps.length);\n this.indexToHashId.push(hash);\n this.maps.push(map);\n }\n\n return this;\n }\n\n /**\n * Removes a component map and re-orders internal storage to maintain density\n * @param componentClass - The component class to remove the map for\n * @returns True if the map was removed, otherwise false\n */\n delete(componentClass: ComponentClass<any>): boolean {\n const hashId = componentClass.hashId!;\n const index = this.hashIdToIndex.get(hashId);\n if (index === undefined) return false;\n\n const lastIndex = this.maps.length - 1;\n const lastHash = this.indexToHashId[lastIndex];\n\n // Swap\n this.maps[index] = this.maps[lastIndex];\n this.indexToHashId[index] = lastHash;\n\n // Update map pointer\n this.hashIdToIndex.set(lastHash, index);\n\n // Pop\n this.maps.pop();\n this.indexToHashId.pop();\n this.hashIdToIndex.delete(hashId);\n return true;\n }\n\n /**\n * Executes a callback for every map, providing the instance and its registered name\n * @param callback - The callback to execute\n */\n forEach(callback: (value: ComponentMap<any>, name: string) => void) {\n for (let i = 0; i < this.maps.length; i++) {\n callback(this.maps[i], this.hashIdToName.get(this.indexToHashId[i])!);\n }\n }\n\n /**\n * Returns an iterator of all component map instances \n * @returns An {@link ArrayIterator} of {@link ComponentMap} instances\n */\n values(): ArrayIterator<ComponentMap<any>> { return this.maps.values() }\n\n /**\n * Returns an iterator of [hashId, componentMap] pairs\n * @returns An {@link Iterator} instance\n */\n entries(): Iterator<[number, ComponentMap<any>]> {\n let index = 0;\n const instances = this.maps;\n const hashes = this.indexToHashId;\n\n return {\n next(): IteratorResult<[number, ComponentMap<any>]> {\n if (index < instances.length) {\n const value: [number, ComponentMap<any>] = [hashes[index], instances[index]];\n index++;\n return { value, done: false };\n }\n return { value: undefined as any, done: true };\n },\n reset() { index = 0; },\n [Symbol.iterator]() { return this; }\n };\n }\n\n /**\n * Called when using JSON.stringify\n * @returns An object representation for serialization\n */\n toJSON() {\n return { [ComponentClassesMapKey]: 1, iterable: [...this.entries()] };\n }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport {\n ComponentMapKey,\n type Iterator\n} from './types.js';\n\n/**\n * Component map for storing entity ids and related component data\n * @category Maps\n */\nexport class ComponentMap<TComponentInstance> implements Iterable<[number, TComponentInstance]> {\n\n private indices: number[] = [];\n\n private entities: number[] = [];\n\n private instances: TComponentInstance[] = [];\n\n /**\n * Creates a new component map\n * @param entries - Optional initial data for the map\n */\n constructor(entries?: readonly (readonly [number, TComponentInstance])[] | null) {\n if (entries) {\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n this.set(entry[0], entry[1]);\n }\n }\n }\n\n /**\n * Returns the number of entities in the map\n */\n get size() {\n return this.instances.length;\n }\n\n /**\n * Clears all component data from the map\n */\n clear() {\n this.indices = [];\n this.entities.length = 0;\n this.instances.length = 0;\n }\n\n /**\n * Adds or updates component data for an entity\n * @param entityId - The entity id to set\n * @param component - The component data to set\n */\n set(entityId: number, component: TComponentInstance) {\n if (this.has(entityId)) {\n this.instances[this.indices[entityId]] = component;\n return;\n }\n this.indices[entityId] = this.instances.length;\n this.entities.push(entityId);\n this.instances.push(component);\n }\n\n /**\n * Gets the component data for an entity\n * @param entityId - The entity id to get\n * @returns The component data, or undefined if not found\n */\n get(entityId: number) {\n const index = this.indices[entityId];\n return index !== undefined && this.entities[index] === entityId\n ? this.instances[index]\n : undefined;\n }\n\n /**\n * Removes component data for an entity\n * @param entityId - The entity id to remove\n * @returns True if the component was removed, otherwise false\n */\n delete(entityId: number) {\n const index = this.indices[entityId];\n if (index === undefined || index === -1 || this.entities[index] !== entityId) return false;\n\n // swap and pop with last element to keep dense\n const lastIdx = this.instances.length - 1;\n const lastEntity = this.entities[lastIdx];\n this.instances[index] = this.instances[lastIdx];\n this.entities[index] = lastEntity;\n this.indices[lastEntity] = index;\n this.instances.pop();\n this.entities.pop();\n\n // invalidate index\n this.indices[entityId] = -1;\n\n return true;\n }\n\n /**\n * Checks if an entity has a component in this map\n * @param entityId - The entity id to check\n * @returns True if the entity has a component, otherwise false\n */\n has(entityId: number) {\n const index = this.indices[entityId];\n return index !== undefined && index !== -1 && this.entities[index] === entityId;\n }\n\n /**\n * Executes a callback for every map, providing the component data and its entity id\n * @param callback - The callback to execute\n */\n forEach(callback: (value: TComponentInstance, key: number) => void) {\n for (let i = 0; i < this.instances.length; i++) {\n callback(this.instances[i], this.entities[i]);\n }\n }\n\n /**\n * Returns the first entry\n * @returns A tuple of [entityId, componentData], or undefined if empty\n */\n firstEntry(): [entityId: number, value: TComponentInstance] | undefined {\n return this.entities.length === 0\n ? undefined\n : [this.entities[0], this.instances[0]];\n }\n\n /**\n * Returns the first entity id\n * @returns The first entity id, or undefined if empty\n */\n firstKey(): number | undefined {\n return this.entities.length === 0\n ? undefined\n : this.entities[0];\n }\n\n /**\n * Returns the first entity data\n * @returns The first component data, or undefined if empty\n */\n firstValue(): TComponentInstance | undefined {\n return this.instances.length === 0\n ? undefined\n : this.instances[0];\n }\n\n /**\n * Returns an iterator of [entityId, componentData] pairs\n * @returns An {@link Iterator} instance\n */\n [Symbol.iterator](): Iterator<[number, TComponentInstance]> {\n return this.entries();\n }\n\n /**\n * Returns an iterator of all entity ids\n * @returns An {@link ArrayIterator} of entity ids\n */\n keys(): ArrayIterator<number> {\n return this.entities.values();\n }\n\n /**\n * Returns an iterator of all component data\n * @returns An {@link ArrayIterator} of component data\n */\n values(): ArrayIterator<TComponentInstance> {\n return this.instances.values();\n }\n\n /**\n * Returns an iterator of [entityId, componentData] pairs\n * @returns An {@link Iterator} instance\n */\n entries(): Iterator<[number, TComponentInstance]> {\n let index = 0;\n const entities = this.entities;\n const instances = this.instances;\n\n return {\n next(): IteratorResult<[number, TComponentInstance]> {\n if (index < instances.length) {\n const result: IteratorResult<[number, TComponentInstance]> = {\n value: [entities[index], instances[index]],\n done: false\n };\n index++;\n return result;\n }\n return { value: <any>undefined, done: true };\n },\n reset() {\n index = 0;\n },\n [Symbol.iterator]() {\n return this;\n },\n };\n }\n\n /**\n * Called when using JSON.stringify\n * @returns An object representation for serialization\n */\n toJSON() {\n return { [ComponentMapKey]: 1, iterable: [...this.entries()] };\n }\n\n /**\n * Returns an array of objects for tabular display\n * @returns An array of objects formatted for {@link console.table}\n */\n toTable(): any[] {\n const table = []\n for (const [key, value] of this.entries()) {\n const entity = value as { new(): TComponentInstance };\n const meta: Record<string, any> = {};\n meta[\"entity.key\"] = key;\n meta[\"entity.type\"] = entity.constructor.name;\n table.push({ ...meta, ...entity });\n }\n return table;\n }\n\n /**\n * Prints entity data in a tabular format to the console\n * @param properties - Optional filter. Specifies which property columns to display in the tables.\n * @returns The {@link ComponentMap} instance for chaining\n */\n printTable(properties: string[] = []): this {\n console.table(this.toTable(), properties);\n return this;\n }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/**\n * Thrown when trying to register a component that is missing a 'name' parameter\n * @category Errors\n */\nexport class ComponentTypeKeyMissing extends Error {\n constructor() {\n super(`Component type is missing the 'name' parameter. i.e. a constructor name`);\n }\n}\n\n/**\n * Thrown when trying to access a component that has not been registered\n * @param componentName - The name of the unregistered component\n * @category Errors\n */\nexport class ComponentNotRegistered extends Error {\n constructor(public componentName: string) {\n super(`Component map does not exist for '${componentName}'`);\n }\n}\n\n/**\n * Thrown when trying to register a component that has already been registered\n * @param componentName - The name of the already registered component\n * @category Errors\n */\nexport class ComponentAlreadyRegistered extends Error {\n constructor(public componentName: string) {\n super(`Component '${componentName}' is already registered`);\n }\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport type { ComponentMap } from './component-map.js';\nimport type { ComponentQuery } from './query.js';\n\n/**\n * An iterator for yielding entity ids and their associated component data\n * @category Iterators\n * @example\n * // construct an instance\n * const iterator = new ComponentIterator(new ComponentQuery(entityMap, Player, Position))\n * \n * // iterate component data that is related to the Player\n * for(const [entityId, player, position] of iterator) { }\n * \n * // you can also reset the iterator back to the start\n * // without having to create a new ComponentIterator instance\n * iterator.reset()\n * for(const [entityId, player, position] of iterator) { }\n */\nexport class ComponentIterator<TKey, TRelated extends any[]> {\n private keyMap: ComponentMap<TKey>;\n private componentMaps: ComponentMap<any>[] = [];\n // iteration state\n private entries: Iterator<[number, TKey]>;\n\n /**\n * Creates a new component iterator\n * @param query - The {@link ComponentQuery} to iterate over\n */\n constructor(query: ComponentQuery<TKey, TRelated>) {\n // @ts-ignore internal private var accessor\n this.keyMap = query.keyMap\n // @ts-ignore internal private var accessor\n this.componentMaps = query.componentMaps\n this.entries = this.keyMap.entries();\n }\n\n /**\n * Gets the next iterator value\n * @returns An {@link IteratorResult} containing the next [entityId, keyComponentData, ...relatedComponentData] tuple\n */\n next(): IteratorResult<[number, TKey, ...TRelated]> {\n const entry = this.entries.next();\n const { value, done } = entry;\n if (done) return { value, done };\n\n const entityId = value[0];\n\n // append the related components\n for (let i = 0; i < this.componentMaps.length; i++) {\n const map = this.componentMaps[i];\n value.push(map.get(entityId));\n }\n\n return { value: value as any, done: false };\n }\n\n /**\n * Resets the iterator back to the first entry\n */\n reset() {\n this.entries = this.keyMap.entries();\n }\n\n /**\n * Returns this iterator instance\n */\n [Symbol.iterator]() { return this; }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport type { ComponentMap } from './component-map.js';\nimport type { EntityMap } from './entity-map.js';\nimport { ComponentIterator } from './iterator.js';\nimport type { ComponentClass, ComponentClasses, Iterator, SingleOrArray } from './types.js';\n\n/**\n * A reusable query for retrieving entities and their components\n * @category Queries\n * @example\n * const query = ecs.query(Player, Position)\n * \n * // get the first entry\n * const [playerId, player, position] = query.firstEntry() ?? []\n * \n * // get the first key\n * const [playerId, position] = query.firstKey() ?? []\n * \n * // get the first value\n * const [player, position] = query.firstValue() ?? []\n * \n * // iterate\n * for (const [playerId, player, position] of query) {\n * \n * }\n */\nexport class ComponentQuery<TKey, TRelated extends any[]> {\n private keyMap: ComponentMap<TKey>\n private componentMaps: ComponentMap<any>[] = []\n\n /**\n * Creates a new component query\n * @param ecs - The {@link EntityMap} instance to query\n * @param components - The first component class <TKey> is the primary component. Optional additional component classes <TRelated> to retrieve for each entity.\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n */\n constructor(\n public ecs: EntityMap,\n ...components: [ComponentClass<TKey>, ...ComponentClasses<TRelated>]\n ) {\n this.keyMap = this.ecs.getMap(components[0])!;\n for (let i = 1; i < components.length; i++) {\n this.componentMaps.push(this.ecs.getMap(components[i])!)\n }\n }\n\n /**\n * Gets the first entry matching the query.\n * \n * Includes the entity id, the key component data, and any related component data.\n * @returns A tuple of `[entityId, keyComponentData, ...relatedComponentData]`, or `undefined` if no entities match.\n */\n firstEntry(): [number, TKey, ...TRelated] | undefined;\n firstEntry() {\n if (this.componentMaps.length === 0) return this.keyMap.firstEntry()\n\n const [entityId, entryValue] = this.keyMap.firstEntry() ?? []\n if (entityId === undefined) return undefined;\n\n const results: any[] = [entityId, entryValue]\n for (let i = 0; i < this.componentMaps.length; i++) {\n results.push(this.componentMaps[i].get(entityId));\n }\n\n return results\n }\n\n /**\n * Gets the first key matching the query.\n * \n * @returns The entity id if no related components were specified in the constructor.\n * \n * Otherwise, a tuple of `[entityId, ...relatedComponentData]`.\n * \n * Returns `undefined` if no entities match.\n */\n firstKey(): SingleOrArray<[number, ...TRelated]> | undefined;\n firstKey() {\n const entityId = this.keyMap.firstKey()\n if (this.componentMaps.length === 0) return entityId;\n if (entityId === undefined) return undefined;\n\n const results: any[] = [entityId]\n for (let i = 0; i < this.componentMaps.length; i++) {\n results.push(this.componentMaps[i].get(entityId));\n }\n\n return results\n }\n\n /**\n * Get the first component data matching the query.\n * \n * @returns The key component data if no related components were specified in the constructor.\n * \n * Otherwise, a tuple of `[keyComponentData, ...relatedComponentData]`.\n * \n * Returns `undefined` if no entities match.\n */\n firstValue(): SingleOrArray<[TKey, ...TRelated]> | undefined;\n firstValue() {\n if (this.componentMaps.length === 0) return this.keyMap.firstValue()\n\n const [entityId, keyValue] = this.keyMap.firstEntry() ?? []\n if (entityId === undefined) return undefined;\n\n const results = [keyValue]\n for (let i = 0; i < this.componentMaps.length; i++) {\n results.push(this.componentMaps[i].get(entityId));\n }\n\n return results\n }\n\n /**\n * Returns the count of entities matching the query\n * @returns The number of entities that have the key component\n */\n get entityCount() {\n return this.keyMap.size\n }\n\n /**\n * Destroys all entities matching the query\n * @returns The total number of components destroyed across all affected entities\n * @example\n * const destroyedCount = query.destroyEntities()\n */\n destroyEntities(): number {\n return this.ecs.destroyEntity(...this.keyMap.keys())\n }\n\n /**\n * Gets all entity ids matching the query.\n * \n * @returns An {@link ArrayIterator} of entity ids.\n */\n keys(): ArrayIterator<number> { return this.keyMap.keys() }\n\n /**\n * Gets all component data matching the query.\n *\n * @returns An {@link Iterator} of `[keyComponentData, ...relatedComponentData]` tuples containing component data\n * @example\n * const query = ecs.query(Player, Position)\n * for (const [player, position] of query.values()) { }\n */\n values(): Iterator<[TKey, ...TRelated]> {\n const keyMap = this.keyMap;\n const componentMaps = this.componentMaps;\n let keysIterator = keyMap.keys();\n\n return {\n next(): IteratorResult<[TKey, ...TRelated]> {\n const key = keysIterator.next();\n if (key.done) return { value: <any>undefined, done: true };\n\n const entityId = key.value;\n const keyComponent = keyMap.get(entityId);\n\n // append the related components\n const value = [keyComponent];\n for (let i = 0; i < componentMaps.length; i++) {\n const map = componentMaps[i];\n value.push(map.get(entityId));\n }\n\n return { value: value as any, done: false };\n },\n reset() { keysIterator = keyMap.keys(); },\n [Symbol.iterator]() { return this; },\n };\n }\n\n /**\n * Gets all component data entries matching the query\n * \n * @returns A {@link ComponentIterator} instance that yields `[entityId, keyComponentData, ...relatedComponentData]`\n * @example\n * const query = ecs.query(Player, Position)\n * for (const [entityId, player, position] of query.entries()) { }\n */\n entries() { return this[Symbol.iterator](); }\n\n /**\n * @returns A {@link ComponentIterator} instance that yields `[entityId, keyComponentData, ...relatedComponentData]`\n */\n [Symbol.iterator]() {\n return new ComponentIterator(this);\n }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { ComponentClassesMap } from './component-classes.js';\nimport { ComponentMap } from './component-map.js';\nimport {\n ComponentAlreadyRegistered,\n ComponentNotRegistered,\n ComponentTypeKeyMissing\n} from './errors.js';\nimport { ComponentIterator } from './iterator.js';\nimport { ComponentQuery } from './query.js';\nimport {\n ComponentClassesMapKey,\n ComponentMapKey,\n type Component,\n type ComponentClass,\n type ComponentClasses,\n type SingleOrArray\n} from './types.js';\nimport { hashString } from './utils.js';\n\n/**\n * Class for storing entities and their relationships.\n * @category Maps\n */\nexport class EntityMap {\n /**\n * Registered component classes that contain the component data\n */\n public components = new ComponentClassesMap()\n\n private nextId: number = 0\n\n private freeIds: number[] = []\n\n /**\n * Registers component classes with the {@link EntityMap}\n * @param componentClasses - One or more component classes to register\n * @returns The {@link EntityMap} instance for chaining\n * @throws {@link ComponentTypeKeyMissing} when the specified component type is missing a 'name' parameter (e.g. anonymous classes)\n * @throws {@link ComponentAlreadyRegistered} when the specified component is already registered\n * @example\n * // component class\n * class MyComponent {\n * constructor(x) {\n * this.x = x;\n * }\n * }\n *\n * ecs.register(MyComponent);\n * // or multiple\n * ecs.register(MyComponent1, MyComponent2);\n */\n register<TComponentClasses extends ComponentClass<any>[]>(...componentClasses: TComponentClasses) {\n for (const componentClass of componentClasses) {\n const componentName = componentClass.name;\n if (componentName === undefined || componentName === '') throw new ComponentTypeKeyMissing();\n if (componentClass.hashId === undefined) componentClass.hashId = hashString(componentName);\n if (this.components.has(componentClass)) throw new ComponentAlreadyRegistered(componentName);\n\n // create the component map\n this.components.set(componentClass, new ComponentMap());\n }\n\n // chain\n return this;\n }\n\n /**\n * Gets a component class map\n * @param component - The component class to get the map for\n * @returns The {@link ComponentMap} for the specified component, or undefined if not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * const positionMap = ecs.getMap(Position)\n * for(const [entityId, position] of positionMap) {\n * position.x += 1\n * }\n */\n getMap<T>(component: ComponentClass<T>): ComponentMap<T> | undefined {\n const map = this.components.get(component);\n if (map === undefined) throw new ComponentNotRegistered(component.name)\n return map\n }\n\n /**\n * @deprecated Use {@link EntityMap.firstEntity} instead\n * \n * Returns an array of component data for the first entity associated with the keyComponent\n * @param keyComponent - The component class used to find the first entity\n * @returns An array of components for the first entity, or undefined if not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * // get the first entity\n * const playerEntity = ecs.first(Player) ?? []\n */\n first(keyComponent: ComponentClass<any>): Component[] | undefined { return this.firstEntity(keyComponent); }\n\n /**\n * Returns an array of component data for the first entity associated with the keyComponent\n * @param keyComponent - The component class used to find the first entity\n * @returns An array of components for the first entity, or undefined if not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * // get the first entity\n * const playerEntity = ecs.firstEntity(Player) ?? []\n */\n firstEntity(keyComponent: ComponentClass<any>): Component[] | undefined {\n const entityId = this.getMap(keyComponent)?.firstKey();\n if (entityId === undefined) return undefined;\n\n return this.get(entityId);\n }\n\n /**\n * Returns an array of component data arrays associated with the keyComponent\n * @param keyComponent - The component class used to find the entities\n * @returns An array of component arrays, or undefined if the key component map is not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * // get an array of component arrays: e.g. [[Player, Position, Velocity], [Player, Position, Velocity], ...]\n * const entities = ecs.entityValues(Player) ?? []\n */\n entityValues(keyComponent: ComponentClass<any>): Array<Component[]> | undefined {\n const entities = this.getMap(keyComponent)?.keys();\n if (entities === undefined) return undefined;\n\n return [...entities].map(x => this.get(x)!)\n }\n\n /**\n * Gets the first entity entry for a component class\n * @param keyComponent - The component class to find the first entry for\n * @param components - Additional component classes to retrieve for the same entity\n * @returns A tuple containing the entity id followed by the component data, or undefined if not found\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * // return the first entry\n * const [entityId, player] = ecs.firstEntry(Player) ?? []\n * \n * // or return multiple related components in addition to the first entry\n * const [entityId, player, position, direction] = ecs.firstEntry(\n * Player,\n * Position,\n * Direction\n * ) ?? []\n */\n firstEntry<TKey, TRelated extends any[]>(\n keyComponent: ComponentClass<TKey>,\n ...components: ComponentClasses<TRelated>\n ): [number, TKey, ...TRelated] | undefined;\n firstEntry(keyComponent: ComponentClass<any>, ...components: ComponentClass<any>[]) {\n if (components.length === 0) return this.getMap(keyComponent)?.firstEntry()\n\n return this.firstKey(keyComponent, keyComponent, ...components);\n }\n\n /**\n * Gets the first entity id for a component class \n * and optionally any related component data\n * @param keyComponent - The component class used to find the first entity\n * @param components - Additional component classes to retrieve for the same entity\n * @returns If only keyComponent is provided, returns the entity id. \n * \n * If related components are provided, returns a tuple with the id and component data. \n * \n * Returns undefined if not found.\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * // return the first entity id\n * const entityId = ecs.firstKey(Player)\n * \n * // or return multiple related component in addition to entity id\n * const [entityId, position, direction] = ecs.firstKey(\n * Player,\n * Position,\n * Direction\n * ) ?? []\n */\n firstKey<TKey, TRelated extends any[]>(\n keyComponent: TKey,\n ...components: ComponentClasses<TRelated>\n ): SingleOrArray<[number, ...TRelated]> | undefined;\n firstKey(keyComponent: ComponentClass<any>, ...components: ComponentClass<any>[]) {\n const entityId = this.getMap(keyComponent)?.firstKey();\n\n // single component key\n if (arguments.length === 1) return entityId;\n if (entityId === undefined) return undefined;\n\n // attach related component data\n return [entityId, ...components.map(x => this.getEntity(entityId, x))];\n }\n\n /**\n * Gets the first entity component data for a component class\n * and optionally any related component data\n * @param keyComponent - The component class used to find the first entity\n * @param components - Additional component classes to retrieve for the same entity\n * @returns If only keyComponent is provided, returns the component data. \n * \n * If related components are provided, returns a tuple with all component data. \n * \n * Returns undefined if not found.\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * // return the first component data\n * const player = ecs.firstValue(Player)\n * \n * // or multiple related data in addition to the first component\n * const [player, position, direction] = ecs.firstValue(\n * Player,\n * Position,\n * Direction\n * ) ?? []\n */\n firstValue<TKey, TRelated extends any[]>(\n keyComponent: ComponentClass<TKey>,\n ...components: ComponentClasses<TRelated>\n ): SingleOrArray<[TKey, ...TRelated]> | undefined;\n firstValue(keyComponent: ComponentClass<any>, ...components: ComponentClass<any>[]) {\n // single component\n if (arguments.length === 1) return this.getMap(keyComponent)?.firstValue();\n\n // get the first entry\n const [entityId, keyValue] = this.getMap(keyComponent)?.firstEntry() ?? [];\n if (entityId === undefined) return undefined;\n\n // attach related components\n return [keyValue, ...components.map(x => this.getEntity(entityId, x))]\n }\n\n /**\n * Internal helper to get component data for an entity\n * @param entityId - The entity id to get the component for\n * @param component - The component class to get the data of\n * @returns The component data if it exists, otherwise undefined\n * @throws {@link ComponentNotRegistered} when the specified component is not registered \n */\n private getEntity<T>(entityId: number, component: ComponentClass<T>): T | undefined {\n const map = this.components.get(component);\n if (map === undefined) throw new ComponentNotRegistered(component.name)\n\n return map.get(entityId);\n }\n\n /**\n * Gets component data related to an entity id\n * @param entityId - The entity id to get component(s) for\n * @param components - One or more component classes to retrieve. If none provided, retrieves all components for the entity.\n * @returns Depending on parameters: a single component, an array of specified components, or an array of all components for the entity.\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * // get one by id\n * const player = ecs.get(entityId, Player)\n * \n * // get multiple by id\n * const [player, position] = ecs.get(entityId, Player, Position) ?? []\n * \n * // get all by id\n * const playerEntity = ecs.get(entityId) ?? []\n */\n get(entityId: number): Component[] | undefined;\n get<T>(entityId: number, component: ComponentClass<T>): T | undefined;\n get<T extends any[]>(entityId: number, ...components: ComponentClasses<T>): SingleOrArray<T> | undefined;\n get(entityId: number, ...components: ComponentClasses<any>): Component | Component[] | undefined {\n // return a single component\n if (components.length === 1) return this.getEntity(entityId, components[0]);\n // return filtered components\n if (components.length > 1) return components.map(x => this.getEntity(entityId, x));\n // return all components\n return [...this.components.values()]\n .filter(v => v.has(entityId))\n .map(v => v.get(entityId));\n }\n\n /**\n * Check if a component exists for an entity\n * @param entityId - The entity id to check for the component\n * @param component - The component class to check\n * @returns True if the entity has the component, otherwise false\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * const exists = ecs.has(entityId, Position)\n */\n has<T>(entityId: number, component: ComponentClass<T>): boolean {\n // get the component map\n const map = this.components.get(component);\n if (map === undefined) throw new ComponentNotRegistered(component.name)\n\n return map.has(entityId);\n }\n\n /**\n * Checks if all of the specified components exist for an entity\n * @param entityId - The entity id to check\n * @param components - One or more component classes to check for\n * @returns True if the entity has ALL specified components, otherwise false\n * @throws {@link ComponentNotRegistered} when any of the specified component(s) are not registered\n * @example\n * const hasAll = ecs.hasAll(entityId, Position, Velocity)\n */\n hasAll<T extends ComponentClass<any>[]>(entityId: number, ...components: T): boolean {\n for (let index = 0; index < components.length; index++) {\n const component = components[index];\n const map = this.components.get(component);\n if (map === undefined) throw new ComponentNotRegistered(component.name)\n if (map.has(entityId) === false) return false;\n }\n return true\n }\n\n /**\n * Checks if any of the specified components exist for an entity\n * @param entityId - The entity id to check\n * @param components - One or more component classes to check for\n * @returns True if the entity has ANY of the specified components, otherwise false\n * @throws {@link ComponentNotRegistered} when any of the specified component(s) are not registered\n * @example\n * const hasAny = ecs.hasAny(entityId, Position, Velocity)\n */\n hasAny<T extends ComponentClass<any>[]>(entityId: number, ...components: T): boolean {\n for (let index = 0; index < components.length; index++) {\n const component = components[index];\n const map = this.components.get(component);\n if (map === undefined) throw new ComponentNotRegistered(component.name)\n if (map.has(entityId)) return true;\n }\n return false\n }\n\n /**\n * Internal helper to set component data for an entity\n * @param entityId - The entity id to set the component for\n * @param componentData - The component data to set\n * @returns The component data that was set\n * @throws {@link ComponentNotRegistered} when the component class of the instance is not registered\n */\n private setEntity<T extends Component>(entityId: number, componentData: T): T {\n // get the component map\n const map = this.components.get((<ComponentClass<any>>componentData.constructor));\n if (map === undefined) throw new ComponentNotRegistered(componentData.constructor.name)\n\n // set the entity on the entity map\n map.set(entityId, componentData);\n\n // return instance\n return componentData;\n }\n\n /**\n * Add or update multiple component data for an entity\n * @param entityId - The entity id to set components for\n * @param component - A single component data instance to set\n * @param components - Multiple component data instances to set\n * @returns The single component data or an array of component data that were set\n * @throws {@link ComponentNotRegistered} when any of the component classes are not registered\n * @example\n * // set one\n * const player = ecs.set(entityId, new Player());\n * \n * // or set multiple\n * const [player, position] = ecs.set(\n * entityId,\n * new Player(),\n * new Position()\n * );\n */\n set<T extends Component>(entityId: number, component: T): T;\n set<T extends Component[]>(entityId: number, ...components: T): T;\n set(entityId: number, ...components: Component[]): Component | Component[] {\n if (components.length > 1) return components.map(x => this.setEntity(entityId, x))\n\n // set and return a single component\n return this.setEntity(entityId, components[0])\n }\n\n /**\n * Removes the specified component(s) from an entity\n * @param entityId - The entity id to remove components from\n * @param components - One or more component classes to remove\n * @throws {@link ComponentNotRegistered} when any of the specified component(s) are not registered\n * @example\n * ecs.remove(entityId, Position);\n */\n remove<T extends ComponentClass<any>[]>(entityId: number, ...components: T) {\n for (const component of components) {\n this.removeByKey(entityId, component)\n }\n }\n\n /**\n * Removes the specified component from an entity\n * @param entityId - The entity id to remove the component from\n * @param component - The component class to remove\n * @returns True if the component was successfully removed, otherwise false\n * @throws {@link ComponentNotRegistered} when the specified component is not registered\n * @example\n * ecs.removeByKey(entityId, Position);\n */\n removeByKey(entityId: number, component: ComponentClass<any>) {\n // get the entity map\n const entityMap = this.components.get(component);\n\n // ensure the map is defined\n if (entityMap === undefined) throw new ComponentNotRegistered(component.name);\n\n // get the entity\n const entity = entityMap.get(entityId);\n if (entity === undefined) return false;\n\n // remove the entity from the entity map\n return entityMap.delete(entityId);\n }\n\n /**\n * Deletes all components from an entity and reclaims the id for reuse\n * @param entityIds - One or more entity ids to destroy\n * @returns The total number of components destroyed\n * @example\n * const destroyedCount = ecs.destroyEntity(entityId1)\n * \n * // or multiple\n * const destroyedCount = ecs.destroyEntity(entityId1, entityId2)\n */\n destroyEntity(...entityIds: number[]): number {\n let deletedCount = 0;\n for (let index = 0; index < entityIds.length; index++) {\n const entityId = entityIds[index];\n let found = false;\n for (const map of this.components.values()) {\n if (map.has(entityId)) {\n map.delete(entityId);\n deletedCount++;\n found = true;\n }\n }\n if (found) {\n this.freeIds.push(entityId);\n }\n }\n return deletedCount;\n }\n\n /**\n * Creates a new entity id for the EntityMap.\n * \n * Reuses Ids from destroyed entities otherwise increments the Id counter.\n * @returns A new unique entity id\n * @example\n * const newEntityId = ecs.getNextId()\n * ecs.set(newEntityId, new Player())\n */\n getNextId(): number {\n const reclaimedId = this.freeIds.pop();\n if (reclaimedId !== undefined) return reclaimedId;\n\n this.nextId++;\n return this.nextId;\n }\n\n /**\n * Clears all registered component classes, all entity data and all reclaimed IDs\n * @returns The {@link EntityMap} instance for chaining\n * @example\n * ecs.clear()\n */\n clear() {\n this.components.clear();\n this.nextId = 0;\n this.freeIds.length = 0;\n return this;\n }\n\n /**\n * Clears all component data and reclaimed IDs while keeping the registered component classes\n * @returns The {@link EntityMap} instance for chaining\n * @example\n * ecs.clearComponents()\n */\n clearComponents() {\n this.components.forEach(x => x.clear())\n this.nextId = 0\n this.freeIds.length = 0;\n return this;\n }\n\n /**\n * Iterates over each entity that contains the specified key component\n * @param keyComponent - The primary component class used to filter entities\n * @param components - Additional component classes to retrieve for each entity\n * @returns A {@link ComponentIterator} that yields tuples of [entityId, keyComponentData, ...relatedComponentData]\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * // iterate each component data that is related to the Player entity\n * const iterator = ecs.iterator(Player, Position)\n * \n * for(const [playerId, player, position] of iterator) { }\n * \n * // you can also declare the type of iterator before it's assigned\n * // using the ComponentIterator type\n * let iterator: ComponentIterator<[Player, Position]>\n * \n * // then with late bound assignment (keeping the iterator intellisense)\n * iterator = ecs.iterator(Player, Position)\n * \n * for(const [playerId, player, position] of iterator) {\n * const moving = player.isMoving\n * }\n */\n iterator<TKey, TRelated extends any[]>(\n keyComponent: ComponentClass<TKey>,\n ...components: ComponentClasses<TRelated>\n ) {\n return new ComponentIterator(new ComponentQuery(this, keyComponent, ...components));\n }\n\n /**\n * Creates a query that can be stored and reused\n * @param keyComponent - The primary component class used to filter entities\n * @param components - Additional component classes to retrieve for each entity\n * @returns A {@link ComponentQuery} instance\n * @throws {@link ComponentNotRegistered} when any of specified component(s) are not registered\n * @example\n * const query = ecs.query(Player, Position)\n * \n * // get the first entry\n * const [playerId, player, position] = query.firstEntry() ?? []\n * \n * // get the first key\n * const [playerId, position] = query.firstKey() ?? []\n * \n * // get the first value\n * const [player, position] = query.firstValue() ?? []\n * \n * // iterate\n * for (const [playerId, player, position] of query) {\n * \n * }\n */\n query<TKey, TRelated extends any[]>(\n keyComponent: ComponentClass<TKey>,\n ...components: ComponentClasses<TRelated>\n ) {\n return new ComponentQuery(this, keyComponent, ...components)\n }\n\n /**\n * Prints all component maps in a tabular format to the console.\n * \n * Additional generated columns are:\n * \n * 'Entity.Key' is the entity id\n * \n * 'Entity.Type' is the component name\n * \n * @param components - Optional filter. Only includes specific component class names.\n * @param properties - Optional filter. Specifies which property columns to display in the tables.\n * @returns The {@link EntityMap} instance for chaining\n */\n printTable(components?: string[]): this;\n printTable(components?: string[], properties?: string[]): this;\n printTable(components: string[] = [], properties: string[] = []) {\n this.components.forEach((map, key) => {\n if (components.length === 0 || components.includes(key)) {\n console.table(map.toTable(), properties);\n }\n });\n return this;\n }\n\n /**\n * Prints all component data for the specified entity id in a tabular format to the console\n * \n * Additional generated columns are:\n * \n * 'Entity.Type' is the component name\n * \n * @param entityId - The entity id to print\n * @param properties - Optional filter. Specifies which property columns to display in the tables.\n * @returns The {@link EntityMap} instance for chaining\n */\n printEntity(entityId: number, properties: string[] = []) {\n for (const map of this.components.values()) {\n if (map.has(entityId) === false) continue;\n\n const data = map.get(entityId);\n const columns = {\n 'entity.type': data.constructor.name,\n ...data\n };\n console.table({ [entityId]: columns }, properties);\n }\n\n return this;\n }\n\n /**\n * Parses the JSON and returns an {@link EntityMap} object\n * @param json - The JSON string representing an {@link EntityMap}\n * @returns A restored {@link EntityMap} instance\n * @example\n * const json = JSON.stringify(ecs);\n * const restoredMap = EntityMap.parse(json);\n */\n static parse(json: string): EntityMap {\n const restored = JSON.parse(json, function (key: string, value: any) {\n if (value.hasOwnProperty('components')) {\n Reflect.setPrototypeOf(value, EntityMap.prototype);\n return value;\n }\n if (value.hasOwnProperty('nextId')) return value;\n if (value.hasOwnProperty('freeIds')) return value;\n if (value.hasOwnProperty(ComponentMapKey)) return new ComponentMap(value.iterable);\n if (value.hasOwnProperty(ComponentClassesMapKey)) return new ComponentClassesMap(value.iterable);\n return this[key];\n });\n return restored;\n }\n\n /**\n * A tracing method used for debugging.\n * Intercepts all functions specified and logs each call to the console.\n * @param funcFilter - A list of function names you want to intercept. If no function names are specified then will log all functions called.\n * @returns A new proxy of an {@link EntityMap} with tracing enabled\n * @example\n * // trace all method calls\n * const ecs = EntityMap.createWithTracing();\n * \n * // trace only 'set' and 'remove' calls\n * const ecs = EntityMap.createWithTracing(['set', 'remove']);\n */\n static createWithTracing(funcFilter: string[] = []) {\n const traceHandler = {\n get(target: any, propKey: string) {\n const targetValue = target[propKey]\n\n if (typeof targetValue === 'function' && (funcFilter.length === 0 || funcFilter.includes(propKey))) {\n return function (this: any, ...args: any[]) {\n console.groupCollapsed('ecs trace', propKey, args);\n console.trace();\n console.groupEnd();\n return targetValue.apply(this, args);\n }\n }\n\n return targetValue;\n }\n }\n\n return new Proxy(new EntityMap(), traceHandler)\n }\n\n}", "/*\n ecsjs is an entity component system library for JavaScript\n Copyright (C) 2014 Peter Flannery\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU Affero General Public License as\n published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Affero General Public License for more details.\n\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**\n * An entity component system library for JavaScript\n * @showCategories\n * @module ecsjs\n */\nimport { EntityMap } from './entity-map.js'\nexport { ComponentClassesMap } from './component-classes.js'\nexport { ComponentMap } from './component-map.js'\nexport { EntityMap } from './entity-map.js'\nexport type { ComponentNotRegistered, ComponentTypeKeyMissing } from './errors.js'\nexport { ComponentIterator } from './iterator.js'\nexport { ComponentQuery } from './query.js'\nexport type { ComponentClass, IComponentIterator } from './types.js'\n\n/**\n * A global default instance of an {@link EntityMap}\n * \n * Provides a convenient way to use the ECS without managing multiple maps.\n * \n * See the [cheat sheet](https://gitlab.com/ecsjs/ecs/-/blob/master/docs/cheat-sheet.md) for more examples.\n * @category Constants\n * @example\n * // register component(s)\n * ecs.register(Player, Position)\n * \n * // create an entity and set its components\n * const [player, position] = ecs.set(ecs.getNextId(), new Player(), new Position(10, 40))\n */\nexport const ecs = new EntityMap()\n\nif (typeof window !== 'undefined') {\n // @ts-ignore: exports to window\n window.ecs = ecs\n} else if (typeof module !== 'undefined' && module !== null) {\n // exports to nodejs\n module.exports = { ecs };\n}"],
5
+ "mappings": "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",
6
+ "names": ["ComponentMapKey", "ComponentClassesMapKey", "hashString", "str", "hash", "i", "char", "ComponentClassesMap", "entries", "i", "entry", "componentClass", "index", "map", "hashString", "hash", "existingIndex", "hashId", "lastIndex", "lastHash", "callback", "instances", "hashes", "value", "ComponentClassesMapKey", "ComponentMap", "entries", "i", "entry", "entityId", "component", "index", "lastIdx", "lastEntity", "callback", "entities", "instances", "result", "ComponentMapKey", "table", "key", "value", "entity", "meta", "properties", "ComponentTypeKeyMissing", "ComponentNotRegistered", "componentName", "ComponentAlreadyRegistered", "ComponentIterator", "query", "entry", "value", "done", "entityId", "i", "map", "ComponentQuery", "ecs", "components", "i", "entityId", "entryValue", "results", "keyValue", "keyMap", "componentMaps", "keysIterator", "key", "value", "map", "ComponentIterator", "EntityMap", "_EntityMap", "ComponentClassesMap", "componentClasses", "componentClass", "componentName", "ComponentTypeKeyMissing", "hashString", "ComponentAlreadyRegistered", "ComponentMap", "component", "map", "ComponentNotRegistered", "keyComponent", "entityId", "entities", "x", "components", "keyValue", "v", "index", "componentData", "entityMap", "entityIds", "deletedCount", "found", "reclaimedId", "ComponentIterator", "ComponentQuery", "properties", "key", "data", "columns", "json", "value", "ComponentMapKey", "ComponentClassesMapKey", "funcFilter", "traceHandler", "target", "propKey", "targetValue", "args", "ecs", "EntityMap"]
7
7
  }
@@ -28,6 +28,12 @@ export declare class ComponentClassesMap {
28
28
  * @returns The {@link ComponentMap} for the specified class, or undefined if not found
29
29
  */
30
30
  get<T>(componentClass: ComponentClass<T>): ComponentMap<T> | undefined;
31
+ /**
32
+ * Checks if a component class map is already registered
33
+ * @param componentClass - The component class to check
34
+ * @returns True if the map is registered, otherwise false
35
+ */
36
+ has<T>(componentClass: ComponentClass<T>): boolean;
31
37
  /**
32
38
  * Registers a component map. Automatically generates a hashId from the class name if missing
33
39
  * @param componentClass - The component class to register
@@ -1 +1 @@
1
- {"version":3,"file":"component-classes.d.ts","sourceRoot":"","sources":["../../../src/component-classes.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACd,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,qBAAa,mBAAmB;IAE9B,OAAO,CAAC,YAAY,CAAiD;IACrE,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,IAAI,CAA2B;IAEvC;;;OAGG;gBACS,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI;IAS9E;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;;;OAIG;IACH,GAAG,CAAC,CAAC,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS;IAKtE;;;;;OAKG;IACH,GAAG,CAAC,CAAC,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IASrE;;;;;OAKG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,IAAI;IAcvD;;;;OAIG;IACH,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,GAAG,CAAC,GAAG,OAAO;IAsBpD;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;IAMlE;;;OAGG;IACH,MAAM,IAAI,aAAa,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAE1C;;;OAGG;IACH,OAAO,IAAI,QAAQ,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;IAmBhD;;;OAGG;IACH,MAAM;;;;CAIP"}
1
+ {"version":3,"file":"component-classes.d.ts","sourceRoot":"","sources":["../../../src/component-classes.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACd,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,qBAAa,mBAAmB;IAE9B,OAAO,CAAC,YAAY,CAAiD;IACrE,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,IAAI,CAA2B;IAEvC;;;OAGG;gBACS,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI;IAS9E;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;;;OAIG;IACH,GAAG,CAAC,CAAC,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS;IAKtE;;;;OAIG;IACH,GAAG,CAAC,CAAC,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,OAAO;IAKlD;;;;;OAKG;IACH,GAAG,CAAC,CAAC,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IASrE;;;;;OAKG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,IAAI;IAcvD;;;;OAIG;IACH,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,GAAG,CAAC,GAAG,OAAO;IAsBpD;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;IAMlE;;;OAGG;IACH,MAAM,IAAI,aAAa,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAE1C;;;OAGG;IACH,OAAO,IAAI,QAAQ,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;IAmBhD;;;OAGG;IACH,MAAM;;;;CAIP"}
@@ -18,7 +18,8 @@ export declare class EntityMap {
18
18
  * Registers component classes with the {@link EntityMap}
19
19
  * @param componentClasses - One or more component classes to register
20
20
  * @returns The {@link EntityMap} instance for chaining
21
- * @throws {@link ComponentTypeKeyMissing} when the specified component type is missing a 'name' parameter
21
+ * @throws {@link ComponentTypeKeyMissing} when the specified component type is missing a 'name' parameter (e.g. anonymous classes)
22
+ * @throws {@link ComponentAlreadyRegistered} when the specified component is already registered
22
23
  * @example
23
24
  * // component class
24
25
  * class MyComponent {
@@ -172,6 +173,7 @@ export declare class EntityMap {
172
173
  * @param entityId - The entity id to check for the component
173
174
  * @param component - The component class to check
174
175
  * @returns True if the entity has the component, otherwise false
176
+ * @throws {@link ComponentNotRegistered} when the specified component is not registered
175
177
  * @example
176
178
  * const exists = ecs.has(entityId, Position)
177
179
  */
@@ -181,6 +183,7 @@ export declare class EntityMap {
181
183
  * @param entityId - The entity id to check
182
184
  * @param components - One or more component classes to check for
183
185
  * @returns True if the entity has ALL specified components, otherwise false
186
+ * @throws {@link ComponentNotRegistered} when any of the specified component(s) are not registered
184
187
  * @example
185
188
  * const hasAll = ecs.hasAll(entityId, Position, Velocity)
186
189
  */
@@ -190,6 +193,7 @@ export declare class EntityMap {
190
193
  * @param entityId - The entity id to check
191
194
  * @param components - One or more component classes to check for
192
195
  * @returns True if the entity has ANY of the specified components, otherwise false
196
+ * @throws {@link ComponentNotRegistered} when any of the specified component(s) are not registered
193
197
  * @example
194
198
  * const hasAny = ecs.hasAny(entityId, Position, Velocity)
195
199
  */
@@ -1 +1 @@
1
- {"version":3,"file":"entity-map.d.ts","sourceRoot":"","sources":["../../../src/entity-map.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity-map.d.ts","sourceRoot":"","sources":["../../../src/entity-map.ts"],"names":[],"mappings":"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"}
@@ -14,4 +14,13 @@ export declare class ComponentNotRegistered extends Error {
14
14
  componentName: string;
15
15
  constructor(componentName: string);
16
16
  }
17
+ /**
18
+ * Thrown when trying to register a component that has already been registered
19
+ * @param componentName - The name of the already registered component
20
+ * @category Errors
21
+ */
22
+ export declare class ComponentAlreadyRegistered extends Error {
23
+ componentName: string;
24
+ constructor(componentName: string);
25
+ }
17
26
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/errors.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;;CAIjD;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC5B,aAAa,EAAE,MAAM;gBAArB,aAAa,EAAE,MAAM;CAGzC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/errors.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;;CAIjD;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC5B,aAAa,EAAE,MAAM;gBAArB,aAAa,EAAE,MAAM;CAGzC;AAED;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IAChC,aAAa,EAAE,MAAM;gBAArB,aAAa,EAAE,MAAM;CAGzC"}
@@ -17,6 +17,8 @@
17
17
  > **IMPORTANT**
18
18
  >
19
19
  > - You must register your components before passing them to other ecsjs functions<br> otherwise a [ComponentNotRegistered](https://ecsjs.gitlab.io/ecs/classes/ComponentNotRegistered.html) error will be thrown.
20
+ > - [ecs.register](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#register) will throw [ComponentTypeKeyMissing](https://ecsjs.gitlab.io/ecs/classes/ComponentTypeKeyMissing.html) if a class name is missing (e.g. anonymous classes).
21
+ > - [ecs.register](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#register) will throw [ComponentAlreadyRegistered](https://ecsjs.gitlab.io/ecs/classes/ComponentAlreadyRegistered.html) if a component is already registered.
20
22
  > - Component data is always returned by reference for all ecsjs functions.<br>
21
23
  > This allows instant mutation of the component data
22
24
 
@@ -0,0 +1,52 @@
1
+ # Developer Documentation
2
+
3
+ This document provides information for developers who want to contribute to the `ecsjs` project.
4
+
5
+ ## Development Tasks
6
+
7
+ The project uses a task runner to manage common development operations. These tasks are defined in `tasks.yml`.
8
+
9
+ ## Getting Started
10
+
11
+ 1. **Install dependencies**:
12
+ ```bash
13
+ npm install
14
+ ```
15
+
16
+ 2. **Run tests**:
17
+ ```bash
18
+ npx task test
19
+ ```
20
+ *(Note: `npm test` typically maps to `task test` in this project's configuration)*
21
+
22
+ 3. **Build the project**:
23
+ ```bash
24
+ npx task bundle
25
+ ```
26
+
27
+
28
+ ### Build Tasks
29
+
30
+ - **`task compile`**: Cleans the output directories and compiles the TypeScript source code.
31
+ - **`task compile:types`**: Compiles the TypeScript type definitions.
32
+ - **`task compile:docs`**: Generates reference documentation using TypeDoc.
33
+ - **`task bundle`**: Compiles types and creates a bundled distribution using esbuild.
34
+ - **`task bundle:dev`**: Creates a development un-minified bundle.
35
+ - **`task clean`**: Removes all generated directories (`public`, `out`, `dist`, `coverage`, etc.).
36
+
37
+ ### Testing and Coverage
38
+
39
+ - **`task test`**: Compiles the project and runs the unit tests.
40
+ - **`task coverage`**: Runs tests with code coverage reporting.
41
+
42
+ ## Project Structure
43
+
44
+ - `src/`: Core library source code.
45
+ - `test/`: Unit tests and test runner.
46
+ - `build/`: Build configuration and scripts.
47
+ - `docs/`: Documentation.
48
+
49
+ ## See Also
50
+
51
+ - [Cheat Sheet](./cheat-sheet.md)
52
+ - [API Reference Documentation](https://ecsjs.gitlab.io/ecs)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "title": "ECS JS",
3
3
  "name": "ecsjs",
4
- "version": "1.4.0-beta.5",
4
+ "version": "1.4.0-beta.6",
5
5
  "description": "An entity component system library for JavaScript",
6
6
  "author": "2013+ pflannery (https://gitlab.com/pflannery)",
7
7
  "license": "GNU GPL v3",
@@ -44,7 +44,7 @@
44
44
  "esbuild": "0.27.3",
45
45
  "js-build-tasks": "1.0.0-rc.18",
46
46
  "mocha": "11.7.5",
47
- "mocha-ui-esm": "1.0.0-beta.14",
47
+ "mocha-ui-esm": "1.0.0-beta.15",
48
48
  "ts-mockito": "2.6.1",
49
49
  "rimraf": "6.1.3",
50
50
  "source-map-support": "0.5.21",
@@ -73,6 +73,16 @@ export class ComponentClassesMap {
73
73
  return index !== undefined ? this.maps[index] : undefined;
74
74
  }
75
75
 
76
+ /**
77
+ * Checks if a component class map is already registered
78
+ * @param componentClass - The component class to check
79
+ * @returns True if the map is registered, otherwise false
80
+ */
81
+ has<T>(componentClass: ComponentClass<T>): boolean {
82
+ if (componentClass.hashId === undefined) return false;
83
+ return this.hashIdToIndex.has(componentClass.hashId);
84
+ }
85
+
76
86
  /**
77
87
  * Registers a component map. Automatically generates a hashId from the class name if missing
78
88
  * @param componentClass - The component class to register
package/src/entity-map.ts CHANGED
@@ -17,7 +17,11 @@
17
17
  */
18
18
  import { ComponentClassesMap } from './component-classes.js';
19
19
  import { ComponentMap } from './component-map.js';
20
- import { ComponentNotRegistered, ComponentTypeKeyMissing } from './errors.js';
20
+ import {
21
+ ComponentAlreadyRegistered,
22
+ ComponentNotRegistered,
23
+ ComponentTypeKeyMissing
24
+ } from './errors.js';
21
25
  import { ComponentIterator } from './iterator.js';
22
26
  import { ComponentQuery } from './query.js';
23
27
  import {
@@ -28,6 +32,7 @@ import {
28
32
  type ComponentClasses,
29
33
  type SingleOrArray
30
34
  } from './types.js';
35
+ import { hashString } from './utils.js';
31
36
 
32
37
  /**
33
38
  * Class for storing entities and their relationships.
@@ -47,7 +52,8 @@ export class EntityMap {
47
52
  * Registers component classes with the {@link EntityMap}
48
53
  * @param componentClasses - One or more component classes to register
49
54
  * @returns The {@link EntityMap} instance for chaining
50
- * @throws {@link ComponentTypeKeyMissing} when the specified component type is missing a 'name' parameter
55
+ * @throws {@link ComponentTypeKeyMissing} when the specified component type is missing a 'name' parameter (e.g. anonymous classes)
56
+ * @throws {@link ComponentAlreadyRegistered} when the specified component is already registered
51
57
  * @example
52
58
  * // component class
53
59
  * class MyComponent {
@@ -63,10 +69,11 @@ export class EntityMap {
63
69
  register<TComponentClasses extends ComponentClass<any>[]>(...componentClasses: TComponentClasses) {
64
70
  for (const componentClass of componentClasses) {
65
71
  const componentName = componentClass.name;
66
- if (componentName === undefined) throw new ComponentTypeKeyMissing();
72
+ if (componentName === undefined || componentName === '') throw new ComponentTypeKeyMissing();
73
+ if (componentClass.hashId === undefined) componentClass.hashId = hashString(componentName);
74
+ if (this.components.has(componentClass)) throw new ComponentAlreadyRegistered(componentName);
67
75
 
68
76
  // create the component map
69
- // const componentDataMap: ComponentMap<any> = new ComponentMap();
70
77
  this.components.set(componentClass, new ComponentMap());
71
78
  }
72
79
 
@@ -287,13 +294,14 @@ export class EntityMap {
287
294
  * @param entityId - The entity id to check for the component
288
295
  * @param component - The component class to check
289
296
  * @returns True if the entity has the component, otherwise false
297
+ * @throws {@link ComponentNotRegistered} when the specified component is not registered
290
298
  * @example
291
299
  * const exists = ecs.has(entityId, Position)
292
300
  */
293
301
  has<T>(entityId: number, component: ComponentClass<T>): boolean {
294
302
  // get the component map
295
303
  const map = this.components.get(component);
296
- if (map === undefined) return false
304
+ if (map === undefined) throw new ComponentNotRegistered(component.name)
297
305
 
298
306
  return map.has(entityId);
299
307
  }
@@ -303,6 +311,7 @@ export class EntityMap {
303
311
  * @param entityId - The entity id to check
304
312
  * @param components - One or more component classes to check for
305
313
  * @returns True if the entity has ALL specified components, otherwise false
314
+ * @throws {@link ComponentNotRegistered} when any of the specified component(s) are not registered
306
315
  * @example
307
316
  * const hasAll = ecs.hasAll(entityId, Position, Velocity)
308
317
  */
@@ -310,7 +319,7 @@ export class EntityMap {
310
319
  for (let index = 0; index < components.length; index++) {
311
320
  const component = components[index];
312
321
  const map = this.components.get(component);
313
- if (map === undefined) return false;
322
+ if (map === undefined) throw new ComponentNotRegistered(component.name)
314
323
  if (map.has(entityId) === false) return false;
315
324
  }
316
325
  return true
@@ -321,6 +330,7 @@ export class EntityMap {
321
330
  * @param entityId - The entity id to check
322
331
  * @param components - One or more component classes to check for
323
332
  * @returns True if the entity has ANY of the specified components, otherwise false
333
+ * @throws {@link ComponentNotRegistered} when any of the specified component(s) are not registered
324
334
  * @example
325
335
  * const hasAny = ecs.hasAny(entityId, Position, Velocity)
326
336
  */
@@ -328,7 +338,7 @@ export class EntityMap {
328
338
  for (let index = 0; index < components.length; index++) {
329
339
  const component = components[index];
330
340
  const map = this.components.get(component);
331
- if (map === undefined) continue;
341
+ if (map === undefined) throw new ComponentNotRegistered(component.name)
332
342
  if (map.has(entityId)) return true;
333
343
  }
334
344
  return false
package/src/errors.ts CHANGED
@@ -34,4 +34,15 @@ export class ComponentNotRegistered extends Error {
34
34
  constructor(public componentName: string) {
35
35
  super(`Component map does not exist for '${componentName}'`);
36
36
  }
37
+ }
38
+
39
+ /**
40
+ * Thrown when trying to register a component that has already been registered
41
+ * @param componentName - The name of the already registered component
42
+ * @category Errors
43
+ */
44
+ export class ComponentAlreadyRegistered extends Error {
45
+ constructor(public componentName: string) {
46
+ super(`Component '${componentName}' is already registered`);
47
+ }
37
48
  }