@alife-sdk/core 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/BehaviorTree.d.ts +172 -0
- package/dist/ai/BehaviorTree.d.ts.map +1 -0
- package/dist/ai/BehaviorTree.js +275 -0
- package/dist/ai/BehaviorTree.js.map +1 -0
- package/dist/ai/StateMachine.d.ts +38 -0
- package/dist/ai/StateMachine.d.ts.map +1 -1
- package/dist/ai/StateMachine.js +80 -0
- package/dist/ai/StateMachine.js.map +1 -1
- package/dist/ai/index.d.ts +3 -1
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +1 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/core/ReactiveQuery.d.ts +85 -0
- package/dist/core/ReactiveQuery.d.ts.map +1 -0
- package/dist/core/ReactiveQuery.js +154 -0
- package/dist/core/ReactiveQuery.js.map +1 -0
- package/dist/entity/EntityHandle.d.ts +76 -0
- package/dist/entity/EntityHandle.d.ts.map +1 -0
- package/dist/entity/EntityHandle.js +133 -0
- package/dist/entity/EntityHandle.js.map +1 -0
- package/dist/entity/index.d.ts +2 -0
- package/dist/entity/index.d.ts.map +1 -1
- package/dist/entity/index.js +1 -1
- package/dist/entity/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/registry/AIStateRegistry.d.ts +4 -0
- package/dist/registry/AIStateRegistry.d.ts.map +1 -1
- package/dist/registry/AIStateRegistry.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Behavior Tree — hierarchical AI task composition.
|
|
3
|
+
*
|
|
4
|
+
* Pairs well with GOAP: GOAP decides *what* goal to pursue, the BT decides
|
|
5
|
+
* *how* to execute it step by step. The BT is driven externally by calling
|
|
6
|
+
* `tree.tick(blackboard)` each frame or simulation step.
|
|
7
|
+
*
|
|
8
|
+
* Node types:
|
|
9
|
+
* Composites — Sequence, Selector, Parallel
|
|
10
|
+
* Decorators — Inverter, Repeater, AlwaysSucceed, AlwaysFail, Cooldown
|
|
11
|
+
* Leaves — Task (action), Condition
|
|
12
|
+
*
|
|
13
|
+
* Blackboard:
|
|
14
|
+
* Typed key-value store shared across all nodes in one tick. Nodes read
|
|
15
|
+
* perception data from it and write intermediate results to it.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const bb = new Blackboard({ canSeeTarget: false, ammoCount: 10 });
|
|
20
|
+
*
|
|
21
|
+
* const tree = new Selector([
|
|
22
|
+
* new Sequence([
|
|
23
|
+
* new Condition((bb) => bb.get('canSeeTarget')),
|
|
24
|
+
* new Condition((bb) => bb.get('ammoCount') > 0),
|
|
25
|
+
* new Task((bb) => { shoot(); return 'success'; }),
|
|
26
|
+
* ]),
|
|
27
|
+
* new Task(() => { patrol(); return 'running'; }),
|
|
28
|
+
* ]);
|
|
29
|
+
*
|
|
30
|
+
* // Each frame:
|
|
31
|
+
* tree.tick(bb);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
/** Result returned by every node's `tick()`. */
|
|
35
|
+
export type TaskStatus = 'success' | 'failure' | 'running';
|
|
36
|
+
/**
|
|
37
|
+
* Typed key-value store shared across all nodes during a single tick.
|
|
38
|
+
*
|
|
39
|
+
* Initialize it with a plain object; the keys become the allowed set of keys
|
|
40
|
+
* via the generic type parameter.
|
|
41
|
+
*/
|
|
42
|
+
export declare class Blackboard<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
43
|
+
private readonly data;
|
|
44
|
+
constructor(initial?: Partial<T>);
|
|
45
|
+
get<K extends keyof T>(key: K): T[K] | undefined;
|
|
46
|
+
set<K extends keyof T>(key: K, value: T[K]): void;
|
|
47
|
+
has(key: keyof T): boolean;
|
|
48
|
+
delete(key: keyof T): void;
|
|
49
|
+
}
|
|
50
|
+
/** Every node in the tree implements this single interface. */
|
|
51
|
+
export interface ITreeNode<TBB extends Blackboard = Blackboard> {
|
|
52
|
+
tick(blackboard: TBB): TaskStatus;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Task (action leaf).
|
|
56
|
+
* Runs an arbitrary callback; the callback returns the status directly.
|
|
57
|
+
*/
|
|
58
|
+
export declare class Task<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
59
|
+
private readonly action;
|
|
60
|
+
constructor(action: (bb: TBB) => TaskStatus);
|
|
61
|
+
tick(bb: TBB): TaskStatus;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Condition (boolean leaf).
|
|
65
|
+
* Returns 'success' when the predicate is true, 'failure' otherwise.
|
|
66
|
+
*/
|
|
67
|
+
export declare class Condition<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
68
|
+
private readonly predicate;
|
|
69
|
+
constructor(predicate: (bb: TBB) => boolean);
|
|
70
|
+
tick(bb: TBB): TaskStatus;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Sequence — AND gate.
|
|
74
|
+
* Ticks children left-to-right. Returns 'failure' on the first failing child,
|
|
75
|
+
* 'running' on the first running child, 'success' when all succeed.
|
|
76
|
+
*/
|
|
77
|
+
export declare class Sequence<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
78
|
+
private readonly children;
|
|
79
|
+
constructor(children: ITreeNode<TBB>[]);
|
|
80
|
+
tick(bb: TBB): TaskStatus;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Selector — OR gate.
|
|
84
|
+
* Ticks children left-to-right. Returns 'success' on the first succeeding
|
|
85
|
+
* child, 'running' on the first running child, 'failure' when all fail.
|
|
86
|
+
*/
|
|
87
|
+
export declare class Selector<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
88
|
+
private readonly children;
|
|
89
|
+
constructor(children: ITreeNode<TBB>[]);
|
|
90
|
+
tick(bb: TBB): TaskStatus;
|
|
91
|
+
}
|
|
92
|
+
/** Policy for the Parallel node. */
|
|
93
|
+
export type ParallelPolicy = 'require-all' | 'require-one';
|
|
94
|
+
/**
|
|
95
|
+
* Parallel — ticks ALL children every tick simultaneously.
|
|
96
|
+
*
|
|
97
|
+
* `require-all` (default — AND semantics):
|
|
98
|
+
* - Success: all children succeed.
|
|
99
|
+
* - Failure: any child fails.
|
|
100
|
+
* - Running: otherwise.
|
|
101
|
+
*
|
|
102
|
+
* `require-one` (OR semantics):
|
|
103
|
+
* - Success: at least one child succeeds.
|
|
104
|
+
* - Failure: all children fail.
|
|
105
|
+
* - Running: otherwise (some running, none succeeded yet).
|
|
106
|
+
*/
|
|
107
|
+
export declare class Parallel<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
108
|
+
private readonly children;
|
|
109
|
+
private readonly successPolicy;
|
|
110
|
+
constructor(children: ITreeNode<TBB>[], successPolicy?: ParallelPolicy);
|
|
111
|
+
tick(bb: TBB): TaskStatus;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Inverter — flips 'success' ↔ 'failure'; passes 'running' unchanged.
|
|
115
|
+
*/
|
|
116
|
+
export declare class Inverter<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
117
|
+
private readonly child;
|
|
118
|
+
constructor(child: ITreeNode<TBB>);
|
|
119
|
+
tick(bb: TBB): TaskStatus;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* AlwaysSucceed — maps any child result to 'success'.
|
|
123
|
+
*/
|
|
124
|
+
export declare class AlwaysSucceed<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
125
|
+
private readonly child;
|
|
126
|
+
constructor(child: ITreeNode<TBB>);
|
|
127
|
+
tick(bb: TBB): TaskStatus;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* AlwaysFail — maps any child result to 'failure'.
|
|
131
|
+
*/
|
|
132
|
+
export declare class AlwaysFail<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
133
|
+
private readonly child;
|
|
134
|
+
constructor(child: ITreeNode<TBB>);
|
|
135
|
+
tick(bb: TBB): TaskStatus;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Repeater — ticks its child N times, returning 'success' after all iterations.
|
|
139
|
+
* If the child returns 'failure' the repeater short-circuits with 'failure'.
|
|
140
|
+
* Pass `Infinity` for an endless loop (returns 'running' every tick).
|
|
141
|
+
*/
|
|
142
|
+
export declare class Repeater<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
143
|
+
private readonly child;
|
|
144
|
+
private readonly times;
|
|
145
|
+
private remaining;
|
|
146
|
+
constructor(child: ITreeNode<TBB>, times: number);
|
|
147
|
+
tick(bb: TBB): TaskStatus;
|
|
148
|
+
/** Reset the repeat counter to allow reuse. */
|
|
149
|
+
reset(): void;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Cooldown — blocks its child while a cooldown timer is active.
|
|
153
|
+
*
|
|
154
|
+
* When the cooldown is inactive: ticks the child normally. If the child
|
|
155
|
+
* succeeds, starts the cooldown and returns 'success'. While the cooldown
|
|
156
|
+
* is active: immediately returns 'failure' (the action is on cooldown).
|
|
157
|
+
*
|
|
158
|
+
* `now` defaults to `Date.now` — inject a custom clock for deterministic tests.
|
|
159
|
+
*/
|
|
160
|
+
export declare class Cooldown<TBB extends Blackboard = Blackboard> implements ITreeNode<TBB> {
|
|
161
|
+
private readonly child;
|
|
162
|
+
private readonly durationMs;
|
|
163
|
+
private readonly clock;
|
|
164
|
+
private readyAt;
|
|
165
|
+
constructor(child: ITreeNode<TBB>, durationMs: number, clock?: () => number);
|
|
166
|
+
tick(bb: TBB): TaskStatus;
|
|
167
|
+
/** Force the cooldown to expire immediately. */
|
|
168
|
+
reset(): void;
|
|
169
|
+
/** `true` while the cooldown timer is active. */
|
|
170
|
+
get isOnCooldown(): boolean;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=BehaviorTree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BehaviorTree.d.ts","sourceRoot":"","sources":["../../src/ai/BehaviorTree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Behavior Tree — hierarchical AI task composition.
|
|
3
|
+
*
|
|
4
|
+
* Pairs well with GOAP: GOAP decides *what* goal to pursue, the BT decides
|
|
5
|
+
* *how* to execute it step by step. The BT is driven externally by calling
|
|
6
|
+
* `tree.tick(blackboard)` each frame or simulation step.
|
|
7
|
+
*
|
|
8
|
+
* Node types:
|
|
9
|
+
* Composites — Sequence, Selector, Parallel
|
|
10
|
+
* Decorators — Inverter, Repeater, AlwaysSucceed, AlwaysFail, Cooldown
|
|
11
|
+
* Leaves — Task (action), Condition
|
|
12
|
+
*
|
|
13
|
+
* Blackboard:
|
|
14
|
+
* Typed key-value store shared across all nodes in one tick. Nodes read
|
|
15
|
+
* perception data from it and write intermediate results to it.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const bb = new Blackboard({ canSeeTarget: false, ammoCount: 10 });
|
|
20
|
+
*
|
|
21
|
+
* const tree = new Selector([
|
|
22
|
+
* new Sequence([
|
|
23
|
+
* new Condition((bb) => bb.get('canSeeTarget')),
|
|
24
|
+
* new Condition((bb) => bb.get('ammoCount') > 0),
|
|
25
|
+
* new Task((bb) => { shoot(); return 'success'; }),
|
|
26
|
+
* ]),
|
|
27
|
+
* new Task(() => { patrol(); return 'running'; }),
|
|
28
|
+
* ]);
|
|
29
|
+
*
|
|
30
|
+
* // Each frame:
|
|
31
|
+
* tree.tick(bb);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Blackboard
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
/**
|
|
38
|
+
* Typed key-value store shared across all nodes during a single tick.
|
|
39
|
+
*
|
|
40
|
+
* Initialize it with a plain object; the keys become the allowed set of keys
|
|
41
|
+
* via the generic type parameter.
|
|
42
|
+
*/
|
|
43
|
+
export class Blackboard {
|
|
44
|
+
constructor(initial = {}) {
|
|
45
|
+
this.data = new Map(Object.entries(initial));
|
|
46
|
+
}
|
|
47
|
+
get(key) {
|
|
48
|
+
return this.data.get(key);
|
|
49
|
+
}
|
|
50
|
+
set(key, value) {
|
|
51
|
+
this.data.set(key, value);
|
|
52
|
+
}
|
|
53
|
+
has(key) {
|
|
54
|
+
return this.data.has(key);
|
|
55
|
+
}
|
|
56
|
+
delete(key) {
|
|
57
|
+
this.data.delete(key);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Leaf nodes
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
/**
|
|
64
|
+
* Task (action leaf).
|
|
65
|
+
* Runs an arbitrary callback; the callback returns the status directly.
|
|
66
|
+
*/
|
|
67
|
+
export class Task {
|
|
68
|
+
constructor(action) {
|
|
69
|
+
this.action = action;
|
|
70
|
+
}
|
|
71
|
+
tick(bb) {
|
|
72
|
+
return this.action(bb);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Condition (boolean leaf).
|
|
77
|
+
* Returns 'success' when the predicate is true, 'failure' otherwise.
|
|
78
|
+
*/
|
|
79
|
+
export class Condition {
|
|
80
|
+
constructor(predicate) {
|
|
81
|
+
this.predicate = predicate;
|
|
82
|
+
}
|
|
83
|
+
tick(bb) {
|
|
84
|
+
return this.predicate(bb) ? 'success' : 'failure';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Composite nodes
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
/**
|
|
91
|
+
* Sequence — AND gate.
|
|
92
|
+
* Ticks children left-to-right. Returns 'failure' on the first failing child,
|
|
93
|
+
* 'running' on the first running child, 'success' when all succeed.
|
|
94
|
+
*/
|
|
95
|
+
export class Sequence {
|
|
96
|
+
constructor(children) {
|
|
97
|
+
this.children = children;
|
|
98
|
+
}
|
|
99
|
+
tick(bb) {
|
|
100
|
+
for (const child of this.children) {
|
|
101
|
+
const status = child.tick(bb);
|
|
102
|
+
if (status !== 'success')
|
|
103
|
+
return status;
|
|
104
|
+
}
|
|
105
|
+
return 'success';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Selector — OR gate.
|
|
110
|
+
* Ticks children left-to-right. Returns 'success' on the first succeeding
|
|
111
|
+
* child, 'running' on the first running child, 'failure' when all fail.
|
|
112
|
+
*/
|
|
113
|
+
export class Selector {
|
|
114
|
+
constructor(children) {
|
|
115
|
+
this.children = children;
|
|
116
|
+
}
|
|
117
|
+
tick(bb) {
|
|
118
|
+
for (const child of this.children) {
|
|
119
|
+
const status = child.tick(bb);
|
|
120
|
+
if (status !== 'failure')
|
|
121
|
+
return status;
|
|
122
|
+
}
|
|
123
|
+
return 'failure';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Parallel — ticks ALL children every tick simultaneously.
|
|
128
|
+
*
|
|
129
|
+
* `require-all` (default — AND semantics):
|
|
130
|
+
* - Success: all children succeed.
|
|
131
|
+
* - Failure: any child fails.
|
|
132
|
+
* - Running: otherwise.
|
|
133
|
+
*
|
|
134
|
+
* `require-one` (OR semantics):
|
|
135
|
+
* - Success: at least one child succeeds.
|
|
136
|
+
* - Failure: all children fail.
|
|
137
|
+
* - Running: otherwise (some running, none succeeded yet).
|
|
138
|
+
*/
|
|
139
|
+
export class Parallel {
|
|
140
|
+
constructor(children, successPolicy = 'require-all') {
|
|
141
|
+
this.children = children;
|
|
142
|
+
this.successPolicy = successPolicy;
|
|
143
|
+
}
|
|
144
|
+
tick(bb) {
|
|
145
|
+
let successCount = 0;
|
|
146
|
+
let failureCount = 0;
|
|
147
|
+
for (const child of this.children) {
|
|
148
|
+
const status = child.tick(bb);
|
|
149
|
+
if (status === 'success')
|
|
150
|
+
successCount++;
|
|
151
|
+
else if (status === 'failure')
|
|
152
|
+
failureCount++;
|
|
153
|
+
}
|
|
154
|
+
if (this.successPolicy === 'require-all') {
|
|
155
|
+
if (failureCount > 0)
|
|
156
|
+
return 'failure';
|
|
157
|
+
if (successCount === this.children.length)
|
|
158
|
+
return 'success';
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
if (successCount > 0)
|
|
162
|
+
return 'success';
|
|
163
|
+
if (failureCount === this.children.length)
|
|
164
|
+
return 'failure';
|
|
165
|
+
}
|
|
166
|
+
return 'running';
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Decorator nodes
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
/**
|
|
173
|
+
* Inverter — flips 'success' ↔ 'failure'; passes 'running' unchanged.
|
|
174
|
+
*/
|
|
175
|
+
export class Inverter {
|
|
176
|
+
constructor(child) {
|
|
177
|
+
this.child = child;
|
|
178
|
+
}
|
|
179
|
+
tick(bb) {
|
|
180
|
+
const status = this.child.tick(bb);
|
|
181
|
+
if (status === 'success')
|
|
182
|
+
return 'failure';
|
|
183
|
+
if (status === 'failure')
|
|
184
|
+
return 'success';
|
|
185
|
+
return 'running';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* AlwaysSucceed — maps any child result to 'success'.
|
|
190
|
+
*/
|
|
191
|
+
export class AlwaysSucceed {
|
|
192
|
+
constructor(child) {
|
|
193
|
+
this.child = child;
|
|
194
|
+
}
|
|
195
|
+
tick(bb) {
|
|
196
|
+
this.child.tick(bb);
|
|
197
|
+
return 'success';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* AlwaysFail — maps any child result to 'failure'.
|
|
202
|
+
*/
|
|
203
|
+
export class AlwaysFail {
|
|
204
|
+
constructor(child) {
|
|
205
|
+
this.child = child;
|
|
206
|
+
}
|
|
207
|
+
tick(bb) {
|
|
208
|
+
this.child.tick(bb);
|
|
209
|
+
return 'failure';
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Repeater — ticks its child N times, returning 'success' after all iterations.
|
|
214
|
+
* If the child returns 'failure' the repeater short-circuits with 'failure'.
|
|
215
|
+
* Pass `Infinity` for an endless loop (returns 'running' every tick).
|
|
216
|
+
*/
|
|
217
|
+
export class Repeater {
|
|
218
|
+
constructor(child, times) {
|
|
219
|
+
this.child = child;
|
|
220
|
+
this.times = times;
|
|
221
|
+
this.remaining = times;
|
|
222
|
+
}
|
|
223
|
+
tick(bb) {
|
|
224
|
+
if (this.remaining <= 0)
|
|
225
|
+
return 'success';
|
|
226
|
+
const status = this.child.tick(bb);
|
|
227
|
+
if (status === 'failure')
|
|
228
|
+
return 'failure';
|
|
229
|
+
if (status === 'success') {
|
|
230
|
+
this.remaining--;
|
|
231
|
+
if (this.remaining <= 0)
|
|
232
|
+
return 'success';
|
|
233
|
+
}
|
|
234
|
+
return 'running';
|
|
235
|
+
}
|
|
236
|
+
/** Reset the repeat counter to allow reuse. */
|
|
237
|
+
reset() {
|
|
238
|
+
this.remaining = this.times;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Cooldown — blocks its child while a cooldown timer is active.
|
|
243
|
+
*
|
|
244
|
+
* When the cooldown is inactive: ticks the child normally. If the child
|
|
245
|
+
* succeeds, starts the cooldown and returns 'success'. While the cooldown
|
|
246
|
+
* is active: immediately returns 'failure' (the action is on cooldown).
|
|
247
|
+
*
|
|
248
|
+
* `now` defaults to `Date.now` — inject a custom clock for deterministic tests.
|
|
249
|
+
*/
|
|
250
|
+
export class Cooldown {
|
|
251
|
+
constructor(child, durationMs, clock = Date.now) {
|
|
252
|
+
this.child = child;
|
|
253
|
+
this.durationMs = durationMs;
|
|
254
|
+
this.clock = clock;
|
|
255
|
+
this.readyAt = 0;
|
|
256
|
+
}
|
|
257
|
+
tick(bb) {
|
|
258
|
+
if (this.clock() < this.readyAt)
|
|
259
|
+
return 'failure';
|
|
260
|
+
const status = this.child.tick(bb);
|
|
261
|
+
if (status === 'success') {
|
|
262
|
+
this.readyAt = this.clock() + this.durationMs;
|
|
263
|
+
}
|
|
264
|
+
return status;
|
|
265
|
+
}
|
|
266
|
+
/** Force the cooldown to expire immediately. */
|
|
267
|
+
reset() {
|
|
268
|
+
this.readyAt = 0;
|
|
269
|
+
}
|
|
270
|
+
/** `true` while the cooldown timer is active. */
|
|
271
|
+
get isOnCooldown() {
|
|
272
|
+
return this.clock() < this.readyAt;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=BehaviorTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BehaviorTree.js","sourceRoot":"","sources":["../../src/ai/BehaviorTree.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,13 +18,51 @@ export type TransitionResult = {
|
|
|
18
18
|
readonly success: false;
|
|
19
19
|
readonly reason: 'not_allowed' | 'exit_guard' | 'enter_guard';
|
|
20
20
|
};
|
|
21
|
+
export interface StateTransitionEvent {
|
|
22
|
+
readonly from: string;
|
|
23
|
+
readonly to: string;
|
|
24
|
+
readonly timestamp: number;
|
|
25
|
+
}
|
|
21
26
|
export declare class StateMachine {
|
|
22
27
|
private currentStateId;
|
|
28
|
+
private previousStateId;
|
|
29
|
+
private stateEnterTime;
|
|
23
30
|
private readonly registry;
|
|
24
31
|
private readonly entity;
|
|
32
|
+
private readonly enterListeners;
|
|
33
|
+
private readonly exitListeners;
|
|
34
|
+
private readonly changeListeners;
|
|
35
|
+
private readonly historyLog;
|
|
25
36
|
constructor(entity: IEntity, registry: AIStateRegistry, initialState: string);
|
|
26
37
|
/** Current active state identifier. */
|
|
27
38
|
get state(): string;
|
|
39
|
+
/** Previous state identifier, or `null` if no transition has occurred yet. */
|
|
40
|
+
get previous(): string | null;
|
|
41
|
+
/** Milliseconds elapsed since entering the current state. */
|
|
42
|
+
get currentStateDuration(): number;
|
|
43
|
+
/** Returns `true` if the current state has the given tag. */
|
|
44
|
+
hasTag(tag: string): boolean;
|
|
45
|
+
/** Returns the metadata object of the current state, or `undefined`. */
|
|
46
|
+
get metadata(): Readonly<Record<string, unknown>> | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Subscribe to the moment the FSM enters `state`.
|
|
49
|
+
* @returns Unsubscribe function.
|
|
50
|
+
*/
|
|
51
|
+
onEnter(state: string, callback: (from: string | null) => void): () => void;
|
|
52
|
+
/**
|
|
53
|
+
* Subscribe to the moment the FSM exits `state`.
|
|
54
|
+
* @returns Unsubscribe function.
|
|
55
|
+
*/
|
|
56
|
+
onExit(state: string, callback: (to: string) => void): () => void;
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to any state change.
|
|
59
|
+
* @returns Unsubscribe function.
|
|
60
|
+
*/
|
|
61
|
+
onChange(callback: (from: string, to: string) => void): () => void;
|
|
62
|
+
/** Returns a snapshot of the transition history (oldest first). */
|
|
63
|
+
getHistory(): readonly StateTransitionEvent[];
|
|
64
|
+
/** Clears the transition history. */
|
|
65
|
+
clearHistory(): void;
|
|
28
66
|
/**
|
|
29
67
|
* Force transition to a new state.
|
|
30
68
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateMachine.d.ts","sourceRoot":"","sources":["../../src/ai/StateMachine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,GAAG,aAAa,CAAA;CAAE,CAAC;AAE/F,qBAAa,YAAY;IACvB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;
|
|
1
|
+
{"version":3,"file":"StateMachine.d.ts","sourceRoot":"","sources":["../../src/ai/StateMachine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,GAAG,aAAa,CAAA;CAAE,CAAC;AAE/F,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IAEjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyD;IACxF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgD;IAC9E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiD;IACjF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8B;gBAE7C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM;IAc5E,uCAAuC;IACvC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,8EAA8E;IAC9E,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,6DAA6D;IAC7D,IAAI,oBAAoB,IAAI,MAAM,CAEjC;IAMD,6DAA6D;IAC7D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAK5B,wEAAwE;IACxE,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAE5D;IAMD;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI;IAM3E;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAMjE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IASlE,mEAAmE;IACnE,UAAU,IAAI,SAAS,oBAAoB,EAAE;IAI7C,qCAAqC;IACrC,YAAY,IAAI,IAAI;IAQpB;;;;;;OAMG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB;IAwC9C;;;;;;;;OAQG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAkB3B,iEAAiE;IACjE,OAAO,IAAI,IAAI;CAIhB"}
|
package/dist/ai/StateMachine.js
CHANGED
|
@@ -12,9 +12,15 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export class StateMachine {
|
|
14
14
|
constructor(entity, registry, initialState) {
|
|
15
|
+
this.previousStateId = null;
|
|
16
|
+
this.enterListeners = new Map();
|
|
17
|
+
this.exitListeners = new Map();
|
|
18
|
+
this.changeListeners = new Set();
|
|
19
|
+
this.historyLog = [];
|
|
15
20
|
this.entity = entity;
|
|
16
21
|
this.registry = registry;
|
|
17
22
|
this.currentStateId = initialState;
|
|
23
|
+
this.stateEnterTime = Date.now();
|
|
18
24
|
const definition = this.registry.get(this.currentStateId);
|
|
19
25
|
definition.handler.enter(this.entity);
|
|
20
26
|
}
|
|
@@ -25,6 +31,68 @@ export class StateMachine {
|
|
|
25
31
|
get state() {
|
|
26
32
|
return this.currentStateId;
|
|
27
33
|
}
|
|
34
|
+
/** Previous state identifier, or `null` if no transition has occurred yet. */
|
|
35
|
+
get previous() {
|
|
36
|
+
return this.previousStateId;
|
|
37
|
+
}
|
|
38
|
+
/** Milliseconds elapsed since entering the current state. */
|
|
39
|
+
get currentStateDuration() {
|
|
40
|
+
return Date.now() - this.stateEnterTime;
|
|
41
|
+
}
|
|
42
|
+
// -----------------------------------------------------------------------
|
|
43
|
+
// Tag queries
|
|
44
|
+
// -----------------------------------------------------------------------
|
|
45
|
+
/** Returns `true` if the current state has the given tag. */
|
|
46
|
+
hasTag(tag) {
|
|
47
|
+
const def = this.registry.tryGet(this.currentStateId);
|
|
48
|
+
return def?.tags?.includes(tag) ?? false;
|
|
49
|
+
}
|
|
50
|
+
/** Returns the metadata object of the current state, or `undefined`. */
|
|
51
|
+
get metadata() {
|
|
52
|
+
return this.registry.tryGet(this.currentStateId)?.metadata;
|
|
53
|
+
}
|
|
54
|
+
// -----------------------------------------------------------------------
|
|
55
|
+
// Event subscriptions
|
|
56
|
+
// -----------------------------------------------------------------------
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to the moment the FSM enters `state`.
|
|
59
|
+
* @returns Unsubscribe function.
|
|
60
|
+
*/
|
|
61
|
+
onEnter(state, callback) {
|
|
62
|
+
if (!this.enterListeners.has(state))
|
|
63
|
+
this.enterListeners.set(state, new Set());
|
|
64
|
+
this.enterListeners.get(state).add(callback);
|
|
65
|
+
return () => this.enterListeners.get(state)?.delete(callback);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Subscribe to the moment the FSM exits `state`.
|
|
69
|
+
* @returns Unsubscribe function.
|
|
70
|
+
*/
|
|
71
|
+
onExit(state, callback) {
|
|
72
|
+
if (!this.exitListeners.has(state))
|
|
73
|
+
this.exitListeners.set(state, new Set());
|
|
74
|
+
this.exitListeners.get(state).add(callback);
|
|
75
|
+
return () => this.exitListeners.get(state)?.delete(callback);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Subscribe to any state change.
|
|
79
|
+
* @returns Unsubscribe function.
|
|
80
|
+
*/
|
|
81
|
+
onChange(callback) {
|
|
82
|
+
this.changeListeners.add(callback);
|
|
83
|
+
return () => this.changeListeners.delete(callback);
|
|
84
|
+
}
|
|
85
|
+
// -----------------------------------------------------------------------
|
|
86
|
+
// History
|
|
87
|
+
// -----------------------------------------------------------------------
|
|
88
|
+
/** Returns a snapshot of the transition history (oldest first). */
|
|
89
|
+
getHistory() {
|
|
90
|
+
return [...this.historyLog];
|
|
91
|
+
}
|
|
92
|
+
/** Clears the transition history. */
|
|
93
|
+
clearHistory() {
|
|
94
|
+
this.historyLog.length = 0;
|
|
95
|
+
}
|
|
28
96
|
// -----------------------------------------------------------------------
|
|
29
97
|
// Transitions
|
|
30
98
|
// -----------------------------------------------------------------------
|
|
@@ -46,9 +114,21 @@ export class StateMachine {
|
|
|
46
114
|
return { success: false, reason: 'exit_guard' };
|
|
47
115
|
if (newDefinition.canEnter?.(this.entity, this.currentStateId) === false)
|
|
48
116
|
return { success: false, reason: 'enter_guard' };
|
|
117
|
+
const from = this.currentStateId;
|
|
118
|
+
// Exit
|
|
49
119
|
oldDefinition.handler.exit(this.entity);
|
|
120
|
+
this.exitListeners.get(from)?.forEach(cb => cb(newState));
|
|
121
|
+
// Advance state
|
|
122
|
+
this.previousStateId = from;
|
|
50
123
|
this.currentStateId = newState;
|
|
124
|
+
this.stateEnterTime = Date.now();
|
|
125
|
+
// Record history
|
|
126
|
+
this.historyLog.push({ from, to: newState, timestamp: this.stateEnterTime });
|
|
127
|
+
// Notify change listeners
|
|
128
|
+
this.changeListeners.forEach(cb => cb(from, newState));
|
|
129
|
+
// Enter
|
|
51
130
|
newDefinition.handler.enter(this.entity);
|
|
131
|
+
this.enterListeners.get(newState)?.forEach(cb => cb(from));
|
|
52
132
|
return { success: true };
|
|
53
133
|
}
|
|
54
134
|
// -----------------------------------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateMachine.js","sourceRoot":"","sources":["../../src/ai/StateMachine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"StateMachine.js","sourceRoot":"","sources":["../../src/ai/StateMachine.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { StateMachine } from './StateMachine';
|
|
2
|
-
export type { TransitionResult } from './StateMachine';
|
|
2
|
+
export type { TransitionResult, StateTransitionEvent } from './StateMachine';
|
|
3
3
|
export { MemoryBank, MemoryChannel } from './MemorySystem';
|
|
4
4
|
export type { MemoryRecord, IMemoryBankConfig, IMemoryInput } from './MemorySystem';
|
|
5
5
|
export { DangerManager, DangerType } from './DangerManager';
|
|
@@ -7,4 +7,6 @@ export type { IDangerEntry } from './DangerManager';
|
|
|
7
7
|
export { WorldState } from './WorldState';
|
|
8
8
|
export { GOAPPlanner } from './GOAPPlanner';
|
|
9
9
|
export { GOAPAction, ActionStatus } from './GOAPAction';
|
|
10
|
+
export { Blackboard, Task, Condition, Sequence, Selector, Parallel, Inverter, AlwaysSucceed, AlwaysFail, Repeater, Cooldown } from './BehaviorTree';
|
|
11
|
+
export type { TaskStatus, ITreeNode, ParallelPolicy } from './BehaviorTree';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/ai/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACpJ,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/ai/index.js
CHANGED
|
@@ -5,4 +5,5 @@ export { DangerManager, DangerType } from './DangerManager';
|
|
|
5
5
|
export { WorldState } from './WorldState';
|
|
6
6
|
export { GOAPPlanner } from './GOAPPlanner';
|
|
7
7
|
export { GOAPAction, ActionStatus } from './GOAPAction';
|
|
8
|
+
export { Blackboard, Task, Condition, Sequence, Selector, Parallel, Inverter, AlwaysSucceed, AlwaysFail, Repeater, Cooldown } from './BehaviorTree';
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
package/dist/ai/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReactiveQuery — observe when entities enter and exit a filtered set.
|
|
3
|
+
*
|
|
4
|
+
* Instead of polling the entire entity set every frame, a ReactiveQuery
|
|
5
|
+
* maintains a stable "matched" set and fires change notifications only when
|
|
6
|
+
* entities enter or leave the query (i.e. when the predicate result changes).
|
|
7
|
+
*
|
|
8
|
+
* Usage pattern:
|
|
9
|
+
* 1. Create a query with a predicate.
|
|
10
|
+
* 2. Subscribe to `onChange` to react to structural changes.
|
|
11
|
+
* 3. Call `update(allEntities)` each tick to re-evaluate.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const hostileQuery = new ReactiveQuery<IEntity>(
|
|
16
|
+
* (e) => e.isAlive && e.hasComponent('hostile')
|
|
17
|
+
* );
|
|
18
|
+
*
|
|
19
|
+
* hostileQuery.onChange(({ added, removed }) => {
|
|
20
|
+
* added.forEach(e => combatSystem.track(e));
|
|
21
|
+
* removed.forEach(e => combatSystem.untrack(e));
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Each tick:
|
|
25
|
+
* hostileQuery.update(world.entities());
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
/** Changes detected since the last `update()` call. */
|
|
29
|
+
export interface QueryChanges<T> {
|
|
30
|
+
/** Entities that newly matched the predicate this update. */
|
|
31
|
+
readonly added: readonly T[];
|
|
32
|
+
/** Entities that no longer match the predicate this update. */
|
|
33
|
+
readonly removed: readonly T[];
|
|
34
|
+
/** All entities currently matching the predicate after this update. */
|
|
35
|
+
readonly current: readonly T[];
|
|
36
|
+
}
|
|
37
|
+
/** Callback invoked when any entities are added or removed from the query. */
|
|
38
|
+
export type QueryChangeListener<T> = (changes: QueryChanges<T>) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Tracks which entities satisfy a predicate and fires change events when
|
|
41
|
+
* the matched set changes.
|
|
42
|
+
*/
|
|
43
|
+
export declare class ReactiveQuery<T> {
|
|
44
|
+
private readonly predicate;
|
|
45
|
+
private readonly matched;
|
|
46
|
+
private readonly listeners;
|
|
47
|
+
constructor(predicate: (entity: T) => boolean);
|
|
48
|
+
/**
|
|
49
|
+
* Re-evaluate the predicate against `allEntities`.
|
|
50
|
+
*
|
|
51
|
+
* Fires `onChange` listeners if the matched set changed.
|
|
52
|
+
* Call this once per tick from the owning system.
|
|
53
|
+
*/
|
|
54
|
+
update(allEntities: Iterable<T>): void;
|
|
55
|
+
/**
|
|
56
|
+
* Subscribe to change events. Called whenever entities enter or exit the
|
|
57
|
+
* matched set.
|
|
58
|
+
*
|
|
59
|
+
* @returns Unsubscribe function.
|
|
60
|
+
*/
|
|
61
|
+
onChange(listener: QueryChangeListener<T>): () => void;
|
|
62
|
+
/** All entities currently matching the predicate (stable snapshot). */
|
|
63
|
+
get current(): readonly T[];
|
|
64
|
+
/** Number of currently matched entities. */
|
|
65
|
+
get size(): number;
|
|
66
|
+
/** Return `true` if the entity is currently in the matched set. */
|
|
67
|
+
has(entity: T): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Manually add an entity to the matched set without re-evaluating the
|
|
70
|
+
* predicate. Fires `onChange` with the single addition.
|
|
71
|
+
*
|
|
72
|
+
* Useful when external code creates entities and knows they should match.
|
|
73
|
+
*/
|
|
74
|
+
track(entity: T): void;
|
|
75
|
+
/**
|
|
76
|
+
* Manually remove an entity from the matched set.
|
|
77
|
+
* Fires `onChange` with the single removal.
|
|
78
|
+
*
|
|
79
|
+
* Useful when entities are destroyed mid-tick.
|
|
80
|
+
*/
|
|
81
|
+
untrack(entity: T): void;
|
|
82
|
+
/** Remove all entities and clear all listeners. */
|
|
83
|
+
dispose(): void;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=ReactiveQuery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactiveQuery.d.ts","sourceRoot":"","sources":["../../src/core/ReactiveQuery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,uDAAuD;AACvD,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;CAChC;AAED,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAMxE;;;GAGG;AACH,qBAAa,aAAa,CAAC,CAAC;IAId,OAAO,CAAC,QAAQ,CAAC,SAAS;IAHtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;gBAElC,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO;IAM9D;;;;;OAKG;IACH,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI;IAwCtC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAStD,uEAAuE;IACvE,IAAI,OAAO,IAAI,SAAS,CAAC,EAAE,CAE1B;IAED,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,mEAAmE;IACnE,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO;IAIvB;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI;IAatB;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI;IAaxB,mDAAmD;IACnD,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReactiveQuery — observe when entities enter and exit a filtered set.
|
|
3
|
+
*
|
|
4
|
+
* Instead of polling the entire entity set every frame, a ReactiveQuery
|
|
5
|
+
* maintains a stable "matched" set and fires change notifications only when
|
|
6
|
+
* entities enter or leave the query (i.e. when the predicate result changes).
|
|
7
|
+
*
|
|
8
|
+
* Usage pattern:
|
|
9
|
+
* 1. Create a query with a predicate.
|
|
10
|
+
* 2. Subscribe to `onChange` to react to structural changes.
|
|
11
|
+
* 3. Call `update(allEntities)` each tick to re-evaluate.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const hostileQuery = new ReactiveQuery<IEntity>(
|
|
16
|
+
* (e) => e.isAlive && e.hasComponent('hostile')
|
|
17
|
+
* );
|
|
18
|
+
*
|
|
19
|
+
* hostileQuery.onChange(({ added, removed }) => {
|
|
20
|
+
* added.forEach(e => combatSystem.track(e));
|
|
21
|
+
* removed.forEach(e => combatSystem.untrack(e));
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Each tick:
|
|
25
|
+
* hostileQuery.update(world.entities());
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// ReactiveQuery
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Tracks which entities satisfy a predicate and fires change events when
|
|
33
|
+
* the matched set changes.
|
|
34
|
+
*/
|
|
35
|
+
export class ReactiveQuery {
|
|
36
|
+
constructor(predicate) {
|
|
37
|
+
this.predicate = predicate;
|
|
38
|
+
this.matched = new Set();
|
|
39
|
+
this.listeners = new Set();
|
|
40
|
+
}
|
|
41
|
+
// -------------------------------------------------------------------------
|
|
42
|
+
// Update
|
|
43
|
+
// -------------------------------------------------------------------------
|
|
44
|
+
/**
|
|
45
|
+
* Re-evaluate the predicate against `allEntities`.
|
|
46
|
+
*
|
|
47
|
+
* Fires `onChange` listeners if the matched set changed.
|
|
48
|
+
* Call this once per tick from the owning system.
|
|
49
|
+
*/
|
|
50
|
+
update(allEntities) {
|
|
51
|
+
const added = [];
|
|
52
|
+
const removed = [];
|
|
53
|
+
const nextMatched = new Set();
|
|
54
|
+
for (const entity of allEntities) {
|
|
55
|
+
if (this.predicate(entity)) {
|
|
56
|
+
nextMatched.add(entity);
|
|
57
|
+
if (!this.matched.has(entity)) {
|
|
58
|
+
added.push(entity);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const entity of this.matched) {
|
|
63
|
+
if (!nextMatched.has(entity)) {
|
|
64
|
+
removed.push(entity);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Commit new matched set
|
|
68
|
+
this.matched.clear();
|
|
69
|
+
for (const e of nextMatched)
|
|
70
|
+
this.matched.add(e);
|
|
71
|
+
if (added.length > 0 || removed.length > 0) {
|
|
72
|
+
const changes = {
|
|
73
|
+
added,
|
|
74
|
+
removed,
|
|
75
|
+
current: [...this.matched],
|
|
76
|
+
};
|
|
77
|
+
for (const listener of this.listeners) {
|
|
78
|
+
listener(changes);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// -------------------------------------------------------------------------
|
|
83
|
+
// Subscriptions
|
|
84
|
+
// -------------------------------------------------------------------------
|
|
85
|
+
/**
|
|
86
|
+
* Subscribe to change events. Called whenever entities enter or exit the
|
|
87
|
+
* matched set.
|
|
88
|
+
*
|
|
89
|
+
* @returns Unsubscribe function.
|
|
90
|
+
*/
|
|
91
|
+
onChange(listener) {
|
|
92
|
+
this.listeners.add(listener);
|
|
93
|
+
return () => this.listeners.delete(listener);
|
|
94
|
+
}
|
|
95
|
+
// -------------------------------------------------------------------------
|
|
96
|
+
// Accessors
|
|
97
|
+
// -------------------------------------------------------------------------
|
|
98
|
+
/** All entities currently matching the predicate (stable snapshot). */
|
|
99
|
+
get current() {
|
|
100
|
+
return [...this.matched];
|
|
101
|
+
}
|
|
102
|
+
/** Number of currently matched entities. */
|
|
103
|
+
get size() {
|
|
104
|
+
return this.matched.size;
|
|
105
|
+
}
|
|
106
|
+
/** Return `true` if the entity is currently in the matched set. */
|
|
107
|
+
has(entity) {
|
|
108
|
+
return this.matched.has(entity);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Manually add an entity to the matched set without re-evaluating the
|
|
112
|
+
* predicate. Fires `onChange` with the single addition.
|
|
113
|
+
*
|
|
114
|
+
* Useful when external code creates entities and knows they should match.
|
|
115
|
+
*/
|
|
116
|
+
track(entity) {
|
|
117
|
+
if (this.matched.has(entity))
|
|
118
|
+
return;
|
|
119
|
+
this.matched.add(entity);
|
|
120
|
+
const changes = {
|
|
121
|
+
added: [entity],
|
|
122
|
+
removed: [],
|
|
123
|
+
current: [...this.matched],
|
|
124
|
+
};
|
|
125
|
+
for (const listener of this.listeners) {
|
|
126
|
+
listener(changes);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Manually remove an entity from the matched set.
|
|
131
|
+
* Fires `onChange` with the single removal.
|
|
132
|
+
*
|
|
133
|
+
* Useful when entities are destroyed mid-tick.
|
|
134
|
+
*/
|
|
135
|
+
untrack(entity) {
|
|
136
|
+
if (!this.matched.has(entity))
|
|
137
|
+
return;
|
|
138
|
+
this.matched.delete(entity);
|
|
139
|
+
const changes = {
|
|
140
|
+
added: [],
|
|
141
|
+
removed: [entity],
|
|
142
|
+
current: [...this.matched],
|
|
143
|
+
};
|
|
144
|
+
for (const listener of this.listeners) {
|
|
145
|
+
listener(changes);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Remove all entities and clear all listeners. */
|
|
149
|
+
dispose() {
|
|
150
|
+
this.matched.clear();
|
|
151
|
+
this.listeners.clear();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=ReactiveQuery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactiveQuery.js","sourceRoot":"","sources":["../../src/core/ReactiveQuery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EntityHandle — versioned entity references with use-after-free protection.
|
|
3
|
+
*
|
|
4
|
+
* Instead of holding raw entity references that can become dangling after an
|
|
5
|
+
* entity is destroyed, systems hold an EntityHandle. The handle encodes both
|
|
6
|
+
* the entity's slot index and a generation counter. When a slot is reused for
|
|
7
|
+
* a new entity the generation is bumped, making all old handles stale.
|
|
8
|
+
*
|
|
9
|
+
* Bit layout (fits in a JavaScript safe integer):
|
|
10
|
+
* [47..20] generation (28 bits, up to ~268 M versions per slot)
|
|
11
|
+
* [19.. 0] index (20 bits, up to ~1 M concurrent slots)
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const manager = new EntityHandleManager();
|
|
15
|
+
* const handle = manager.alloc('wolf-1');
|
|
16
|
+
* manager.resolve(handle); // → 'wolf-1'
|
|
17
|
+
* manager.free(handle);
|
|
18
|
+
* manager.resolve(handle); // → null (stale)
|
|
19
|
+
*/
|
|
20
|
+
/** Opaque numeric type that encodes (generation, index). */
|
|
21
|
+
export type EntityHandle = number & {
|
|
22
|
+
readonly __brand: 'EntityHandle';
|
|
23
|
+
};
|
|
24
|
+
/** Sentinel value for an absent or invalid handle. */
|
|
25
|
+
export declare const NULL_HANDLE: EntityHandle;
|
|
26
|
+
/** Pack (index, generation) into a single handle. */
|
|
27
|
+
export declare function makeHandle(index: number, generation: number): EntityHandle;
|
|
28
|
+
/** Extract the slot index from a handle. */
|
|
29
|
+
export declare function indexOf(handle: EntityHandle): number;
|
|
30
|
+
/** Extract the generation counter from a handle. */
|
|
31
|
+
export declare function genOf(handle: EntityHandle): number;
|
|
32
|
+
/** Return `true` if the handle is not the null sentinel. */
|
|
33
|
+
export declare function isValidHandle(handle: EntityHandle): boolean;
|
|
34
|
+
/** Human-readable description for logging / debugging. */
|
|
35
|
+
export declare function handleToString(handle: EntityHandle): string;
|
|
36
|
+
/**
|
|
37
|
+
* Central registry that owns the slot → entity-id mapping.
|
|
38
|
+
*
|
|
39
|
+
* Recycles freed slots so slot count stays bounded. Old handles pointing at
|
|
40
|
+
* recycled slots resolve to `null` because their stored generation no longer
|
|
41
|
+
* matches the slot's current generation.
|
|
42
|
+
*/
|
|
43
|
+
export declare class EntityHandleManager<TId = string> {
|
|
44
|
+
/** Generation counter per slot (index = slot index). */
|
|
45
|
+
private readonly generations;
|
|
46
|
+
/** Entity ID stored in each live slot. `null` = free. */
|
|
47
|
+
private readonly ids;
|
|
48
|
+
/** Slot indices available for reuse. */
|
|
49
|
+
private readonly freeList;
|
|
50
|
+
/** Next slot to allocate when freeList is empty. */
|
|
51
|
+
private nextSlot;
|
|
52
|
+
/**
|
|
53
|
+
* Allocate a new handle for the given entity id.
|
|
54
|
+
* @throws if the slot limit is exhausted.
|
|
55
|
+
*/
|
|
56
|
+
alloc(id: TId): EntityHandle;
|
|
57
|
+
/**
|
|
58
|
+
* Release a handle, incrementing the slot's generation.
|
|
59
|
+
* All existing handles pointing at this slot become stale.
|
|
60
|
+
*
|
|
61
|
+
* Does nothing (and does not throw) if the handle is already stale.
|
|
62
|
+
*/
|
|
63
|
+
free(handle: EntityHandle): void;
|
|
64
|
+
/**
|
|
65
|
+
* Resolve a handle to its entity id.
|
|
66
|
+
* Returns `null` if the handle is stale (entity was freed) or null.
|
|
67
|
+
*/
|
|
68
|
+
resolve(handle: EntityHandle): TId | null;
|
|
69
|
+
/**
|
|
70
|
+
* Return `true` if the handle points to a currently-live slot.
|
|
71
|
+
*/
|
|
72
|
+
isAlive(handle: EntityHandle): boolean;
|
|
73
|
+
/** Number of currently-live slots. */
|
|
74
|
+
get size(): number;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=EntityHandle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityHandle.d.ts","sourceRoot":"","sources":["../../src/entity/EntityHandle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAgBH,4DAA4D;AAC5D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;CAAE,CAAC;AAEzE,sDAAsD;AACtD,eAAO,MAAM,WAAW,EAAQ,YAAY,CAAC;AAE7C,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,CAG1E;AAED,4CAA4C;AAC5C,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAEpD;AAED,oDAAoD;AACpD,wBAAgB,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAElD;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAE3D;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAG3D;AAMD;;;;;;GAMG;AACH,qBAAa,mBAAmB,CAAC,GAAG,GAAG,MAAM;IAC3C,wDAAwD;IACxD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAsB;IAC1C,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,oDAAoD;IACpD,OAAO,CAAC,QAAQ,CAAK;IAErB;;;OAGG;IACH,KAAK,CAAC,EAAE,EAAE,GAAG,GAAG,YAAY;IAiB5B;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAQhC;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,GAAG,GAAG,IAAI;IAOzC;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;IAMtC,sCAAsC;IACtC,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EntityHandle — versioned entity references with use-after-free protection.
|
|
3
|
+
*
|
|
4
|
+
* Instead of holding raw entity references that can become dangling after an
|
|
5
|
+
* entity is destroyed, systems hold an EntityHandle. The handle encodes both
|
|
6
|
+
* the entity's slot index and a generation counter. When a slot is reused for
|
|
7
|
+
* a new entity the generation is bumped, making all old handles stale.
|
|
8
|
+
*
|
|
9
|
+
* Bit layout (fits in a JavaScript safe integer):
|
|
10
|
+
* [47..20] generation (28 bits, up to ~268 M versions per slot)
|
|
11
|
+
* [19.. 0] index (20 bits, up to ~1 M concurrent slots)
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const manager = new EntityHandleManager();
|
|
15
|
+
* const handle = manager.alloc('wolf-1');
|
|
16
|
+
* manager.resolve(handle); // → 'wolf-1'
|
|
17
|
+
* manager.free(handle);
|
|
18
|
+
* manager.resolve(handle); // → null (stale)
|
|
19
|
+
*/
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Bit-layout constants
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
const INDEX_BITS = 20;
|
|
24
|
+
const GEN_BITS = 28;
|
|
25
|
+
const INDEX_MASK = (1 << INDEX_BITS) - 1; // 0x000FFFFF — 1 048 575 max slots
|
|
26
|
+
const GEN_MASK = (1 << GEN_BITS) - 1; // 0x0FFFFFFF — 268 435 455 max gen
|
|
27
|
+
const MAX_SLOTS = 1 << INDEX_BITS;
|
|
28
|
+
/** Sentinel value for an absent or invalid handle. */
|
|
29
|
+
export const NULL_HANDLE = 0;
|
|
30
|
+
/** Pack (index, generation) into a single handle. */
|
|
31
|
+
export function makeHandle(index, generation) {
|
|
32
|
+
// Use multiplication instead of bit-shift to avoid 32-bit truncation.
|
|
33
|
+
return ((generation & GEN_MASK) * MAX_SLOTS + (index & INDEX_MASK));
|
|
34
|
+
}
|
|
35
|
+
/** Extract the slot index from a handle. */
|
|
36
|
+
export function indexOf(handle) {
|
|
37
|
+
return handle & INDEX_MASK;
|
|
38
|
+
}
|
|
39
|
+
/** Extract the generation counter from a handle. */
|
|
40
|
+
export function genOf(handle) {
|
|
41
|
+
return Math.floor(handle / MAX_SLOTS) & GEN_MASK;
|
|
42
|
+
}
|
|
43
|
+
/** Return `true` if the handle is not the null sentinel. */
|
|
44
|
+
export function isValidHandle(handle) {
|
|
45
|
+
return handle !== NULL_HANDLE;
|
|
46
|
+
}
|
|
47
|
+
/** Human-readable description for logging / debugging. */
|
|
48
|
+
export function handleToString(handle) {
|
|
49
|
+
if (!isValidHandle(handle))
|
|
50
|
+
return 'Entity(NULL)';
|
|
51
|
+
return `Entity(idx=${indexOf(handle)}, gen=${genOf(handle)})`;
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// EntityHandleManager
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
/**
|
|
57
|
+
* Central registry that owns the slot → entity-id mapping.
|
|
58
|
+
*
|
|
59
|
+
* Recycles freed slots so slot count stays bounded. Old handles pointing at
|
|
60
|
+
* recycled slots resolve to `null` because their stored generation no longer
|
|
61
|
+
* matches the slot's current generation.
|
|
62
|
+
*/
|
|
63
|
+
export class EntityHandleManager {
|
|
64
|
+
constructor() {
|
|
65
|
+
/** Generation counter per slot (index = slot index). */
|
|
66
|
+
this.generations = [];
|
|
67
|
+
/** Entity ID stored in each live slot. `null` = free. */
|
|
68
|
+
this.ids = [];
|
|
69
|
+
/** Slot indices available for reuse. */
|
|
70
|
+
this.freeList = [];
|
|
71
|
+
/** Next slot to allocate when freeList is empty. */
|
|
72
|
+
this.nextSlot = 0;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Allocate a new handle for the given entity id.
|
|
76
|
+
* @throws if the slot limit is exhausted.
|
|
77
|
+
*/
|
|
78
|
+
alloc(id) {
|
|
79
|
+
let index;
|
|
80
|
+
if (this.freeList.length > 0) {
|
|
81
|
+
index = this.freeList.pop();
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
if (this.nextSlot >= MAX_SLOTS) {
|
|
85
|
+
throw new Error(`EntityHandleManager: slot limit (${MAX_SLOTS}) reached`);
|
|
86
|
+
}
|
|
87
|
+
index = this.nextSlot++;
|
|
88
|
+
this.generations[index] = 1;
|
|
89
|
+
}
|
|
90
|
+
this.ids[index] = id;
|
|
91
|
+
return makeHandle(index, this.generations[index]);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Release a handle, incrementing the slot's generation.
|
|
95
|
+
* All existing handles pointing at this slot become stale.
|
|
96
|
+
*
|
|
97
|
+
* Does nothing (and does not throw) if the handle is already stale.
|
|
98
|
+
*/
|
|
99
|
+
free(handle) {
|
|
100
|
+
if (!this.isAlive(handle))
|
|
101
|
+
return;
|
|
102
|
+
const index = indexOf(handle);
|
|
103
|
+
this.ids[index] = null;
|
|
104
|
+
this.generations[index] = (this.generations[index] + 1) & GEN_MASK || 1; // skip 0
|
|
105
|
+
this.freeList.push(index);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Resolve a handle to its entity id.
|
|
109
|
+
* Returns `null` if the handle is stale (entity was freed) or null.
|
|
110
|
+
*/
|
|
111
|
+
resolve(handle) {
|
|
112
|
+
if (!isValidHandle(handle))
|
|
113
|
+
return null;
|
|
114
|
+
const index = indexOf(handle);
|
|
115
|
+
if (this.generations[index] !== genOf(handle))
|
|
116
|
+
return null;
|
|
117
|
+
return this.ids[index] ?? null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Return `true` if the handle points to a currently-live slot.
|
|
121
|
+
*/
|
|
122
|
+
isAlive(handle) {
|
|
123
|
+
if (!isValidHandle(handle))
|
|
124
|
+
return false;
|
|
125
|
+
const index = indexOf(handle);
|
|
126
|
+
return this.generations[index] === genOf(handle) && this.ids[index] !== null;
|
|
127
|
+
}
|
|
128
|
+
/** Number of currently-live slots. */
|
|
129
|
+
get size() {
|
|
130
|
+
return this.nextSlot - this.freeList.length;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=EntityHandle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityHandle.js","sourceRoot":"","sources":["../../src/entity/EntityHandle.ts"],"names":[],"mappings":"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"}
|
package/dist/entity/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export type { IEntity } from './IEntity';
|
|
2
2
|
export type { IComponent } from './IComponent';
|
|
3
|
+
export type { EntityHandle } from './EntityHandle';
|
|
4
|
+
export { NULL_HANDLE, makeHandle, indexOf, genOf, isValidHandle, handleToString, EntityHandleManager, } from './EntityHandle';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/entity/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/entity/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EACL,WAAW,EACX,UAAU,EACV,OAAO,EACP,KAAK,EACL,aAAa,EACb,cAAc,EACd,mBAAmB,GACpB,MAAM,gBAAgB,CAAC"}
|
package/dist/entity/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { NULL_HANDLE, makeHandle, indexOf, genOf, isValidHandle, handleToString, EntityHandleManager, } from './EntityHandle';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/entity/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/entity/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/entity/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,WAAW,EACX,UAAU,EACV,OAAO,EACP,KAAK,EACL,aAAa,EACb,cAAc,EACd,mBAAmB,GACpB,MAAM,gBAAgB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export { Clock } from './core/Clock';
|
|
|
11
11
|
export type { IClockConfig, IClockState } from './core/Clock';
|
|
12
12
|
export { SpatialGrid } from './core/SpatialGrid';
|
|
13
13
|
export type { IRect } from './core/SpatialGrid';
|
|
14
|
+
export { ReactiveQuery } from './core/ReactiveQuery';
|
|
15
|
+
export type { QueryChanges, QueryChangeListener } from './core/ReactiveQuery';
|
|
14
16
|
export type { ISerializable } from './core/ISerializable';
|
|
15
17
|
export * from './core/math/index';
|
|
16
18
|
export * from './events/index';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACvE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1I,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAChF,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAK1D,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACvE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1I,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAChF,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAK1D,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export { Ports, REQUIRED_PORTS } from './core/PortTokens';
|
|
|
11
11
|
export { DiagnosticsCollector, ALifeValidationError } from './core/Diagnostics';
|
|
12
12
|
export { Clock } from './core/Clock';
|
|
13
13
|
export { SpatialGrid } from './core/SpatialGrid';
|
|
14
|
+
export { ReactiveQuery } from './core/ReactiveQuery';
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
// Sub-path re-exports
|
|
16
17
|
// ---------------------------------------------------------------------------
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sEAAsE;AACtE,+CAA+C;AAE/C,8EAA8E;AAC9E,mCAAmC;AACnC,8EAA8E;AAC9E,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sEAAsE;AACtE,+CAA+C;AAE/C,8EAA8E;AAC9E,mCAAmC;AACnC,8EAA8E;AAC9E,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAIrD,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAC9E,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
|
|
@@ -30,6 +30,10 @@ export interface IAIStateDefinition {
|
|
|
30
30
|
readonly canEnter?: (entity: IEntity, fromState: string) => boolean;
|
|
31
31
|
/** Guard: return `false` to prevent leaving this state for `toState`. */
|
|
32
32
|
readonly canExit?: (entity: IEntity, toState: string) => boolean;
|
|
33
|
+
/** Categorical tags for querying state group membership (e.g. 'combat', 'grounded'). */
|
|
34
|
+
readonly tags?: readonly string[];
|
|
35
|
+
/** Arbitrary metadata for tooling/debugging (e.g. animation hints, priority weights). */
|
|
36
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
33
37
|
}
|
|
34
38
|
/** Registry of AI FSM states with built-in transition evaluation. */
|
|
35
39
|
export declare class AIStateRegistry extends Registry<string, IAIStateDefinition> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIStateRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/AIStateRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,0FAA0F;IAC1F,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,8DAA8D;IAC9D,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7B;AAED,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IACjD,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,iFAAiF;IACjF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,iFAAiF;IACjF,QAAQ,CAAC,oBAAoB,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAChE,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IACpE,yEAAyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"AIStateRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/AIStateRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,0FAA0F;IAC1F,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,8DAA8D;IAC9D,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7B;AAED,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IACjD,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,iFAAiF;IACjF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,iFAAiF;IACjF,QAAQ,CAAC,oBAAoB,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAChE,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IACpE,yEAAyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACjE,wFAAwF;IACxF,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAED,qEAAqE;AACrE,qBAAa,eAAgB,SAAQ,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;;IAKvE,8FAA8F;IACrF,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAQ/D,8HAA8H;IAC9H,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;CAS1E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIStateRegistry.js","sourceRoot":"","sources":["../../src/registry/AIStateRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"AIStateRegistry.js","sourceRoot":"","sources":["../../src/registry/AIStateRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAyCtC,qEAAqE;AACrE,MAAM,OAAO,eAAgB,SAAQ,QAAoC;IACvE;QACE,KAAK,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,8FAA8F;IACrF,QAAQ,CAAC,EAAU,EAAE,MAA0B;QACtD,MAAM,MAAM,GAAG,MAAM,CAAC,oBAAoB;YACxC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,oBAAoB,EAAE,CAAC,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE;YAC/G,CAAC,CAAC,MAAM,CAAC;QACX,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8HAA8H;IAC9H,mBAAmB,CAAC,YAAoB,EAAE,MAAe;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,oBAAoB;YAAE,OAAO,IAAI,CAAC;QAE9C,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,oBAAoB,EAAE,CAAC;YAC5C,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;gBAAE,OAAO,EAAE,CAAC,WAAW,CAAC;QAClD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|