@vworlds/vecs 1.0.10 → 1.0.12
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/.husky/pre-commit +1 -0
- package/README.md +299 -228
- package/dist/command.d.ts +1 -46
- package/dist/component.d.ts +51 -59
- package/dist/component.js +31 -25
- package/dist/component.js.map +1 -1
- package/dist/dsl.d.ts +34 -26
- package/dist/dsl.js +46 -20
- package/dist/dsl.js.map +1 -1
- package/dist/entity.d.ts +96 -106
- package/dist/entity.js +261 -190
- package/dist/entity.js.map +1 -1
- package/dist/filter.d.ts +31 -23
- package/dist/filter.js +24 -17
- package/dist/filter.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/package.json +3 -1
- package/dist/phase.d.ts +12 -30
- package/dist/phase.js +11 -10
- package/dist/phase.js.map +1 -1
- package/dist/query.d.ts +107 -144
- package/dist/query.js +200 -169
- package/dist/query.js.map +1 -1
- package/dist/system.d.ts +170 -86
- package/dist/system.js +253 -114
- package/dist/system.js.map +1 -1
- package/dist/timer.d.ts +50 -0
- package/dist/timer.js +154 -0
- package/dist/timer.js.map +1 -0
- package/dist/util/array_map.d.ts +4 -55
- package/dist/util/array_map.js +35 -37
- package/dist/util/array_map.js.map +1 -1
- package/dist/util/bitset.d.ts +40 -50
- package/dist/util/bitset.js +76 -62
- package/dist/util/bitset.js.map +1 -1
- package/dist/util/events.d.ts +14 -18
- package/dist/util/events.js +24 -3
- package/dist/util/events.js.map +1 -1
- package/dist/util/ordered_set.d.ts +1 -17
- package/dist/util/ordered_set.js +74 -25
- package/dist/util/ordered_set.js.map +1 -1
- package/dist/world.d.ts +230 -218
- package/dist/world.js +422 -327
- package/dist/world.js.map +1 -1
- package/eslint-rules/internal-underscore.js +60 -0
- package/eslint.config.js +5 -0
- package/package.json +3 -1
package/dist/command.d.ts
CHANGED
|
@@ -1,46 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Entity } from "./entity.js";
|
|
3
|
-
export declare const enum CommandKind {
|
|
4
|
-
CreateEntity = 0,
|
|
5
|
-
Set = 1,
|
|
6
|
-
Modified = 2,
|
|
7
|
-
Remove = 3,
|
|
8
|
-
Destroy = 4,
|
|
9
|
-
SetParent = 5
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Command kinds emitted by {@link Entity} and routed by {@link World}.
|
|
13
|
-
*
|
|
14
|
-
* Commands are produced by `entity.add` / `entity.set` / `entity.remove` /
|
|
15
|
-
* `entity.destroy` (and `Component.modified`). In deferred mode they are
|
|
16
|
-
* pushed onto the world's command queue and processed at well-defined
|
|
17
|
-
* boundaries (after each system run, on `flush()`, on the next `runPhase`,
|
|
18
|
-
* etc.). Outside deferred mode they execute inline.
|
|
19
|
-
*
|
|
20
|
-
* @internal
|
|
21
|
-
*/
|
|
22
|
-
export type Command = {
|
|
23
|
-
kind: CommandKind.CreateEntity;
|
|
24
|
-
entity: Entity;
|
|
25
|
-
} | {
|
|
26
|
-
kind: CommandKind.Set;
|
|
27
|
-
entity: Entity;
|
|
28
|
-
type: number;
|
|
29
|
-
/** Properties to assign. `undefined` for `entity.add(C)` (ensure-exists, no data). */
|
|
30
|
-
props: Partial<Component> | undefined;
|
|
31
|
-
} | {
|
|
32
|
-
kind: CommandKind.Modified;
|
|
33
|
-
entity: Entity;
|
|
34
|
-
type: number;
|
|
35
|
-
} | {
|
|
36
|
-
kind: CommandKind.Remove;
|
|
37
|
-
entity: Entity;
|
|
38
|
-
type: number;
|
|
39
|
-
} | {
|
|
40
|
-
kind: CommandKind.Destroy;
|
|
41
|
-
entity: Entity;
|
|
42
|
-
} | {
|
|
43
|
-
kind: CommandKind.SetParent;
|
|
44
|
-
entity: Entity;
|
|
45
|
-
parent: Entity | undefined;
|
|
46
|
-
};
|
|
1
|
+
export {};
|
package/dist/component.d.ts
CHANGED
|
@@ -1,63 +1,68 @@
|
|
|
1
|
-
import { BitPtr
|
|
1
|
+
import { BitPtr } from "./util/bitset.js";
|
|
2
2
|
import type { Entity } from "./entity.js";
|
|
3
|
-
import { type World } from "./world.js";
|
|
4
3
|
/**
|
|
5
|
-
* Lifecycle hook for a component
|
|
4
|
+
* Lifecycle hook for a registered component class. Obtained via
|
|
5
|
+
* {@link World.hook}.
|
|
6
6
|
*
|
|
7
|
-
* Hooks
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Hooks are a lightweight alternative to building a {@link System} when all
|
|
8
|
+
* you need is a callback on add / remove / set for a single component type.
|
|
9
|
+
* The same `Hook` is returned on every call to `world.hook(C)`, so registration
|
|
10
|
+
* methods chain:
|
|
10
11
|
*
|
|
11
12
|
* ```ts
|
|
12
13
|
* world.hook(Sprite)
|
|
13
|
-
* .onAdd(c
|
|
14
|
+
* .onAdd(c => initSprite(c))
|
|
14
15
|
* .onRemove(c => destroySprite(c))
|
|
15
|
-
* .onSet(c
|
|
16
|
+
* .onSet(c => syncSprite(c));
|
|
16
17
|
* ```
|
|
17
18
|
*
|
|
18
|
-
* Callbacks
|
|
19
|
-
*
|
|
19
|
+
* Callbacks fire synchronously when the corresponding entity command is
|
|
20
|
+
* applied: inline outside deferred mode, or while the world drains its command
|
|
21
|
+
* queue inside a system / `forEach` / `defer` block.
|
|
20
22
|
*
|
|
21
|
-
* @typeParam C -
|
|
23
|
+
* @typeParam C - Component subclass this hook is bound to.
|
|
22
24
|
*/
|
|
23
25
|
export interface Hook<C extends Component = Component> {
|
|
24
26
|
/**
|
|
25
|
-
* Register a
|
|
26
|
-
* an entity.
|
|
27
|
+
* Register a handler invoked when a component of this type is first attached
|
|
28
|
+
* to an entity (`entity.add(C)` or `entity.set(C, ...)` on an entity that
|
|
29
|
+
* does not yet have the component).
|
|
27
30
|
*
|
|
28
|
-
* @param handler - Receives the
|
|
29
|
-
* @returns
|
|
31
|
+
* @param handler - Receives the freshly created component instance.
|
|
32
|
+
* @returns This hook, for chaining.
|
|
30
33
|
*/
|
|
31
34
|
onAdd(handler: (c: C) => void): Hook<C>;
|
|
32
35
|
/**
|
|
33
|
-
* Register a
|
|
34
|
-
*
|
|
36
|
+
* Register a handler invoked when a component of this type is removed from
|
|
37
|
+
* an entity (explicit `entity.remove(C)` or implicit removal during
|
|
38
|
+
* `entity.destroy()`).
|
|
35
39
|
*
|
|
36
|
-
* @param handler - Receives the component instance
|
|
37
|
-
* @returns
|
|
40
|
+
* @param handler - Receives the component instance that was removed.
|
|
41
|
+
* @returns This hook, for chaining.
|
|
38
42
|
*/
|
|
39
43
|
onRemove(handler: (c: C) => void): Hook<C>;
|
|
40
44
|
/**
|
|
41
|
-
* Register a
|
|
42
|
-
*
|
|
45
|
+
* Register a handler invoked when a component's data has been marked as
|
|
46
|
+
* changed (`component.modified()` or `entity.modified(c)`), and when
|
|
47
|
+
* `entity.set(C, props)` is called on an entity that already has the
|
|
48
|
+
* component.
|
|
43
49
|
*
|
|
44
|
-
* @param handler - Receives the component instance
|
|
45
|
-
* @returns
|
|
50
|
+
* @param handler - Receives the component instance whose data changed.
|
|
51
|
+
* @returns This hook, for chaining.
|
|
46
52
|
*/
|
|
47
53
|
onSet(handler: (c: C) => void): Hook<C>;
|
|
48
54
|
}
|
|
49
55
|
/**
|
|
50
|
-
*
|
|
56
|
+
* Bookkeeping record produced for each component class registered via
|
|
57
|
+
* {@link World.registerComponent}.
|
|
51
58
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* `ComponentMeta` also implements {@link Hook}, so you can attach lifecycle
|
|
57
|
-
* callbacks directly on the meta object (as `World.hook()` returns it).
|
|
59
|
+
* Holds the constructor, numeric type id, display name, and pre-computed
|
|
60
|
+
* {@link BitPtr} used by archetype checks. Implements {@link Hook}, so the
|
|
61
|
+
* lifecycle handlers attached via `world.hook(C)` are stored directly on the
|
|
62
|
+
* meta object.
|
|
58
63
|
*/
|
|
59
64
|
export declare class ComponentMeta implements Hook<Component> {
|
|
60
|
-
/** The component class constructor. */
|
|
65
|
+
/** The component class constructor this meta represents. */
|
|
61
66
|
readonly Class: typeof Component;
|
|
62
67
|
/** Numeric type id assigned at registration time. */
|
|
63
68
|
readonly type: number;
|
|
@@ -65,15 +70,10 @@ export declare class ComponentMeta implements Hook<Component> {
|
|
|
65
70
|
readonly componentName: string;
|
|
66
71
|
/** Pre-computed bit-pointer into the entity archetype {@link Bitset}. */
|
|
67
72
|
readonly bitPtr: BitPtr;
|
|
68
|
-
/** @internal */
|
|
69
|
-
_onAddHandler: ((c: Component) => void) | undefined;
|
|
70
|
-
/** @internal */
|
|
71
|
-
_onRemoveHandler: ((c: Component) => void) | undefined;
|
|
72
|
-
/** @internal */
|
|
73
|
-
_onSetHandler: ((c: Component) => void) | undefined;
|
|
74
73
|
/**
|
|
75
|
-
* Type ids of components that cannot coexist with this one on the same
|
|
76
|
-
* Set via {@link World.setExclusiveComponents}
|
|
74
|
+
* Type ids of components that cannot coexist with this one on the same
|
|
75
|
+
* entity. Set via {@link World.setExclusiveComponents}; `undefined` means
|
|
76
|
+
* no restriction.
|
|
77
77
|
*/
|
|
78
78
|
exclusive: number[] | undefined;
|
|
79
79
|
constructor(Class: typeof Component, type: number, componentName: string);
|
|
@@ -86,12 +86,12 @@ export declare class ComponentMeta implements Hook<Component> {
|
|
|
86
86
|
}
|
|
87
87
|
/** A component class constructor or its numeric type id. */
|
|
88
88
|
export type ComponentClassOrType = number | typeof Component;
|
|
89
|
-
/** An array of component class constructors or type ids. */
|
|
90
|
-
export type ComponentClassArray = ComponentClassOrType[];
|
|
91
89
|
/**
|
|
92
90
|
* Base class for all ECS components.
|
|
93
91
|
*
|
|
94
|
-
*
|
|
92
|
+
* Subclass `Component` to declare data that can be attached to an
|
|
93
|
+
* {@link Entity}. Instances are constructed by the world when
|
|
94
|
+
* {@link Entity.add} or {@link Entity.set} runs — never instantiate manually.
|
|
95
95
|
*
|
|
96
96
|
* ```ts
|
|
97
97
|
* class Position extends Component {
|
|
@@ -103,20 +103,18 @@ export type ComponentClassArray = ComponentClassOrType[];
|
|
|
103
103
|
* entity.set(Position, { x: 100 });
|
|
104
104
|
* ```
|
|
105
105
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
106
|
+
* Each instance is bound to a single entity via {@link entity}; that link is
|
|
107
|
+
* permanent for the component's lifetime.
|
|
108
108
|
*/
|
|
109
109
|
export declare class Component {
|
|
110
110
|
/** The entity this component belongs to. */
|
|
111
111
|
readonly entity: Entity;
|
|
112
|
-
/** Registration metadata (type id, name, bit-pointer). */
|
|
112
|
+
/** Registration metadata (type id, display name, bit-pointer). */
|
|
113
113
|
readonly meta: ComponentMeta;
|
|
114
|
-
/** @internal */
|
|
115
|
-
_dirty: boolean;
|
|
116
114
|
constructor(
|
|
117
115
|
/** The entity this component belongs to. */
|
|
118
116
|
entity: Entity,
|
|
119
|
-
/** Registration metadata (type id, name, bit-pointer). */
|
|
117
|
+
/** Registration metadata (type id, display name, bit-pointer). */
|
|
120
118
|
meta: ComponentMeta);
|
|
121
119
|
/** Numeric type id — shorthand for `this.meta.type`. */
|
|
122
120
|
get type(): number;
|
|
@@ -125,18 +123,12 @@ export declare class Component {
|
|
|
125
123
|
/**
|
|
126
124
|
* Notify the world that this component's data has changed.
|
|
127
125
|
*
|
|
128
|
-
* Queues
|
|
129
|
-
* that
|
|
130
|
-
*
|
|
126
|
+
* Queues a modified event that fires `update` callbacks on every system /
|
|
127
|
+
* query that watches this component type, plus the component's `onSet`
|
|
128
|
+
* hook. Repeated calls before the world drains its queue are coalesced
|
|
129
|
+
* into one delivery.
|
|
131
130
|
*/
|
|
132
131
|
modified(): void;
|
|
133
|
-
/** Returns the component's registered name
|
|
132
|
+
/** Returns the component's registered display name (e.g. `"Position"`). */
|
|
134
133
|
toString(): string;
|
|
135
134
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Compute a {@link Bitset} that has a bit set for every component class or
|
|
138
|
-
* type id in `classes`.
|
|
139
|
-
*
|
|
140
|
-
* @internal Used internally to build archetype masks for system queries.
|
|
141
|
-
*/
|
|
142
|
-
export declare function calculateComponentBitmask(classes: ComponentClassArray, world: World): Bitset;
|
package/dist/component.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { BitPtr, Bitset } from "./util/bitset.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Bookkeeping record produced for each component class registered via
|
|
4
|
+
* {@link World.registerComponent}.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `ComponentMeta` also implements {@link Hook}, so you can attach lifecycle
|
|
10
|
-
* callbacks directly on the meta object (as `World.hook()` returns it).
|
|
6
|
+
* Holds the constructor, numeric type id, display name, and pre-computed
|
|
7
|
+
* {@link BitPtr} used by archetype checks. Implements {@link Hook}, so the
|
|
8
|
+
* lifecycle handlers attached via `world.hook(C)` are stored directly on the
|
|
9
|
+
* meta object.
|
|
11
10
|
*/
|
|
12
11
|
export class ComponentMeta {
|
|
13
12
|
constructor(Class, type, componentName) {
|
|
14
13
|
/**
|
|
15
|
-
* Type ids of components that cannot coexist with this one on the same
|
|
16
|
-
* Set via {@link World.setExclusiveComponents}
|
|
14
|
+
* Type ids of components that cannot coexist with this one on the same
|
|
15
|
+
* entity. Set via {@link World.setExclusiveComponents}; `undefined` means
|
|
16
|
+
* no restriction.
|
|
17
17
|
*/
|
|
18
18
|
this.exclusive = undefined;
|
|
19
19
|
this.Class = Class;
|
|
@@ -23,24 +23,26 @@ export class ComponentMeta {
|
|
|
23
23
|
}
|
|
24
24
|
/** @inheritdoc */
|
|
25
25
|
onAdd(handler) {
|
|
26
|
-
this.
|
|
26
|
+
(this._onAddHandlers ?? (this._onAddHandlers = [])).unshift(handler);
|
|
27
27
|
return this;
|
|
28
28
|
}
|
|
29
29
|
/** @inheritdoc */
|
|
30
30
|
onRemove(handler) {
|
|
31
|
-
this.
|
|
31
|
+
(this._onRemoveHandlers ?? (this._onRemoveHandlers = [])).unshift(handler);
|
|
32
32
|
return this;
|
|
33
33
|
}
|
|
34
34
|
/** @inheritdoc */
|
|
35
35
|
onSet(handler) {
|
|
36
|
-
this.
|
|
36
|
+
(this._onSetHandlers ?? (this._onSetHandlers = [])).unshift(handler);
|
|
37
37
|
return this;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* Base class for all ECS components.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
43
|
+
* Subclass `Component` to declare data that can be attached to an
|
|
44
|
+
* {@link Entity}. Instances are constructed by the world when
|
|
45
|
+
* {@link Entity.add} or {@link Entity.set} runs — never instantiate manually.
|
|
44
46
|
*
|
|
45
47
|
* ```ts
|
|
46
48
|
* class Position extends Component {
|
|
@@ -52,18 +54,18 @@ export class ComponentMeta {
|
|
|
52
54
|
* entity.set(Position, { x: 100 });
|
|
53
55
|
* ```
|
|
54
56
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
+
* Each instance is bound to a single entity via {@link entity}; that link is
|
|
58
|
+
* permanent for the component's lifetime.
|
|
57
59
|
*/
|
|
58
60
|
export class Component {
|
|
59
61
|
constructor(
|
|
60
62
|
/** The entity this component belongs to. */
|
|
61
63
|
entity,
|
|
62
|
-
/** Registration metadata (type id, name, bit-pointer). */
|
|
64
|
+
/** Registration metadata (type id, display name, bit-pointer). */
|
|
63
65
|
meta) {
|
|
64
66
|
this.entity = entity;
|
|
65
67
|
this.meta = meta;
|
|
66
|
-
/** @internal */
|
|
68
|
+
/** @internal Set by {@link Entity.modified} to coalesce repeated calls until the world routes the modified command. */
|
|
67
69
|
this._dirty = false;
|
|
68
70
|
}
|
|
69
71
|
/** Numeric type id — shorthand for `this.meta.type`. */
|
|
@@ -77,25 +79,29 @@ export class Component {
|
|
|
77
79
|
/**
|
|
78
80
|
* Notify the world that this component's data has changed.
|
|
79
81
|
*
|
|
80
|
-
* Queues
|
|
81
|
-
* that
|
|
82
|
-
*
|
|
82
|
+
* Queues a modified event that fires `update` callbacks on every system /
|
|
83
|
+
* query that watches this component type, plus the component's `onSet`
|
|
84
|
+
* hook. Repeated calls before the world drains its queue are coalesced
|
|
85
|
+
* into one delivery.
|
|
83
86
|
*/
|
|
84
87
|
modified() {
|
|
85
88
|
this.entity.modified(this);
|
|
86
89
|
}
|
|
87
|
-
/** Returns the component's registered name
|
|
90
|
+
/** Returns the component's registered display name (e.g. `"Position"`). */
|
|
88
91
|
toString() {
|
|
89
92
|
return this.meta.componentName;
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
/**
|
|
93
|
-
* Compute a {@link Bitset}
|
|
94
|
-
* type id in `classes`.
|
|
96
|
+
* Compute a {@link Bitset} with one bit set for every component class or
|
|
97
|
+
* numeric type id in `classes`.
|
|
98
|
+
*
|
|
99
|
+
* @internal Used to build archetype masks for `HAS` / `HAS_ONLY` queries.
|
|
95
100
|
*
|
|
96
|
-
* @
|
|
101
|
+
* @param classes - Component classes or type ids to include.
|
|
102
|
+
* @param world - World used to resolve classes to type ids.
|
|
97
103
|
*/
|
|
98
|
-
export function
|
|
104
|
+
export function _calculateComponentBitmask(classes, world) {
|
|
99
105
|
const bitmask = new Bitset();
|
|
100
106
|
classes.forEach((C) => {
|
|
101
107
|
bitmask.add(world.getComponentType(C));
|
package/dist/component.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA2DlD;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAa;IAwBxB,YAAY,KAAuB,EAAE,IAAY,EAAE,aAAqB;QAdxE;;;;WAIG;QACI,cAAS,GAAyB,SAAS,CAAC;QAUjD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,CAAC,IAAI,CAAC,cAAc,KAAnB,IAAI,CAAC,cAAc,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,QAAQ,CAAC,OAA+B;QAC7C,CAAC,IAAI,CAAC,iBAAiB,KAAtB,IAAI,CAAC,iBAAiB,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,CAAC,IAAI,CAAC,cAAc,KAAnB,IAAI,CAAC,cAAc,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAYD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,SAAS;IAIpB;IACE,4CAA4C;IAC5B,MAAc;IAC9B,kEAAkE;IAClD,IAAmB;QAFnB,WAAM,GAAN,MAAM,CAAQ;QAEd,SAAI,GAAJ,IAAI,CAAe;QAPrC,uHAAuH;QAChH,WAAM,GAAY,KAAK,CAAC;IAO5B,CAAC;IAEJ,wDAAwD;IACxD,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,mEAAmE;IACnE,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED;;;;;;;OAOG;IACI,QAAQ;QACb,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,2EAA2E;IACpE,QAAQ;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAA4B,EAAE,KAAY;IACnF,MAAM,OAAO,GAAG,IAAI,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/dsl.d.ts
CHANGED
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
import { Component, ComponentClassArray, ComponentClassOrType } from "./component.js";
|
|
2
2
|
import type { Entity } from "./entity.js";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* A predicate that decides whether a given entity belongs to a query.
|
|
5
|
+
*
|
|
6
|
+
* Pass one directly inside {@link QueryDSL} to express membership rules that
|
|
7
|
+
* the structured operators cannot reach.
|
|
8
|
+
*/
|
|
5
9
|
export type EntityTestFunc = (e: Entity) => boolean;
|
|
6
10
|
/**
|
|
7
|
-
* A composable
|
|
8
|
-
* {@link
|
|
11
|
+
* A composable expression describing which entities a {@link Query},
|
|
12
|
+
* {@link System}, or {@link Filter} should match.
|
|
9
13
|
*
|
|
10
|
-
*
|
|
14
|
+
* Operators can be nested arbitrarily:
|
|
11
15
|
*
|
|
12
16
|
* ```ts
|
|
13
|
-
* //
|
|
17
|
+
* // Position AND (Sprite OR Container):
|
|
14
18
|
* world.system("render").query({
|
|
15
|
-
* AND: [Position, { OR: [Sprite, Container] }]
|
|
19
|
+
* AND: [Position, { OR: [Sprite, Container] }],
|
|
16
20
|
* });
|
|
17
21
|
*
|
|
18
|
-
* //
|
|
22
|
+
* // Parent has Player AND Container:
|
|
19
23
|
* world.system("attach").query({
|
|
20
|
-
* PARENT: { AND: [Player, Container] }
|
|
24
|
+
* PARENT: { AND: [Player, Container] },
|
|
21
25
|
* });
|
|
22
26
|
* ```
|
|
23
27
|
*
|
|
24
|
-
* Short forms
|
|
25
|
-
* - A single class or type id is
|
|
26
|
-
* - An array `[A, B]` is
|
|
27
|
-
* -
|
|
28
|
+
* Short forms recognized by `query` / `filter`:
|
|
29
|
+
* - A single class or numeric type id is shorthand for `{ HAS: [C] }`.
|
|
30
|
+
* - An array `[A, B]` is shorthand for `{ HAS: [A, B] }`.
|
|
31
|
+
* - An {@link EntityTestFunc} is invoked directly for fully custom logic.
|
|
28
32
|
*/
|
|
29
33
|
export type QueryDSL = ComponentClassArray | ComponentClassOrType | EntityTestFunc | {
|
|
30
34
|
HAS: ComponentClassArray | ComponentClassOrType;
|
|
@@ -39,24 +43,30 @@ export type QueryDSL = ComponentClassArray | ComponentClassOrType | EntityTestFu
|
|
|
39
43
|
} | {
|
|
40
44
|
PARENT: QueryDSL;
|
|
41
45
|
};
|
|
42
|
-
export declare function HAS(world: World, ...components: ComponentClassArray): EntityTestFunc;
|
|
43
46
|
/**
|
|
44
|
-
*
|
|
45
|
-
* the `_guaranteed` hint).
|
|
46
|
-
*
|
|
47
|
+
* Resolve component nullability based on what was declared in `requires` (or
|
|
48
|
+
* the `_guaranteed` hint).
|
|
49
|
+
*
|
|
50
|
+
* Components in `R` resolve to `InstanceType<C>`; every other component class
|
|
51
|
+
* resolves to `InstanceType<C> | undefined`.
|
|
52
|
+
*
|
|
53
|
+
* @typeParam C - Component class being injected.
|
|
54
|
+
* @typeParam R - Tuple of component classes guaranteed present.
|
|
47
55
|
*/
|
|
48
56
|
export type MaybeRequired<C, R extends (typeof Component)[]> = C extends typeof Component ? C extends R[number] ? InstanceType<C> : InstanceType<C> | undefined : never;
|
|
49
57
|
/**
|
|
50
|
-
* Statically
|
|
58
|
+
* Statically extract the component classes that are **guaranteed present** on
|
|
51
59
|
* every entity matched by a {@link QueryDSL} expression.
|
|
52
60
|
*
|
|
53
61
|
* Rules:
|
|
54
62
|
* - Plain class `C` → `[C]`
|
|
55
63
|
* - Plain array `[A, B]` → `[A, B]`
|
|
56
|
-
* - `{HAS: ...}` / `{HAS_ONLY: ...}` → recurse into the payload
|
|
57
|
-
* - `{AND: [q1, q2, ...]}` →
|
|
58
|
-
* - `{OR: ...}` / `{NOT: ...}` / `{PARENT: ...}` → `[]` (no guarantee)
|
|
64
|
+
* - `{ HAS: ... }` / `{ HAS_ONLY: ... }` → recurse into the payload
|
|
65
|
+
* - `{ AND: [q1, q2, ...] }` → concatenate each branch's extraction
|
|
66
|
+
* - `{ OR: ... }` / `{ NOT: ... }` / `{ PARENT: ... }` → `[]` (no guarantee)
|
|
59
67
|
* - `EntityTestFunc` / numeric type id → `[]` (opaque)
|
|
68
|
+
*
|
|
69
|
+
* @typeParam Q - Query expression to analyse.
|
|
60
70
|
*/
|
|
61
71
|
export type ExtractRequired<Q> = Q extends typeof Component ? [Q] : Q extends readonly (typeof Component)[] ? Q : Q extends {
|
|
62
72
|
HAS: infer H;
|
|
@@ -64,11 +74,9 @@ export type ExtractRequired<Q> = Q extends typeof Component ? [Q] : Q extends re
|
|
|
64
74
|
HAS_ONLY: infer H;
|
|
65
75
|
} ? ExtractRequired<H> : Q extends {
|
|
66
76
|
AND: infer A extends readonly QueryDSL[];
|
|
67
|
-
} ?
|
|
68
|
-
type
|
|
77
|
+
} ? _ExtractAndChain<A> : [];
|
|
78
|
+
type _ExtractAndChain<A extends readonly QueryDSL[]> = A extends readonly [
|
|
69
79
|
infer First,
|
|
70
80
|
...infer Rest extends readonly QueryDSL[]
|
|
71
|
-
] ? [...ExtractRequired<First>, ...
|
|
72
|
-
/** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
|
|
73
|
-
export declare function buildEntityTest(world: World, q: QueryDSL): EntityTestFunc;
|
|
81
|
+
] ? [...ExtractRequired<First>, ..._ExtractAndChain<Rest>] : [];
|
|
74
82
|
export {};
|
package/dist/dsl.js
CHANGED
|
@@ -1,56 +1,82 @@
|
|
|
1
|
-
import { Component,
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Component, _calculateComponentBitmask, } from "./component.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build a predicate that returns `true` when an entity has every component
|
|
4
|
+
* type in `components` set on its archetype.
|
|
5
|
+
*
|
|
6
|
+
* @internal Factory used by {@link _buildEntityTest} and by `Query.update`'s
|
|
7
|
+
* watchlist auto-expansion.
|
|
8
|
+
*
|
|
9
|
+
* @param world - World used to resolve component classes to type ids.
|
|
10
|
+
* @param components - Component classes or numeric type ids to require.
|
|
11
|
+
*/
|
|
12
|
+
export function _HAS(world, ...components) {
|
|
13
|
+
const testBitmask = _calculateComponentBitmask(components, world);
|
|
4
14
|
return (e) => e.componentBitmask.hasBitset(testBitmask);
|
|
5
15
|
}
|
|
6
|
-
|
|
7
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Build a predicate that returns `true` only when an entity's archetype is
|
|
18
|
+
* exactly the set in `components` (no other components attached).
|
|
19
|
+
*/
|
|
20
|
+
function _HAS_ONLY(world, ...components) {
|
|
21
|
+
const testBitmask = _calculateComponentBitmask(components, world);
|
|
8
22
|
return (e) => e.componentBitmask.equal(testBitmask);
|
|
9
23
|
}
|
|
10
|
-
|
|
24
|
+
/** Negate a predicate. */
|
|
25
|
+
function _NOT(func) {
|
|
11
26
|
return (e) => !func(e);
|
|
12
27
|
}
|
|
13
|
-
|
|
28
|
+
/** Conjunction of multiple predicates. */
|
|
29
|
+
function _AND(...funcs) {
|
|
14
30
|
return (e) => funcs.every((f) => f(e));
|
|
15
31
|
}
|
|
16
|
-
|
|
32
|
+
/** Disjunction of multiple predicates. */
|
|
33
|
+
function _OR(...funcs) {
|
|
17
34
|
return (e) => funcs.some((f) => f(e));
|
|
18
35
|
}
|
|
19
|
-
|
|
36
|
+
/** Lift a predicate to apply to the entity's parent (false when no parent). */
|
|
37
|
+
function _PARENT(func) {
|
|
20
38
|
return (e) => (e.parent && func(e.parent)) || false;
|
|
21
39
|
}
|
|
22
|
-
/**
|
|
23
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Compile a {@link QueryDSL} expression into a runtime entity-test predicate.
|
|
42
|
+
*
|
|
43
|
+
* @internal Used by `Query`, `System`, and `Filter` to translate user-supplied
|
|
44
|
+
* DSL expressions into the predicate stored on `Query._belongs`.
|
|
45
|
+
*
|
|
46
|
+
* @param world - World used to resolve component classes to type ids.
|
|
47
|
+
* @param q - Query expression.
|
|
48
|
+
*/
|
|
49
|
+
export function _buildEntityTest(world, q) {
|
|
24
50
|
if (typeof q === "number" || (typeof q === "function" && q.prototype instanceof Component)) {
|
|
25
|
-
return
|
|
51
|
+
return _HAS(world, q);
|
|
26
52
|
}
|
|
27
53
|
else if (typeof q === "function") {
|
|
28
54
|
return q;
|
|
29
55
|
}
|
|
30
56
|
if (q instanceof Array) {
|
|
31
|
-
return
|
|
57
|
+
return _HAS(world, ...q);
|
|
32
58
|
}
|
|
33
59
|
if ("HAS" in q) {
|
|
34
|
-
return
|
|
60
|
+
return _buildEntityTest(world, q.HAS);
|
|
35
61
|
}
|
|
36
62
|
if ("HAS_ONLY" in q) {
|
|
37
63
|
const v = q.HAS_ONLY;
|
|
38
64
|
if (v instanceof Array) {
|
|
39
|
-
return
|
|
65
|
+
return _HAS_ONLY(world, ...v);
|
|
40
66
|
}
|
|
41
|
-
return
|
|
67
|
+
return _HAS_ONLY(world, v);
|
|
42
68
|
}
|
|
43
69
|
if ("AND" in q) {
|
|
44
|
-
return
|
|
70
|
+
return _AND(...q.AND.map((sq) => _buildEntityTest(world, sq)));
|
|
45
71
|
}
|
|
46
72
|
if ("OR" in q) {
|
|
47
|
-
return
|
|
73
|
+
return _OR(...q.OR.map((sq) => _buildEntityTest(world, sq)));
|
|
48
74
|
}
|
|
49
75
|
if ("NOT" in q) {
|
|
50
|
-
return
|
|
76
|
+
return _NOT(_buildEntityTest(world, q.NOT));
|
|
51
77
|
}
|
|
52
78
|
if ("PARENT" in q) {
|
|
53
|
-
return
|
|
79
|
+
return _PARENT(_buildEntityTest(world, q.PARENT));
|
|
54
80
|
}
|
|
55
81
|
throw "Unrecognized query term";
|
|
56
82
|
}
|
package/dist/dsl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dsl.js","sourceRoot":"","sources":["../src/dsl.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAGT,
|
|
1
|
+
{"version":3,"file":"dsl.js","sourceRoot":"","sources":["../src/dsl.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAGT,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AA+FxB;;;;;;;;;GASG;AACH,MAAM,UAAU,IAAI,CAAC,KAAY,EAAE,GAAG,UAA+B;IACnE,MAAM,WAAW,GAAG,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClE,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,KAAY,EAAE,GAAG,UAA+B;IACjE,MAAM,WAAW,GAAG,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClE,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AAC9D,CAAC;AAED,0BAA0B;AAC1B,SAAS,IAAI,CAAC,IAAoB;IAChC,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,0CAA0C;AAC1C,SAAS,IAAI,CAAC,GAAG,KAAuB;IACtC,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,0CAA0C;AAC1C,SAAS,GAAG,CAAC,GAAG,KAAuB;IACrC,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,+EAA+E;AAC/E,SAAS,OAAO,CAAC,IAAoB;IACnC,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC;AAC9D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAY,EAAE,CAAW;IACxD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,UAAU,IAAI,CAAC,CAAC,SAAS,YAAY,SAAS,CAAC,EAAE;QAC1F,OAAO,IAAI,CAAC,KAAK,EAAE,CAAqB,CAAC,CAAC;KAC3C;SAAM,IAAI,OAAO,CAAC,KAAK,UAAU,EAAE;QAClC,OAAO,CAAmB,CAAC;KAC5B;IAED,IAAI,CAAC,YAAY,KAAK,EAAE;QACtB,OAAO,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;KAC1B;IAED,IAAI,KAAK,IAAI,CAAC,EAAE;QACd,OAAO,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;KACvC;IAED,IAAI,UAAU,IAAI,CAAC,EAAE;QACnB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACrB,IAAI,CAAC,YAAY,KAAK,EAAE;YACtB,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;SAC/B;QACD,OAAO,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;KAC5B;IAED,IAAI,KAAK,IAAI,CAAC,EAAE;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;KAChE;IAED,IAAI,IAAI,IAAI,CAAC,EAAE;QACb,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;KAC9D;IAED,IAAI,KAAK,IAAI,CAAC,EAAE;QACd,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;KAC7C;IAED,IAAI,QAAQ,IAAI,CAAC,EAAE;QACjB,OAAO,OAAO,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;KACnD;IAED,MAAM,yBAAyB,CAAC;AAClC,CAAC"}
|