@yagejs/core 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1413 -454
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +923 -483
- package/dist/index.d.ts +923 -483
- package/dist/index.js +1402 -454
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -231,14 +231,6 @@ var MathUtils = {
|
|
|
231
231
|
const wrapped = MathUtils.wrap(t, 0, length * 2);
|
|
232
232
|
return length - Math.abs(wrapped - length);
|
|
233
233
|
},
|
|
234
|
-
/** Random float in [min, max). */
|
|
235
|
-
randomRange(min, max) {
|
|
236
|
-
return min + Math.random() * (max - min);
|
|
237
|
-
},
|
|
238
|
-
/** Random integer in [min, max] (inclusive). */
|
|
239
|
-
randomInt(min, max) {
|
|
240
|
-
return Math.floor(min + Math.random() * (max - min + 1));
|
|
241
|
-
},
|
|
242
234
|
/** Convert degrees to radians. */
|
|
243
235
|
degToRad(degrees) {
|
|
244
236
|
return degrees * Math.PI / 180;
|
|
@@ -289,12 +281,138 @@ var MathUtils = {
|
|
|
289
281
|
}
|
|
290
282
|
};
|
|
291
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
|
+
|
|
292
409
|
// src/EventBus.ts
|
|
293
410
|
var EventBus = class {
|
|
294
411
|
static {
|
|
295
412
|
__name(this, "EventBus");
|
|
296
413
|
}
|
|
297
414
|
handlers = /* @__PURE__ */ new Map();
|
|
415
|
+
observers = /* @__PURE__ */ new Set();
|
|
298
416
|
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
299
417
|
on(event, handler) {
|
|
300
418
|
let list = this.handlers.get(event);
|
|
@@ -321,6 +439,12 @@ var EventBus = class {
|
|
|
321
439
|
}
|
|
322
440
|
/** Emit an event. Handlers are called synchronously in registration order. */
|
|
323
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
|
+
}
|
|
324
448
|
const list = this.handlers.get(event);
|
|
325
449
|
if (!list) return;
|
|
326
450
|
const snapshot = [...list];
|
|
@@ -328,6 +452,16 @@ var EventBus = class {
|
|
|
328
452
|
handler(data);
|
|
329
453
|
}
|
|
330
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
|
+
}
|
|
331
465
|
/** Remove all handlers for an event, or all handlers if no event specified. */
|
|
332
466
|
clear(event) {
|
|
333
467
|
if (event !== void 0) {
|
|
@@ -437,63 +571,6 @@ var Logger = class {
|
|
|
437
571
|
}
|
|
438
572
|
};
|
|
439
573
|
|
|
440
|
-
// src/EngineContext.ts
|
|
441
|
-
var ServiceKey = class {
|
|
442
|
-
constructor(id, options) {
|
|
443
|
-
this.id = id;
|
|
444
|
-
this.scope = options?.scope ?? "engine";
|
|
445
|
-
}
|
|
446
|
-
id;
|
|
447
|
-
static {
|
|
448
|
-
__name(this, "ServiceKey");
|
|
449
|
-
}
|
|
450
|
-
/** Declared scope (engine or scene). Defaults to `"engine"`. */
|
|
451
|
-
scope;
|
|
452
|
-
};
|
|
453
|
-
var EngineContext = class {
|
|
454
|
-
static {
|
|
455
|
-
__name(this, "EngineContext");
|
|
456
|
-
}
|
|
457
|
-
services = /* @__PURE__ */ new Map();
|
|
458
|
-
/** Register a service. Throws if the key is already registered. */
|
|
459
|
-
register(key, service) {
|
|
460
|
-
if (this.services.has(key.id)) {
|
|
461
|
-
throw new Error(`Service "${key.id}" is already registered.`);
|
|
462
|
-
}
|
|
463
|
-
this.services.set(key.id, service);
|
|
464
|
-
}
|
|
465
|
-
/** Resolve a service. Throws if not registered. */
|
|
466
|
-
resolve(key) {
|
|
467
|
-
if (!this.services.has(key.id)) {
|
|
468
|
-
throw new Error(`Service "${key.id}" is not registered.`);
|
|
469
|
-
}
|
|
470
|
-
return this.services.get(key.id);
|
|
471
|
-
}
|
|
472
|
-
/** Resolve a service, returning undefined if not registered. */
|
|
473
|
-
tryResolve(key) {
|
|
474
|
-
return this.services.get(key.id);
|
|
475
|
-
}
|
|
476
|
-
/** Remove a registered service. No-op if not registered. */
|
|
477
|
-
unregister(key) {
|
|
478
|
-
this.services.delete(key.id);
|
|
479
|
-
}
|
|
480
|
-
/** Check if a service is registered. */
|
|
481
|
-
has(key) {
|
|
482
|
-
return this.services.has(key.id);
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
var EngineKey = new ServiceKey("engine");
|
|
486
|
-
var EventBusKey = new ServiceKey("eventBus");
|
|
487
|
-
var SceneManagerKey = new ServiceKey("sceneManager");
|
|
488
|
-
var LoggerKey = new ServiceKey("logger");
|
|
489
|
-
var InspectorKey = new ServiceKey("inspector");
|
|
490
|
-
var QueryCacheKey = new ServiceKey("queryCache");
|
|
491
|
-
var ErrorBoundaryKey = new ServiceKey("errorBoundary");
|
|
492
|
-
var GameLoopKey = new ServiceKey("gameLoop");
|
|
493
|
-
var SystemSchedulerKey = new ServiceKey("systemScheduler");
|
|
494
|
-
var ProcessSystemKey = new ServiceKey("processSystem");
|
|
495
|
-
var AssetManagerKey = new ServiceKey("assetManager");
|
|
496
|
-
|
|
497
574
|
// src/SceneHooks.ts
|
|
498
575
|
var SceneHookRegistry = class {
|
|
499
576
|
static {
|
|
@@ -1230,6 +1307,7 @@ var Entity = class {
|
|
|
1230
1307
|
}
|
|
1231
1308
|
}
|
|
1232
1309
|
this._scene?._onEntityEvent(token.name, data, this);
|
|
1310
|
+
this._scene?._observeEntityEvent(token.name, data, this);
|
|
1233
1311
|
}
|
|
1234
1312
|
/** Get all components as an iterable. */
|
|
1235
1313
|
getAll() {
|
|
@@ -1652,89 +1730,908 @@ var GameLoop = class {
|
|
|
1652
1730
|
}
|
|
1653
1731
|
};
|
|
1654
1732
|
|
|
1655
|
-
// src/
|
|
1656
|
-
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 {
|
|
1657
1740
|
static {
|
|
1658
|
-
__name(this, "
|
|
1741
|
+
__name(this, "Inspector");
|
|
1659
1742
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
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
|
+
/**
|
|
1807
|
+
* Inject a synthetic pointer-move with full pointer addressing. Pass `opts`
|
|
1808
|
+
* with `id` / `type: "touch"` to drive a specific finger; defaults match
|
|
1809
|
+
* the primary mouse pointer (same as `mouseMove`).
|
|
1810
|
+
*/
|
|
1811
|
+
pointerMove: /* @__PURE__ */ __name((x, y, opts) => {
|
|
1812
|
+
this.requireInputManager().firePointerMove(x, y, opts);
|
|
1813
|
+
}, "pointerMove"),
|
|
1814
|
+
/**
|
|
1815
|
+
* Inject a synthetic pointer-down. With `opts.id` and `opts.type: "touch"`
|
|
1816
|
+
* this drives a multi-touch contact, exercising `getPointers()`,
|
|
1817
|
+
* per-pointer event hooks, and the any-pointer aggregate for `MouseLeft`.
|
|
1818
|
+
*/
|
|
1819
|
+
pointerDown: /* @__PURE__ */ __name((button = 0, opts) => {
|
|
1820
|
+
this.requireInputManager().firePointerDown(button, opts);
|
|
1821
|
+
}, "pointerDown"),
|
|
1822
|
+
pointerUp: /* @__PURE__ */ __name((button = 0, opts) => {
|
|
1823
|
+
this.requireInputManager().firePointerUp(button, opts);
|
|
1824
|
+
}, "pointerUp"),
|
|
1825
|
+
gamepadButton: /* @__PURE__ */ __name((code, pressed) => {
|
|
1826
|
+
this.requireInputManager().fireGamepadButton(code, pressed);
|
|
1827
|
+
}, "gamepadButton"),
|
|
1828
|
+
gamepadAxis: /* @__PURE__ */ __name((side, value) => {
|
|
1829
|
+
this.requireInputManager().fireGamepadAxis(side, value);
|
|
1830
|
+
}, "gamepadAxis"),
|
|
1831
|
+
tap: /* @__PURE__ */ __name((code, frames = 1) => {
|
|
1832
|
+
this.assertNonNegativeInteger(frames, "Inspector.input.tap(frames)");
|
|
1833
|
+
const input = this.requireInputManager();
|
|
1834
|
+
input.fireKeyDown(code);
|
|
1835
|
+
try {
|
|
1836
|
+
this.time.step(frames);
|
|
1837
|
+
} finally {
|
|
1838
|
+
input.fireKeyUp(code);
|
|
1839
|
+
}
|
|
1840
|
+
}, "tap"),
|
|
1841
|
+
hold: /* @__PURE__ */ __name((code, frames) => {
|
|
1842
|
+
this.assertNonNegativeInteger(frames, "Inspector.input.hold(frames)");
|
|
1843
|
+
const input = this.requireInputManager();
|
|
1844
|
+
input.fireKeyDown(code);
|
|
1845
|
+
try {
|
|
1846
|
+
this.time.step(frames);
|
|
1847
|
+
} finally {
|
|
1848
|
+
input.fireKeyUp(code);
|
|
1849
|
+
}
|
|
1850
|
+
}, "hold"),
|
|
1851
|
+
fireAction: /* @__PURE__ */ __name((name, frames = 1) => {
|
|
1852
|
+
this.assertNonNegativeInteger(
|
|
1853
|
+
frames,
|
|
1854
|
+
"Inspector.input.fireAction(frames)"
|
|
1855
|
+
);
|
|
1856
|
+
const input = this.requireInputManager();
|
|
1857
|
+
for (let i = 0; i < frames; i++) {
|
|
1858
|
+
input.fireAction(name);
|
|
1859
|
+
this.time.step(1);
|
|
1860
|
+
}
|
|
1861
|
+
}, "fireAction"),
|
|
1862
|
+
clearAll: /* @__PURE__ */ __name(() => {
|
|
1863
|
+
this.requireInputManager().clearAll();
|
|
1864
|
+
}, "clearAll")
|
|
1865
|
+
};
|
|
1866
|
+
events = {
|
|
1867
|
+
getLog: /* @__PURE__ */ __name(() => this.iterateLog().map(({ entry }) => ({ ...entry })), "getLog"),
|
|
1868
|
+
clearLog: /* @__PURE__ */ __name(() => {
|
|
1869
|
+
this.eventLog.length = 0;
|
|
1870
|
+
this.eventLogHead = 0;
|
|
1871
|
+
}, "clearLog"),
|
|
1872
|
+
setCapacity: /* @__PURE__ */ __name((n) => {
|
|
1873
|
+
this.assertNonNegativeInteger(
|
|
1874
|
+
n,
|
|
1875
|
+
"Inspector.events.setCapacity(capacity)"
|
|
1876
|
+
);
|
|
1877
|
+
const ordered = n === 0 ? [] : this.iterateLog().slice(-n);
|
|
1878
|
+
this.eventCapacity = n;
|
|
1879
|
+
this.eventLog = ordered;
|
|
1880
|
+
this.eventLogHead = 0;
|
|
1881
|
+
}, "setCapacity"),
|
|
1882
|
+
waitFor: /* @__PURE__ */ __name((pattern, options) => {
|
|
1883
|
+
const existing = this.findMatchingEvent(pattern, options?.source);
|
|
1884
|
+
if (existing) return Promise.resolve(existing);
|
|
1885
|
+
const withinFrames = options?.withinFrames;
|
|
1886
|
+
if (withinFrames !== void 0 && (!Number.isInteger(withinFrames) || withinFrames < 0)) {
|
|
1887
|
+
throw new Error(
|
|
1888
|
+
"Inspector.events.waitFor(withinFrames) requires a non-negative integer."
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
return new Promise((resolve, reject) => {
|
|
1892
|
+
const waiter = {
|
|
1893
|
+
pattern,
|
|
1894
|
+
source: options?.source,
|
|
1895
|
+
withinFrames,
|
|
1896
|
+
deadlineFrame: withinFrames !== void 0 ? this.time.getFrame() + withinFrames : void 0,
|
|
1897
|
+
resolve,
|
|
1898
|
+
reject
|
|
1899
|
+
};
|
|
1900
|
+
this.eventWaiters.add(waiter);
|
|
1901
|
+
});
|
|
1902
|
+
}, "waitFor")
|
|
1903
|
+
};
|
|
1904
|
+
capture = {
|
|
1905
|
+
png: /* @__PURE__ */ __name(async () => {
|
|
1906
|
+
const base64 = await this.capture.pngBase64();
|
|
1907
|
+
return decodeBase64(base64);
|
|
1908
|
+
}, "png"),
|
|
1909
|
+
dataURL: /* @__PURE__ */ __name(async () => {
|
|
1910
|
+
const renderer = this.engine.context.tryResolve(RendererRuntimeKey);
|
|
1911
|
+
if (!renderer) {
|
|
1912
|
+
throw new Error(
|
|
1913
|
+
"Inspector.capture requires RendererPlugin to be active."
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
const canvas = renderer.application.renderer.extract.canvas(
|
|
1917
|
+
renderer.application.stage
|
|
1918
|
+
);
|
|
1919
|
+
return canvas.toDataURL("image/png");
|
|
1920
|
+
}, "dataURL"),
|
|
1921
|
+
pngBase64: /* @__PURE__ */ __name(async () => {
|
|
1922
|
+
const dataUrl = await this.capture.dataURL();
|
|
1923
|
+
const comma = dataUrl.indexOf(",");
|
|
1924
|
+
return comma === -1 ? dataUrl : dataUrl.slice(comma + 1);
|
|
1925
|
+
}, "pngBase64")
|
|
1926
|
+
};
|
|
1927
|
+
constructor(engine) {
|
|
1928
|
+
this.engine = engine;
|
|
1683
1929
|
}
|
|
1684
|
-
/**
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
for (let i = idx + 1; i < stack.length; i++) {
|
|
1693
|
-
if (stack[i].pauseBelow) return true;
|
|
1930
|
+
/** Register a namespaced extension API for plugin-specific debug helpers. */
|
|
1931
|
+
addExtension(namespace, api) {
|
|
1932
|
+
this.assertNonEmptyString(
|
|
1933
|
+
namespace,
|
|
1934
|
+
"Inspector.addExtension(namespace)"
|
|
1935
|
+
);
|
|
1936
|
+
if (!api || typeof api !== "object") {
|
|
1937
|
+
throw new Error("Inspector.addExtension(api) requires an object.");
|
|
1694
1938
|
}
|
|
1695
|
-
|
|
1939
|
+
if (this.extensions.has(namespace)) {
|
|
1940
|
+
throw new Error(
|
|
1941
|
+
`Inspector.addExtension(): namespace "${namespace}" is already registered.`
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
this.extensions.set(namespace, api);
|
|
1945
|
+
return api;
|
|
1696
1946
|
}
|
|
1697
|
-
/**
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1947
|
+
/** Look up a previously registered extension API by namespace. */
|
|
1948
|
+
getExtension(namespace) {
|
|
1949
|
+
this.assertNonEmptyString(
|
|
1950
|
+
namespace,
|
|
1951
|
+
"Inspector.getExtension(namespace)"
|
|
1952
|
+
);
|
|
1953
|
+
return this.extensions.get(namespace);
|
|
1701
1954
|
}
|
|
1702
|
-
/**
|
|
1703
|
-
|
|
1704
|
-
|
|
1955
|
+
/** Remove a previously registered extension namespace. */
|
|
1956
|
+
removeExtension(namespace) {
|
|
1957
|
+
this.assertNonEmptyString(
|
|
1958
|
+
namespace,
|
|
1959
|
+
"Inspector.removeExtension(namespace)"
|
|
1960
|
+
);
|
|
1961
|
+
this.extensions.delete(namespace);
|
|
1705
1962
|
}
|
|
1706
|
-
/**
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
resolved ??= this._context.resolve(key);
|
|
1723
|
-
resolved[prop] = value;
|
|
1724
|
-
return true;
|
|
1725
|
-
}, "set")
|
|
1726
|
-
});
|
|
1963
|
+
/** Full deterministic state snapshot (stable ordering, serializable). */
|
|
1964
|
+
snapshot() {
|
|
1965
|
+
const scenes = this.engine.scenes.all.map(
|
|
1966
|
+
(scene) => this.sceneToWorldSnapshot(scene)
|
|
1967
|
+
);
|
|
1968
|
+
return {
|
|
1969
|
+
version: 1,
|
|
1970
|
+
frame: this.time.getFrame(),
|
|
1971
|
+
sceneStack: this.getSceneStack(),
|
|
1972
|
+
entityCount: this.countEntities(),
|
|
1973
|
+
systemCount: this.getSystems().length,
|
|
1974
|
+
errors: this.getErrors(),
|
|
1975
|
+
scenes,
|
|
1976
|
+
camera: this.buildCameraSnapshot(),
|
|
1977
|
+
input: this.buildInputSnapshot()
|
|
1978
|
+
};
|
|
1727
1979
|
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1980
|
+
/** Stable JSON form of {@link snapshot}. */
|
|
1981
|
+
snapshotJSON() {
|
|
1982
|
+
return stableStringify(this.snapshot());
|
|
1983
|
+
}
|
|
1984
|
+
/** Snapshot one scene by inspector scene id. */
|
|
1985
|
+
snapshotScene(id) {
|
|
1986
|
+
const scene = this.engine.scenes.all.find(
|
|
1987
|
+
(candidate) => this.getSceneId(candidate) === id
|
|
1988
|
+
);
|
|
1989
|
+
if (!scene) {
|
|
1990
|
+
throw new Error(`Inspector.snapshotScene(): unknown scene id "${id}".`);
|
|
1736
1991
|
}
|
|
1737
|
-
|
|
1992
|
+
return this.sceneToWorldSnapshot(scene);
|
|
1993
|
+
}
|
|
1994
|
+
/** Find entity by name in the active scene. */
|
|
1995
|
+
getEntityByName(name) {
|
|
1996
|
+
const entity = this.findActiveEntity(name);
|
|
1997
|
+
if (!entity) return void 0;
|
|
1998
|
+
return this.entityToQuerySnapshot(entity);
|
|
1999
|
+
}
|
|
2000
|
+
/** Get entity position (from Transform component). */
|
|
2001
|
+
getEntityPosition(name) {
|
|
2002
|
+
const entity = this.findActiveEntity(name);
|
|
2003
|
+
if (!entity) return void 0;
|
|
2004
|
+
const transform = this.getTransform(entity);
|
|
2005
|
+
if (!transform) return void 0;
|
|
2006
|
+
return { x: transform.position.x, y: transform.position.y };
|
|
2007
|
+
}
|
|
2008
|
+
/** Check if an entity has a component by class name string. */
|
|
2009
|
+
hasComponent(entityName, componentClass) {
|
|
2010
|
+
return this.findComponentByName(entityName, componentClass) !== void 0;
|
|
2011
|
+
}
|
|
2012
|
+
/** Get component data (serializable subset) by class name string. */
|
|
2013
|
+
getComponentData(entityName, componentClass) {
|
|
2014
|
+
const comp = this.findComponentByName(entityName, componentClass);
|
|
2015
|
+
if (!comp) return void 0;
|
|
2016
|
+
if (typeof comp.serialize === "function") {
|
|
2017
|
+
const data = trySerialize(comp);
|
|
2018
|
+
if (data !== void 0) return data;
|
|
2019
|
+
}
|
|
2020
|
+
return this.serializeComponentOwnProperties(comp);
|
|
2021
|
+
}
|
|
2022
|
+
/** Get all entities in the active scene as lightweight snapshots. */
|
|
2023
|
+
getEntities() {
|
|
2024
|
+
const scene = this.engine.scenes.active;
|
|
2025
|
+
if (!scene) return [];
|
|
2026
|
+
const result = [];
|
|
2027
|
+
for (const entity of scene.getEntities()) {
|
|
2028
|
+
if (!entity.isDestroyed) {
|
|
2029
|
+
result.push(this.entityToQuerySnapshot(entity));
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
return result;
|
|
2033
|
+
}
|
|
2034
|
+
/** Get scene stack info. */
|
|
2035
|
+
getSceneStack() {
|
|
2036
|
+
return this.engine.scenes.all.map((scene) => ({
|
|
2037
|
+
name: scene.name,
|
|
2038
|
+
entityCount: scene.getEntities().size,
|
|
2039
|
+
paused: scene.isPaused
|
|
2040
|
+
}));
|
|
2041
|
+
}
|
|
2042
|
+
/** Get active system info. */
|
|
2043
|
+
getSystems() {
|
|
2044
|
+
const scheduler = this.engine.context.tryResolve(SystemSchedulerKey);
|
|
2045
|
+
if (!scheduler) return [];
|
|
2046
|
+
return scheduler.getAllSystems().map((sys) => ({
|
|
2047
|
+
name: sys.constructor.name,
|
|
2048
|
+
phase: sys.phase,
|
|
2049
|
+
priority: sys.priority,
|
|
2050
|
+
enabled: sys.enabled
|
|
2051
|
+
}));
|
|
2052
|
+
}
|
|
2053
|
+
/** Get disabled components/systems from error boundary. */
|
|
2054
|
+
getErrors() {
|
|
2055
|
+
const boundary = this.engine.context.tryResolve(ErrorBoundaryKey);
|
|
2056
|
+
if (!boundary) return { disabledSystems: [], disabledComponents: [] };
|
|
2057
|
+
const disabled = boundary.getDisabled();
|
|
2058
|
+
return {
|
|
2059
|
+
disabledSystems: disabled.systems.map(
|
|
2060
|
+
(s) => s.system.constructor.name
|
|
2061
|
+
),
|
|
2062
|
+
disabledComponents: disabled.components.map((c) => ({
|
|
2063
|
+
entity: c.component.entity?.name ?? "unknown",
|
|
2064
|
+
component: c.component.constructor.name,
|
|
2065
|
+
error: c.error
|
|
2066
|
+
}))
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
/** Create a new scene-scoped RNG instance using the current inspector seed policy. */
|
|
2070
|
+
createSceneRandom() {
|
|
2071
|
+
const seed = this.sceneSeedOverride ?? this.defaultSceneSeed ?? createDefaultRandomSeed();
|
|
2072
|
+
return createRandomService(seed);
|
|
2073
|
+
}
|
|
2074
|
+
/** Force every current and future scene RNG to the provided seed. */
|
|
2075
|
+
setSeed(seed) {
|
|
2076
|
+
const normalized = normalizeSeed(seed);
|
|
2077
|
+
this.sceneSeedOverride = normalized;
|
|
2078
|
+
for (const scene of this.engine.scenes.all) {
|
|
2079
|
+
this.resolveInternalRandom(scene)?.setSeed(normalized);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
/** @internal DebugPlugin installs a deterministic default seed through this hook. */
|
|
2083
|
+
setDefaultSceneSeed(seed) {
|
|
2084
|
+
this.defaultSceneSeed = seed === void 0 ? void 0 : normalizeSeed(seed);
|
|
2085
|
+
if (this.sceneSeedOverride !== void 0 || this.defaultSceneSeed === void 0) {
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2088
|
+
for (const scene of this.engine.scenes.all) {
|
|
2089
|
+
this.resolveInternalRandom(scene)?.setSeed(this.defaultSceneSeed);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
resolveInternalRandom(scene) {
|
|
2093
|
+
return scene._resolveScoped(RandomKey);
|
|
2094
|
+
}
|
|
2095
|
+
/** @internal DebugPlugin attaches the frozen-time controller through this hook. */
|
|
2096
|
+
attachTimeController(controller) {
|
|
2097
|
+
this.timeController = controller;
|
|
2098
|
+
}
|
|
2099
|
+
/** @internal Clear a previously attached time controller. */
|
|
2100
|
+
detachTimeController(controller) {
|
|
2101
|
+
if (!controller || this.timeController === controller) {
|
|
2102
|
+
this.timeController = null;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
/** @internal Enable or disable event log recording. */
|
|
2106
|
+
setEventLogEnabled(enabled) {
|
|
2107
|
+
if (this.eventLogEnabled === enabled) return;
|
|
2108
|
+
this.eventLogEnabled = enabled;
|
|
2109
|
+
if (enabled) {
|
|
2110
|
+
if (!this.detachBusTap && this.engine.events?.tap) {
|
|
2111
|
+
this.detachBusTap = this.engine.events.tap(this.busEventObserver);
|
|
2112
|
+
}
|
|
2113
|
+
} else {
|
|
2114
|
+
this.detachBusTap?.();
|
|
2115
|
+
this.detachBusTap = null;
|
|
2116
|
+
}
|
|
2117
|
+
for (const scene of this.engine.scenes.all) {
|
|
2118
|
+
if (enabled) {
|
|
2119
|
+
this.attachSceneEventObserver(scene);
|
|
2120
|
+
} else {
|
|
2121
|
+
this.detachSceneEventObserver(scene);
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
/** @internal Install entity-event observation for one scene. No-op if disabled. */
|
|
2126
|
+
attachSceneEventObserver(scene) {
|
|
2127
|
+
if (!this.eventLogEnabled) return;
|
|
2128
|
+
scene._setEntityEventObserver(this.sceneEventObserver);
|
|
2129
|
+
}
|
|
2130
|
+
/** @internal Clear entity-event observation for one scene. */
|
|
2131
|
+
detachSceneEventObserver(scene) {
|
|
2132
|
+
scene._setEntityEventObserver(void 0);
|
|
2133
|
+
}
|
|
2134
|
+
/** @internal Scene hooks forward entity events through this method. */
|
|
2135
|
+
recordEntityEvent(eventName, data, entity) {
|
|
2136
|
+
if (!this.eventLogEnabled) return;
|
|
2137
|
+
const scene = entity.tryScene;
|
|
2138
|
+
this.appendEvent(
|
|
2139
|
+
{
|
|
2140
|
+
frame: this.time.getFrame(),
|
|
2141
|
+
source: "entity",
|
|
2142
|
+
type: eventName,
|
|
2143
|
+
targetId: String(entity.id),
|
|
2144
|
+
payload: serializeEventPayload(data)
|
|
2145
|
+
},
|
|
2146
|
+
scene ? this.getSceneId(scene) : void 0
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
/** @internal Engine teardown releases the event-bus tap through this hook. */
|
|
2150
|
+
dispose() {
|
|
2151
|
+
this.detachBusTap?.();
|
|
2152
|
+
this.detachBusTap = null;
|
|
2153
|
+
for (const scene of this.engine.scenes.all) {
|
|
2154
|
+
scene._setEntityEventObserver(void 0);
|
|
2155
|
+
}
|
|
2156
|
+
this.extensions.clear();
|
|
2157
|
+
}
|
|
2158
|
+
requireTimeController() {
|
|
2159
|
+
if (!this.timeController) {
|
|
2160
|
+
throw new Error(
|
|
2161
|
+
"Inspector.time requires DebugPlugin to be active."
|
|
2162
|
+
);
|
|
2163
|
+
}
|
|
2164
|
+
return this.timeController;
|
|
2165
|
+
}
|
|
2166
|
+
requireInputManager() {
|
|
2167
|
+
const input = this.engine.context.tryResolve(InputManagerRuntimeKey);
|
|
2168
|
+
if (!input) {
|
|
2169
|
+
throw new Error(
|
|
2170
|
+
"Inspector.input requires InputPlugin to be active."
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
return input;
|
|
2174
|
+
}
|
|
2175
|
+
recordBusEvent(type, data) {
|
|
2176
|
+
if (!this.eventLogEnabled) return;
|
|
2177
|
+
this.appendEvent(
|
|
2178
|
+
{
|
|
2179
|
+
frame: this.time.getFrame(),
|
|
2180
|
+
source: "bus",
|
|
2181
|
+
type,
|
|
2182
|
+
payload: serializeEventPayload(data)
|
|
2183
|
+
},
|
|
2184
|
+
this.inferSceneIdFromPayload(data)
|
|
2185
|
+
);
|
|
2186
|
+
}
|
|
2187
|
+
appendEvent(entry, sceneId) {
|
|
2188
|
+
if (this.eventCapacity === 0) {
|
|
2189
|
+
this.flushMatchingWaiter(entry);
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
const logged = { entry, sceneId };
|
|
2193
|
+
if (this.eventLog.length < this.eventCapacity) {
|
|
2194
|
+
this.eventLog.push(logged);
|
|
2195
|
+
} else {
|
|
2196
|
+
this.eventLog[this.eventLogHead] = logged;
|
|
2197
|
+
this.eventLogHead = (this.eventLogHead + 1) % this.eventCapacity;
|
|
2198
|
+
}
|
|
2199
|
+
this.flushMatchingWaiter(entry);
|
|
2200
|
+
}
|
|
2201
|
+
/** Resolve waiters whose deadline has passed without a match. */
|
|
2202
|
+
expireDeadlineWaiters() {
|
|
2203
|
+
if (this.eventWaiters.size === 0) return;
|
|
2204
|
+
const frame = this.time.getFrame();
|
|
2205
|
+
for (const waiter of [...this.eventWaiters]) {
|
|
2206
|
+
if (waiter.deadlineFrame !== void 0 && frame > waiter.deadlineFrame) {
|
|
2207
|
+
this.eventWaiters.delete(waiter);
|
|
2208
|
+
waiter.reject(
|
|
2209
|
+
new Error(
|
|
2210
|
+
`Inspector.events.waitFor() timed out after ${waiter.withinFrames} frames.`
|
|
2211
|
+
)
|
|
2212
|
+
);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
/** Resolve any waiter that matches the just-appended entry. */
|
|
2217
|
+
flushMatchingWaiter(entry) {
|
|
2218
|
+
if (this.eventWaiters.size === 0) return;
|
|
2219
|
+
for (const waiter of [...this.eventWaiters]) {
|
|
2220
|
+
if (this.eventMatches(entry, waiter.pattern, waiter.source)) {
|
|
2221
|
+
this.eventWaiters.delete(waiter);
|
|
2222
|
+
waiter.resolve(entry);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
/**
|
|
2227
|
+
* Walk the ring buffer in chronological order. We avoid materializing the
|
|
2228
|
+
* ordered array on every event append; instead, every consumer that needs
|
|
2229
|
+
* order calls this helper.
|
|
2230
|
+
*/
|
|
2231
|
+
iterateLog() {
|
|
2232
|
+
if (this.eventLog.length < this.eventCapacity || this.eventLogHead === 0) {
|
|
2233
|
+
return this.eventLog;
|
|
2234
|
+
}
|
|
2235
|
+
return [
|
|
2236
|
+
...this.eventLog.slice(this.eventLogHead),
|
|
2237
|
+
...this.eventLog.slice(0, this.eventLogHead)
|
|
2238
|
+
];
|
|
2239
|
+
}
|
|
2240
|
+
findMatchingEvent(pattern, source) {
|
|
2241
|
+
for (const { entry } of this.iterateLog()) {
|
|
2242
|
+
if (this.eventMatches(entry, pattern, source)) {
|
|
2243
|
+
return { ...entry };
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
return void 0;
|
|
2247
|
+
}
|
|
2248
|
+
eventMatches(entry, pattern, source) {
|
|
2249
|
+
if (source && entry.source !== source) return false;
|
|
2250
|
+
return typeof pattern === "string" ? entry.type === pattern : pattern.test(entry.type);
|
|
2251
|
+
}
|
|
2252
|
+
sceneToWorldSnapshot(scene) {
|
|
2253
|
+
const random = scene._resolveScoped(RandomKey);
|
|
2254
|
+
const physicsManager = this.engine.context.tryResolve(
|
|
2255
|
+
PhysicsWorldManagerRuntimeKey
|
|
2256
|
+
);
|
|
2257
|
+
return {
|
|
2258
|
+
id: this.getSceneId(scene),
|
|
2259
|
+
name: scene.name,
|
|
2260
|
+
paused: scene.isPaused,
|
|
2261
|
+
timeScale: scene.timeScale,
|
|
2262
|
+
seed: random?.getSeed() ?? 0,
|
|
2263
|
+
entities: this.getSceneEntities(scene),
|
|
2264
|
+
ui: this.buildUISnapshot(scene),
|
|
2265
|
+
physics: physicsManager?.getContext(scene)?.world.snapshot() ?? {
|
|
2266
|
+
bodies: [],
|
|
2267
|
+
contacts: []
|
|
2268
|
+
},
|
|
2269
|
+
events: this.getSceneEvents(scene)
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
getSceneEntities(scene) {
|
|
2273
|
+
return [...scene.getEntities()].filter((entity) => !entity.isDestroyed).sort((a, b) => a.id - b.id).map((entity) => this.entityToWorldSnapshot(entity));
|
|
2274
|
+
}
|
|
2275
|
+
entityToWorldSnapshot(entity) {
|
|
2276
|
+
const transform = entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
2277
|
+
const worldPosition = transform?.worldPosition;
|
|
2278
|
+
const worldScale = transform?.worldScale;
|
|
2279
|
+
const components = [...entity.getAll()].map((component) => this.componentToSnapshot(component)).sort((a, b) => a.type < b.type ? -1 : a.type > b.type ? 1 : 0);
|
|
2280
|
+
return {
|
|
2281
|
+
id: String(entity.id),
|
|
2282
|
+
type: entity.constructor.name,
|
|
2283
|
+
parent: entity.parent ? String(entity.parent.id) : null,
|
|
2284
|
+
transform: {
|
|
2285
|
+
x: worldPosition?.x ?? 0,
|
|
2286
|
+
y: worldPosition?.y ?? 0,
|
|
2287
|
+
rotation: transform?.worldRotation ?? 0,
|
|
2288
|
+
scaleX: worldScale?.x ?? 1,
|
|
2289
|
+
scaleY: worldScale?.y ?? 1
|
|
2290
|
+
},
|
|
2291
|
+
components
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
componentToSnapshot(component) {
|
|
2295
|
+
return {
|
|
2296
|
+
type: component.constructor.name,
|
|
2297
|
+
state: typeof component.serialize === "function" ? trySerialize(component) ?? null : null
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
buildUISnapshot(scene) {
|
|
2301
|
+
const roots = [...scene.getEntities()].filter((entity) => !entity.isDestroyed).flatMap(
|
|
2302
|
+
(entity) => [...entity.getAll()].filter(
|
|
2303
|
+
(component) => component.constructor.name === "UIPanel" && "_node" in component
|
|
2304
|
+
).map(
|
|
2305
|
+
(component, index) => this.buildUINodeSnapshot(
|
|
2306
|
+
component._node,
|
|
2307
|
+
`entity-${entity.id}:UIPanel:${index}`
|
|
2308
|
+
)
|
|
2309
|
+
)
|
|
2310
|
+
);
|
|
2311
|
+
if (roots.length === 0) return null;
|
|
2312
|
+
if (roots.length === 1) {
|
|
2313
|
+
return { root: roots[0] };
|
|
2314
|
+
}
|
|
2315
|
+
return {
|
|
2316
|
+
root: {
|
|
2317
|
+
id: `scene-${this.getSceneId(scene)}:ui`,
|
|
2318
|
+
type: "UIRoot",
|
|
2319
|
+
layout: { x: 0, y: 0, width: 0, height: 0 },
|
|
2320
|
+
children: roots,
|
|
2321
|
+
state: null
|
|
2322
|
+
}
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
buildUINodeSnapshot(node, id) {
|
|
2326
|
+
const layout = node.yogaNode?.getComputedLayout();
|
|
2327
|
+
const children = (node.children ?? []).map(
|
|
2328
|
+
(child, index) => this.buildUINodeSnapshot(child, `${id}/${index}`)
|
|
2329
|
+
);
|
|
2330
|
+
return {
|
|
2331
|
+
id,
|
|
2332
|
+
type: node.constructor.name,
|
|
2333
|
+
layout: {
|
|
2334
|
+
x: layout?.left ?? 0,
|
|
2335
|
+
y: layout?.top ?? 0,
|
|
2336
|
+
width: layout?.width ?? 0,
|
|
2337
|
+
height: layout?.height ?? 0
|
|
2338
|
+
},
|
|
2339
|
+
children,
|
|
2340
|
+
state: null
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
buildCameraSnapshot() {
|
|
2344
|
+
const match = this.findTopmostCamera();
|
|
2345
|
+
if (!match) return null;
|
|
2346
|
+
const { scene, camera } = match;
|
|
2347
|
+
return {
|
|
2348
|
+
sceneId: this.getSceneId(scene),
|
|
2349
|
+
sceneName: scene.name,
|
|
2350
|
+
name: camera.cameraName ?? null,
|
|
2351
|
+
priority: camera.priority ?? 0,
|
|
2352
|
+
position: {
|
|
2353
|
+
x: camera.position.x,
|
|
2354
|
+
y: camera.position.y
|
|
2355
|
+
},
|
|
2356
|
+
zoom: camera.zoom,
|
|
2357
|
+
rotation: camera.rotation
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
findTopmostCamera() {
|
|
2361
|
+
const stack = this.engine.scenes.all;
|
|
2362
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
2363
|
+
const scene = stack[i];
|
|
2364
|
+
if (!scene) continue;
|
|
2365
|
+
let highest;
|
|
2366
|
+
for (const entity of scene.getEntities()) {
|
|
2367
|
+
if (entity.isDestroyed) continue;
|
|
2368
|
+
for (const component of entity.getAll()) {
|
|
2369
|
+
if (component.constructor.name !== "CameraComponent") continue;
|
|
2370
|
+
const camera = component;
|
|
2371
|
+
if (camera.enabled && (!highest || (camera.priority ?? 0) > (highest.priority ?? 0))) {
|
|
2372
|
+
highest = camera;
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
if (highest) {
|
|
2377
|
+
return { scene, camera: highest };
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
return void 0;
|
|
2381
|
+
}
|
|
2382
|
+
buildInputSnapshot() {
|
|
2383
|
+
const input = this.engine.context.tryResolve(InputManagerRuntimeKey);
|
|
2384
|
+
return input?.snapshotState() ?? {
|
|
2385
|
+
keys: [],
|
|
2386
|
+
actions: [],
|
|
2387
|
+
mouse: { x: 0, y: 0, buttons: [], down: false },
|
|
2388
|
+
pointers: [],
|
|
2389
|
+
gamepad: { buttons: [], axes: [] }
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
getSceneEvents(scene) {
|
|
2393
|
+
const sceneId = this.getSceneId(scene);
|
|
2394
|
+
return this.iterateLog().filter((entry) => entry.sceneId === sceneId).map(({ entry }) => ({ ...entry }));
|
|
2395
|
+
}
|
|
2396
|
+
inferSceneIdFromPayload(data) {
|
|
2397
|
+
if (!data || typeof data !== "object") return void 0;
|
|
2398
|
+
const record = data;
|
|
2399
|
+
const scene = this.extractScene(record["scene"]) ?? this.extractSceneFromEntity(record["entity"]) ?? this.extractSceneFromEntity(record["oldScene"]) ?? this.extractSceneFromEntity(record["newScene"]);
|
|
2400
|
+
return scene ? this.getSceneId(scene) : void 0;
|
|
2401
|
+
}
|
|
2402
|
+
extractScene(value) {
|
|
2403
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2404
|
+
return this.engine.scenes.all.find((scene) => scene === value);
|
|
2405
|
+
}
|
|
2406
|
+
extractSceneFromEntity(value) {
|
|
2407
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2408
|
+
const maybeEntity = value;
|
|
2409
|
+
return maybeEntity.tryScene ?? this.extractScene(value);
|
|
2410
|
+
}
|
|
2411
|
+
findActiveEntity(name) {
|
|
2412
|
+
return this.engine.scenes.active?.findEntity(name);
|
|
2413
|
+
}
|
|
2414
|
+
findComponentByName(entityName, componentClass) {
|
|
2415
|
+
const entity = this.findActiveEntity(entityName);
|
|
2416
|
+
if (!entity) return void 0;
|
|
2417
|
+
for (const comp of entity.getAll()) {
|
|
2418
|
+
if (comp.constructor.name === componentClass) return comp;
|
|
2419
|
+
}
|
|
2420
|
+
return void 0;
|
|
2421
|
+
}
|
|
2422
|
+
entityToQuerySnapshot(entity) {
|
|
2423
|
+
const transform = this.getTransform(entity);
|
|
2424
|
+
const snapshot = {
|
|
2425
|
+
id: entity.id,
|
|
2426
|
+
name: entity.name,
|
|
2427
|
+
tags: [...entity.tags].sort((a, b) => a < b ? -1 : a > b ? 1 : 0),
|
|
2428
|
+
components: [...entity.getAll()].map((component) => component.constructor.name).sort((a, b) => a < b ? -1 : a > b ? 1 : 0)
|
|
2429
|
+
};
|
|
2430
|
+
if (transform) {
|
|
2431
|
+
snapshot.position = {
|
|
2432
|
+
x: transform.position.x,
|
|
2433
|
+
y: transform.position.y
|
|
2434
|
+
};
|
|
2435
|
+
}
|
|
2436
|
+
return snapshot;
|
|
2437
|
+
}
|
|
2438
|
+
getTransform(entity) {
|
|
2439
|
+
return entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
2440
|
+
}
|
|
2441
|
+
serializeComponentOwnProperties(comp) {
|
|
2442
|
+
const result = {};
|
|
2443
|
+
for (const key of Object.getOwnPropertyNames(comp)) {
|
|
2444
|
+
if (key === "entity") continue;
|
|
2445
|
+
if (key.startsWith("_")) continue;
|
|
2446
|
+
const value = comp[key];
|
|
2447
|
+
if (!isSerializableValue(value)) continue;
|
|
2448
|
+
result[key] = value;
|
|
2449
|
+
}
|
|
2450
|
+
return result;
|
|
2451
|
+
}
|
|
2452
|
+
countEntities() {
|
|
2453
|
+
let count = 0;
|
|
2454
|
+
for (const scene of this.engine.scenes.all) {
|
|
2455
|
+
for (const entity of scene.getEntities()) {
|
|
2456
|
+
if (!entity.isDestroyed) count++;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
return count;
|
|
2460
|
+
}
|
|
2461
|
+
getSceneId(scene) {
|
|
2462
|
+
let id = this.sceneIds.get(scene);
|
|
2463
|
+
if (!id) {
|
|
2464
|
+
this.nextSceneId++;
|
|
2465
|
+
id = `scene-${this.nextSceneId}`;
|
|
2466
|
+
this.sceneIds.set(scene, id);
|
|
2467
|
+
}
|
|
2468
|
+
return id;
|
|
2469
|
+
}
|
|
2470
|
+
assertNonNegativeInteger(value, name) {
|
|
2471
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
2472
|
+
throw new Error(`${name} requires a non-negative integer.`);
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
assertNonEmptyString(value, name) {
|
|
2476
|
+
if (value.trim().length === 0) {
|
|
2477
|
+
throw new Error(`${name} requires a non-empty string.`);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
function isSerializableValue(value) {
|
|
2482
|
+
if (value === null || value === void 0) return true;
|
|
2483
|
+
const t = typeof value;
|
|
2484
|
+
if (t === "function") return false;
|
|
2485
|
+
if (t !== "object") return true;
|
|
2486
|
+
if (Array.isArray(value)) return true;
|
|
2487
|
+
const proto = Object.getPrototypeOf(value);
|
|
2488
|
+
return proto === Object.prototype || proto === null;
|
|
2489
|
+
}
|
|
2490
|
+
__name(isSerializableValue, "isSerializableValue");
|
|
2491
|
+
function safeClone(value) {
|
|
2492
|
+
try {
|
|
2493
|
+
return JSON.parse(JSON.stringify(value));
|
|
2494
|
+
} catch {
|
|
2495
|
+
return void 0;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
__name(safeClone, "safeClone");
|
|
2499
|
+
function trySerialize(component) {
|
|
2500
|
+
try {
|
|
2501
|
+
return safeClone(component.serialize?.());
|
|
2502
|
+
} catch {
|
|
2503
|
+
return void 0;
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
__name(trySerialize, "trySerialize");
|
|
2507
|
+
function serializeEventPayload(payload) {
|
|
2508
|
+
if (payload === void 0) return null;
|
|
2509
|
+
const cloned = safeClone(payload);
|
|
2510
|
+
return cloned === void 0 ? { _unserializable: true } : cloned;
|
|
2511
|
+
}
|
|
2512
|
+
__name(serializeEventPayload, "serializeEventPayload");
|
|
2513
|
+
function stableStringify(value) {
|
|
2514
|
+
return JSON.stringify(sortJsonValue(value));
|
|
2515
|
+
}
|
|
2516
|
+
__name(stableStringify, "stableStringify");
|
|
2517
|
+
function sortJsonValue(value) {
|
|
2518
|
+
if (Array.isArray(value)) {
|
|
2519
|
+
return value.map((item) => sortJsonValue(item));
|
|
2520
|
+
}
|
|
2521
|
+
if (value && typeof value === "object") {
|
|
2522
|
+
const entries = Object.entries(value).sort(
|
|
2523
|
+
([left], [right]) => left < right ? -1 : left > right ? 1 : 0
|
|
2524
|
+
);
|
|
2525
|
+
const result = {};
|
|
2526
|
+
for (const [key, child] of entries) {
|
|
2527
|
+
result[key] = sortJsonValue(child);
|
|
2528
|
+
}
|
|
2529
|
+
return result;
|
|
2530
|
+
}
|
|
2531
|
+
return value;
|
|
2532
|
+
}
|
|
2533
|
+
__name(sortJsonValue, "sortJsonValue");
|
|
2534
|
+
function decodeBase64(base64) {
|
|
2535
|
+
if (typeof atob === "function") {
|
|
2536
|
+
const binary = atob(base64);
|
|
2537
|
+
const bytes = new Uint8Array(binary.length);
|
|
2538
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2539
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2540
|
+
}
|
|
2541
|
+
return bytes;
|
|
2542
|
+
}
|
|
2543
|
+
const bufferCtor = globalThis.Buffer;
|
|
2544
|
+
if (bufferCtor) {
|
|
2545
|
+
return bufferCtor.from(base64, "base64");
|
|
2546
|
+
}
|
|
2547
|
+
throw new Error("Inspector.capture.png() is not supported in this environment.");
|
|
2548
|
+
}
|
|
2549
|
+
__name(decodeBase64, "decodeBase64");
|
|
2550
|
+
|
|
2551
|
+
// src/Scene.ts
|
|
2552
|
+
var Scene = class {
|
|
2553
|
+
static {
|
|
2554
|
+
__name(this, "Scene");
|
|
2555
|
+
}
|
|
2556
|
+
/** Whether scenes below this one in the stack should be paused. Default: true. */
|
|
2557
|
+
pauseBelow = true;
|
|
2558
|
+
/** Whether scenes below this one should still render. Default: false. */
|
|
2559
|
+
transparentBelow = false;
|
|
2560
|
+
/** Asset handles to load before onEnter(). Override in subclasses. */
|
|
2561
|
+
preload;
|
|
2562
|
+
/** Default transition used when this scene is the destination of a push/pop/replace. */
|
|
2563
|
+
defaultTransition;
|
|
2564
|
+
/** Manual pause flag. Set by game code to pause this scene regardless of stack position. */
|
|
2565
|
+
paused = false;
|
|
2566
|
+
/** Time scale multiplier for this scene. 1.0 = normal, 0.5 = half speed. Default: 1. */
|
|
2567
|
+
timeScale = 1;
|
|
2568
|
+
entities = /* @__PURE__ */ new Set();
|
|
2569
|
+
destroyQueue = [];
|
|
2570
|
+
_context;
|
|
2571
|
+
entityCallbacks;
|
|
2572
|
+
queryCache;
|
|
2573
|
+
bus;
|
|
2574
|
+
_entityEventHandlers;
|
|
2575
|
+
_entityEventObserver;
|
|
2576
|
+
_scopedServices;
|
|
2577
|
+
/** Access the EngineContext. */
|
|
2578
|
+
get context() {
|
|
2579
|
+
return this._context;
|
|
2580
|
+
}
|
|
2581
|
+
/** Whether this scene is effectively paused (manual pause or paused by stack). */
|
|
2582
|
+
get isPaused() {
|
|
2583
|
+
if (this.paused) return true;
|
|
2584
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
2585
|
+
if (!sm) return false;
|
|
2586
|
+
const stack = sm.all;
|
|
2587
|
+
const idx = stack.indexOf(this);
|
|
2588
|
+
if (idx === -1) return false;
|
|
2589
|
+
for (let i = idx + 1; i < stack.length; i++) {
|
|
2590
|
+
if (stack[i].pauseBelow) return true;
|
|
2591
|
+
}
|
|
2592
|
+
return false;
|
|
2593
|
+
}
|
|
2594
|
+
/** Whether a scene transition is currently running. */
|
|
2595
|
+
get isTransitioning() {
|
|
2596
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
2597
|
+
return sm?.isTransitioning ?? false;
|
|
2598
|
+
}
|
|
2599
|
+
/** Convenience accessor for the AssetManager. */
|
|
2600
|
+
get assets() {
|
|
2601
|
+
return this._context.resolve(AssetManagerKey);
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Lazy proxy-based service resolution. Can be used at field-declaration time:
|
|
2605
|
+
* ```ts
|
|
2606
|
+
* readonly layers = this.service(RenderLayerManagerKey);
|
|
2607
|
+
* ```
|
|
2608
|
+
* The actual resolution is deferred until first property access.
|
|
2609
|
+
*/
|
|
2610
|
+
service(key) {
|
|
2611
|
+
let resolved;
|
|
2612
|
+
return new Proxy({}, {
|
|
2613
|
+
get: /* @__PURE__ */ __name((_target, prop) => {
|
|
2614
|
+
resolved ??= this._context.resolve(key);
|
|
2615
|
+
const value = resolved[prop];
|
|
2616
|
+
return typeof value === "function" ? value.bind(resolved) : value;
|
|
2617
|
+
}, "get"),
|
|
2618
|
+
set: /* @__PURE__ */ __name((_target, prop, value) => {
|
|
2619
|
+
resolved ??= this._context.resolve(key);
|
|
2620
|
+
resolved[prop] = value;
|
|
2621
|
+
return true;
|
|
2622
|
+
}, "set")
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
spawn(nameOrBlueprintOrClass, params) {
|
|
2626
|
+
if (typeof nameOrBlueprintOrClass === "function") {
|
|
2627
|
+
const entity2 = new nameOrBlueprintOrClass();
|
|
2628
|
+
entity2._setScene(this, this.entityCallbacks);
|
|
2629
|
+
this.entities.add(entity2);
|
|
2630
|
+
this.bus?.emit("entity:created", { entity: entity2 });
|
|
2631
|
+
entity2.setup?.(params);
|
|
2632
|
+
return entity2;
|
|
2633
|
+
}
|
|
2634
|
+
const isBlueprint = typeof nameOrBlueprintOrClass === "object" && nameOrBlueprintOrClass !== null && "build" in nameOrBlueprintOrClass;
|
|
1738
2635
|
const name = isBlueprint ? nameOrBlueprintOrClass.name : nameOrBlueprintOrClass;
|
|
1739
2636
|
const entity = new Entity(name);
|
|
1740
2637
|
entity._setScene(this, this.entityCallbacks);
|
|
@@ -1822,6 +2719,14 @@ var Scene = class {
|
|
|
1822
2719
|
}
|
|
1823
2720
|
}
|
|
1824
2721
|
}
|
|
2722
|
+
/**
|
|
2723
|
+
* Observe entity-scoped event emissions after they dispatch locally and
|
|
2724
|
+
* bubble to the scene. Tooling only; game code should keep using `on()`.
|
|
2725
|
+
* @internal
|
|
2726
|
+
*/
|
|
2727
|
+
_observeEntityEvent(eventName, data, entity) {
|
|
2728
|
+
this._entityEventObserver?.(eventName, data, entity);
|
|
2729
|
+
}
|
|
1825
2730
|
// ---- Internal methods ----
|
|
1826
2731
|
/**
|
|
1827
2732
|
* Register a scene-scoped service. Called from a plugin's `beforeEnter`
|
|
@@ -1833,6 +2738,13 @@ var Scene = class {
|
|
|
1833
2738
|
this._scopedServices ??= /* @__PURE__ */ new Map();
|
|
1834
2739
|
this._scopedServices.set(key.id, value);
|
|
1835
2740
|
}
|
|
2741
|
+
/**
|
|
2742
|
+
* Install or clear a tooling-only observer for bubbled entity events.
|
|
2743
|
+
* @internal
|
|
2744
|
+
*/
|
|
2745
|
+
_setEntityEventObserver(observer) {
|
|
2746
|
+
this._entityEventObserver = observer;
|
|
2747
|
+
}
|
|
1836
2748
|
/**
|
|
1837
2749
|
* Resolve a scene-scoped service, or `undefined` if none was registered.
|
|
1838
2750
|
* @internal
|
|
@@ -1899,6 +2811,128 @@ var Scene = class {
|
|
|
1899
2811
|
}
|
|
1900
2812
|
};
|
|
1901
2813
|
|
|
2814
|
+
// src/Process.ts
|
|
2815
|
+
var Process = class _Process {
|
|
2816
|
+
static {
|
|
2817
|
+
__name(this, "Process");
|
|
2818
|
+
}
|
|
2819
|
+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
2820
|
+
updateFn;
|
|
2821
|
+
onCompleteFn;
|
|
2822
|
+
duration;
|
|
2823
|
+
loop;
|
|
2824
|
+
/** Tags for filtering/grouping. */
|
|
2825
|
+
tags;
|
|
2826
|
+
elapsed = 0;
|
|
2827
|
+
_completed = false;
|
|
2828
|
+
_paused = false;
|
|
2829
|
+
_cancelled = false;
|
|
2830
|
+
resolvePromise;
|
|
2831
|
+
/** Create a timer that fires `onComplete` after `duration` ms. */
|
|
2832
|
+
static delay(duration, onComplete, tags) {
|
|
2833
|
+
const opts = { duration };
|
|
2834
|
+
if (onComplete !== void 0) opts.onComplete = onComplete;
|
|
2835
|
+
if (tags !== void 0) opts.tags = tags;
|
|
2836
|
+
return new _Process(opts);
|
|
2837
|
+
}
|
|
2838
|
+
constructor(options) {
|
|
2839
|
+
this.updateFn = options.update ?? (() => {
|
|
2840
|
+
});
|
|
2841
|
+
this.onCompleteFn = options.onComplete;
|
|
2842
|
+
this.duration = options.duration;
|
|
2843
|
+
this.loop = options.loop ?? false;
|
|
2844
|
+
this.tags = options.tags ?? [];
|
|
2845
|
+
}
|
|
2846
|
+
/** Whether the process has completed. */
|
|
2847
|
+
get completed() {
|
|
2848
|
+
return this._completed;
|
|
2849
|
+
}
|
|
2850
|
+
/** Whether the process is paused. */
|
|
2851
|
+
get paused() {
|
|
2852
|
+
return this._paused;
|
|
2853
|
+
}
|
|
2854
|
+
/** Pause the process. */
|
|
2855
|
+
pause() {
|
|
2856
|
+
this._paused = true;
|
|
2857
|
+
}
|
|
2858
|
+
/** Resume the process. */
|
|
2859
|
+
resume() {
|
|
2860
|
+
this._paused = false;
|
|
2861
|
+
}
|
|
2862
|
+
/** Cancel the process. */
|
|
2863
|
+
cancel() {
|
|
2864
|
+
this._cancelled = true;
|
|
2865
|
+
this._completed = true;
|
|
2866
|
+
this.resolvePromise?.();
|
|
2867
|
+
}
|
|
2868
|
+
/** Returns a promise that resolves when the process completes or is cancelled. */
|
|
2869
|
+
toPromise() {
|
|
2870
|
+
if (this._completed) return Promise.resolve();
|
|
2871
|
+
return new Promise((resolve) => {
|
|
2872
|
+
this.resolvePromise = resolve;
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* Advance the process by dt milliseconds.
|
|
2877
|
+
* @internal
|
|
2878
|
+
*/
|
|
2879
|
+
_update(dt) {
|
|
2880
|
+
if (this._completed || this._paused || this._cancelled) return;
|
|
2881
|
+
this.elapsed += dt;
|
|
2882
|
+
if (this.duration !== void 0 && this.elapsed >= this.duration) {
|
|
2883
|
+
const result2 = this.updateFn(dt, this.elapsed);
|
|
2884
|
+
if (this.loop && result2 !== true) {
|
|
2885
|
+
this.elapsed = this.elapsed % this.duration;
|
|
2886
|
+
return;
|
|
2887
|
+
}
|
|
2888
|
+
this.complete();
|
|
2889
|
+
return;
|
|
2890
|
+
}
|
|
2891
|
+
const result = this.updateFn(dt, this.elapsed);
|
|
2892
|
+
if (result === true) {
|
|
2893
|
+
if (this.loop) {
|
|
2894
|
+
this.elapsed = 0;
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
2897
|
+
this.complete();
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
/**
|
|
2901
|
+
* Reset the process to its initial state so it can be re-run.
|
|
2902
|
+
* @internal Used by Sequence for loop/repeat with direct instances.
|
|
2903
|
+
*/
|
|
2904
|
+
_reset() {
|
|
2905
|
+
this.elapsed = 0;
|
|
2906
|
+
this._completed = false;
|
|
2907
|
+
this._paused = false;
|
|
2908
|
+
this._cancelled = false;
|
|
2909
|
+
delete this.resolvePromise;
|
|
2910
|
+
}
|
|
2911
|
+
complete() {
|
|
2912
|
+
this._completed = true;
|
|
2913
|
+
this.onCompleteFn?.();
|
|
2914
|
+
this.resolvePromise?.();
|
|
2915
|
+
}
|
|
2916
|
+
};
|
|
2917
|
+
var easeLinear = /* @__PURE__ */ __name((t) => t, "easeLinear");
|
|
2918
|
+
var easeInQuad = /* @__PURE__ */ __name((t) => t * t, "easeInQuad");
|
|
2919
|
+
var easeOutQuad = /* @__PURE__ */ __name((t) => t * (2 - t), "easeOutQuad");
|
|
2920
|
+
var easeInOutQuad = /* @__PURE__ */ __name((t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, "easeInOutQuad");
|
|
2921
|
+
var easeOutBounce = /* @__PURE__ */ __name((t) => {
|
|
2922
|
+
if (t < 1 / 2.75) {
|
|
2923
|
+
return 7.5625 * t * t;
|
|
2924
|
+
} else if (t < 2 / 2.75) {
|
|
2925
|
+
const t2 = t - 1.5 / 2.75;
|
|
2926
|
+
return 7.5625 * t2 * t2 + 0.75;
|
|
2927
|
+
} else if (t < 2.5 / 2.75) {
|
|
2928
|
+
const t2 = t - 2.25 / 2.75;
|
|
2929
|
+
return 7.5625 * t2 * t2 + 0.9375;
|
|
2930
|
+
} else {
|
|
2931
|
+
const t2 = t - 2.625 / 2.75;
|
|
2932
|
+
return 7.5625 * t2 * t2 + 0.984375;
|
|
2933
|
+
}
|
|
2934
|
+
}, "easeOutBounce");
|
|
2935
|
+
|
|
1902
2936
|
// src/LoadingScene.ts
|
|
1903
2937
|
var LoadingScene = class extends Scene {
|
|
1904
2938
|
static {
|
|
@@ -1923,6 +2957,7 @@ var LoadingScene = class extends Scene {
|
|
|
1923
2957
|
_active = true;
|
|
1924
2958
|
_continueRequested = false;
|
|
1925
2959
|
_continueGate;
|
|
2960
|
+
_pendingWaits = /* @__PURE__ */ new Set();
|
|
1926
2961
|
// Bumped on every `_run` attempt. `AssetManager.loadAll` uses `Promise.all`
|
|
1927
2962
|
// under the hood, so individual loaders from a failed attempt can still
|
|
1928
2963
|
// resolve and fire `onProgress` after the attempt rejects. Without this
|
|
@@ -1977,28 +3012,32 @@ var LoadingScene = class extends Scene {
|
|
|
1977
3012
|
onExit() {
|
|
1978
3013
|
this._active = false;
|
|
1979
3014
|
this._continueGate?.();
|
|
3015
|
+
for (const wait of this._pendingWaits) {
|
|
3016
|
+
wait.cancel();
|
|
3017
|
+
}
|
|
3018
|
+
this._pendingWaits.clear();
|
|
1980
3019
|
}
|
|
1981
3020
|
async _run() {
|
|
1982
|
-
await
|
|
3021
|
+
await Promise.resolve();
|
|
1983
3022
|
if (!this._active) return;
|
|
1984
3023
|
const attempt = ++this._attempt;
|
|
1985
3024
|
const target = typeof this.target === "function" ? this.target() : this.target;
|
|
1986
|
-
const startedAt = performance.now();
|
|
1987
3025
|
const bus = this.context.resolve(EventBusKey);
|
|
3026
|
+
const minDuration = this._createEngineTimeDelay(this.minDuration);
|
|
1988
3027
|
try {
|
|
1989
3028
|
await this.assets.loadAll(target.preload ?? [], (ratio) => {
|
|
1990
3029
|
if (!this._active || attempt !== this._attempt) return;
|
|
1991
3030
|
this._progress = ratio;
|
|
1992
3031
|
bus.emit("scene:loading:progress", { scene: this, ratio });
|
|
1993
3032
|
});
|
|
1994
|
-
if (!this._active || attempt !== this._attempt)
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
if (remaining > 0) {
|
|
1998
|
-
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
1999
|
-
if (!this._active || attempt !== this._attempt) return;
|
|
3033
|
+
if (!this._active || attempt !== this._attempt) {
|
|
3034
|
+
minDuration.cancel();
|
|
3035
|
+
return;
|
|
2000
3036
|
}
|
|
3037
|
+
await minDuration.promise;
|
|
3038
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
2001
3039
|
} catch (err) {
|
|
3040
|
+
minDuration.cancel();
|
|
2002
3041
|
if (!this._active || attempt !== this._attempt) return;
|
|
2003
3042
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
2004
3043
|
this._started = false;
|
|
@@ -2022,6 +3061,26 @@ var LoadingScene = class extends Scene {
|
|
|
2022
3061
|
this.transition ? { transition: this.transition } : void 0
|
|
2023
3062
|
);
|
|
2024
3063
|
}
|
|
3064
|
+
_createEngineTimeDelay(ms) {
|
|
3065
|
+
if (ms <= 0) {
|
|
3066
|
+
return {
|
|
3067
|
+
promise: Promise.resolve(),
|
|
3068
|
+
cancel: /* @__PURE__ */ __name(() => {
|
|
3069
|
+
}, "cancel")
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
const wait = Process.delay(ms);
|
|
3073
|
+
this._pendingWaits.add(wait);
|
|
3074
|
+
this.context.resolve(ProcessSystemKey).add(wait);
|
|
3075
|
+
return {
|
|
3076
|
+
promise: wait.toPromise().finally(() => {
|
|
3077
|
+
this._pendingWaits.delete(wait);
|
|
3078
|
+
}),
|
|
3079
|
+
cancel: /* @__PURE__ */ __name(() => {
|
|
3080
|
+
wait.cancel();
|
|
3081
|
+
}, "cancel")
|
|
3082
|
+
};
|
|
3083
|
+
}
|
|
2025
3084
|
};
|
|
2026
3085
|
|
|
2027
3086
|
// src/SceneTransition.ts
|
|
@@ -2430,174 +3489,52 @@ var SceneManager = class {
|
|
|
2430
3489
|
toScene: run.toScene
|
|
2431
3490
|
};
|
|
2432
3491
|
}
|
|
2433
|
-
_snapshotPauseStates() {
|
|
2434
|
-
return new Map(
|
|
2435
|
-
this.stack.map((scene) => [scene, scene.isPaused])
|
|
2436
|
-
);
|
|
2437
|
-
}
|
|
2438
|
-
_assertNotMutating(method) {
|
|
2439
|
-
if (this._mutationDepth === 0) return;
|
|
2440
|
-
throw new Error(
|
|
2441
|
-
`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().`
|
|
2442
|
-
);
|
|
2443
|
-
}
|
|
2444
|
-
async _withMutation(work) {
|
|
2445
|
-
this._mutationDepth++;
|
|
2446
|
-
try {
|
|
2447
|
-
return await work();
|
|
2448
|
-
} finally {
|
|
2449
|
-
this._mutationDepth--;
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
_withMutationSync(work) {
|
|
2453
|
-
this._mutationDepth++;
|
|
2454
|
-
try {
|
|
2455
|
-
return work();
|
|
2456
|
-
} finally {
|
|
2457
|
-
this._mutationDepth--;
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
2461
|
-
_firePauseTransitions(wasPaused) {
|
|
2462
|
-
for (const scene of this.stack) {
|
|
2463
|
-
const was = wasPaused.get(scene) ?? false;
|
|
2464
|
-
if (scene.isPaused && !was) {
|
|
2465
|
-
scene.onPause?.();
|
|
2466
|
-
}
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
2470
|
-
_fireResumeTransitions(wasPaused) {
|
|
2471
|
-
for (const scene of this.stack) {
|
|
2472
|
-
const was = wasPaused.get(scene) ?? false;
|
|
2473
|
-
if (!scene.isPaused && was) {
|
|
2474
|
-
scene.onResume?.();
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
};
|
|
2479
|
-
|
|
2480
|
-
// src/Process.ts
|
|
2481
|
-
var Process = class _Process {
|
|
2482
|
-
static {
|
|
2483
|
-
__name(this, "Process");
|
|
2484
|
-
}
|
|
2485
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
2486
|
-
updateFn;
|
|
2487
|
-
onCompleteFn;
|
|
2488
|
-
duration;
|
|
2489
|
-
loop;
|
|
2490
|
-
/** Tags for filtering/grouping. */
|
|
2491
|
-
tags;
|
|
2492
|
-
elapsed = 0;
|
|
2493
|
-
_completed = false;
|
|
2494
|
-
_paused = false;
|
|
2495
|
-
_cancelled = false;
|
|
2496
|
-
resolvePromise;
|
|
2497
|
-
/** Create a timer that fires `onComplete` after `duration` ms. */
|
|
2498
|
-
static delay(duration, onComplete, tags) {
|
|
2499
|
-
const opts = { duration };
|
|
2500
|
-
if (onComplete !== void 0) opts.onComplete = onComplete;
|
|
2501
|
-
if (tags !== void 0) opts.tags = tags;
|
|
2502
|
-
return new _Process(opts);
|
|
2503
|
-
}
|
|
2504
|
-
constructor(options) {
|
|
2505
|
-
this.updateFn = options.update ?? (() => {
|
|
2506
|
-
});
|
|
2507
|
-
this.onCompleteFn = options.onComplete;
|
|
2508
|
-
this.duration = options.duration;
|
|
2509
|
-
this.loop = options.loop ?? false;
|
|
2510
|
-
this.tags = options.tags ?? [];
|
|
2511
|
-
}
|
|
2512
|
-
/** Whether the process has completed. */
|
|
2513
|
-
get completed() {
|
|
2514
|
-
return this._completed;
|
|
2515
|
-
}
|
|
2516
|
-
/** Whether the process is paused. */
|
|
2517
|
-
get paused() {
|
|
2518
|
-
return this._paused;
|
|
2519
|
-
}
|
|
2520
|
-
/** Pause the process. */
|
|
2521
|
-
pause() {
|
|
2522
|
-
this._paused = true;
|
|
2523
|
-
}
|
|
2524
|
-
/** Resume the process. */
|
|
2525
|
-
resume() {
|
|
2526
|
-
this._paused = false;
|
|
3492
|
+
_snapshotPauseStates() {
|
|
3493
|
+
return new Map(
|
|
3494
|
+
this.stack.map((scene) => [scene, scene.isPaused])
|
|
3495
|
+
);
|
|
2527
3496
|
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
3497
|
+
_assertNotMutating(method) {
|
|
3498
|
+
if (this._mutationDepth === 0) return;
|
|
3499
|
+
throw new Error(
|
|
3500
|
+
`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().`
|
|
3501
|
+
);
|
|
2533
3502
|
}
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
3503
|
+
async _withMutation(work) {
|
|
3504
|
+
this._mutationDepth++;
|
|
3505
|
+
try {
|
|
3506
|
+
return await work();
|
|
3507
|
+
} finally {
|
|
3508
|
+
this._mutationDepth--;
|
|
3509
|
+
}
|
|
2540
3510
|
}
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
this.elapsed += dt;
|
|
2548
|
-
if (this.duration !== void 0 && this.elapsed >= this.duration) {
|
|
2549
|
-
const result2 = this.updateFn(dt, this.elapsed);
|
|
2550
|
-
if (this.loop && result2 !== true) {
|
|
2551
|
-
this.elapsed = this.elapsed % this.duration;
|
|
2552
|
-
return;
|
|
2553
|
-
}
|
|
2554
|
-
this.complete();
|
|
2555
|
-
return;
|
|
3511
|
+
_withMutationSync(work) {
|
|
3512
|
+
this._mutationDepth++;
|
|
3513
|
+
try {
|
|
3514
|
+
return work();
|
|
3515
|
+
} finally {
|
|
3516
|
+
this._mutationDepth--;
|
|
2556
3517
|
}
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
3518
|
+
}
|
|
3519
|
+
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
3520
|
+
_firePauseTransitions(wasPaused) {
|
|
3521
|
+
for (const scene of this.stack) {
|
|
3522
|
+
const was = wasPaused.get(scene) ?? false;
|
|
3523
|
+
if (scene.isPaused && !was) {
|
|
3524
|
+
scene.onPause?.();
|
|
2562
3525
|
}
|
|
2563
|
-
this.complete();
|
|
2564
3526
|
}
|
|
2565
3527
|
}
|
|
2566
|
-
/**
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
this._cancelled = false;
|
|
2575
|
-
delete this.resolvePromise;
|
|
2576
|
-
}
|
|
2577
|
-
complete() {
|
|
2578
|
-
this._completed = true;
|
|
2579
|
-
this.onCompleteFn?.();
|
|
2580
|
-
this.resolvePromise?.();
|
|
3528
|
+
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
3529
|
+
_fireResumeTransitions(wasPaused) {
|
|
3530
|
+
for (const scene of this.stack) {
|
|
3531
|
+
const was = wasPaused.get(scene) ?? false;
|
|
3532
|
+
if (!scene.isPaused && was) {
|
|
3533
|
+
scene.onResume?.();
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
2581
3536
|
}
|
|
2582
3537
|
};
|
|
2583
|
-
var easeLinear = /* @__PURE__ */ __name((t) => t, "easeLinear");
|
|
2584
|
-
var easeInQuad = /* @__PURE__ */ __name((t) => t * t, "easeInQuad");
|
|
2585
|
-
var easeOutQuad = /* @__PURE__ */ __name((t) => t * (2 - t), "easeOutQuad");
|
|
2586
|
-
var easeInOutQuad = /* @__PURE__ */ __name((t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, "easeInOutQuad");
|
|
2587
|
-
var easeOutBounce = /* @__PURE__ */ __name((t) => {
|
|
2588
|
-
if (t < 1 / 2.75) {
|
|
2589
|
-
return 7.5625 * t * t;
|
|
2590
|
-
} else if (t < 2 / 2.75) {
|
|
2591
|
-
const t2 = t - 1.5 / 2.75;
|
|
2592
|
-
return 7.5625 * t2 * t2 + 0.75;
|
|
2593
|
-
} else if (t < 2.5 / 2.75) {
|
|
2594
|
-
const t2 = t - 2.25 / 2.75;
|
|
2595
|
-
return 7.5625 * t2 * t2 + 0.9375;
|
|
2596
|
-
} else {
|
|
2597
|
-
const t2 = t - 2.625 / 2.75;
|
|
2598
|
-
return 7.5625 * t2 * t2 + 0.984375;
|
|
2599
|
-
}
|
|
2600
|
-
}, "easeOutBounce");
|
|
2601
3538
|
|
|
2602
3539
|
// src/Tween.ts
|
|
2603
3540
|
var Tween = {
|
|
@@ -2824,7 +3761,9 @@ var ProcessSlot = class {
|
|
|
2824
3761
|
};
|
|
2825
3762
|
|
|
2826
3763
|
// src/ProcessComponent.ts
|
|
2827
|
-
var
|
|
3764
|
+
var _ProcessComponent_decorators, _init2, _a2;
|
|
3765
|
+
_ProcessComponent_decorators = [serializable];
|
|
3766
|
+
var ProcessComponent = class extends (_a2 = Component) {
|
|
2828
3767
|
static {
|
|
2829
3768
|
__name(this, "ProcessComponent");
|
|
2830
3769
|
}
|
|
@@ -2894,10 +3833,18 @@ var ProcessComponent = class extends Component {
|
|
|
2894
3833
|
onDestroy() {
|
|
2895
3834
|
this.cancel();
|
|
2896
3835
|
}
|
|
3836
|
+
serialize() {
|
|
3837
|
+
return null;
|
|
3838
|
+
}
|
|
2897
3839
|
};
|
|
3840
|
+
_init2 = __decoratorStart(_a2);
|
|
3841
|
+
ProcessComponent = __decorateElement(_init2, 0, "ProcessComponent", _ProcessComponent_decorators, ProcessComponent);
|
|
3842
|
+
__runInitializers(_init2, 1, ProcessComponent);
|
|
2898
3843
|
|
|
2899
3844
|
// src/KeyframeAnimator.ts
|
|
2900
|
-
var
|
|
3845
|
+
var _KeyframeAnimator_decorators, _init3, _a3;
|
|
3846
|
+
_KeyframeAnimator_decorators = [serializable];
|
|
3847
|
+
var KeyframeAnimator = class extends (_a3 = Component) {
|
|
2901
3848
|
static {
|
|
2902
3849
|
__name(this, "KeyframeAnimator");
|
|
2903
3850
|
}
|
|
@@ -2949,6 +3896,9 @@ var KeyframeAnimator = class extends Component {
|
|
|
2949
3896
|
onDestroy() {
|
|
2950
3897
|
this.stopAll();
|
|
2951
3898
|
}
|
|
3899
|
+
serialize() {
|
|
3900
|
+
return null;
|
|
3901
|
+
}
|
|
2952
3902
|
stopInternal(name, complete) {
|
|
2953
3903
|
const process = this.active.get(name);
|
|
2954
3904
|
if (!process) return;
|
|
@@ -2957,6 +3907,9 @@ var KeyframeAnimator = class extends Component {
|
|
|
2957
3907
|
this.defs[name]?.onExit?.(complete);
|
|
2958
3908
|
}
|
|
2959
3909
|
};
|
|
3910
|
+
_init3 = __decoratorStart(_a3);
|
|
3911
|
+
KeyframeAnimator = __decorateElement(_init3, 0, "KeyframeAnimator", _KeyframeAnimator_decorators, KeyframeAnimator);
|
|
3912
|
+
__runInitializers(_init3, 1, KeyframeAnimator);
|
|
2960
3913
|
|
|
2961
3914
|
// src/Sequence.ts
|
|
2962
3915
|
var Sequence = class {
|
|
@@ -3108,36 +4061,94 @@ var ProcessSystem = class extends System {
|
|
|
3108
4061
|
/** Global time scale multiplier. Stacks multiplicatively with per-scene timeScale. */
|
|
3109
4062
|
timeScale = 1;
|
|
3110
4063
|
sceneManager;
|
|
3111
|
-
|
|
4064
|
+
globalProcesses = /* @__PURE__ */ new Set();
|
|
4065
|
+
scenePools = /* @__PURE__ */ new Map();
|
|
4066
|
+
_unregisterSceneHook = null;
|
|
3112
4067
|
onRegister(context) {
|
|
3113
4068
|
this.sceneManager = context.resolve(SceneManagerKey);
|
|
4069
|
+
const hooks = context.tryResolve(SceneHookRegistryKey);
|
|
4070
|
+
this._unregisterSceneHook = hooks?.register({
|
|
4071
|
+
afterExit: /* @__PURE__ */ __name((scene) => this.cancelForScene(scene), "afterExit")
|
|
4072
|
+
}) ?? null;
|
|
4073
|
+
}
|
|
4074
|
+
onUnregister() {
|
|
4075
|
+
this._unregisterSceneHook?.();
|
|
4076
|
+
this._unregisterSceneHook = null;
|
|
4077
|
+
for (const p of this.globalProcesses) {
|
|
4078
|
+
if (!p.completed) p.cancel();
|
|
4079
|
+
}
|
|
4080
|
+
this.globalProcesses.clear();
|
|
4081
|
+
for (const pool of this.scenePools.values()) {
|
|
4082
|
+
for (const p of pool) {
|
|
4083
|
+
if (!p.completed) p.cancel();
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
this.scenePools.clear();
|
|
3114
4087
|
}
|
|
3115
|
-
/**
|
|
4088
|
+
/**
|
|
4089
|
+
* Add an engine-global process. Ticked under the global timeScale only;
|
|
4090
|
+
* NOT gated by per-scene pause or scaled by per-scene timeScale. Use this
|
|
4091
|
+
* for cross-scene effects (e.g. screen-scope filter fades on `app.stage`)
|
|
4092
|
+
* or processes that have no owning scene.
|
|
4093
|
+
*/
|
|
3116
4094
|
add(process) {
|
|
3117
|
-
this.
|
|
4095
|
+
this.globalProcesses.add(process);
|
|
4096
|
+
return process;
|
|
4097
|
+
}
|
|
4098
|
+
/**
|
|
4099
|
+
* Add a process bound to a specific scene's lifecycle. Ticked only while
|
|
4100
|
+
* the scene is active (not paused) and scaled by the scene's `timeScale`,
|
|
4101
|
+
* exactly like an entity-owned `ProcessComponent`. Use this for layer or
|
|
4102
|
+
* scene-scope effect fades that should pause with the scene.
|
|
4103
|
+
*/
|
|
4104
|
+
addForScene(scene, process) {
|
|
4105
|
+
let pool = this.scenePools.get(scene);
|
|
4106
|
+
if (!pool) {
|
|
4107
|
+
pool = /* @__PURE__ */ new Set();
|
|
4108
|
+
this.scenePools.set(scene, pool);
|
|
4109
|
+
}
|
|
4110
|
+
pool.add(process);
|
|
3118
4111
|
return process;
|
|
3119
4112
|
}
|
|
3120
|
-
/** Cancel
|
|
4113
|
+
/** Cancel engine-global processes, optionally by tag. */
|
|
3121
4114
|
cancel(tag) {
|
|
3122
|
-
for (const p of this.
|
|
4115
|
+
for (const p of this.globalProcesses) {
|
|
3123
4116
|
if (tag === void 0 || p.tags.includes(tag)) {
|
|
3124
4117
|
p.cancel();
|
|
4118
|
+
this.globalProcesses.delete(p);
|
|
3125
4119
|
}
|
|
3126
4120
|
}
|
|
3127
|
-
|
|
3128
|
-
|
|
4121
|
+
}
|
|
4122
|
+
/** Cancel every scene-bound process for `scene`, optionally by tag. */
|
|
4123
|
+
cancelForScene(scene, tag) {
|
|
4124
|
+
const pool = this.scenePools.get(scene);
|
|
4125
|
+
if (!pool) return;
|
|
4126
|
+
for (const p of pool) {
|
|
4127
|
+
if (tag === void 0 || p.tags.includes(tag)) {
|
|
4128
|
+
p.cancel();
|
|
4129
|
+
pool.delete(p);
|
|
4130
|
+
}
|
|
3129
4131
|
}
|
|
4132
|
+
if (pool.size === 0) this.scenePools.delete(scene);
|
|
3130
4133
|
}
|
|
3131
4134
|
update(dt) {
|
|
3132
4135
|
const globalScaledDt = dt * this.timeScale;
|
|
3133
|
-
for (const p of this.
|
|
4136
|
+
for (const p of this.globalProcesses) {
|
|
3134
4137
|
p._update(globalScaledDt);
|
|
3135
4138
|
if (p.completed) {
|
|
3136
|
-
this.
|
|
4139
|
+
this.globalProcesses.delete(p);
|
|
3137
4140
|
}
|
|
3138
4141
|
}
|
|
3139
4142
|
for (const scene of this.sceneManager.activeScenes) {
|
|
3140
4143
|
const effectiveDt = globalScaledDt * scene.timeScale;
|
|
4144
|
+
const pool = this.scenePools.get(scene);
|
|
4145
|
+
if (pool) {
|
|
4146
|
+
for (const p of pool) {
|
|
4147
|
+
p._update(effectiveDt);
|
|
4148
|
+
if (p.completed) pool.delete(p);
|
|
4149
|
+
}
|
|
4150
|
+
if (pool.size === 0) this.scenePools.delete(scene);
|
|
4151
|
+
}
|
|
3141
4152
|
for (const entity of scene.getEntities()) {
|
|
3142
4153
|
if (entity.isDestroyed) continue;
|
|
3143
4154
|
const pc = entity.tryGet(ProcessComponent);
|
|
@@ -3148,145 +4159,45 @@ var ProcessSystem = class extends System {
|
|
|
3148
4159
|
}
|
|
3149
4160
|
};
|
|
3150
4161
|
|
|
3151
|
-
// src/
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
this.engine = engine;
|
|
3159
|
-
}
|
|
3160
|
-
/** Full state snapshot (serializable). */
|
|
3161
|
-
snapshot() {
|
|
3162
|
-
return {
|
|
3163
|
-
frameCount: this.engine.loop.frameCount,
|
|
3164
|
-
sceneStack: this.getSceneStack(),
|
|
3165
|
-
entityCount: this.countEntities(),
|
|
3166
|
-
systemCount: this.getSystems().length,
|
|
3167
|
-
errors: this.getErrors()
|
|
3168
|
-
};
|
|
3169
|
-
}
|
|
3170
|
-
/** Find entity by name in the active scene. */
|
|
3171
|
-
getEntityByName(name) {
|
|
3172
|
-
const entity = this.findActiveEntity(name);
|
|
3173
|
-
if (!entity) return void 0;
|
|
3174
|
-
return this.entityToSnapshot(entity);
|
|
3175
|
-
}
|
|
3176
|
-
/** Get entity position (from Transform component). */
|
|
3177
|
-
getEntityPosition(name) {
|
|
3178
|
-
const entity = this.findActiveEntity(name);
|
|
3179
|
-
if (!entity) return void 0;
|
|
3180
|
-
const transform = this.getTransform(entity);
|
|
3181
|
-
if (!transform) return void 0;
|
|
3182
|
-
return { x: transform.position.x, y: transform.position.y };
|
|
3183
|
-
}
|
|
3184
|
-
/** Check if an entity has a component by class name string. */
|
|
3185
|
-
hasComponent(entityName, componentClass) {
|
|
3186
|
-
return this.findComponentByName(entityName, componentClass) !== void 0;
|
|
3187
|
-
}
|
|
3188
|
-
/** Get component data (serializable subset) by class name string. */
|
|
3189
|
-
getComponentData(entityName, componentClass) {
|
|
3190
|
-
const comp = this.findComponentByName(entityName, componentClass);
|
|
3191
|
-
if (!comp) return void 0;
|
|
3192
|
-
return this.serializeComponent(comp);
|
|
3193
|
-
}
|
|
3194
|
-
/** Get all entities in the active scene as snapshots. */
|
|
3195
|
-
getEntities() {
|
|
3196
|
-
const scene = this.engine.scenes.active;
|
|
3197
|
-
if (!scene) return [];
|
|
3198
|
-
const result = [];
|
|
3199
|
-
for (const entity of scene.getEntities()) {
|
|
3200
|
-
if (!entity.isDestroyed) {
|
|
3201
|
-
result.push(this.entityToSnapshot(entity));
|
|
4162
|
+
// src/ProcessQueue.ts
|
|
4163
|
+
function makeQueue(route) {
|
|
4164
|
+
const ours = /* @__PURE__ */ new Set();
|
|
4165
|
+
return {
|
|
4166
|
+
run(p) {
|
|
4167
|
+
for (const old of ours) {
|
|
4168
|
+
if (old.completed) ours.delete(old);
|
|
3202
4169
|
}
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
entityCount: scene.getEntities().size,
|
|
3211
|
-
paused: scene.isPaused
|
|
3212
|
-
}));
|
|
3213
|
-
}
|
|
3214
|
-
/** Get active system info. */
|
|
3215
|
-
getSystems() {
|
|
3216
|
-
const scheduler = this.engine.context.tryResolve(SystemSchedulerKey);
|
|
3217
|
-
if (!scheduler) return [];
|
|
3218
|
-
return scheduler.getAllSystems().map((sys) => ({
|
|
3219
|
-
name: sys.constructor.name,
|
|
3220
|
-
phase: sys.phase,
|
|
3221
|
-
priority: sys.priority,
|
|
3222
|
-
enabled: sys.enabled
|
|
3223
|
-
}));
|
|
3224
|
-
}
|
|
3225
|
-
/** Get disabled components/systems from error boundary. */
|
|
3226
|
-
getErrors() {
|
|
3227
|
-
const boundary = this.engine.context.tryResolve(ErrorBoundaryKey);
|
|
3228
|
-
if (!boundary) return { disabledSystems: [], disabledComponents: [] };
|
|
3229
|
-
const disabled = boundary.getDisabled();
|
|
3230
|
-
return {
|
|
3231
|
-
disabledSystems: disabled.systems.map(
|
|
3232
|
-
(s) => s.system.constructor.name
|
|
3233
|
-
),
|
|
3234
|
-
disabledComponents: disabled.components.map((c) => ({
|
|
3235
|
-
entity: c.component.entity?.name ?? "unknown",
|
|
3236
|
-
component: c.component.constructor.name,
|
|
3237
|
-
error: c.error
|
|
3238
|
-
}))
|
|
3239
|
-
};
|
|
3240
|
-
}
|
|
3241
|
-
findActiveEntity(name) {
|
|
3242
|
-
return this.engine.scenes.active?.findEntity(name);
|
|
3243
|
-
}
|
|
3244
|
-
findComponentByName(entityName, componentClass) {
|
|
3245
|
-
const entity = this.findActiveEntity(entityName);
|
|
3246
|
-
if (!entity) return void 0;
|
|
3247
|
-
for (const comp of entity.getAll()) {
|
|
3248
|
-
if (comp.constructor.name === componentClass) return comp;
|
|
3249
|
-
}
|
|
3250
|
-
return void 0;
|
|
3251
|
-
}
|
|
3252
|
-
entityToSnapshot(entity) {
|
|
3253
|
-
const transform = this.getTransform(entity);
|
|
3254
|
-
const snapshot = {
|
|
3255
|
-
id: entity.id,
|
|
3256
|
-
name: entity.name,
|
|
3257
|
-
tags: [...entity.tags],
|
|
3258
|
-
components: [...entity.getAll()].map((c) => c.constructor.name)
|
|
3259
|
-
};
|
|
3260
|
-
if (transform) {
|
|
3261
|
-
snapshot.position = {
|
|
3262
|
-
x: transform.position.x,
|
|
3263
|
-
y: transform.position.y
|
|
3264
|
-
};
|
|
3265
|
-
}
|
|
3266
|
-
return snapshot;
|
|
3267
|
-
}
|
|
3268
|
-
getTransform(entity) {
|
|
3269
|
-
return entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
3270
|
-
}
|
|
3271
|
-
serializeComponent(comp) {
|
|
3272
|
-
const result = {};
|
|
3273
|
-
for (const key of Object.getOwnPropertyNames(comp)) {
|
|
3274
|
-
if (key === "entity") continue;
|
|
3275
|
-
const value = comp[key];
|
|
3276
|
-
if (typeof value !== "function") {
|
|
3277
|
-
result[key] = value;
|
|
4170
|
+
route(p);
|
|
4171
|
+
ours.add(p);
|
|
4172
|
+
return p;
|
|
4173
|
+
},
|
|
4174
|
+
cancelAll() {
|
|
4175
|
+
for (const p of ours) {
|
|
4176
|
+
if (!p.completed) p.cancel();
|
|
3278
4177
|
}
|
|
4178
|
+
ours.clear();
|
|
3279
4179
|
}
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
4180
|
+
};
|
|
4181
|
+
}
|
|
4182
|
+
__name(makeQueue, "makeQueue");
|
|
4183
|
+
function makeEntityScopedQueue(entity) {
|
|
4184
|
+
return makeQueue((p) => {
|
|
4185
|
+
let pc = entity.tryGet(ProcessComponent);
|
|
4186
|
+
if (!pc) {
|
|
4187
|
+
pc = entity.add(new ProcessComponent());
|
|
4188
|
+
}
|
|
4189
|
+
pc.run(p);
|
|
4190
|
+
});
|
|
4191
|
+
}
|
|
4192
|
+
__name(makeEntityScopedQueue, "makeEntityScopedQueue");
|
|
4193
|
+
function makeSceneScopedQueue(processSystem, scene) {
|
|
4194
|
+
return makeQueue((p) => processSystem.addForScene(scene, p));
|
|
4195
|
+
}
|
|
4196
|
+
__name(makeSceneScopedQueue, "makeSceneScopedQueue");
|
|
4197
|
+
function makeGlobalScopedQueue(processSystem) {
|
|
4198
|
+
return makeQueue((p) => processSystem.add(p));
|
|
4199
|
+
}
|
|
4200
|
+
__name(makeGlobalScopedQueue, "makeGlobalScopedQueue");
|
|
3290
4201
|
|
|
3291
4202
|
// src/Engine.ts
|
|
3292
4203
|
var Engine = class {
|
|
@@ -3340,6 +4251,15 @@ var Engine = class {
|
|
|
3340
4251
|
this.context.register(SystemSchedulerKey, this.scheduler);
|
|
3341
4252
|
this.context.register(AssetManagerKey, this.assets);
|
|
3342
4253
|
this.context.register(SceneHookRegistryKey, this.sceneHooks);
|
|
4254
|
+
this.sceneHooks.register({
|
|
4255
|
+
beforeEnter: /* @__PURE__ */ __name((scene) => {
|
|
4256
|
+
scene._registerScoped(RandomKey, this.inspector.createSceneRandom());
|
|
4257
|
+
this.inspector.attachSceneEventObserver(scene);
|
|
4258
|
+
}, "beforeEnter"),
|
|
4259
|
+
afterExit: /* @__PURE__ */ __name((scene) => {
|
|
4260
|
+
this.inspector.detachSceneEventObserver(scene);
|
|
4261
|
+
}, "afterExit")
|
|
4262
|
+
});
|
|
3343
4263
|
this.scenes._setContext(this.context);
|
|
3344
4264
|
this.registerBuiltInSystems();
|
|
3345
4265
|
this.loop.setCallbacks({
|
|
@@ -3421,6 +4341,7 @@ var Engine = class {
|
|
|
3421
4341
|
if (this.debug && typeof globalThis !== "undefined" && "__yage__" in globalThis) {
|
|
3422
4342
|
delete globalThis["__yage__"];
|
|
3423
4343
|
}
|
|
4344
|
+
this.inspector.dispose();
|
|
3424
4345
|
this.events.clear();
|
|
3425
4346
|
this.started = false;
|
|
3426
4347
|
}
|
|
@@ -3488,6 +4409,21 @@ var RendererAdapterKey = new ServiceKey(
|
|
|
3488
4409
|
"rendererAdapter"
|
|
3489
4410
|
);
|
|
3490
4411
|
|
|
4412
|
+
// src/ui-consume-registry.ts
|
|
4413
|
+
var registry2 = /* @__PURE__ */ new WeakSet();
|
|
4414
|
+
function markPointerConsumeContainer(container) {
|
|
4415
|
+
registry2.add(container);
|
|
4416
|
+
}
|
|
4417
|
+
__name(markPointerConsumeContainer, "markPointerConsumeContainer");
|
|
4418
|
+
function unmarkPointerConsumeContainer(container) {
|
|
4419
|
+
registry2.delete(container);
|
|
4420
|
+
}
|
|
4421
|
+
__name(unmarkPointerConsumeContainer, "unmarkPointerConsumeContainer");
|
|
4422
|
+
function isPointerConsumeContainer(container) {
|
|
4423
|
+
return registry2.has(container);
|
|
4424
|
+
}
|
|
4425
|
+
__name(isPointerConsumeContainer, "isPointerConsumeContainer");
|
|
4426
|
+
|
|
3491
4427
|
// src/test-utils.ts
|
|
3492
4428
|
var _TestScene = class extends Scene {
|
|
3493
4429
|
static {
|
|
@@ -3518,6 +4454,7 @@ function createMockScene(name = "mock-scene") {
|
|
|
3518
4454
|
ctx.register(ErrorBoundaryKey, boundary);
|
|
3519
4455
|
const scene = new _TestScene(name);
|
|
3520
4456
|
scene._setContext(ctx);
|
|
4457
|
+
scene._registerScoped(RandomKey, createRandomService(1234));
|
|
3521
4458
|
return { scene, context: ctx };
|
|
3522
4459
|
}
|
|
3523
4460
|
__name(createMockScene, "createMockScene");
|
|
@@ -3571,6 +4508,7 @@ export {
|
|
|
3571
4508
|
QueryCache,
|
|
3572
4509
|
QueryCacheKey,
|
|
3573
4510
|
QueryResult,
|
|
4511
|
+
RandomKey,
|
|
3574
4512
|
RendererAdapterKey,
|
|
3575
4513
|
SERIALIZABLE_KEY,
|
|
3576
4514
|
Scene,
|
|
@@ -3592,9 +4530,11 @@ export {
|
|
|
3592
4530
|
Vec2,
|
|
3593
4531
|
_resetEntityIdCounter,
|
|
3594
4532
|
advanceFrames,
|
|
4533
|
+
createDefaultRandomSeed,
|
|
3595
4534
|
createKeyframeTrack,
|
|
3596
4535
|
createMockEntity,
|
|
3597
4536
|
createMockScene,
|
|
4537
|
+
createRandomService,
|
|
3598
4538
|
createTestEngine,
|
|
3599
4539
|
defineBlueprint,
|
|
3600
4540
|
defineEvent,
|
|
@@ -3606,10 +4546,18 @@ export {
|
|
|
3606
4546
|
easeOutQuad,
|
|
3607
4547
|
filterEntities,
|
|
3608
4548
|
getSerializableType,
|
|
4549
|
+
globalRandom,
|
|
3609
4550
|
interpolate,
|
|
4551
|
+
isPointerConsumeContainer,
|
|
3610
4552
|
isSerializable,
|
|
4553
|
+
makeEntityScopedQueue,
|
|
4554
|
+
makeGlobalScopedQueue,
|
|
4555
|
+
makeSceneScopedQueue,
|
|
4556
|
+
markPointerConsumeContainer,
|
|
4557
|
+
normalizeSeed,
|
|
3611
4558
|
resolveTransition,
|
|
3612
4559
|
serializable,
|
|
3613
|
-
trait
|
|
4560
|
+
trait,
|
|
4561
|
+
unmarkPointerConsumeContainer
|
|
3614
4562
|
};
|
|
3615
4563
|
//# sourceMappingURL=index.js.map
|