@yagejs/core 0.2.0 → 0.4.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/index.cjs +1504 -446
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +895 -485
- package/dist/index.d.ts +895 -485
- package/dist/index.js +1496 -447
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -170,14 +170,52 @@ var Vec2 = class _Vec2 {
|
|
|
170
170
|
static lerp(a, b, t) {
|
|
171
171
|
return new _Vec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
|
|
172
172
|
}
|
|
173
|
+
/** Move current toward target by at most maxDelta without overshooting. */
|
|
174
|
+
static moveTowards(current, target, maxDelta) {
|
|
175
|
+
const dx = target.x - current.x;
|
|
176
|
+
const dy = target.y - current.y;
|
|
177
|
+
const distanceSq = dx * dx + dy * dy;
|
|
178
|
+
if (distanceSq < EPSILON * EPSILON) {
|
|
179
|
+
return new _Vec2(target.x, target.y);
|
|
180
|
+
}
|
|
181
|
+
if (maxDelta <= 0) {
|
|
182
|
+
return new _Vec2(current.x, current.y);
|
|
183
|
+
}
|
|
184
|
+
const distance = Math.sqrt(distanceSq);
|
|
185
|
+
if (distance <= maxDelta) {
|
|
186
|
+
return new _Vec2(target.x, target.y);
|
|
187
|
+
}
|
|
188
|
+
const scale = maxDelta / distance;
|
|
189
|
+
return new _Vec2(current.x + dx * scale, current.y + dy * scale);
|
|
190
|
+
}
|
|
173
191
|
};
|
|
174
192
|
|
|
175
193
|
// src/MathUtils.ts
|
|
194
|
+
var TAU = Math.PI * 2;
|
|
195
|
+
var MIN_SMOOTH_TIME = 1e-4;
|
|
196
|
+
function normalizeAngle(radians) {
|
|
197
|
+
const wrapped = ((radians + Math.PI) % TAU + TAU) % TAU - Math.PI;
|
|
198
|
+
return wrapped === -Math.PI && radians > 0 ? Math.PI : wrapped;
|
|
199
|
+
}
|
|
200
|
+
__name(normalizeAngle, "normalizeAngle");
|
|
176
201
|
var MathUtils = {
|
|
177
202
|
/** Linear interpolation between a and b. */
|
|
178
203
|
lerp(a, b, t) {
|
|
179
204
|
return a + (b - a) * t;
|
|
180
205
|
},
|
|
206
|
+
/** Return the clamped interpolation factor that produces v between a and b. */
|
|
207
|
+
inverseLerp(a, b, v) {
|
|
208
|
+
if (a === b) return 0;
|
|
209
|
+
return MathUtils.clamp((v - a) / (b - a), 0, 1);
|
|
210
|
+
},
|
|
211
|
+
/** Interpolate between angles in radians along the shortest path. */
|
|
212
|
+
lerpAngle(a, b, t) {
|
|
213
|
+
return normalizeAngle(a + MathUtils.shortestAngleBetween(a, b) * t);
|
|
214
|
+
},
|
|
215
|
+
/** Signed shortest angular delta from a to b, in radians. */
|
|
216
|
+
shortestAngleBetween(a, b) {
|
|
217
|
+
return normalizeAngle(b - a);
|
|
218
|
+
},
|
|
181
219
|
/** Clamp a value between min and max. */
|
|
182
220
|
clamp(value, min, max) {
|
|
183
221
|
return Math.max(min, Math.min(max, value));
|
|
@@ -187,13 +225,11 @@ var MathUtils = {
|
|
|
187
225
|
const t = (value - inMin) / (inMax - inMin);
|
|
188
226
|
return outMin + (outMax - outMin) * t;
|
|
189
227
|
},
|
|
190
|
-
/**
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
randomInt(min, max) {
|
|
196
|
-
return Math.floor(min + Math.random() * (max - min + 1));
|
|
228
|
+
/** Bounce t between 0 and length. */
|
|
229
|
+
pingPong(t, length) {
|
|
230
|
+
if (length <= 0) return 0;
|
|
231
|
+
const wrapped = MathUtils.wrap(t, 0, length * 2);
|
|
232
|
+
return length - Math.abs(wrapped - length);
|
|
197
233
|
},
|
|
198
234
|
/** Convert degrees to radians. */
|
|
199
235
|
degToRad(degrees) {
|
|
@@ -210,6 +246,34 @@ var MathUtils = {
|
|
|
210
246
|
}
|
|
211
247
|
return Math.max(current - step, target);
|
|
212
248
|
},
|
|
249
|
+
/**
|
|
250
|
+
* Smoothly damp current toward target without overshooting.
|
|
251
|
+
* Pass the returned velocity back into the next call.
|
|
252
|
+
*/
|
|
253
|
+
smoothDamp(current, target, velocity, smoothTime, deltaTime, maxSpeed = Infinity) {
|
|
254
|
+
if (deltaTime <= 0) {
|
|
255
|
+
return { value: current, velocity };
|
|
256
|
+
}
|
|
257
|
+
const safeSmoothTime = Math.max(MIN_SMOOTH_TIME, smoothTime);
|
|
258
|
+
const omega = 2 / safeSmoothTime;
|
|
259
|
+
const x = omega * deltaTime;
|
|
260
|
+
const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
|
|
261
|
+
const originalTarget = target;
|
|
262
|
+
const maxChange = maxSpeed * safeSmoothTime;
|
|
263
|
+
const change = MathUtils.clamp(current - target, -maxChange, maxChange);
|
|
264
|
+
const adjustedTarget = current - change;
|
|
265
|
+
const temp = (velocity + omega * change) * deltaTime;
|
|
266
|
+
const nextVelocity = (velocity - omega * temp) * exp;
|
|
267
|
+
let value = adjustedTarget + (change + temp) * exp;
|
|
268
|
+
let resultVelocity = nextVelocity;
|
|
269
|
+
const targetIsAboveCurrent = originalTarget - current > 0;
|
|
270
|
+
const valuePassedTarget = targetIsAboveCurrent ? value > originalTarget : value < originalTarget;
|
|
271
|
+
if (valuePassedTarget) {
|
|
272
|
+
value = originalTarget;
|
|
273
|
+
resultVelocity = 0;
|
|
274
|
+
}
|
|
275
|
+
return { value, velocity: resultVelocity };
|
|
276
|
+
},
|
|
213
277
|
/** Wrap value into the range [min, max). */
|
|
214
278
|
wrap(value, min, max) {
|
|
215
279
|
const range = max - min;
|
|
@@ -217,12 +281,138 @@ var MathUtils = {
|
|
|
217
281
|
}
|
|
218
282
|
};
|
|
219
283
|
|
|
284
|
+
// src/EngineContext.ts
|
|
285
|
+
var ServiceKey = class {
|
|
286
|
+
constructor(id, options) {
|
|
287
|
+
this.id = id;
|
|
288
|
+
this.scope = options?.scope ?? "engine";
|
|
289
|
+
}
|
|
290
|
+
id;
|
|
291
|
+
static {
|
|
292
|
+
__name(this, "ServiceKey");
|
|
293
|
+
}
|
|
294
|
+
/** Declared scope (engine or scene). Defaults to `"engine"`. */
|
|
295
|
+
scope;
|
|
296
|
+
};
|
|
297
|
+
var EngineContext = class {
|
|
298
|
+
static {
|
|
299
|
+
__name(this, "EngineContext");
|
|
300
|
+
}
|
|
301
|
+
services = /* @__PURE__ */ new Map();
|
|
302
|
+
/** Register a service. Throws if the key is already registered. */
|
|
303
|
+
register(key, service) {
|
|
304
|
+
if (this.services.has(key.id)) {
|
|
305
|
+
throw new Error(`Service "${key.id}" is already registered.`);
|
|
306
|
+
}
|
|
307
|
+
this.services.set(key.id, service);
|
|
308
|
+
}
|
|
309
|
+
/** Resolve a service. Throws if not registered. */
|
|
310
|
+
resolve(key) {
|
|
311
|
+
if (!this.services.has(key.id)) {
|
|
312
|
+
throw new Error(`Service "${key.id}" is not registered.`);
|
|
313
|
+
}
|
|
314
|
+
return this.services.get(key.id);
|
|
315
|
+
}
|
|
316
|
+
/** Resolve a service, returning undefined if not registered. */
|
|
317
|
+
tryResolve(key) {
|
|
318
|
+
return this.services.get(key.id);
|
|
319
|
+
}
|
|
320
|
+
/** Remove a registered service. No-op if not registered. */
|
|
321
|
+
unregister(key) {
|
|
322
|
+
this.services.delete(key.id);
|
|
323
|
+
}
|
|
324
|
+
/** Check if a service is registered. */
|
|
325
|
+
has(key) {
|
|
326
|
+
return this.services.has(key.id);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
var EngineKey = new ServiceKey("engine");
|
|
330
|
+
var EventBusKey = new ServiceKey("eventBus");
|
|
331
|
+
var SceneManagerKey = new ServiceKey("sceneManager");
|
|
332
|
+
var LoggerKey = new ServiceKey("logger");
|
|
333
|
+
var InspectorKey = new ServiceKey("inspector");
|
|
334
|
+
var QueryCacheKey = new ServiceKey("queryCache");
|
|
335
|
+
var ErrorBoundaryKey = new ServiceKey("errorBoundary");
|
|
336
|
+
var GameLoopKey = new ServiceKey("gameLoop");
|
|
337
|
+
var SystemSchedulerKey = new ServiceKey(
|
|
338
|
+
"systemScheduler"
|
|
339
|
+
);
|
|
340
|
+
var ProcessSystemKey = new ServiceKey("processSystem");
|
|
341
|
+
var AssetManagerKey = new ServiceKey("assetManager");
|
|
342
|
+
|
|
343
|
+
// src/Random.ts
|
|
344
|
+
var RandomKey = new ServiceKey("random", {
|
|
345
|
+
scope: "scene"
|
|
346
|
+
});
|
|
347
|
+
var UINT32_MAX = 4294967296;
|
|
348
|
+
function normalizeSeed(seed) {
|
|
349
|
+
return seed >>> 0;
|
|
350
|
+
}
|
|
351
|
+
__name(normalizeSeed, "normalizeSeed");
|
|
352
|
+
function createDefaultRandomSeed() {
|
|
353
|
+
return normalizeSeed(Date.now() ^ Math.floor(Math.random() * 1e9));
|
|
354
|
+
}
|
|
355
|
+
__name(createDefaultRandomSeed, "createDefaultRandomSeed");
|
|
356
|
+
var Mulberry32Random = class {
|
|
357
|
+
static {
|
|
358
|
+
__name(this, "Mulberry32Random");
|
|
359
|
+
}
|
|
360
|
+
seed;
|
|
361
|
+
state;
|
|
362
|
+
constructor(seed) {
|
|
363
|
+
const normalized = normalizeSeed(seed);
|
|
364
|
+
this.seed = normalized;
|
|
365
|
+
this.state = normalized;
|
|
366
|
+
}
|
|
367
|
+
float() {
|
|
368
|
+
let t = this.state += 1831565813;
|
|
369
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
370
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
371
|
+
return ((t ^ t >>> 14) >>> 0) / UINT32_MAX;
|
|
372
|
+
}
|
|
373
|
+
range(min, max) {
|
|
374
|
+
return min + this.float() * (max - min);
|
|
375
|
+
}
|
|
376
|
+
int(min, max) {
|
|
377
|
+
return Math.floor(this.range(min, max + 1));
|
|
378
|
+
}
|
|
379
|
+
pick(arr) {
|
|
380
|
+
if (arr.length === 0) {
|
|
381
|
+
throw new Error("RandomService.pick() requires a non-empty array.");
|
|
382
|
+
}
|
|
383
|
+
return arr[this.int(0, arr.length - 1)];
|
|
384
|
+
}
|
|
385
|
+
shuffle(arr) {
|
|
386
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
387
|
+
const j = this.int(0, i);
|
|
388
|
+
const tmp = arr[i];
|
|
389
|
+
arr[i] = arr[j];
|
|
390
|
+
arr[j] = tmp;
|
|
391
|
+
}
|
|
392
|
+
return arr;
|
|
393
|
+
}
|
|
394
|
+
setSeed(seed) {
|
|
395
|
+
const normalized = normalizeSeed(seed);
|
|
396
|
+
this.seed = normalized;
|
|
397
|
+
this.state = normalized;
|
|
398
|
+
}
|
|
399
|
+
getSeed() {
|
|
400
|
+
return this.seed;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
function createRandomService(seed = createDefaultRandomSeed()) {
|
|
404
|
+
return new Mulberry32Random(seed);
|
|
405
|
+
}
|
|
406
|
+
__name(createRandomService, "createRandomService");
|
|
407
|
+
var globalRandom = createRandomService();
|
|
408
|
+
|
|
220
409
|
// src/EventBus.ts
|
|
221
410
|
var EventBus = class {
|
|
222
411
|
static {
|
|
223
412
|
__name(this, "EventBus");
|
|
224
413
|
}
|
|
225
414
|
handlers = /* @__PURE__ */ new Map();
|
|
415
|
+
observers = /* @__PURE__ */ new Set();
|
|
226
416
|
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
227
417
|
on(event, handler) {
|
|
228
418
|
let list = this.handlers.get(event);
|
|
@@ -249,6 +439,12 @@ var EventBus = class {
|
|
|
249
439
|
}
|
|
250
440
|
/** Emit an event. Handlers are called synchronously in registration order. */
|
|
251
441
|
emit(event, data) {
|
|
442
|
+
if (this.observers.size > 0) {
|
|
443
|
+
const observers = [...this.observers];
|
|
444
|
+
for (const observer of observers) {
|
|
445
|
+
observer(event, data);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
252
448
|
const list = this.handlers.get(event);
|
|
253
449
|
if (!list) return;
|
|
254
450
|
const snapshot = [...list];
|
|
@@ -256,6 +452,16 @@ var EventBus = class {
|
|
|
256
452
|
handler(data);
|
|
257
453
|
}
|
|
258
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Observe every emitted event without affecting handler order or control
|
|
457
|
+
* flow. Used by tooling such as the Inspector event log.
|
|
458
|
+
*/
|
|
459
|
+
tap(observer) {
|
|
460
|
+
this.observers.add(observer);
|
|
461
|
+
return () => {
|
|
462
|
+
this.observers.delete(observer);
|
|
463
|
+
};
|
|
464
|
+
}
|
|
259
465
|
/** Remove all handlers for an event, or all handlers if no event specified. */
|
|
260
466
|
clear(event) {
|
|
261
467
|
if (event !== void 0) {
|
|
@@ -365,63 +571,6 @@ var Logger = class {
|
|
|
365
571
|
}
|
|
366
572
|
};
|
|
367
573
|
|
|
368
|
-
// src/EngineContext.ts
|
|
369
|
-
var ServiceKey = class {
|
|
370
|
-
constructor(id, options) {
|
|
371
|
-
this.id = id;
|
|
372
|
-
this.scope = options?.scope ?? "engine";
|
|
373
|
-
}
|
|
374
|
-
id;
|
|
375
|
-
static {
|
|
376
|
-
__name(this, "ServiceKey");
|
|
377
|
-
}
|
|
378
|
-
/** Declared scope (engine or scene). Defaults to `"engine"`. */
|
|
379
|
-
scope;
|
|
380
|
-
};
|
|
381
|
-
var EngineContext = class {
|
|
382
|
-
static {
|
|
383
|
-
__name(this, "EngineContext");
|
|
384
|
-
}
|
|
385
|
-
services = /* @__PURE__ */ new Map();
|
|
386
|
-
/** Register a service. Throws if the key is already registered. */
|
|
387
|
-
register(key, service) {
|
|
388
|
-
if (this.services.has(key.id)) {
|
|
389
|
-
throw new Error(`Service "${key.id}" is already registered.`);
|
|
390
|
-
}
|
|
391
|
-
this.services.set(key.id, service);
|
|
392
|
-
}
|
|
393
|
-
/** Resolve a service. Throws if not registered. */
|
|
394
|
-
resolve(key) {
|
|
395
|
-
if (!this.services.has(key.id)) {
|
|
396
|
-
throw new Error(`Service "${key.id}" is not registered.`);
|
|
397
|
-
}
|
|
398
|
-
return this.services.get(key.id);
|
|
399
|
-
}
|
|
400
|
-
/** Resolve a service, returning undefined if not registered. */
|
|
401
|
-
tryResolve(key) {
|
|
402
|
-
return this.services.get(key.id);
|
|
403
|
-
}
|
|
404
|
-
/** Remove a registered service. No-op if not registered. */
|
|
405
|
-
unregister(key) {
|
|
406
|
-
this.services.delete(key.id);
|
|
407
|
-
}
|
|
408
|
-
/** Check if a service is registered. */
|
|
409
|
-
has(key) {
|
|
410
|
-
return this.services.has(key.id);
|
|
411
|
-
}
|
|
412
|
-
};
|
|
413
|
-
var EngineKey = new ServiceKey("engine");
|
|
414
|
-
var EventBusKey = new ServiceKey("eventBus");
|
|
415
|
-
var SceneManagerKey = new ServiceKey("sceneManager");
|
|
416
|
-
var LoggerKey = new ServiceKey("logger");
|
|
417
|
-
var InspectorKey = new ServiceKey("inspector");
|
|
418
|
-
var QueryCacheKey = new ServiceKey("queryCache");
|
|
419
|
-
var ErrorBoundaryKey = new ServiceKey("errorBoundary");
|
|
420
|
-
var GameLoopKey = new ServiceKey("gameLoop");
|
|
421
|
-
var SystemSchedulerKey = new ServiceKey("systemScheduler");
|
|
422
|
-
var ProcessSystemKey = new ServiceKey("processSystem");
|
|
423
|
-
var AssetManagerKey = new ServiceKey("assetManager");
|
|
424
|
-
|
|
425
574
|
// src/SceneHooks.ts
|
|
426
575
|
var SceneHookRegistry = class {
|
|
427
576
|
static {
|
|
@@ -1158,6 +1307,7 @@ var Entity = class {
|
|
|
1158
1307
|
}
|
|
1159
1308
|
}
|
|
1160
1309
|
this._scene?._onEntityEvent(token.name, data, this);
|
|
1310
|
+
this._scene?._observeEntityEvent(token.name, data, this);
|
|
1161
1311
|
}
|
|
1162
1312
|
/** Get all components as an iterable. */
|
|
1163
1313
|
getAll() {
|
|
@@ -1580,85 +1730,884 @@ var GameLoop = class {
|
|
|
1580
1730
|
}
|
|
1581
1731
|
};
|
|
1582
1732
|
|
|
1583
|
-
// src/
|
|
1584
|
-
var
|
|
1733
|
+
// src/Inspector.ts
|
|
1734
|
+
var InputManagerRuntimeKey = new ServiceKey("inputManager");
|
|
1735
|
+
var PhysicsWorldManagerRuntimeKey = new ServiceKey(
|
|
1736
|
+
"physicsWorldManager"
|
|
1737
|
+
);
|
|
1738
|
+
var RendererRuntimeKey = new ServiceKey("renderer");
|
|
1739
|
+
var Inspector = class {
|
|
1585
1740
|
static {
|
|
1586
|
-
__name(this, "
|
|
1741
|
+
__name(this, "Inspector");
|
|
1587
1742
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1743
|
+
engine;
|
|
1744
|
+
extensions = /* @__PURE__ */ new Map();
|
|
1745
|
+
sceneIds = /* @__PURE__ */ new WeakMap();
|
|
1746
|
+
nextSceneId = 0;
|
|
1747
|
+
defaultSceneSeed;
|
|
1748
|
+
sceneSeedOverride;
|
|
1749
|
+
timeController = null;
|
|
1750
|
+
eventLogEnabled = false;
|
|
1751
|
+
eventCapacity = 500;
|
|
1752
|
+
/**
|
|
1753
|
+
* Ring buffer of recent events. `eventLogHead` points at the oldest slot;
|
|
1754
|
+
* a full ring contains exactly `eventCapacity` entries. We avoid `splice` to
|
|
1755
|
+
* keep `appendEvent` O(1) — the previous shift-on-overflow approach was
|
|
1756
|
+
* O(n) per event once the buffer was full.
|
|
1757
|
+
*/
|
|
1758
|
+
eventLog = [];
|
|
1759
|
+
eventLogHead = 0;
|
|
1760
|
+
eventWaiters = /* @__PURE__ */ new Set();
|
|
1761
|
+
detachBusTap = null;
|
|
1762
|
+
busEventObserver = /* @__PURE__ */ __name((event, data) => {
|
|
1763
|
+
this.recordBusEvent(String(event), data);
|
|
1764
|
+
}, "busEventObserver");
|
|
1765
|
+
sceneEventObserver = /* @__PURE__ */ __name((eventName, data, entity) => {
|
|
1766
|
+
this.recordEntityEvent(eventName, data, entity);
|
|
1767
|
+
}, "sceneEventObserver");
|
|
1768
|
+
time = {
|
|
1769
|
+
freeze: /* @__PURE__ */ __name(() => {
|
|
1770
|
+
this.requireTimeController().freeze();
|
|
1771
|
+
}, "freeze"),
|
|
1772
|
+
thaw: /* @__PURE__ */ __name(() => {
|
|
1773
|
+
this.requireTimeController().thaw();
|
|
1774
|
+
}, "thaw"),
|
|
1775
|
+
step: /* @__PURE__ */ __name((frames = 1) => {
|
|
1776
|
+
this.assertNonNegativeInteger(frames, "Inspector.time.step(frames)");
|
|
1777
|
+
if (frames === 0) return;
|
|
1778
|
+
this.requireTimeController().stepFrames(frames);
|
|
1779
|
+
this.expireDeadlineWaiters();
|
|
1780
|
+
}, "step"),
|
|
1781
|
+
setDelta: /* @__PURE__ */ __name((ms) => {
|
|
1782
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
1783
|
+
throw new Error("Inspector.time.setDelta(ms) requires a positive number.");
|
|
1784
|
+
}
|
|
1785
|
+
this.requireTimeController().setDelta(ms);
|
|
1786
|
+
}, "setDelta"),
|
|
1787
|
+
isFrozen: /* @__PURE__ */ __name(() => this.timeController?.isFrozen ?? false, "isFrozen"),
|
|
1788
|
+
getFrame: /* @__PURE__ */ __name(() => this.timeController?.getFrame() ?? this.engine.loop.frameCount, "getFrame")
|
|
1789
|
+
};
|
|
1790
|
+
input = {
|
|
1791
|
+
keyDown: /* @__PURE__ */ __name((code) => {
|
|
1792
|
+
this.requireInputManager().fireKeyDown(code);
|
|
1793
|
+
}, "keyDown"),
|
|
1794
|
+
keyUp: /* @__PURE__ */ __name((code) => {
|
|
1795
|
+
this.requireInputManager().fireKeyUp(code);
|
|
1796
|
+
}, "keyUp"),
|
|
1797
|
+
mouseMove: /* @__PURE__ */ __name((x, y) => {
|
|
1798
|
+
this.requireInputManager().firePointerMove(x, y);
|
|
1799
|
+
}, "mouseMove"),
|
|
1800
|
+
mouseDown: /* @__PURE__ */ __name((button = 0) => {
|
|
1801
|
+
this.requireInputManager().firePointerDown(button);
|
|
1802
|
+
}, "mouseDown"),
|
|
1803
|
+
mouseUp: /* @__PURE__ */ __name((button = 0) => {
|
|
1804
|
+
this.requireInputManager().firePointerUp(button);
|
|
1805
|
+
}, "mouseUp"),
|
|
1806
|
+
gamepadButton: /* @__PURE__ */ __name((idx, pressed) => {
|
|
1807
|
+
this.requireInputManager().fireGamepadButton(idx, pressed);
|
|
1808
|
+
}, "gamepadButton"),
|
|
1809
|
+
gamepadAxis: /* @__PURE__ */ __name((idx, value) => {
|
|
1810
|
+
this.requireInputManager().fireGamepadAxis(idx, value);
|
|
1811
|
+
}, "gamepadAxis"),
|
|
1812
|
+
tap: /* @__PURE__ */ __name((code, frames = 1) => {
|
|
1813
|
+
this.assertNonNegativeInteger(frames, "Inspector.input.tap(frames)");
|
|
1814
|
+
const input = this.requireInputManager();
|
|
1815
|
+
input.fireKeyDown(code);
|
|
1816
|
+
try {
|
|
1817
|
+
this.time.step(frames);
|
|
1818
|
+
} finally {
|
|
1819
|
+
input.fireKeyUp(code);
|
|
1820
|
+
}
|
|
1821
|
+
}, "tap"),
|
|
1822
|
+
hold: /* @__PURE__ */ __name((code, frames) => {
|
|
1823
|
+
this.assertNonNegativeInteger(frames, "Inspector.input.hold(frames)");
|
|
1824
|
+
const input = this.requireInputManager();
|
|
1825
|
+
input.fireKeyDown(code);
|
|
1826
|
+
try {
|
|
1827
|
+
this.time.step(frames);
|
|
1828
|
+
} finally {
|
|
1829
|
+
input.fireKeyUp(code);
|
|
1830
|
+
}
|
|
1831
|
+
}, "hold"),
|
|
1832
|
+
fireAction: /* @__PURE__ */ __name((name, frames = 1) => {
|
|
1833
|
+
this.assertNonNegativeInteger(
|
|
1834
|
+
frames,
|
|
1835
|
+
"Inspector.input.fireAction(frames)"
|
|
1836
|
+
);
|
|
1837
|
+
const input = this.requireInputManager();
|
|
1838
|
+
for (let i = 0; i < frames; i++) {
|
|
1839
|
+
input.fireAction(name);
|
|
1840
|
+
this.time.step(1);
|
|
1841
|
+
}
|
|
1842
|
+
}, "fireAction"),
|
|
1843
|
+
clearAll: /* @__PURE__ */ __name(() => {
|
|
1844
|
+
this.requireInputManager().clearAll();
|
|
1845
|
+
}, "clearAll")
|
|
1846
|
+
};
|
|
1847
|
+
events = {
|
|
1848
|
+
getLog: /* @__PURE__ */ __name(() => this.iterateLog().map(({ entry }) => ({ ...entry })), "getLog"),
|
|
1849
|
+
clearLog: /* @__PURE__ */ __name(() => {
|
|
1850
|
+
this.eventLog.length = 0;
|
|
1851
|
+
this.eventLogHead = 0;
|
|
1852
|
+
}, "clearLog"),
|
|
1853
|
+
setCapacity: /* @__PURE__ */ __name((n) => {
|
|
1854
|
+
this.assertNonNegativeInteger(
|
|
1855
|
+
n,
|
|
1856
|
+
"Inspector.events.setCapacity(capacity)"
|
|
1857
|
+
);
|
|
1858
|
+
const ordered = n === 0 ? [] : this.iterateLog().slice(-n);
|
|
1859
|
+
this.eventCapacity = n;
|
|
1860
|
+
this.eventLog = ordered;
|
|
1861
|
+
this.eventLogHead = 0;
|
|
1862
|
+
}, "setCapacity"),
|
|
1863
|
+
waitFor: /* @__PURE__ */ __name((pattern, options) => {
|
|
1864
|
+
const existing = this.findMatchingEvent(pattern, options?.source);
|
|
1865
|
+
if (existing) return Promise.resolve(existing);
|
|
1866
|
+
const withinFrames = options?.withinFrames;
|
|
1867
|
+
if (withinFrames !== void 0 && (!Number.isInteger(withinFrames) || withinFrames < 0)) {
|
|
1868
|
+
throw new Error(
|
|
1869
|
+
"Inspector.events.waitFor(withinFrames) requires a non-negative integer."
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
return new Promise((resolve, reject) => {
|
|
1873
|
+
const waiter = {
|
|
1874
|
+
pattern,
|
|
1875
|
+
source: options?.source,
|
|
1876
|
+
withinFrames,
|
|
1877
|
+
deadlineFrame: withinFrames !== void 0 ? this.time.getFrame() + withinFrames : void 0,
|
|
1878
|
+
resolve,
|
|
1879
|
+
reject
|
|
1880
|
+
};
|
|
1881
|
+
this.eventWaiters.add(waiter);
|
|
1882
|
+
});
|
|
1883
|
+
}, "waitFor")
|
|
1884
|
+
};
|
|
1885
|
+
capture = {
|
|
1886
|
+
png: /* @__PURE__ */ __name(async () => {
|
|
1887
|
+
const base64 = await this.capture.pngBase64();
|
|
1888
|
+
return decodeBase64(base64);
|
|
1889
|
+
}, "png"),
|
|
1890
|
+
dataURL: /* @__PURE__ */ __name(async () => {
|
|
1891
|
+
const renderer = this.engine.context.tryResolve(RendererRuntimeKey);
|
|
1892
|
+
if (!renderer) {
|
|
1893
|
+
throw new Error(
|
|
1894
|
+
"Inspector.capture requires RendererPlugin to be active."
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
const canvas = renderer.application.renderer.extract.canvas(
|
|
1898
|
+
renderer.application.stage
|
|
1899
|
+
);
|
|
1900
|
+
return canvas.toDataURL("image/png");
|
|
1901
|
+
}, "dataURL"),
|
|
1902
|
+
pngBase64: /* @__PURE__ */ __name(async () => {
|
|
1903
|
+
const dataUrl = await this.capture.dataURL();
|
|
1904
|
+
const comma = dataUrl.indexOf(",");
|
|
1905
|
+
return comma === -1 ? dataUrl : dataUrl.slice(comma + 1);
|
|
1906
|
+
}, "pngBase64")
|
|
1907
|
+
};
|
|
1908
|
+
constructor(engine) {
|
|
1909
|
+
this.engine = engine;
|
|
1611
1910
|
}
|
|
1612
|
-
/**
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
for (let i = idx + 1; i < stack.length; i++) {
|
|
1621
|
-
if (stack[i].pauseBelow) return true;
|
|
1911
|
+
/** Register a namespaced extension API for plugin-specific debug helpers. */
|
|
1912
|
+
addExtension(namespace, api) {
|
|
1913
|
+
this.assertNonEmptyString(
|
|
1914
|
+
namespace,
|
|
1915
|
+
"Inspector.addExtension(namespace)"
|
|
1916
|
+
);
|
|
1917
|
+
if (!api || typeof api !== "object") {
|
|
1918
|
+
throw new Error("Inspector.addExtension(api) requires an object.");
|
|
1622
1919
|
}
|
|
1623
|
-
|
|
1920
|
+
if (this.extensions.has(namespace)) {
|
|
1921
|
+
throw new Error(
|
|
1922
|
+
`Inspector.addExtension(): namespace "${namespace}" is already registered.`
|
|
1923
|
+
);
|
|
1924
|
+
}
|
|
1925
|
+
this.extensions.set(namespace, api);
|
|
1926
|
+
return api;
|
|
1927
|
+
}
|
|
1928
|
+
/** Look up a previously registered extension API by namespace. */
|
|
1929
|
+
getExtension(namespace) {
|
|
1930
|
+
this.assertNonEmptyString(
|
|
1931
|
+
namespace,
|
|
1932
|
+
"Inspector.getExtension(namespace)"
|
|
1933
|
+
);
|
|
1934
|
+
return this.extensions.get(namespace);
|
|
1624
1935
|
}
|
|
1625
|
-
/**
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1936
|
+
/** Remove a previously registered extension namespace. */
|
|
1937
|
+
removeExtension(namespace) {
|
|
1938
|
+
this.assertNonEmptyString(
|
|
1939
|
+
namespace,
|
|
1940
|
+
"Inspector.removeExtension(namespace)"
|
|
1941
|
+
);
|
|
1942
|
+
this.extensions.delete(namespace);
|
|
1629
1943
|
}
|
|
1630
|
-
/**
|
|
1631
|
-
|
|
1632
|
-
|
|
1944
|
+
/** Full deterministic state snapshot (stable ordering, serializable). */
|
|
1945
|
+
snapshot() {
|
|
1946
|
+
const scenes = this.engine.scenes.all.map(
|
|
1947
|
+
(scene) => this.sceneToWorldSnapshot(scene)
|
|
1948
|
+
);
|
|
1949
|
+
return {
|
|
1950
|
+
version: 1,
|
|
1951
|
+
frame: this.time.getFrame(),
|
|
1952
|
+
sceneStack: this.getSceneStack(),
|
|
1953
|
+
entityCount: this.countEntities(),
|
|
1954
|
+
systemCount: this.getSystems().length,
|
|
1955
|
+
errors: this.getErrors(),
|
|
1956
|
+
scenes,
|
|
1957
|
+
camera: this.buildCameraSnapshot(),
|
|
1958
|
+
input: this.buildInputSnapshot()
|
|
1959
|
+
};
|
|
1633
1960
|
}
|
|
1634
|
-
/**
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
* readonly layers = this.service(RenderLayerManagerKey);
|
|
1638
|
-
* ```
|
|
1639
|
-
* The actual resolution is deferred until first property access.
|
|
1640
|
-
*/
|
|
1641
|
-
service(key) {
|
|
1642
|
-
let resolved;
|
|
1643
|
-
return new Proxy({}, {
|
|
1644
|
-
get: /* @__PURE__ */ __name((_target, prop) => {
|
|
1645
|
-
resolved ??= this._context.resolve(key);
|
|
1646
|
-
const value = resolved[prop];
|
|
1647
|
-
return typeof value === "function" ? value.bind(resolved) : value;
|
|
1648
|
-
}, "get"),
|
|
1649
|
-
set: /* @__PURE__ */ __name((_target, prop, value) => {
|
|
1650
|
-
resolved ??= this._context.resolve(key);
|
|
1651
|
-
resolved[prop] = value;
|
|
1652
|
-
return true;
|
|
1653
|
-
}, "set")
|
|
1654
|
-
});
|
|
1961
|
+
/** Stable JSON form of {@link snapshot}. */
|
|
1962
|
+
snapshotJSON() {
|
|
1963
|
+
return stableStringify(this.snapshot());
|
|
1655
1964
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1965
|
+
/** Snapshot one scene by inspector scene id. */
|
|
1966
|
+
snapshotScene(id) {
|
|
1967
|
+
const scene = this.engine.scenes.all.find(
|
|
1968
|
+
(candidate) => this.getSceneId(candidate) === id
|
|
1969
|
+
);
|
|
1970
|
+
if (!scene) {
|
|
1971
|
+
throw new Error(`Inspector.snapshotScene(): unknown scene id "${id}".`);
|
|
1972
|
+
}
|
|
1973
|
+
return this.sceneToWorldSnapshot(scene);
|
|
1974
|
+
}
|
|
1975
|
+
/** Find entity by name in the active scene. */
|
|
1976
|
+
getEntityByName(name) {
|
|
1977
|
+
const entity = this.findActiveEntity(name);
|
|
1978
|
+
if (!entity) return void 0;
|
|
1979
|
+
return this.entityToQuerySnapshot(entity);
|
|
1980
|
+
}
|
|
1981
|
+
/** Get entity position (from Transform component). */
|
|
1982
|
+
getEntityPosition(name) {
|
|
1983
|
+
const entity = this.findActiveEntity(name);
|
|
1984
|
+
if (!entity) return void 0;
|
|
1985
|
+
const transform = this.getTransform(entity);
|
|
1986
|
+
if (!transform) return void 0;
|
|
1987
|
+
return { x: transform.position.x, y: transform.position.y };
|
|
1988
|
+
}
|
|
1989
|
+
/** Check if an entity has a component by class name string. */
|
|
1990
|
+
hasComponent(entityName, componentClass) {
|
|
1991
|
+
return this.findComponentByName(entityName, componentClass) !== void 0;
|
|
1992
|
+
}
|
|
1993
|
+
/** Get component data (serializable subset) by class name string. */
|
|
1994
|
+
getComponentData(entityName, componentClass) {
|
|
1995
|
+
const comp = this.findComponentByName(entityName, componentClass);
|
|
1996
|
+
if (!comp) return void 0;
|
|
1997
|
+
if (typeof comp.serialize === "function") {
|
|
1998
|
+
const data = trySerialize(comp);
|
|
1999
|
+
if (data !== void 0) return data;
|
|
2000
|
+
}
|
|
2001
|
+
return this.serializeComponentOwnProperties(comp);
|
|
2002
|
+
}
|
|
2003
|
+
/** Get all entities in the active scene as lightweight snapshots. */
|
|
2004
|
+
getEntities() {
|
|
2005
|
+
const scene = this.engine.scenes.active;
|
|
2006
|
+
if (!scene) return [];
|
|
2007
|
+
const result = [];
|
|
2008
|
+
for (const entity of scene.getEntities()) {
|
|
2009
|
+
if (!entity.isDestroyed) {
|
|
2010
|
+
result.push(this.entityToQuerySnapshot(entity));
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
return result;
|
|
2014
|
+
}
|
|
2015
|
+
/** Get scene stack info. */
|
|
2016
|
+
getSceneStack() {
|
|
2017
|
+
return this.engine.scenes.all.map((scene) => ({
|
|
2018
|
+
name: scene.name,
|
|
2019
|
+
entityCount: scene.getEntities().size,
|
|
2020
|
+
paused: scene.isPaused
|
|
2021
|
+
}));
|
|
2022
|
+
}
|
|
2023
|
+
/** Get active system info. */
|
|
2024
|
+
getSystems() {
|
|
2025
|
+
const scheduler = this.engine.context.tryResolve(SystemSchedulerKey);
|
|
2026
|
+
if (!scheduler) return [];
|
|
2027
|
+
return scheduler.getAllSystems().map((sys) => ({
|
|
2028
|
+
name: sys.constructor.name,
|
|
2029
|
+
phase: sys.phase,
|
|
2030
|
+
priority: sys.priority,
|
|
2031
|
+
enabled: sys.enabled
|
|
2032
|
+
}));
|
|
2033
|
+
}
|
|
2034
|
+
/** Get disabled components/systems from error boundary. */
|
|
2035
|
+
getErrors() {
|
|
2036
|
+
const boundary = this.engine.context.tryResolve(ErrorBoundaryKey);
|
|
2037
|
+
if (!boundary) return { disabledSystems: [], disabledComponents: [] };
|
|
2038
|
+
const disabled = boundary.getDisabled();
|
|
2039
|
+
return {
|
|
2040
|
+
disabledSystems: disabled.systems.map(
|
|
2041
|
+
(s) => s.system.constructor.name
|
|
2042
|
+
),
|
|
2043
|
+
disabledComponents: disabled.components.map((c) => ({
|
|
2044
|
+
entity: c.component.entity?.name ?? "unknown",
|
|
2045
|
+
component: c.component.constructor.name,
|
|
2046
|
+
error: c.error
|
|
2047
|
+
}))
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
/** Create a new scene-scoped RNG instance using the current inspector seed policy. */
|
|
2051
|
+
createSceneRandom() {
|
|
2052
|
+
const seed = this.sceneSeedOverride ?? this.defaultSceneSeed ?? createDefaultRandomSeed();
|
|
2053
|
+
return createRandomService(seed);
|
|
2054
|
+
}
|
|
2055
|
+
/** Force every current and future scene RNG to the provided seed. */
|
|
2056
|
+
setSeed(seed) {
|
|
2057
|
+
const normalized = normalizeSeed(seed);
|
|
2058
|
+
this.sceneSeedOverride = normalized;
|
|
2059
|
+
for (const scene of this.engine.scenes.all) {
|
|
2060
|
+
this.resolveInternalRandom(scene)?.setSeed(normalized);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
/** @internal DebugPlugin installs a deterministic default seed through this hook. */
|
|
2064
|
+
setDefaultSceneSeed(seed) {
|
|
2065
|
+
this.defaultSceneSeed = seed === void 0 ? void 0 : normalizeSeed(seed);
|
|
2066
|
+
if (this.sceneSeedOverride !== void 0 || this.defaultSceneSeed === void 0) {
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
for (const scene of this.engine.scenes.all) {
|
|
2070
|
+
this.resolveInternalRandom(scene)?.setSeed(this.defaultSceneSeed);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
resolveInternalRandom(scene) {
|
|
2074
|
+
return scene._resolveScoped(RandomKey);
|
|
2075
|
+
}
|
|
2076
|
+
/** @internal DebugPlugin attaches the frozen-time controller through this hook. */
|
|
2077
|
+
attachTimeController(controller) {
|
|
2078
|
+
this.timeController = controller;
|
|
2079
|
+
}
|
|
2080
|
+
/** @internal Clear a previously attached time controller. */
|
|
2081
|
+
detachTimeController(controller) {
|
|
2082
|
+
if (!controller || this.timeController === controller) {
|
|
2083
|
+
this.timeController = null;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
/** @internal Enable or disable event log recording. */
|
|
2087
|
+
setEventLogEnabled(enabled) {
|
|
2088
|
+
if (this.eventLogEnabled === enabled) return;
|
|
2089
|
+
this.eventLogEnabled = enabled;
|
|
2090
|
+
if (enabled) {
|
|
2091
|
+
if (!this.detachBusTap && this.engine.events?.tap) {
|
|
2092
|
+
this.detachBusTap = this.engine.events.tap(this.busEventObserver);
|
|
2093
|
+
}
|
|
2094
|
+
} else {
|
|
2095
|
+
this.detachBusTap?.();
|
|
2096
|
+
this.detachBusTap = null;
|
|
2097
|
+
}
|
|
2098
|
+
for (const scene of this.engine.scenes.all) {
|
|
2099
|
+
if (enabled) {
|
|
2100
|
+
this.attachSceneEventObserver(scene);
|
|
2101
|
+
} else {
|
|
2102
|
+
this.detachSceneEventObserver(scene);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
/** @internal Install entity-event observation for one scene. No-op if disabled. */
|
|
2107
|
+
attachSceneEventObserver(scene) {
|
|
2108
|
+
if (!this.eventLogEnabled) return;
|
|
2109
|
+
scene._setEntityEventObserver(this.sceneEventObserver);
|
|
2110
|
+
}
|
|
2111
|
+
/** @internal Clear entity-event observation for one scene. */
|
|
2112
|
+
detachSceneEventObserver(scene) {
|
|
2113
|
+
scene._setEntityEventObserver(void 0);
|
|
2114
|
+
}
|
|
2115
|
+
/** @internal Scene hooks forward entity events through this method. */
|
|
2116
|
+
recordEntityEvent(eventName, data, entity) {
|
|
2117
|
+
if (!this.eventLogEnabled) return;
|
|
2118
|
+
const scene = entity.tryScene;
|
|
2119
|
+
this.appendEvent(
|
|
2120
|
+
{
|
|
2121
|
+
frame: this.time.getFrame(),
|
|
2122
|
+
source: "entity",
|
|
2123
|
+
type: eventName,
|
|
2124
|
+
targetId: String(entity.id),
|
|
2125
|
+
payload: serializeEventPayload(data)
|
|
2126
|
+
},
|
|
2127
|
+
scene ? this.getSceneId(scene) : void 0
|
|
2128
|
+
);
|
|
2129
|
+
}
|
|
2130
|
+
/** @internal Engine teardown releases the event-bus tap through this hook. */
|
|
2131
|
+
dispose() {
|
|
2132
|
+
this.detachBusTap?.();
|
|
2133
|
+
this.detachBusTap = null;
|
|
2134
|
+
for (const scene of this.engine.scenes.all) {
|
|
2135
|
+
scene._setEntityEventObserver(void 0);
|
|
2136
|
+
}
|
|
2137
|
+
this.extensions.clear();
|
|
2138
|
+
}
|
|
2139
|
+
requireTimeController() {
|
|
2140
|
+
if (!this.timeController) {
|
|
2141
|
+
throw new Error(
|
|
2142
|
+
"Inspector.time requires DebugPlugin to be active."
|
|
2143
|
+
);
|
|
2144
|
+
}
|
|
2145
|
+
return this.timeController;
|
|
2146
|
+
}
|
|
2147
|
+
requireInputManager() {
|
|
2148
|
+
const input = this.engine.context.tryResolve(InputManagerRuntimeKey);
|
|
2149
|
+
if (!input) {
|
|
2150
|
+
throw new Error(
|
|
2151
|
+
"Inspector.input requires InputPlugin to be active."
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
return input;
|
|
2155
|
+
}
|
|
2156
|
+
recordBusEvent(type, data) {
|
|
2157
|
+
if (!this.eventLogEnabled) return;
|
|
2158
|
+
this.appendEvent(
|
|
2159
|
+
{
|
|
2160
|
+
frame: this.time.getFrame(),
|
|
2161
|
+
source: "bus",
|
|
2162
|
+
type,
|
|
2163
|
+
payload: serializeEventPayload(data)
|
|
2164
|
+
},
|
|
2165
|
+
this.inferSceneIdFromPayload(data)
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2168
|
+
appendEvent(entry, sceneId) {
|
|
2169
|
+
if (this.eventCapacity === 0) {
|
|
2170
|
+
this.flushMatchingWaiter(entry);
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
const logged = { entry, sceneId };
|
|
2174
|
+
if (this.eventLog.length < this.eventCapacity) {
|
|
2175
|
+
this.eventLog.push(logged);
|
|
2176
|
+
} else {
|
|
2177
|
+
this.eventLog[this.eventLogHead] = logged;
|
|
2178
|
+
this.eventLogHead = (this.eventLogHead + 1) % this.eventCapacity;
|
|
2179
|
+
}
|
|
2180
|
+
this.flushMatchingWaiter(entry);
|
|
2181
|
+
}
|
|
2182
|
+
/** Resolve waiters whose deadline has passed without a match. */
|
|
2183
|
+
expireDeadlineWaiters() {
|
|
2184
|
+
if (this.eventWaiters.size === 0) return;
|
|
2185
|
+
const frame = this.time.getFrame();
|
|
2186
|
+
for (const waiter of [...this.eventWaiters]) {
|
|
2187
|
+
if (waiter.deadlineFrame !== void 0 && frame > waiter.deadlineFrame) {
|
|
2188
|
+
this.eventWaiters.delete(waiter);
|
|
2189
|
+
waiter.reject(
|
|
2190
|
+
new Error(
|
|
2191
|
+
`Inspector.events.waitFor() timed out after ${waiter.withinFrames} frames.`
|
|
2192
|
+
)
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
/** Resolve any waiter that matches the just-appended entry. */
|
|
2198
|
+
flushMatchingWaiter(entry) {
|
|
2199
|
+
if (this.eventWaiters.size === 0) return;
|
|
2200
|
+
for (const waiter of [...this.eventWaiters]) {
|
|
2201
|
+
if (this.eventMatches(entry, waiter.pattern, waiter.source)) {
|
|
2202
|
+
this.eventWaiters.delete(waiter);
|
|
2203
|
+
waiter.resolve(entry);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Walk the ring buffer in chronological order. We avoid materializing the
|
|
2209
|
+
* ordered array on every event append; instead, every consumer that needs
|
|
2210
|
+
* order calls this helper.
|
|
2211
|
+
*/
|
|
2212
|
+
iterateLog() {
|
|
2213
|
+
if (this.eventLog.length < this.eventCapacity || this.eventLogHead === 0) {
|
|
2214
|
+
return this.eventLog;
|
|
2215
|
+
}
|
|
2216
|
+
return [
|
|
2217
|
+
...this.eventLog.slice(this.eventLogHead),
|
|
2218
|
+
...this.eventLog.slice(0, this.eventLogHead)
|
|
2219
|
+
];
|
|
2220
|
+
}
|
|
2221
|
+
findMatchingEvent(pattern, source) {
|
|
2222
|
+
for (const { entry } of this.iterateLog()) {
|
|
2223
|
+
if (this.eventMatches(entry, pattern, source)) {
|
|
2224
|
+
return { ...entry };
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
return void 0;
|
|
2228
|
+
}
|
|
2229
|
+
eventMatches(entry, pattern, source) {
|
|
2230
|
+
if (source && entry.source !== source) return false;
|
|
2231
|
+
return typeof pattern === "string" ? entry.type === pattern : pattern.test(entry.type);
|
|
2232
|
+
}
|
|
2233
|
+
sceneToWorldSnapshot(scene) {
|
|
2234
|
+
const random = scene._resolveScoped(RandomKey);
|
|
2235
|
+
const physicsManager = this.engine.context.tryResolve(
|
|
2236
|
+
PhysicsWorldManagerRuntimeKey
|
|
2237
|
+
);
|
|
2238
|
+
return {
|
|
2239
|
+
id: this.getSceneId(scene),
|
|
2240
|
+
name: scene.name,
|
|
2241
|
+
paused: scene.isPaused,
|
|
2242
|
+
timeScale: scene.timeScale,
|
|
2243
|
+
seed: random?.getSeed() ?? 0,
|
|
2244
|
+
entities: this.getSceneEntities(scene),
|
|
2245
|
+
ui: this.buildUISnapshot(scene),
|
|
2246
|
+
physics: physicsManager?.getContext(scene)?.world.snapshot() ?? {
|
|
2247
|
+
bodies: [],
|
|
2248
|
+
contacts: []
|
|
2249
|
+
},
|
|
2250
|
+
events: this.getSceneEvents(scene)
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
getSceneEntities(scene) {
|
|
2254
|
+
return [...scene.getEntities()].filter((entity) => !entity.isDestroyed).sort((a, b) => a.id - b.id).map((entity) => this.entityToWorldSnapshot(entity));
|
|
2255
|
+
}
|
|
2256
|
+
entityToWorldSnapshot(entity) {
|
|
2257
|
+
const transform = entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
2258
|
+
const worldPosition = transform?.worldPosition;
|
|
2259
|
+
const worldScale = transform?.worldScale;
|
|
2260
|
+
const components = [...entity.getAll()].map((component) => this.componentToSnapshot(component)).sort((a, b) => a.type < b.type ? -1 : a.type > b.type ? 1 : 0);
|
|
2261
|
+
return {
|
|
2262
|
+
id: String(entity.id),
|
|
2263
|
+
type: entity.constructor.name,
|
|
2264
|
+
parent: entity.parent ? String(entity.parent.id) : null,
|
|
2265
|
+
transform: {
|
|
2266
|
+
x: worldPosition?.x ?? 0,
|
|
2267
|
+
y: worldPosition?.y ?? 0,
|
|
2268
|
+
rotation: transform?.worldRotation ?? 0,
|
|
2269
|
+
scaleX: worldScale?.x ?? 1,
|
|
2270
|
+
scaleY: worldScale?.y ?? 1
|
|
2271
|
+
},
|
|
2272
|
+
components
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
componentToSnapshot(component) {
|
|
2276
|
+
return {
|
|
2277
|
+
type: component.constructor.name,
|
|
2278
|
+
state: typeof component.serialize === "function" ? trySerialize(component) ?? null : null
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
buildUISnapshot(scene) {
|
|
2282
|
+
const roots = [...scene.getEntities()].filter((entity) => !entity.isDestroyed).flatMap(
|
|
2283
|
+
(entity) => [...entity.getAll()].filter(
|
|
2284
|
+
(component) => component.constructor.name === "UIPanel" && "_node" in component
|
|
2285
|
+
).map(
|
|
2286
|
+
(component, index) => this.buildUINodeSnapshot(
|
|
2287
|
+
component._node,
|
|
2288
|
+
`entity-${entity.id}:UIPanel:${index}`
|
|
2289
|
+
)
|
|
2290
|
+
)
|
|
2291
|
+
);
|
|
2292
|
+
if (roots.length === 0) return null;
|
|
2293
|
+
if (roots.length === 1) {
|
|
2294
|
+
return { root: roots[0] };
|
|
2295
|
+
}
|
|
2296
|
+
return {
|
|
2297
|
+
root: {
|
|
2298
|
+
id: `scene-${this.getSceneId(scene)}:ui`,
|
|
2299
|
+
type: "UIRoot",
|
|
2300
|
+
layout: { x: 0, y: 0, width: 0, height: 0 },
|
|
2301
|
+
children: roots,
|
|
2302
|
+
state: null
|
|
2303
|
+
}
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
buildUINodeSnapshot(node, id) {
|
|
2307
|
+
const layout = node.yogaNode?.getComputedLayout();
|
|
2308
|
+
const children = (node.children ?? []).map(
|
|
2309
|
+
(child, index) => this.buildUINodeSnapshot(child, `${id}/${index}`)
|
|
2310
|
+
);
|
|
2311
|
+
return {
|
|
2312
|
+
id,
|
|
2313
|
+
type: node.constructor.name,
|
|
2314
|
+
layout: {
|
|
2315
|
+
x: layout?.left ?? 0,
|
|
2316
|
+
y: layout?.top ?? 0,
|
|
2317
|
+
width: layout?.width ?? 0,
|
|
2318
|
+
height: layout?.height ?? 0
|
|
2319
|
+
},
|
|
2320
|
+
children,
|
|
2321
|
+
state: null
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
buildCameraSnapshot() {
|
|
2325
|
+
const match = this.findTopmostCamera();
|
|
2326
|
+
if (!match) return null;
|
|
2327
|
+
const { scene, camera } = match;
|
|
2328
|
+
return {
|
|
2329
|
+
sceneId: this.getSceneId(scene),
|
|
2330
|
+
sceneName: scene.name,
|
|
2331
|
+
name: camera.cameraName ?? null,
|
|
2332
|
+
priority: camera.priority ?? 0,
|
|
2333
|
+
position: {
|
|
2334
|
+
x: camera.position.x,
|
|
2335
|
+
y: camera.position.y
|
|
2336
|
+
},
|
|
2337
|
+
zoom: camera.zoom,
|
|
2338
|
+
rotation: camera.rotation
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
findTopmostCamera() {
|
|
2342
|
+
const stack = this.engine.scenes.all;
|
|
2343
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
2344
|
+
const scene = stack[i];
|
|
2345
|
+
if (!scene) continue;
|
|
2346
|
+
let highest;
|
|
2347
|
+
for (const entity of scene.getEntities()) {
|
|
2348
|
+
if (entity.isDestroyed) continue;
|
|
2349
|
+
for (const component of entity.getAll()) {
|
|
2350
|
+
if (component.constructor.name !== "CameraComponent") continue;
|
|
2351
|
+
const camera = component;
|
|
2352
|
+
if (camera.enabled && (!highest || (camera.priority ?? 0) > (highest.priority ?? 0))) {
|
|
2353
|
+
highest = camera;
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
if (highest) {
|
|
2358
|
+
return { scene, camera: highest };
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
return void 0;
|
|
2362
|
+
}
|
|
2363
|
+
buildInputSnapshot() {
|
|
2364
|
+
const input = this.engine.context.tryResolve(InputManagerRuntimeKey);
|
|
2365
|
+
return input?.snapshotState() ?? {
|
|
2366
|
+
keys: [],
|
|
2367
|
+
actions: [],
|
|
2368
|
+
mouse: { x: 0, y: 0, buttons: [], down: false },
|
|
2369
|
+
gamepad: { buttons: [], axes: [] }
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
getSceneEvents(scene) {
|
|
2373
|
+
const sceneId = this.getSceneId(scene);
|
|
2374
|
+
return this.iterateLog().filter((entry) => entry.sceneId === sceneId).map(({ entry }) => ({ ...entry }));
|
|
2375
|
+
}
|
|
2376
|
+
inferSceneIdFromPayload(data) {
|
|
2377
|
+
if (!data || typeof data !== "object") return void 0;
|
|
2378
|
+
const record = data;
|
|
2379
|
+
const scene = this.extractScene(record["scene"]) ?? this.extractSceneFromEntity(record["entity"]) ?? this.extractSceneFromEntity(record["oldScene"]) ?? this.extractSceneFromEntity(record["newScene"]);
|
|
2380
|
+
return scene ? this.getSceneId(scene) : void 0;
|
|
2381
|
+
}
|
|
2382
|
+
extractScene(value) {
|
|
2383
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2384
|
+
return this.engine.scenes.all.find((scene) => scene === value);
|
|
2385
|
+
}
|
|
2386
|
+
extractSceneFromEntity(value) {
|
|
2387
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2388
|
+
const maybeEntity = value;
|
|
2389
|
+
return maybeEntity.tryScene ?? this.extractScene(value);
|
|
2390
|
+
}
|
|
2391
|
+
findActiveEntity(name) {
|
|
2392
|
+
return this.engine.scenes.active?.findEntity(name);
|
|
2393
|
+
}
|
|
2394
|
+
findComponentByName(entityName, componentClass) {
|
|
2395
|
+
const entity = this.findActiveEntity(entityName);
|
|
2396
|
+
if (!entity) return void 0;
|
|
2397
|
+
for (const comp of entity.getAll()) {
|
|
2398
|
+
if (comp.constructor.name === componentClass) return comp;
|
|
2399
|
+
}
|
|
2400
|
+
return void 0;
|
|
2401
|
+
}
|
|
2402
|
+
entityToQuerySnapshot(entity) {
|
|
2403
|
+
const transform = this.getTransform(entity);
|
|
2404
|
+
const snapshot = {
|
|
2405
|
+
id: entity.id,
|
|
2406
|
+
name: entity.name,
|
|
2407
|
+
tags: [...entity.tags].sort((a, b) => a < b ? -1 : a > b ? 1 : 0),
|
|
2408
|
+
components: [...entity.getAll()].map((component) => component.constructor.name).sort((a, b) => a < b ? -1 : a > b ? 1 : 0)
|
|
2409
|
+
};
|
|
2410
|
+
if (transform) {
|
|
2411
|
+
snapshot.position = {
|
|
2412
|
+
x: transform.position.x,
|
|
2413
|
+
y: transform.position.y
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
return snapshot;
|
|
2417
|
+
}
|
|
2418
|
+
getTransform(entity) {
|
|
2419
|
+
return entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
2420
|
+
}
|
|
2421
|
+
serializeComponentOwnProperties(comp) {
|
|
2422
|
+
const result = {};
|
|
2423
|
+
for (const key of Object.getOwnPropertyNames(comp)) {
|
|
2424
|
+
if (key === "entity") continue;
|
|
2425
|
+
if (key.startsWith("_")) continue;
|
|
2426
|
+
const value = comp[key];
|
|
2427
|
+
if (!isSerializableValue(value)) continue;
|
|
2428
|
+
result[key] = value;
|
|
2429
|
+
}
|
|
2430
|
+
return result;
|
|
2431
|
+
}
|
|
2432
|
+
countEntities() {
|
|
2433
|
+
let count = 0;
|
|
2434
|
+
for (const scene of this.engine.scenes.all) {
|
|
2435
|
+
for (const entity of scene.getEntities()) {
|
|
2436
|
+
if (!entity.isDestroyed) count++;
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
return count;
|
|
2440
|
+
}
|
|
2441
|
+
getSceneId(scene) {
|
|
2442
|
+
let id = this.sceneIds.get(scene);
|
|
2443
|
+
if (!id) {
|
|
2444
|
+
this.nextSceneId++;
|
|
2445
|
+
id = `scene-${this.nextSceneId}`;
|
|
2446
|
+
this.sceneIds.set(scene, id);
|
|
2447
|
+
}
|
|
2448
|
+
return id;
|
|
2449
|
+
}
|
|
2450
|
+
assertNonNegativeInteger(value, name) {
|
|
2451
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
2452
|
+
throw new Error(`${name} requires a non-negative integer.`);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
assertNonEmptyString(value, name) {
|
|
2456
|
+
if (value.trim().length === 0) {
|
|
2457
|
+
throw new Error(`${name} requires a non-empty string.`);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
};
|
|
2461
|
+
function isSerializableValue(value) {
|
|
2462
|
+
if (value === null || value === void 0) return true;
|
|
2463
|
+
const t = typeof value;
|
|
2464
|
+
if (t === "function") return false;
|
|
2465
|
+
if (t !== "object") return true;
|
|
2466
|
+
if (Array.isArray(value)) return true;
|
|
2467
|
+
const proto = Object.getPrototypeOf(value);
|
|
2468
|
+
return proto === Object.prototype || proto === null;
|
|
2469
|
+
}
|
|
2470
|
+
__name(isSerializableValue, "isSerializableValue");
|
|
2471
|
+
function safeClone(value) {
|
|
2472
|
+
try {
|
|
2473
|
+
return JSON.parse(JSON.stringify(value));
|
|
2474
|
+
} catch {
|
|
2475
|
+
return void 0;
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
__name(safeClone, "safeClone");
|
|
2479
|
+
function trySerialize(component) {
|
|
2480
|
+
try {
|
|
2481
|
+
return safeClone(component.serialize?.());
|
|
2482
|
+
} catch {
|
|
2483
|
+
return void 0;
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
__name(trySerialize, "trySerialize");
|
|
2487
|
+
function serializeEventPayload(payload) {
|
|
2488
|
+
if (payload === void 0) return null;
|
|
2489
|
+
const cloned = safeClone(payload);
|
|
2490
|
+
return cloned === void 0 ? { _unserializable: true } : cloned;
|
|
2491
|
+
}
|
|
2492
|
+
__name(serializeEventPayload, "serializeEventPayload");
|
|
2493
|
+
function stableStringify(value) {
|
|
2494
|
+
return JSON.stringify(sortJsonValue(value));
|
|
2495
|
+
}
|
|
2496
|
+
__name(stableStringify, "stableStringify");
|
|
2497
|
+
function sortJsonValue(value) {
|
|
2498
|
+
if (Array.isArray(value)) {
|
|
2499
|
+
return value.map((item) => sortJsonValue(item));
|
|
2500
|
+
}
|
|
2501
|
+
if (value && typeof value === "object") {
|
|
2502
|
+
const entries = Object.entries(value).sort(
|
|
2503
|
+
([left], [right]) => left < right ? -1 : left > right ? 1 : 0
|
|
2504
|
+
);
|
|
2505
|
+
const result = {};
|
|
2506
|
+
for (const [key, child] of entries) {
|
|
2507
|
+
result[key] = sortJsonValue(child);
|
|
2508
|
+
}
|
|
2509
|
+
return result;
|
|
2510
|
+
}
|
|
2511
|
+
return value;
|
|
2512
|
+
}
|
|
2513
|
+
__name(sortJsonValue, "sortJsonValue");
|
|
2514
|
+
function decodeBase64(base64) {
|
|
2515
|
+
if (typeof atob === "function") {
|
|
2516
|
+
const binary = atob(base64);
|
|
2517
|
+
const bytes = new Uint8Array(binary.length);
|
|
2518
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2519
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2520
|
+
}
|
|
2521
|
+
return bytes;
|
|
2522
|
+
}
|
|
2523
|
+
const bufferCtor = globalThis.Buffer;
|
|
2524
|
+
if (bufferCtor) {
|
|
2525
|
+
return bufferCtor.from(base64, "base64");
|
|
2526
|
+
}
|
|
2527
|
+
throw new Error("Inspector.capture.png() is not supported in this environment.");
|
|
2528
|
+
}
|
|
2529
|
+
__name(decodeBase64, "decodeBase64");
|
|
2530
|
+
|
|
2531
|
+
// src/Scene.ts
|
|
2532
|
+
var Scene = class {
|
|
2533
|
+
static {
|
|
2534
|
+
__name(this, "Scene");
|
|
2535
|
+
}
|
|
2536
|
+
/** Whether scenes below this one in the stack should be paused. Default: true. */
|
|
2537
|
+
pauseBelow = true;
|
|
2538
|
+
/** Whether scenes below this one should still render. Default: false. */
|
|
2539
|
+
transparentBelow = false;
|
|
2540
|
+
/** Asset handles to load before onEnter(). Override in subclasses. */
|
|
2541
|
+
preload;
|
|
2542
|
+
/** Default transition used when this scene is the destination of a push/pop/replace. */
|
|
2543
|
+
defaultTransition;
|
|
2544
|
+
/** Manual pause flag. Set by game code to pause this scene regardless of stack position. */
|
|
2545
|
+
paused = false;
|
|
2546
|
+
/** Time scale multiplier for this scene. 1.0 = normal, 0.5 = half speed. Default: 1. */
|
|
2547
|
+
timeScale = 1;
|
|
2548
|
+
entities = /* @__PURE__ */ new Set();
|
|
2549
|
+
destroyQueue = [];
|
|
2550
|
+
_context;
|
|
2551
|
+
entityCallbacks;
|
|
2552
|
+
queryCache;
|
|
2553
|
+
bus;
|
|
2554
|
+
_entityEventHandlers;
|
|
2555
|
+
_entityEventObserver;
|
|
2556
|
+
_scopedServices;
|
|
2557
|
+
/** Access the EngineContext. */
|
|
2558
|
+
get context() {
|
|
2559
|
+
return this._context;
|
|
2560
|
+
}
|
|
2561
|
+
/** Whether this scene is effectively paused (manual pause or paused by stack). */
|
|
2562
|
+
get isPaused() {
|
|
2563
|
+
if (this.paused) return true;
|
|
2564
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
2565
|
+
if (!sm) return false;
|
|
2566
|
+
const stack = sm.all;
|
|
2567
|
+
const idx = stack.indexOf(this);
|
|
2568
|
+
if (idx === -1) return false;
|
|
2569
|
+
for (let i = idx + 1; i < stack.length; i++) {
|
|
2570
|
+
if (stack[i].pauseBelow) return true;
|
|
2571
|
+
}
|
|
2572
|
+
return false;
|
|
2573
|
+
}
|
|
2574
|
+
/** Whether a scene transition is currently running. */
|
|
2575
|
+
get isTransitioning() {
|
|
2576
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
2577
|
+
return sm?.isTransitioning ?? false;
|
|
2578
|
+
}
|
|
2579
|
+
/** Convenience accessor for the AssetManager. */
|
|
2580
|
+
get assets() {
|
|
2581
|
+
return this._context.resolve(AssetManagerKey);
|
|
2582
|
+
}
|
|
2583
|
+
/**
|
|
2584
|
+
* Lazy proxy-based service resolution. Can be used at field-declaration time:
|
|
2585
|
+
* ```ts
|
|
2586
|
+
* readonly layers = this.service(RenderLayerManagerKey);
|
|
2587
|
+
* ```
|
|
2588
|
+
* The actual resolution is deferred until first property access.
|
|
2589
|
+
*/
|
|
2590
|
+
service(key) {
|
|
2591
|
+
let resolved;
|
|
2592
|
+
return new Proxy({}, {
|
|
2593
|
+
get: /* @__PURE__ */ __name((_target, prop) => {
|
|
2594
|
+
resolved ??= this._context.resolve(key);
|
|
2595
|
+
const value = resolved[prop];
|
|
2596
|
+
return typeof value === "function" ? value.bind(resolved) : value;
|
|
2597
|
+
}, "get"),
|
|
2598
|
+
set: /* @__PURE__ */ __name((_target, prop, value) => {
|
|
2599
|
+
resolved ??= this._context.resolve(key);
|
|
2600
|
+
resolved[prop] = value;
|
|
2601
|
+
return true;
|
|
2602
|
+
}, "set")
|
|
2603
|
+
});
|
|
2604
|
+
}
|
|
2605
|
+
spawn(nameOrBlueprintOrClass, params) {
|
|
2606
|
+
if (typeof nameOrBlueprintOrClass === "function") {
|
|
2607
|
+
const entity2 = new nameOrBlueprintOrClass();
|
|
2608
|
+
entity2._setScene(this, this.entityCallbacks);
|
|
2609
|
+
this.entities.add(entity2);
|
|
2610
|
+
this.bus?.emit("entity:created", { entity: entity2 });
|
|
1662
2611
|
entity2.setup?.(params);
|
|
1663
2612
|
return entity2;
|
|
1664
2613
|
}
|
|
@@ -1750,6 +2699,14 @@ var Scene = class {
|
|
|
1750
2699
|
}
|
|
1751
2700
|
}
|
|
1752
2701
|
}
|
|
2702
|
+
/**
|
|
2703
|
+
* Observe entity-scoped event emissions after they dispatch locally and
|
|
2704
|
+
* bubble to the scene. Tooling only; game code should keep using `on()`.
|
|
2705
|
+
* @internal
|
|
2706
|
+
*/
|
|
2707
|
+
_observeEntityEvent(eventName, data, entity) {
|
|
2708
|
+
this._entityEventObserver?.(eventName, data, entity);
|
|
2709
|
+
}
|
|
1753
2710
|
// ---- Internal methods ----
|
|
1754
2711
|
/**
|
|
1755
2712
|
* Register a scene-scoped service. Called from a plugin's `beforeEnter`
|
|
@@ -1761,6 +2718,13 @@ var Scene = class {
|
|
|
1761
2718
|
this._scopedServices ??= /* @__PURE__ */ new Map();
|
|
1762
2719
|
this._scopedServices.set(key.id, value);
|
|
1763
2720
|
}
|
|
2721
|
+
/**
|
|
2722
|
+
* Install or clear a tooling-only observer for bubbled entity events.
|
|
2723
|
+
* @internal
|
|
2724
|
+
*/
|
|
2725
|
+
_setEntityEventObserver(observer) {
|
|
2726
|
+
this._entityEventObserver = observer;
|
|
2727
|
+
}
|
|
1764
2728
|
/**
|
|
1765
2729
|
* Resolve a scene-scoped service, or `undefined` if none was registered.
|
|
1766
2730
|
* @internal
|
|
@@ -1827,6 +2791,128 @@ var Scene = class {
|
|
|
1827
2791
|
}
|
|
1828
2792
|
};
|
|
1829
2793
|
|
|
2794
|
+
// src/Process.ts
|
|
2795
|
+
var Process = class _Process {
|
|
2796
|
+
static {
|
|
2797
|
+
__name(this, "Process");
|
|
2798
|
+
}
|
|
2799
|
+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
2800
|
+
updateFn;
|
|
2801
|
+
onCompleteFn;
|
|
2802
|
+
duration;
|
|
2803
|
+
loop;
|
|
2804
|
+
/** Tags for filtering/grouping. */
|
|
2805
|
+
tags;
|
|
2806
|
+
elapsed = 0;
|
|
2807
|
+
_completed = false;
|
|
2808
|
+
_paused = false;
|
|
2809
|
+
_cancelled = false;
|
|
2810
|
+
resolvePromise;
|
|
2811
|
+
/** Create a timer that fires `onComplete` after `duration` ms. */
|
|
2812
|
+
static delay(duration, onComplete, tags) {
|
|
2813
|
+
const opts = { duration };
|
|
2814
|
+
if (onComplete !== void 0) opts.onComplete = onComplete;
|
|
2815
|
+
if (tags !== void 0) opts.tags = tags;
|
|
2816
|
+
return new _Process(opts);
|
|
2817
|
+
}
|
|
2818
|
+
constructor(options) {
|
|
2819
|
+
this.updateFn = options.update ?? (() => {
|
|
2820
|
+
});
|
|
2821
|
+
this.onCompleteFn = options.onComplete;
|
|
2822
|
+
this.duration = options.duration;
|
|
2823
|
+
this.loop = options.loop ?? false;
|
|
2824
|
+
this.tags = options.tags ?? [];
|
|
2825
|
+
}
|
|
2826
|
+
/** Whether the process has completed. */
|
|
2827
|
+
get completed() {
|
|
2828
|
+
return this._completed;
|
|
2829
|
+
}
|
|
2830
|
+
/** Whether the process is paused. */
|
|
2831
|
+
get paused() {
|
|
2832
|
+
return this._paused;
|
|
2833
|
+
}
|
|
2834
|
+
/** Pause the process. */
|
|
2835
|
+
pause() {
|
|
2836
|
+
this._paused = true;
|
|
2837
|
+
}
|
|
2838
|
+
/** Resume the process. */
|
|
2839
|
+
resume() {
|
|
2840
|
+
this._paused = false;
|
|
2841
|
+
}
|
|
2842
|
+
/** Cancel the process. */
|
|
2843
|
+
cancel() {
|
|
2844
|
+
this._cancelled = true;
|
|
2845
|
+
this._completed = true;
|
|
2846
|
+
this.resolvePromise?.();
|
|
2847
|
+
}
|
|
2848
|
+
/** Returns a promise that resolves when the process completes or is cancelled. */
|
|
2849
|
+
toPromise() {
|
|
2850
|
+
if (this._completed) return Promise.resolve();
|
|
2851
|
+
return new Promise((resolve) => {
|
|
2852
|
+
this.resolvePromise = resolve;
|
|
2853
|
+
});
|
|
2854
|
+
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Advance the process by dt milliseconds.
|
|
2857
|
+
* @internal
|
|
2858
|
+
*/
|
|
2859
|
+
_update(dt) {
|
|
2860
|
+
if (this._completed || this._paused || this._cancelled) return;
|
|
2861
|
+
this.elapsed += dt;
|
|
2862
|
+
if (this.duration !== void 0 && this.elapsed >= this.duration) {
|
|
2863
|
+
const result2 = this.updateFn(dt, this.elapsed);
|
|
2864
|
+
if (this.loop && result2 !== true) {
|
|
2865
|
+
this.elapsed = this.elapsed % this.duration;
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
this.complete();
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
const result = this.updateFn(dt, this.elapsed);
|
|
2872
|
+
if (result === true) {
|
|
2873
|
+
if (this.loop) {
|
|
2874
|
+
this.elapsed = 0;
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
this.complete();
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
/**
|
|
2881
|
+
* Reset the process to its initial state so it can be re-run.
|
|
2882
|
+
* @internal Used by Sequence for loop/repeat with direct instances.
|
|
2883
|
+
*/
|
|
2884
|
+
_reset() {
|
|
2885
|
+
this.elapsed = 0;
|
|
2886
|
+
this._completed = false;
|
|
2887
|
+
this._paused = false;
|
|
2888
|
+
this._cancelled = false;
|
|
2889
|
+
delete this.resolvePromise;
|
|
2890
|
+
}
|
|
2891
|
+
complete() {
|
|
2892
|
+
this._completed = true;
|
|
2893
|
+
this.onCompleteFn?.();
|
|
2894
|
+
this.resolvePromise?.();
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2897
|
+
var easeLinear = /* @__PURE__ */ __name((t) => t, "easeLinear");
|
|
2898
|
+
var easeInQuad = /* @__PURE__ */ __name((t) => t * t, "easeInQuad");
|
|
2899
|
+
var easeOutQuad = /* @__PURE__ */ __name((t) => t * (2 - t), "easeOutQuad");
|
|
2900
|
+
var easeInOutQuad = /* @__PURE__ */ __name((t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, "easeInOutQuad");
|
|
2901
|
+
var easeOutBounce = /* @__PURE__ */ __name((t) => {
|
|
2902
|
+
if (t < 1 / 2.75) {
|
|
2903
|
+
return 7.5625 * t * t;
|
|
2904
|
+
} else if (t < 2 / 2.75) {
|
|
2905
|
+
const t2 = t - 1.5 / 2.75;
|
|
2906
|
+
return 7.5625 * t2 * t2 + 0.75;
|
|
2907
|
+
} else if (t < 2.5 / 2.75) {
|
|
2908
|
+
const t2 = t - 2.25 / 2.75;
|
|
2909
|
+
return 7.5625 * t2 * t2 + 0.9375;
|
|
2910
|
+
} else {
|
|
2911
|
+
const t2 = t - 2.625 / 2.75;
|
|
2912
|
+
return 7.5625 * t2 * t2 + 0.984375;
|
|
2913
|
+
}
|
|
2914
|
+
}, "easeOutBounce");
|
|
2915
|
+
|
|
1830
2916
|
// src/LoadingScene.ts
|
|
1831
2917
|
var LoadingScene = class extends Scene {
|
|
1832
2918
|
static {
|
|
@@ -1851,6 +2937,7 @@ var LoadingScene = class extends Scene {
|
|
|
1851
2937
|
_active = true;
|
|
1852
2938
|
_continueRequested = false;
|
|
1853
2939
|
_continueGate;
|
|
2940
|
+
_pendingWaits = /* @__PURE__ */ new Set();
|
|
1854
2941
|
// Bumped on every `_run` attempt. `AssetManager.loadAll` uses `Promise.all`
|
|
1855
2942
|
// under the hood, so individual loaders from a failed attempt can still
|
|
1856
2943
|
// resolve and fire `onProgress` after the attempt rejects. Without this
|
|
@@ -1905,28 +2992,32 @@ var LoadingScene = class extends Scene {
|
|
|
1905
2992
|
onExit() {
|
|
1906
2993
|
this._active = false;
|
|
1907
2994
|
this._continueGate?.();
|
|
2995
|
+
for (const wait of this._pendingWaits) {
|
|
2996
|
+
wait.cancel();
|
|
2997
|
+
}
|
|
2998
|
+
this._pendingWaits.clear();
|
|
1908
2999
|
}
|
|
1909
3000
|
async _run() {
|
|
1910
|
-
await
|
|
3001
|
+
await Promise.resolve();
|
|
1911
3002
|
if (!this._active) return;
|
|
1912
3003
|
const attempt = ++this._attempt;
|
|
1913
3004
|
const target = typeof this.target === "function" ? this.target() : this.target;
|
|
1914
|
-
const startedAt = performance.now();
|
|
1915
3005
|
const bus = this.context.resolve(EventBusKey);
|
|
3006
|
+
const minDuration = this._createEngineTimeDelay(this.minDuration);
|
|
1916
3007
|
try {
|
|
1917
3008
|
await this.assets.loadAll(target.preload ?? [], (ratio) => {
|
|
1918
3009
|
if (!this._active || attempt !== this._attempt) return;
|
|
1919
3010
|
this._progress = ratio;
|
|
1920
3011
|
bus.emit("scene:loading:progress", { scene: this, ratio });
|
|
1921
3012
|
});
|
|
1922
|
-
if (!this._active || attempt !== this._attempt)
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
if (remaining > 0) {
|
|
1926
|
-
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
1927
|
-
if (!this._active || attempt !== this._attempt) return;
|
|
3013
|
+
if (!this._active || attempt !== this._attempt) {
|
|
3014
|
+
minDuration.cancel();
|
|
3015
|
+
return;
|
|
1928
3016
|
}
|
|
3017
|
+
await minDuration.promise;
|
|
3018
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
1929
3019
|
} catch (err) {
|
|
3020
|
+
minDuration.cancel();
|
|
1930
3021
|
if (!this._active || attempt !== this._attempt) return;
|
|
1931
3022
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1932
3023
|
this._started = false;
|
|
@@ -1950,6 +3041,26 @@ var LoadingScene = class extends Scene {
|
|
|
1950
3041
|
this.transition ? { transition: this.transition } : void 0
|
|
1951
3042
|
);
|
|
1952
3043
|
}
|
|
3044
|
+
_createEngineTimeDelay(ms) {
|
|
3045
|
+
if (ms <= 0) {
|
|
3046
|
+
return {
|
|
3047
|
+
promise: Promise.resolve(),
|
|
3048
|
+
cancel: /* @__PURE__ */ __name(() => {
|
|
3049
|
+
}, "cancel")
|
|
3050
|
+
};
|
|
3051
|
+
}
|
|
3052
|
+
const wait = Process.delay(ms);
|
|
3053
|
+
this._pendingWaits.add(wait);
|
|
3054
|
+
this.context.resolve(ProcessSystemKey).add(wait);
|
|
3055
|
+
return {
|
|
3056
|
+
promise: wait.toPromise().finally(() => {
|
|
3057
|
+
this._pendingWaits.delete(wait);
|
|
3058
|
+
}),
|
|
3059
|
+
cancel: /* @__PURE__ */ __name(() => {
|
|
3060
|
+
wait.cancel();
|
|
3061
|
+
}, "cancel")
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
1953
3064
|
};
|
|
1954
3065
|
|
|
1955
3066
|
// src/SceneTransition.ts
|
|
@@ -1974,6 +3085,29 @@ var SceneManager = class {
|
|
|
1974
3085
|
_pendingChain = Promise.resolve();
|
|
1975
3086
|
_mutationDepth = 0;
|
|
1976
3087
|
_destroyed = false;
|
|
3088
|
+
_autoPauseOnBlur = false;
|
|
3089
|
+
_isBlurred = false;
|
|
3090
|
+
_visibilityPausedScenes = /* @__PURE__ */ new Set();
|
|
3091
|
+
_visibilityListenerCleanup;
|
|
3092
|
+
/**
|
|
3093
|
+
* Pause all non-paused scenes when `document.hidden` becomes `true`; restore
|
|
3094
|
+
* them on focus. Default: `false`. Only scenes paused by this mechanism are
|
|
3095
|
+
* restored — user-paused scenes (manual `scene.paused = true` or `pauseBelow`
|
|
3096
|
+
* cascade) are never touched.
|
|
3097
|
+
*/
|
|
3098
|
+
get autoPauseOnBlur() {
|
|
3099
|
+
return this._autoPauseOnBlur;
|
|
3100
|
+
}
|
|
3101
|
+
set autoPauseOnBlur(value) {
|
|
3102
|
+
if (this._autoPauseOnBlur === value) return;
|
|
3103
|
+
this._autoPauseOnBlur = value;
|
|
3104
|
+
if (!this._isBlurred) return;
|
|
3105
|
+
if (value) {
|
|
3106
|
+
this._applyBlurPause();
|
|
3107
|
+
} else if (this._visibilityPausedScenes.size > 0) {
|
|
3108
|
+
this._restoreBlurPause();
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
1977
3111
|
/**
|
|
1978
3112
|
* Set the engine context.
|
|
1979
3113
|
* @internal
|
|
@@ -1984,6 +3118,40 @@ var SceneManager = class {
|
|
|
1984
3118
|
this.assetManager = context.tryResolve(AssetManagerKey);
|
|
1985
3119
|
this.hookRegistry = context.tryResolve(SceneHookRegistryKey);
|
|
1986
3120
|
this.logger = context.tryResolve(LoggerKey);
|
|
3121
|
+
if (this._visibilityListenerCleanup || typeof document === "undefined") {
|
|
3122
|
+
return;
|
|
3123
|
+
}
|
|
3124
|
+
const onVisibilityChange = /* @__PURE__ */ __name(() => {
|
|
3125
|
+
this._handleVisibilityChange(document.hidden);
|
|
3126
|
+
}, "onVisibilityChange");
|
|
3127
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
3128
|
+
this._visibilityListenerCleanup = () => document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
3129
|
+
}
|
|
3130
|
+
/**
|
|
3131
|
+
* React to a visibility change. Parameterised on `hidden` so unit tests can
|
|
3132
|
+
* drive it without a real `document`.
|
|
3133
|
+
* @internal
|
|
3134
|
+
*/
|
|
3135
|
+
_handleVisibilityChange(hidden) {
|
|
3136
|
+
if (hidden && !this._isBlurred) {
|
|
3137
|
+
this._isBlurred = true;
|
|
3138
|
+
if (this._autoPauseOnBlur) this._applyBlurPause();
|
|
3139
|
+
} else if (!hidden && this._isBlurred) {
|
|
3140
|
+
this._isBlurred = false;
|
|
3141
|
+
if (this._visibilityPausedScenes.size > 0) this._restoreBlurPause();
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
_applyBlurPause() {
|
|
3145
|
+
for (const scene of this.activeScenes) {
|
|
3146
|
+
scene.paused = true;
|
|
3147
|
+
this._visibilityPausedScenes.add(scene);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
_restoreBlurPause() {
|
|
3151
|
+
for (const scene of this._visibilityPausedScenes) {
|
|
3152
|
+
scene.paused = false;
|
|
3153
|
+
}
|
|
3154
|
+
this._visibilityPausedScenes.clear();
|
|
1987
3155
|
}
|
|
1988
3156
|
/** The topmost (active) scene. */
|
|
1989
3157
|
get active() {
|
|
@@ -2110,6 +3278,9 @@ var SceneManager = class {
|
|
|
2110
3278
|
this._cleanupRun(this._currentRun);
|
|
2111
3279
|
}
|
|
2112
3280
|
this._pendingChain = Promise.resolve();
|
|
3281
|
+
this._visibilityListenerCleanup?.();
|
|
3282
|
+
this._visibilityListenerCleanup = void 0;
|
|
3283
|
+
this._visibilityPausedScenes.clear();
|
|
2113
3284
|
this._withMutationSync(() => {
|
|
2114
3285
|
while (this.stack.length > 0) {
|
|
2115
3286
|
const scene = this.stack.pop();
|
|
@@ -2229,6 +3400,7 @@ var SceneManager = class {
|
|
|
2229
3400
|
scene._destroyAllEntities();
|
|
2230
3401
|
this.hookRegistry?.runAfterExit(scene);
|
|
2231
3402
|
scene._clearScopedServices();
|
|
3403
|
+
this._visibilityPausedScenes.delete(scene);
|
|
2232
3404
|
}
|
|
2233
3405
|
async _runTransition(kind, transition, fromScene, toScene) {
|
|
2234
3406
|
if (this._destroyed) return;
|
|
@@ -2297,174 +3469,52 @@ var SceneManager = class {
|
|
|
2297
3469
|
toScene: run.toScene
|
|
2298
3470
|
};
|
|
2299
3471
|
}
|
|
2300
|
-
_snapshotPauseStates() {
|
|
2301
|
-
return new Map(
|
|
2302
|
-
this.stack.map((scene) => [scene, scene.isPaused])
|
|
2303
|
-
);
|
|
2304
|
-
}
|
|
2305
|
-
_assertNotMutating(method) {
|
|
2306
|
-
if (this._mutationDepth === 0) return;
|
|
2307
|
-
throw new Error(
|
|
2308
|
-
`SceneManager.${method}() called reentrantly from a scene lifecycle hook (onEnter/onExit/onPause/onResume or a beforeEnter/afterExit hook). Defer the call outside the hook, e.g. via queueMicrotask() or from a component update().`
|
|
2309
|
-
);
|
|
2310
|
-
}
|
|
2311
|
-
async _withMutation(work) {
|
|
2312
|
-
this._mutationDepth++;
|
|
2313
|
-
try {
|
|
2314
|
-
return await work();
|
|
2315
|
-
} finally {
|
|
2316
|
-
this._mutationDepth--;
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
_withMutationSync(work) {
|
|
2320
|
-
this._mutationDepth++;
|
|
2321
|
-
try {
|
|
2322
|
-
return work();
|
|
2323
|
-
} finally {
|
|
2324
|
-
this._mutationDepth--;
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
2328
|
-
_firePauseTransitions(wasPaused) {
|
|
2329
|
-
for (const scene of this.stack) {
|
|
2330
|
-
const was = wasPaused.get(scene) ?? false;
|
|
2331
|
-
if (scene.isPaused && !was) {
|
|
2332
|
-
scene.onPause?.();
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
}
|
|
2336
|
-
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
2337
|
-
_fireResumeTransitions(wasPaused) {
|
|
2338
|
-
for (const scene of this.stack) {
|
|
2339
|
-
const was = wasPaused.get(scene) ?? false;
|
|
2340
|
-
if (!scene.isPaused && was) {
|
|
2341
|
-
scene.onResume?.();
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
};
|
|
2346
|
-
|
|
2347
|
-
// src/Process.ts
|
|
2348
|
-
var Process = class _Process {
|
|
2349
|
-
static {
|
|
2350
|
-
__name(this, "Process");
|
|
2351
|
-
}
|
|
2352
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
2353
|
-
updateFn;
|
|
2354
|
-
onCompleteFn;
|
|
2355
|
-
duration;
|
|
2356
|
-
loop;
|
|
2357
|
-
/** Tags for filtering/grouping. */
|
|
2358
|
-
tags;
|
|
2359
|
-
elapsed = 0;
|
|
2360
|
-
_completed = false;
|
|
2361
|
-
_paused = false;
|
|
2362
|
-
_cancelled = false;
|
|
2363
|
-
resolvePromise;
|
|
2364
|
-
/** Create a timer that fires `onComplete` after `duration` ms. */
|
|
2365
|
-
static delay(duration, onComplete, tags) {
|
|
2366
|
-
const opts = { duration };
|
|
2367
|
-
if (onComplete !== void 0) opts.onComplete = onComplete;
|
|
2368
|
-
if (tags !== void 0) opts.tags = tags;
|
|
2369
|
-
return new _Process(opts);
|
|
2370
|
-
}
|
|
2371
|
-
constructor(options) {
|
|
2372
|
-
this.updateFn = options.update ?? (() => {
|
|
2373
|
-
});
|
|
2374
|
-
this.onCompleteFn = options.onComplete;
|
|
2375
|
-
this.duration = options.duration;
|
|
2376
|
-
this.loop = options.loop ?? false;
|
|
2377
|
-
this.tags = options.tags ?? [];
|
|
2378
|
-
}
|
|
2379
|
-
/** Whether the process has completed. */
|
|
2380
|
-
get completed() {
|
|
2381
|
-
return this._completed;
|
|
2382
|
-
}
|
|
2383
|
-
/** Whether the process is paused. */
|
|
2384
|
-
get paused() {
|
|
2385
|
-
return this._paused;
|
|
2386
|
-
}
|
|
2387
|
-
/** Pause the process. */
|
|
2388
|
-
pause() {
|
|
2389
|
-
this._paused = true;
|
|
3472
|
+
_snapshotPauseStates() {
|
|
3473
|
+
return new Map(
|
|
3474
|
+
this.stack.map((scene) => [scene, scene.isPaused])
|
|
3475
|
+
);
|
|
2390
3476
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
3477
|
+
_assertNotMutating(method) {
|
|
3478
|
+
if (this._mutationDepth === 0) return;
|
|
3479
|
+
throw new Error(
|
|
3480
|
+
`SceneManager.${method}() called reentrantly from a scene lifecycle hook (onEnter/onExit/onPause/onResume or a beforeEnter/afterExit hook). Defer the call outside the hook, e.g. via queueMicrotask() or from a component update().`
|
|
3481
|
+
);
|
|
2394
3482
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
3483
|
+
async _withMutation(work) {
|
|
3484
|
+
this._mutationDepth++;
|
|
3485
|
+
try {
|
|
3486
|
+
return await work();
|
|
3487
|
+
} finally {
|
|
3488
|
+
this._mutationDepth--;
|
|
3489
|
+
}
|
|
2400
3490
|
}
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
3491
|
+
_withMutationSync(work) {
|
|
3492
|
+
this._mutationDepth++;
|
|
3493
|
+
try {
|
|
3494
|
+
return work();
|
|
3495
|
+
} finally {
|
|
3496
|
+
this._mutationDepth--;
|
|
3497
|
+
}
|
|
2407
3498
|
}
|
|
2408
|
-
/**
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
this.elapsed += dt;
|
|
2415
|
-
if (this.duration !== void 0 && this.elapsed >= this.duration) {
|
|
2416
|
-
const result2 = this.updateFn(dt, this.elapsed);
|
|
2417
|
-
if (this.loop && result2 !== true) {
|
|
2418
|
-
this.elapsed = this.elapsed % this.duration;
|
|
2419
|
-
return;
|
|
3499
|
+
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
3500
|
+
_firePauseTransitions(wasPaused) {
|
|
3501
|
+
for (const scene of this.stack) {
|
|
3502
|
+
const was = wasPaused.get(scene) ?? false;
|
|
3503
|
+
if (scene.isPaused && !was) {
|
|
3504
|
+
scene.onPause?.();
|
|
2420
3505
|
}
|
|
2421
|
-
this.complete();
|
|
2422
|
-
return;
|
|
2423
3506
|
}
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
3507
|
+
}
|
|
3508
|
+
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
3509
|
+
_fireResumeTransitions(wasPaused) {
|
|
3510
|
+
for (const scene of this.stack) {
|
|
3511
|
+
const was = wasPaused.get(scene) ?? false;
|
|
3512
|
+
if (!scene.isPaused && was) {
|
|
3513
|
+
scene.onResume?.();
|
|
2429
3514
|
}
|
|
2430
|
-
this.complete();
|
|
2431
3515
|
}
|
|
2432
3516
|
}
|
|
2433
|
-
/**
|
|
2434
|
-
* Reset the process to its initial state so it can be re-run.
|
|
2435
|
-
* @internal Used by Sequence for loop/repeat with direct instances.
|
|
2436
|
-
*/
|
|
2437
|
-
_reset() {
|
|
2438
|
-
this.elapsed = 0;
|
|
2439
|
-
this._completed = false;
|
|
2440
|
-
this._paused = false;
|
|
2441
|
-
this._cancelled = false;
|
|
2442
|
-
delete this.resolvePromise;
|
|
2443
|
-
}
|
|
2444
|
-
complete() {
|
|
2445
|
-
this._completed = true;
|
|
2446
|
-
this.onCompleteFn?.();
|
|
2447
|
-
this.resolvePromise?.();
|
|
2448
|
-
}
|
|
2449
3517
|
};
|
|
2450
|
-
var easeLinear = /* @__PURE__ */ __name((t) => t, "easeLinear");
|
|
2451
|
-
var easeInQuad = /* @__PURE__ */ __name((t) => t * t, "easeInQuad");
|
|
2452
|
-
var easeOutQuad = /* @__PURE__ */ __name((t) => t * (2 - t), "easeOutQuad");
|
|
2453
|
-
var easeInOutQuad = /* @__PURE__ */ __name((t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, "easeInOutQuad");
|
|
2454
|
-
var easeOutBounce = /* @__PURE__ */ __name((t) => {
|
|
2455
|
-
if (t < 1 / 2.75) {
|
|
2456
|
-
return 7.5625 * t * t;
|
|
2457
|
-
} else if (t < 2 / 2.75) {
|
|
2458
|
-
const t2 = t - 1.5 / 2.75;
|
|
2459
|
-
return 7.5625 * t2 * t2 + 0.75;
|
|
2460
|
-
} else if (t < 2.5 / 2.75) {
|
|
2461
|
-
const t2 = t - 2.25 / 2.75;
|
|
2462
|
-
return 7.5625 * t2 * t2 + 0.9375;
|
|
2463
|
-
} else {
|
|
2464
|
-
const t2 = t - 2.625 / 2.75;
|
|
2465
|
-
return 7.5625 * t2 * t2 + 0.984375;
|
|
2466
|
-
}
|
|
2467
|
-
}, "easeOutBounce");
|
|
2468
3518
|
|
|
2469
3519
|
// src/Tween.ts
|
|
2470
3520
|
var Tween = {
|
|
@@ -2691,7 +3741,9 @@ var ProcessSlot = class {
|
|
|
2691
3741
|
};
|
|
2692
3742
|
|
|
2693
3743
|
// src/ProcessComponent.ts
|
|
2694
|
-
var
|
|
3744
|
+
var _ProcessComponent_decorators, _init2, _a2;
|
|
3745
|
+
_ProcessComponent_decorators = [serializable];
|
|
3746
|
+
var ProcessComponent = class extends (_a2 = Component) {
|
|
2695
3747
|
static {
|
|
2696
3748
|
__name(this, "ProcessComponent");
|
|
2697
3749
|
}
|
|
@@ -2761,10 +3813,18 @@ var ProcessComponent = class extends Component {
|
|
|
2761
3813
|
onDestroy() {
|
|
2762
3814
|
this.cancel();
|
|
2763
3815
|
}
|
|
3816
|
+
serialize() {
|
|
3817
|
+
return null;
|
|
3818
|
+
}
|
|
2764
3819
|
};
|
|
3820
|
+
_init2 = __decoratorStart(_a2);
|
|
3821
|
+
ProcessComponent = __decorateElement(_init2, 0, "ProcessComponent", _ProcessComponent_decorators, ProcessComponent);
|
|
3822
|
+
__runInitializers(_init2, 1, ProcessComponent);
|
|
2765
3823
|
|
|
2766
3824
|
// src/KeyframeAnimator.ts
|
|
2767
|
-
var
|
|
3825
|
+
var _KeyframeAnimator_decorators, _init3, _a3;
|
|
3826
|
+
_KeyframeAnimator_decorators = [serializable];
|
|
3827
|
+
var KeyframeAnimator = class extends (_a3 = Component) {
|
|
2768
3828
|
static {
|
|
2769
3829
|
__name(this, "KeyframeAnimator");
|
|
2770
3830
|
}
|
|
@@ -2816,6 +3876,9 @@ var KeyframeAnimator = class extends Component {
|
|
|
2816
3876
|
onDestroy() {
|
|
2817
3877
|
this.stopAll();
|
|
2818
3878
|
}
|
|
3879
|
+
serialize() {
|
|
3880
|
+
return null;
|
|
3881
|
+
}
|
|
2819
3882
|
stopInternal(name, complete) {
|
|
2820
3883
|
const process = this.active.get(name);
|
|
2821
3884
|
if (!process) return;
|
|
@@ -2824,6 +3887,9 @@ var KeyframeAnimator = class extends Component {
|
|
|
2824
3887
|
this.defs[name]?.onExit?.(complete);
|
|
2825
3888
|
}
|
|
2826
3889
|
};
|
|
3890
|
+
_init3 = __decoratorStart(_a3);
|
|
3891
|
+
KeyframeAnimator = __decorateElement(_init3, 0, "KeyframeAnimator", _KeyframeAnimator_decorators, KeyframeAnimator);
|
|
3892
|
+
__runInitializers(_init3, 1, KeyframeAnimator);
|
|
2827
3893
|
|
|
2828
3894
|
// src/Sequence.ts
|
|
2829
3895
|
var Sequence = class {
|
|
@@ -2975,36 +4041,94 @@ var ProcessSystem = class extends System {
|
|
|
2975
4041
|
/** Global time scale multiplier. Stacks multiplicatively with per-scene timeScale. */
|
|
2976
4042
|
timeScale = 1;
|
|
2977
4043
|
sceneManager;
|
|
2978
|
-
|
|
4044
|
+
globalProcesses = /* @__PURE__ */ new Set();
|
|
4045
|
+
scenePools = /* @__PURE__ */ new Map();
|
|
4046
|
+
_unregisterSceneHook = null;
|
|
2979
4047
|
onRegister(context) {
|
|
2980
4048
|
this.sceneManager = context.resolve(SceneManagerKey);
|
|
4049
|
+
const hooks = context.tryResolve(SceneHookRegistryKey);
|
|
4050
|
+
this._unregisterSceneHook = hooks?.register({
|
|
4051
|
+
afterExit: /* @__PURE__ */ __name((scene) => this.cancelForScene(scene), "afterExit")
|
|
4052
|
+
}) ?? null;
|
|
4053
|
+
}
|
|
4054
|
+
onUnregister() {
|
|
4055
|
+
this._unregisterSceneHook?.();
|
|
4056
|
+
this._unregisterSceneHook = null;
|
|
4057
|
+
for (const p of this.globalProcesses) {
|
|
4058
|
+
if (!p.completed) p.cancel();
|
|
4059
|
+
}
|
|
4060
|
+
this.globalProcesses.clear();
|
|
4061
|
+
for (const pool of this.scenePools.values()) {
|
|
4062
|
+
for (const p of pool) {
|
|
4063
|
+
if (!p.completed) p.cancel();
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
this.scenePools.clear();
|
|
2981
4067
|
}
|
|
2982
|
-
/**
|
|
4068
|
+
/**
|
|
4069
|
+
* Add an engine-global process. Ticked under the global timeScale only;
|
|
4070
|
+
* NOT gated by per-scene pause or scaled by per-scene timeScale. Use this
|
|
4071
|
+
* for cross-scene effects (e.g. screen-scope filter fades on `app.stage`)
|
|
4072
|
+
* or processes that have no owning scene.
|
|
4073
|
+
*/
|
|
2983
4074
|
add(process) {
|
|
2984
|
-
this.
|
|
4075
|
+
this.globalProcesses.add(process);
|
|
4076
|
+
return process;
|
|
4077
|
+
}
|
|
4078
|
+
/**
|
|
4079
|
+
* Add a process bound to a specific scene's lifecycle. Ticked only while
|
|
4080
|
+
* the scene is active (not paused) and scaled by the scene's `timeScale`,
|
|
4081
|
+
* exactly like an entity-owned `ProcessComponent`. Use this for layer or
|
|
4082
|
+
* scene-scope effect fades that should pause with the scene.
|
|
4083
|
+
*/
|
|
4084
|
+
addForScene(scene, process) {
|
|
4085
|
+
let pool = this.scenePools.get(scene);
|
|
4086
|
+
if (!pool) {
|
|
4087
|
+
pool = /* @__PURE__ */ new Set();
|
|
4088
|
+
this.scenePools.set(scene, pool);
|
|
4089
|
+
}
|
|
4090
|
+
pool.add(process);
|
|
2985
4091
|
return process;
|
|
2986
4092
|
}
|
|
2987
|
-
/** Cancel
|
|
4093
|
+
/** Cancel engine-global processes, optionally by tag. */
|
|
2988
4094
|
cancel(tag) {
|
|
2989
|
-
for (const p of this.
|
|
4095
|
+
for (const p of this.globalProcesses) {
|
|
2990
4096
|
if (tag === void 0 || p.tags.includes(tag)) {
|
|
2991
4097
|
p.cancel();
|
|
4098
|
+
this.globalProcesses.delete(p);
|
|
2992
4099
|
}
|
|
2993
4100
|
}
|
|
2994
|
-
|
|
2995
|
-
|
|
4101
|
+
}
|
|
4102
|
+
/** Cancel every scene-bound process for `scene`, optionally by tag. */
|
|
4103
|
+
cancelForScene(scene, tag) {
|
|
4104
|
+
const pool = this.scenePools.get(scene);
|
|
4105
|
+
if (!pool) return;
|
|
4106
|
+
for (const p of pool) {
|
|
4107
|
+
if (tag === void 0 || p.tags.includes(tag)) {
|
|
4108
|
+
p.cancel();
|
|
4109
|
+
pool.delete(p);
|
|
4110
|
+
}
|
|
2996
4111
|
}
|
|
4112
|
+
if (pool.size === 0) this.scenePools.delete(scene);
|
|
2997
4113
|
}
|
|
2998
4114
|
update(dt) {
|
|
2999
4115
|
const globalScaledDt = dt * this.timeScale;
|
|
3000
|
-
for (const p of this.
|
|
4116
|
+
for (const p of this.globalProcesses) {
|
|
3001
4117
|
p._update(globalScaledDt);
|
|
3002
4118
|
if (p.completed) {
|
|
3003
|
-
this.
|
|
4119
|
+
this.globalProcesses.delete(p);
|
|
3004
4120
|
}
|
|
3005
4121
|
}
|
|
3006
4122
|
for (const scene of this.sceneManager.activeScenes) {
|
|
3007
4123
|
const effectiveDt = globalScaledDt * scene.timeScale;
|
|
4124
|
+
const pool = this.scenePools.get(scene);
|
|
4125
|
+
if (pool) {
|
|
4126
|
+
for (const p of pool) {
|
|
4127
|
+
p._update(effectiveDt);
|
|
4128
|
+
if (p.completed) pool.delete(p);
|
|
4129
|
+
}
|
|
4130
|
+
if (pool.size === 0) this.scenePools.delete(scene);
|
|
4131
|
+
}
|
|
3008
4132
|
for (const entity of scene.getEntities()) {
|
|
3009
4133
|
if (entity.isDestroyed) continue;
|
|
3010
4134
|
const pc = entity.tryGet(ProcessComponent);
|
|
@@ -3015,145 +4139,45 @@ var ProcessSystem = class extends System {
|
|
|
3015
4139
|
}
|
|
3016
4140
|
};
|
|
3017
4141
|
|
|
3018
|
-
// src/
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
this.engine = engine;
|
|
3026
|
-
}
|
|
3027
|
-
/** Full state snapshot (serializable). */
|
|
3028
|
-
snapshot() {
|
|
3029
|
-
return {
|
|
3030
|
-
frameCount: this.engine.loop.frameCount,
|
|
3031
|
-
sceneStack: this.getSceneStack(),
|
|
3032
|
-
entityCount: this.countEntities(),
|
|
3033
|
-
systemCount: this.getSystems().length,
|
|
3034
|
-
errors: this.getErrors()
|
|
3035
|
-
};
|
|
3036
|
-
}
|
|
3037
|
-
/** Find entity by name in the active scene. */
|
|
3038
|
-
getEntityByName(name) {
|
|
3039
|
-
const entity = this.findActiveEntity(name);
|
|
3040
|
-
if (!entity) return void 0;
|
|
3041
|
-
return this.entityToSnapshot(entity);
|
|
3042
|
-
}
|
|
3043
|
-
/** Get entity position (from Transform component). */
|
|
3044
|
-
getEntityPosition(name) {
|
|
3045
|
-
const entity = this.findActiveEntity(name);
|
|
3046
|
-
if (!entity) return void 0;
|
|
3047
|
-
const transform = this.getTransform(entity);
|
|
3048
|
-
if (!transform) return void 0;
|
|
3049
|
-
return { x: transform.position.x, y: transform.position.y };
|
|
3050
|
-
}
|
|
3051
|
-
/** Check if an entity has a component by class name string. */
|
|
3052
|
-
hasComponent(entityName, componentClass) {
|
|
3053
|
-
return this.findComponentByName(entityName, componentClass) !== void 0;
|
|
3054
|
-
}
|
|
3055
|
-
/** Get component data (serializable subset) by class name string. */
|
|
3056
|
-
getComponentData(entityName, componentClass) {
|
|
3057
|
-
const comp = this.findComponentByName(entityName, componentClass);
|
|
3058
|
-
if (!comp) return void 0;
|
|
3059
|
-
return this.serializeComponent(comp);
|
|
3060
|
-
}
|
|
3061
|
-
/** Get all entities in the active scene as snapshots. */
|
|
3062
|
-
getEntities() {
|
|
3063
|
-
const scene = this.engine.scenes.active;
|
|
3064
|
-
if (!scene) return [];
|
|
3065
|
-
const result = [];
|
|
3066
|
-
for (const entity of scene.getEntities()) {
|
|
3067
|
-
if (!entity.isDestroyed) {
|
|
3068
|
-
result.push(this.entityToSnapshot(entity));
|
|
4142
|
+
// src/ProcessQueue.ts
|
|
4143
|
+
function makeQueue(route) {
|
|
4144
|
+
const ours = /* @__PURE__ */ new Set();
|
|
4145
|
+
return {
|
|
4146
|
+
run(p) {
|
|
4147
|
+
for (const old of ours) {
|
|
4148
|
+
if (old.completed) ours.delete(old);
|
|
3069
4149
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
entityCount: scene.getEntities().size,
|
|
3078
|
-
paused: scene.isPaused
|
|
3079
|
-
}));
|
|
3080
|
-
}
|
|
3081
|
-
/** Get active system info. */
|
|
3082
|
-
getSystems() {
|
|
3083
|
-
const scheduler = this.engine.context.tryResolve(SystemSchedulerKey);
|
|
3084
|
-
if (!scheduler) return [];
|
|
3085
|
-
return scheduler.getAllSystems().map((sys) => ({
|
|
3086
|
-
name: sys.constructor.name,
|
|
3087
|
-
phase: sys.phase,
|
|
3088
|
-
priority: sys.priority,
|
|
3089
|
-
enabled: sys.enabled
|
|
3090
|
-
}));
|
|
3091
|
-
}
|
|
3092
|
-
/** Get disabled components/systems from error boundary. */
|
|
3093
|
-
getErrors() {
|
|
3094
|
-
const boundary = this.engine.context.tryResolve(ErrorBoundaryKey);
|
|
3095
|
-
if (!boundary) return { disabledSystems: [], disabledComponents: [] };
|
|
3096
|
-
const disabled = boundary.getDisabled();
|
|
3097
|
-
return {
|
|
3098
|
-
disabledSystems: disabled.systems.map(
|
|
3099
|
-
(s) => s.system.constructor.name
|
|
3100
|
-
),
|
|
3101
|
-
disabledComponents: disabled.components.map((c) => ({
|
|
3102
|
-
entity: c.component.entity?.name ?? "unknown",
|
|
3103
|
-
component: c.component.constructor.name,
|
|
3104
|
-
error: c.error
|
|
3105
|
-
}))
|
|
3106
|
-
};
|
|
3107
|
-
}
|
|
3108
|
-
findActiveEntity(name) {
|
|
3109
|
-
return this.engine.scenes.active?.findEntity(name);
|
|
3110
|
-
}
|
|
3111
|
-
findComponentByName(entityName, componentClass) {
|
|
3112
|
-
const entity = this.findActiveEntity(entityName);
|
|
3113
|
-
if (!entity) return void 0;
|
|
3114
|
-
for (const comp of entity.getAll()) {
|
|
3115
|
-
if (comp.constructor.name === componentClass) return comp;
|
|
3116
|
-
}
|
|
3117
|
-
return void 0;
|
|
3118
|
-
}
|
|
3119
|
-
entityToSnapshot(entity) {
|
|
3120
|
-
const transform = this.getTransform(entity);
|
|
3121
|
-
const snapshot = {
|
|
3122
|
-
id: entity.id,
|
|
3123
|
-
name: entity.name,
|
|
3124
|
-
tags: [...entity.tags],
|
|
3125
|
-
components: [...entity.getAll()].map((c) => c.constructor.name)
|
|
3126
|
-
};
|
|
3127
|
-
if (transform) {
|
|
3128
|
-
snapshot.position = {
|
|
3129
|
-
x: transform.position.x,
|
|
3130
|
-
y: transform.position.y
|
|
3131
|
-
};
|
|
3132
|
-
}
|
|
3133
|
-
return snapshot;
|
|
3134
|
-
}
|
|
3135
|
-
getTransform(entity) {
|
|
3136
|
-
return entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
3137
|
-
}
|
|
3138
|
-
serializeComponent(comp) {
|
|
3139
|
-
const result = {};
|
|
3140
|
-
for (const key of Object.getOwnPropertyNames(comp)) {
|
|
3141
|
-
if (key === "entity") continue;
|
|
3142
|
-
const value = comp[key];
|
|
3143
|
-
if (typeof value !== "function") {
|
|
3144
|
-
result[key] = value;
|
|
4150
|
+
route(p);
|
|
4151
|
+
ours.add(p);
|
|
4152
|
+
return p;
|
|
4153
|
+
},
|
|
4154
|
+
cancelAll() {
|
|
4155
|
+
for (const p of ours) {
|
|
4156
|
+
if (!p.completed) p.cancel();
|
|
3145
4157
|
}
|
|
4158
|
+
ours.clear();
|
|
3146
4159
|
}
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
4160
|
+
};
|
|
4161
|
+
}
|
|
4162
|
+
__name(makeQueue, "makeQueue");
|
|
4163
|
+
function makeEntityScopedQueue(entity) {
|
|
4164
|
+
return makeQueue((p) => {
|
|
4165
|
+
let pc = entity.tryGet(ProcessComponent);
|
|
4166
|
+
if (!pc) {
|
|
4167
|
+
pc = entity.add(new ProcessComponent());
|
|
3153
4168
|
}
|
|
3154
|
-
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
4169
|
+
pc.run(p);
|
|
4170
|
+
});
|
|
4171
|
+
}
|
|
4172
|
+
__name(makeEntityScopedQueue, "makeEntityScopedQueue");
|
|
4173
|
+
function makeSceneScopedQueue(processSystem, scene) {
|
|
4174
|
+
return makeQueue((p) => processSystem.addForScene(scene, p));
|
|
4175
|
+
}
|
|
4176
|
+
__name(makeSceneScopedQueue, "makeSceneScopedQueue");
|
|
4177
|
+
function makeGlobalScopedQueue(processSystem) {
|
|
4178
|
+
return makeQueue((p) => processSystem.add(p));
|
|
4179
|
+
}
|
|
4180
|
+
__name(makeGlobalScopedQueue, "makeGlobalScopedQueue");
|
|
3157
4181
|
|
|
3158
4182
|
// src/Engine.ts
|
|
3159
4183
|
var Engine = class {
|
|
@@ -3207,6 +4231,15 @@ var Engine = class {
|
|
|
3207
4231
|
this.context.register(SystemSchedulerKey, this.scheduler);
|
|
3208
4232
|
this.context.register(AssetManagerKey, this.assets);
|
|
3209
4233
|
this.context.register(SceneHookRegistryKey, this.sceneHooks);
|
|
4234
|
+
this.sceneHooks.register({
|
|
4235
|
+
beforeEnter: /* @__PURE__ */ __name((scene) => {
|
|
4236
|
+
scene._registerScoped(RandomKey, this.inspector.createSceneRandom());
|
|
4237
|
+
this.inspector.attachSceneEventObserver(scene);
|
|
4238
|
+
}, "beforeEnter"),
|
|
4239
|
+
afterExit: /* @__PURE__ */ __name((scene) => {
|
|
4240
|
+
this.inspector.detachSceneEventObserver(scene);
|
|
4241
|
+
}, "afterExit")
|
|
4242
|
+
});
|
|
3210
4243
|
this.scenes._setContext(this.context);
|
|
3211
4244
|
this.registerBuiltInSystems();
|
|
3212
4245
|
this.loop.setCallbacks({
|
|
@@ -3288,6 +4321,7 @@ var Engine = class {
|
|
|
3288
4321
|
if (this.debug && typeof globalThis !== "undefined" && "__yage__" in globalThis) {
|
|
3289
4322
|
delete globalThis["__yage__"];
|
|
3290
4323
|
}
|
|
4324
|
+
this.inspector.dispose();
|
|
3291
4325
|
this.events.clear();
|
|
3292
4326
|
this.started = false;
|
|
3293
4327
|
}
|
|
@@ -3350,6 +4384,11 @@ var Engine = class {
|
|
|
3350
4384
|
}
|
|
3351
4385
|
};
|
|
3352
4386
|
|
|
4387
|
+
// src/RendererAdapter.ts
|
|
4388
|
+
var RendererAdapterKey = new ServiceKey(
|
|
4389
|
+
"rendererAdapter"
|
|
4390
|
+
);
|
|
4391
|
+
|
|
3353
4392
|
// src/test-utils.ts
|
|
3354
4393
|
var _TestScene = class extends Scene {
|
|
3355
4394
|
static {
|
|
@@ -3380,6 +4419,7 @@ function createMockScene(name = "mock-scene") {
|
|
|
3380
4419
|
ctx.register(ErrorBoundaryKey, boundary);
|
|
3381
4420
|
const scene = new _TestScene(name);
|
|
3382
4421
|
scene._setContext(ctx);
|
|
4422
|
+
scene._registerScoped(RandomKey, createRandomService(1234));
|
|
3383
4423
|
return { scene, context: ctx };
|
|
3384
4424
|
}
|
|
3385
4425
|
__name(createMockScene, "createMockScene");
|
|
@@ -3433,6 +4473,8 @@ export {
|
|
|
3433
4473
|
QueryCache,
|
|
3434
4474
|
QueryCacheKey,
|
|
3435
4475
|
QueryResult,
|
|
4476
|
+
RandomKey,
|
|
4477
|
+
RendererAdapterKey,
|
|
3436
4478
|
SERIALIZABLE_KEY,
|
|
3437
4479
|
Scene,
|
|
3438
4480
|
SceneHookRegistry,
|
|
@@ -3453,9 +4495,11 @@ export {
|
|
|
3453
4495
|
Vec2,
|
|
3454
4496
|
_resetEntityIdCounter,
|
|
3455
4497
|
advanceFrames,
|
|
4498
|
+
createDefaultRandomSeed,
|
|
3456
4499
|
createKeyframeTrack,
|
|
3457
4500
|
createMockEntity,
|
|
3458
4501
|
createMockScene,
|
|
4502
|
+
createRandomService,
|
|
3459
4503
|
createTestEngine,
|
|
3460
4504
|
defineBlueprint,
|
|
3461
4505
|
defineEvent,
|
|
@@ -3467,8 +4511,13 @@ export {
|
|
|
3467
4511
|
easeOutQuad,
|
|
3468
4512
|
filterEntities,
|
|
3469
4513
|
getSerializableType,
|
|
4514
|
+
globalRandom,
|
|
3470
4515
|
interpolate,
|
|
3471
4516
|
isSerializable,
|
|
4517
|
+
makeEntityScopedQueue,
|
|
4518
|
+
makeGlobalScopedQueue,
|
|
4519
|
+
makeSceneScopedQueue,
|
|
4520
|
+
normalizeSeed,
|
|
3472
4521
|
resolveTransition,
|
|
3473
4522
|
serializable,
|
|
3474
4523
|
trait
|