@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.cjs
CHANGED
|
@@ -99,6 +99,7 @@ __export(index_exports, {
|
|
|
99
99
|
QueryCache: () => QueryCache,
|
|
100
100
|
QueryCacheKey: () => QueryCacheKey,
|
|
101
101
|
QueryResult: () => QueryResult,
|
|
102
|
+
RandomKey: () => RandomKey,
|
|
102
103
|
RendererAdapterKey: () => RendererAdapterKey,
|
|
103
104
|
SERIALIZABLE_KEY: () => SERIALIZABLE_KEY,
|
|
104
105
|
Scene: () => Scene,
|
|
@@ -120,9 +121,11 @@ __export(index_exports, {
|
|
|
120
121
|
Vec2: () => Vec2,
|
|
121
122
|
_resetEntityIdCounter: () => _resetEntityIdCounter,
|
|
122
123
|
advanceFrames: () => advanceFrames,
|
|
124
|
+
createDefaultRandomSeed: () => createDefaultRandomSeed,
|
|
123
125
|
createKeyframeTrack: () => createKeyframeTrack,
|
|
124
126
|
createMockEntity: () => createMockEntity,
|
|
125
127
|
createMockScene: () => createMockScene,
|
|
128
|
+
createRandomService: () => createRandomService,
|
|
126
129
|
createTestEngine: () => createTestEngine,
|
|
127
130
|
defineBlueprint: () => defineBlueprint,
|
|
128
131
|
defineEvent: () => defineEvent,
|
|
@@ -134,11 +137,19 @@ __export(index_exports, {
|
|
|
134
137
|
easeOutQuad: () => easeOutQuad,
|
|
135
138
|
filterEntities: () => filterEntities,
|
|
136
139
|
getSerializableType: () => getSerializableType,
|
|
140
|
+
globalRandom: () => globalRandom,
|
|
137
141
|
interpolate: () => interpolate,
|
|
142
|
+
isPointerConsumeContainer: () => isPointerConsumeContainer,
|
|
138
143
|
isSerializable: () => isSerializable,
|
|
144
|
+
makeEntityScopedQueue: () => makeEntityScopedQueue,
|
|
145
|
+
makeGlobalScopedQueue: () => makeGlobalScopedQueue,
|
|
146
|
+
makeSceneScopedQueue: () => makeSceneScopedQueue,
|
|
147
|
+
markPointerConsumeContainer: () => markPointerConsumeContainer,
|
|
148
|
+
normalizeSeed: () => normalizeSeed,
|
|
139
149
|
resolveTransition: () => resolveTransition,
|
|
140
150
|
serializable: () => serializable,
|
|
141
|
-
trait: () => trait
|
|
151
|
+
trait: () => trait,
|
|
152
|
+
unmarkPointerConsumeContainer: () => unmarkPointerConsumeContainer
|
|
142
153
|
});
|
|
143
154
|
module.exports = __toCommonJS(index_exports);
|
|
144
155
|
|
|
@@ -327,14 +338,6 @@ var MathUtils = {
|
|
|
327
338
|
const wrapped = MathUtils.wrap(t, 0, length * 2);
|
|
328
339
|
return length - Math.abs(wrapped - length);
|
|
329
340
|
},
|
|
330
|
-
/** Random float in [min, max). */
|
|
331
|
-
randomRange(min, max) {
|
|
332
|
-
return min + Math.random() * (max - min);
|
|
333
|
-
},
|
|
334
|
-
/** Random integer in [min, max] (inclusive). */
|
|
335
|
-
randomInt(min, max) {
|
|
336
|
-
return Math.floor(min + Math.random() * (max - min + 1));
|
|
337
|
-
},
|
|
338
341
|
/** Convert degrees to radians. */
|
|
339
342
|
degToRad(degrees) {
|
|
340
343
|
return degrees * Math.PI / 180;
|
|
@@ -385,12 +388,138 @@ var MathUtils = {
|
|
|
385
388
|
}
|
|
386
389
|
};
|
|
387
390
|
|
|
391
|
+
// src/EngineContext.ts
|
|
392
|
+
var ServiceKey = class {
|
|
393
|
+
constructor(id, options) {
|
|
394
|
+
this.id = id;
|
|
395
|
+
this.scope = options?.scope ?? "engine";
|
|
396
|
+
}
|
|
397
|
+
id;
|
|
398
|
+
static {
|
|
399
|
+
__name(this, "ServiceKey");
|
|
400
|
+
}
|
|
401
|
+
/** Declared scope (engine or scene). Defaults to `"engine"`. */
|
|
402
|
+
scope;
|
|
403
|
+
};
|
|
404
|
+
var EngineContext = class {
|
|
405
|
+
static {
|
|
406
|
+
__name(this, "EngineContext");
|
|
407
|
+
}
|
|
408
|
+
services = /* @__PURE__ */ new Map();
|
|
409
|
+
/** Register a service. Throws if the key is already registered. */
|
|
410
|
+
register(key, service) {
|
|
411
|
+
if (this.services.has(key.id)) {
|
|
412
|
+
throw new Error(`Service "${key.id}" is already registered.`);
|
|
413
|
+
}
|
|
414
|
+
this.services.set(key.id, service);
|
|
415
|
+
}
|
|
416
|
+
/** Resolve a service. Throws if not registered. */
|
|
417
|
+
resolve(key) {
|
|
418
|
+
if (!this.services.has(key.id)) {
|
|
419
|
+
throw new Error(`Service "${key.id}" is not registered.`);
|
|
420
|
+
}
|
|
421
|
+
return this.services.get(key.id);
|
|
422
|
+
}
|
|
423
|
+
/** Resolve a service, returning undefined if not registered. */
|
|
424
|
+
tryResolve(key) {
|
|
425
|
+
return this.services.get(key.id);
|
|
426
|
+
}
|
|
427
|
+
/** Remove a registered service. No-op if not registered. */
|
|
428
|
+
unregister(key) {
|
|
429
|
+
this.services.delete(key.id);
|
|
430
|
+
}
|
|
431
|
+
/** Check if a service is registered. */
|
|
432
|
+
has(key) {
|
|
433
|
+
return this.services.has(key.id);
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
var EngineKey = new ServiceKey("engine");
|
|
437
|
+
var EventBusKey = new ServiceKey("eventBus");
|
|
438
|
+
var SceneManagerKey = new ServiceKey("sceneManager");
|
|
439
|
+
var LoggerKey = new ServiceKey("logger");
|
|
440
|
+
var InspectorKey = new ServiceKey("inspector");
|
|
441
|
+
var QueryCacheKey = new ServiceKey("queryCache");
|
|
442
|
+
var ErrorBoundaryKey = new ServiceKey("errorBoundary");
|
|
443
|
+
var GameLoopKey = new ServiceKey("gameLoop");
|
|
444
|
+
var SystemSchedulerKey = new ServiceKey(
|
|
445
|
+
"systemScheduler"
|
|
446
|
+
);
|
|
447
|
+
var ProcessSystemKey = new ServiceKey("processSystem");
|
|
448
|
+
var AssetManagerKey = new ServiceKey("assetManager");
|
|
449
|
+
|
|
450
|
+
// src/Random.ts
|
|
451
|
+
var RandomKey = new ServiceKey("random", {
|
|
452
|
+
scope: "scene"
|
|
453
|
+
});
|
|
454
|
+
var UINT32_MAX = 4294967296;
|
|
455
|
+
function normalizeSeed(seed) {
|
|
456
|
+
return seed >>> 0;
|
|
457
|
+
}
|
|
458
|
+
__name(normalizeSeed, "normalizeSeed");
|
|
459
|
+
function createDefaultRandomSeed() {
|
|
460
|
+
return normalizeSeed(Date.now() ^ Math.floor(Math.random() * 1e9));
|
|
461
|
+
}
|
|
462
|
+
__name(createDefaultRandomSeed, "createDefaultRandomSeed");
|
|
463
|
+
var Mulberry32Random = class {
|
|
464
|
+
static {
|
|
465
|
+
__name(this, "Mulberry32Random");
|
|
466
|
+
}
|
|
467
|
+
seed;
|
|
468
|
+
state;
|
|
469
|
+
constructor(seed) {
|
|
470
|
+
const normalized = normalizeSeed(seed);
|
|
471
|
+
this.seed = normalized;
|
|
472
|
+
this.state = normalized;
|
|
473
|
+
}
|
|
474
|
+
float() {
|
|
475
|
+
let t = this.state += 1831565813;
|
|
476
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
477
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
478
|
+
return ((t ^ t >>> 14) >>> 0) / UINT32_MAX;
|
|
479
|
+
}
|
|
480
|
+
range(min, max) {
|
|
481
|
+
return min + this.float() * (max - min);
|
|
482
|
+
}
|
|
483
|
+
int(min, max) {
|
|
484
|
+
return Math.floor(this.range(min, max + 1));
|
|
485
|
+
}
|
|
486
|
+
pick(arr) {
|
|
487
|
+
if (arr.length === 0) {
|
|
488
|
+
throw new Error("RandomService.pick() requires a non-empty array.");
|
|
489
|
+
}
|
|
490
|
+
return arr[this.int(0, arr.length - 1)];
|
|
491
|
+
}
|
|
492
|
+
shuffle(arr) {
|
|
493
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
494
|
+
const j = this.int(0, i);
|
|
495
|
+
const tmp = arr[i];
|
|
496
|
+
arr[i] = arr[j];
|
|
497
|
+
arr[j] = tmp;
|
|
498
|
+
}
|
|
499
|
+
return arr;
|
|
500
|
+
}
|
|
501
|
+
setSeed(seed) {
|
|
502
|
+
const normalized = normalizeSeed(seed);
|
|
503
|
+
this.seed = normalized;
|
|
504
|
+
this.state = normalized;
|
|
505
|
+
}
|
|
506
|
+
getSeed() {
|
|
507
|
+
return this.seed;
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
function createRandomService(seed = createDefaultRandomSeed()) {
|
|
511
|
+
return new Mulberry32Random(seed);
|
|
512
|
+
}
|
|
513
|
+
__name(createRandomService, "createRandomService");
|
|
514
|
+
var globalRandom = createRandomService();
|
|
515
|
+
|
|
388
516
|
// src/EventBus.ts
|
|
389
517
|
var EventBus = class {
|
|
390
518
|
static {
|
|
391
519
|
__name(this, "EventBus");
|
|
392
520
|
}
|
|
393
521
|
handlers = /* @__PURE__ */ new Map();
|
|
522
|
+
observers = /* @__PURE__ */ new Set();
|
|
394
523
|
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
395
524
|
on(event, handler) {
|
|
396
525
|
let list = this.handlers.get(event);
|
|
@@ -417,6 +546,12 @@ var EventBus = class {
|
|
|
417
546
|
}
|
|
418
547
|
/** Emit an event. Handlers are called synchronously in registration order. */
|
|
419
548
|
emit(event, data) {
|
|
549
|
+
if (this.observers.size > 0) {
|
|
550
|
+
const observers = [...this.observers];
|
|
551
|
+
for (const observer of observers) {
|
|
552
|
+
observer(event, data);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
420
555
|
const list = this.handlers.get(event);
|
|
421
556
|
if (!list) return;
|
|
422
557
|
const snapshot = [...list];
|
|
@@ -424,6 +559,16 @@ var EventBus = class {
|
|
|
424
559
|
handler(data);
|
|
425
560
|
}
|
|
426
561
|
}
|
|
562
|
+
/**
|
|
563
|
+
* Observe every emitted event without affecting handler order or control
|
|
564
|
+
* flow. Used by tooling such as the Inspector event log.
|
|
565
|
+
*/
|
|
566
|
+
tap(observer) {
|
|
567
|
+
this.observers.add(observer);
|
|
568
|
+
return () => {
|
|
569
|
+
this.observers.delete(observer);
|
|
570
|
+
};
|
|
571
|
+
}
|
|
427
572
|
/** Remove all handlers for an event, or all handlers if no event specified. */
|
|
428
573
|
clear(event) {
|
|
429
574
|
if (event !== void 0) {
|
|
@@ -533,63 +678,6 @@ var Logger = class {
|
|
|
533
678
|
}
|
|
534
679
|
};
|
|
535
680
|
|
|
536
|
-
// src/EngineContext.ts
|
|
537
|
-
var ServiceKey = class {
|
|
538
|
-
constructor(id, options) {
|
|
539
|
-
this.id = id;
|
|
540
|
-
this.scope = options?.scope ?? "engine";
|
|
541
|
-
}
|
|
542
|
-
id;
|
|
543
|
-
static {
|
|
544
|
-
__name(this, "ServiceKey");
|
|
545
|
-
}
|
|
546
|
-
/** Declared scope (engine or scene). Defaults to `"engine"`. */
|
|
547
|
-
scope;
|
|
548
|
-
};
|
|
549
|
-
var EngineContext = class {
|
|
550
|
-
static {
|
|
551
|
-
__name(this, "EngineContext");
|
|
552
|
-
}
|
|
553
|
-
services = /* @__PURE__ */ new Map();
|
|
554
|
-
/** Register a service. Throws if the key is already registered. */
|
|
555
|
-
register(key, service) {
|
|
556
|
-
if (this.services.has(key.id)) {
|
|
557
|
-
throw new Error(`Service "${key.id}" is already registered.`);
|
|
558
|
-
}
|
|
559
|
-
this.services.set(key.id, service);
|
|
560
|
-
}
|
|
561
|
-
/** Resolve a service. Throws if not registered. */
|
|
562
|
-
resolve(key) {
|
|
563
|
-
if (!this.services.has(key.id)) {
|
|
564
|
-
throw new Error(`Service "${key.id}" is not registered.`);
|
|
565
|
-
}
|
|
566
|
-
return this.services.get(key.id);
|
|
567
|
-
}
|
|
568
|
-
/** Resolve a service, returning undefined if not registered. */
|
|
569
|
-
tryResolve(key) {
|
|
570
|
-
return this.services.get(key.id);
|
|
571
|
-
}
|
|
572
|
-
/** Remove a registered service. No-op if not registered. */
|
|
573
|
-
unregister(key) {
|
|
574
|
-
this.services.delete(key.id);
|
|
575
|
-
}
|
|
576
|
-
/** Check if a service is registered. */
|
|
577
|
-
has(key) {
|
|
578
|
-
return this.services.has(key.id);
|
|
579
|
-
}
|
|
580
|
-
};
|
|
581
|
-
var EngineKey = new ServiceKey("engine");
|
|
582
|
-
var EventBusKey = new ServiceKey("eventBus");
|
|
583
|
-
var SceneManagerKey = new ServiceKey("sceneManager");
|
|
584
|
-
var LoggerKey = new ServiceKey("logger");
|
|
585
|
-
var InspectorKey = new ServiceKey("inspector");
|
|
586
|
-
var QueryCacheKey = new ServiceKey("queryCache");
|
|
587
|
-
var ErrorBoundaryKey = new ServiceKey("errorBoundary");
|
|
588
|
-
var GameLoopKey = new ServiceKey("gameLoop");
|
|
589
|
-
var SystemSchedulerKey = new ServiceKey("systemScheduler");
|
|
590
|
-
var ProcessSystemKey = new ServiceKey("processSystem");
|
|
591
|
-
var AssetManagerKey = new ServiceKey("assetManager");
|
|
592
|
-
|
|
593
681
|
// src/SceneHooks.ts
|
|
594
682
|
var SceneHookRegistry = class {
|
|
595
683
|
static {
|
|
@@ -1326,6 +1414,7 @@ var Entity = class {
|
|
|
1326
1414
|
}
|
|
1327
1415
|
}
|
|
1328
1416
|
this._scene?._onEntityEvent(token.name, data, this);
|
|
1417
|
+
this._scene?._observeEntityEvent(token.name, data, this);
|
|
1329
1418
|
}
|
|
1330
1419
|
/** Get all components as an iterable. */
|
|
1331
1420
|
getAll() {
|
|
@@ -1748,87 +1837,906 @@ var GameLoop = class {
|
|
|
1748
1837
|
}
|
|
1749
1838
|
};
|
|
1750
1839
|
|
|
1751
|
-
// src/
|
|
1752
|
-
var
|
|
1840
|
+
// src/Inspector.ts
|
|
1841
|
+
var InputManagerRuntimeKey = new ServiceKey("inputManager");
|
|
1842
|
+
var PhysicsWorldManagerRuntimeKey = new ServiceKey(
|
|
1843
|
+
"physicsWorldManager"
|
|
1844
|
+
);
|
|
1845
|
+
var RendererRuntimeKey = new ServiceKey("renderer");
|
|
1846
|
+
var Inspector = class {
|
|
1753
1847
|
static {
|
|
1754
|
-
__name(this, "
|
|
1848
|
+
__name(this, "Inspector");
|
|
1755
1849
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1850
|
+
engine;
|
|
1851
|
+
extensions = /* @__PURE__ */ new Map();
|
|
1852
|
+
sceneIds = /* @__PURE__ */ new WeakMap();
|
|
1853
|
+
nextSceneId = 0;
|
|
1854
|
+
defaultSceneSeed;
|
|
1855
|
+
sceneSeedOverride;
|
|
1856
|
+
timeController = null;
|
|
1857
|
+
eventLogEnabled = false;
|
|
1858
|
+
eventCapacity = 500;
|
|
1859
|
+
/**
|
|
1860
|
+
* Ring buffer of recent events. `eventLogHead` points at the oldest slot;
|
|
1861
|
+
* a full ring contains exactly `eventCapacity` entries. We avoid `splice` to
|
|
1862
|
+
* keep `appendEvent` O(1) — the previous shift-on-overflow approach was
|
|
1863
|
+
* O(n) per event once the buffer was full.
|
|
1864
|
+
*/
|
|
1865
|
+
eventLog = [];
|
|
1866
|
+
eventLogHead = 0;
|
|
1867
|
+
eventWaiters = /* @__PURE__ */ new Set();
|
|
1868
|
+
detachBusTap = null;
|
|
1869
|
+
busEventObserver = /* @__PURE__ */ __name((event, data) => {
|
|
1870
|
+
this.recordBusEvent(String(event), data);
|
|
1871
|
+
}, "busEventObserver");
|
|
1872
|
+
sceneEventObserver = /* @__PURE__ */ __name((eventName, data, entity) => {
|
|
1873
|
+
this.recordEntityEvent(eventName, data, entity);
|
|
1874
|
+
}, "sceneEventObserver");
|
|
1875
|
+
time = {
|
|
1876
|
+
freeze: /* @__PURE__ */ __name(() => {
|
|
1877
|
+
this.requireTimeController().freeze();
|
|
1878
|
+
}, "freeze"),
|
|
1879
|
+
thaw: /* @__PURE__ */ __name(() => {
|
|
1880
|
+
this.requireTimeController().thaw();
|
|
1881
|
+
}, "thaw"),
|
|
1882
|
+
step: /* @__PURE__ */ __name((frames = 1) => {
|
|
1883
|
+
this.assertNonNegativeInteger(frames, "Inspector.time.step(frames)");
|
|
1884
|
+
if (frames === 0) return;
|
|
1885
|
+
this.requireTimeController().stepFrames(frames);
|
|
1886
|
+
this.expireDeadlineWaiters();
|
|
1887
|
+
}, "step"),
|
|
1888
|
+
setDelta: /* @__PURE__ */ __name((ms) => {
|
|
1889
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
1890
|
+
throw new Error("Inspector.time.setDelta(ms) requires a positive number.");
|
|
1891
|
+
}
|
|
1892
|
+
this.requireTimeController().setDelta(ms);
|
|
1893
|
+
}, "setDelta"),
|
|
1894
|
+
isFrozen: /* @__PURE__ */ __name(() => this.timeController?.isFrozen ?? false, "isFrozen"),
|
|
1895
|
+
getFrame: /* @__PURE__ */ __name(() => this.timeController?.getFrame() ?? this.engine.loop.frameCount, "getFrame")
|
|
1896
|
+
};
|
|
1897
|
+
input = {
|
|
1898
|
+
keyDown: /* @__PURE__ */ __name((code) => {
|
|
1899
|
+
this.requireInputManager().fireKeyDown(code);
|
|
1900
|
+
}, "keyDown"),
|
|
1901
|
+
keyUp: /* @__PURE__ */ __name((code) => {
|
|
1902
|
+
this.requireInputManager().fireKeyUp(code);
|
|
1903
|
+
}, "keyUp"),
|
|
1904
|
+
mouseMove: /* @__PURE__ */ __name((x, y) => {
|
|
1905
|
+
this.requireInputManager().firePointerMove(x, y);
|
|
1906
|
+
}, "mouseMove"),
|
|
1907
|
+
mouseDown: /* @__PURE__ */ __name((button = 0) => {
|
|
1908
|
+
this.requireInputManager().firePointerDown(button);
|
|
1909
|
+
}, "mouseDown"),
|
|
1910
|
+
mouseUp: /* @__PURE__ */ __name((button = 0) => {
|
|
1911
|
+
this.requireInputManager().firePointerUp(button);
|
|
1912
|
+
}, "mouseUp"),
|
|
1913
|
+
/**
|
|
1914
|
+
* Inject a synthetic pointer-move with full pointer addressing. Pass `opts`
|
|
1915
|
+
* with `id` / `type: "touch"` to drive a specific finger; defaults match
|
|
1916
|
+
* the primary mouse pointer (same as `mouseMove`).
|
|
1917
|
+
*/
|
|
1918
|
+
pointerMove: /* @__PURE__ */ __name((x, y, opts) => {
|
|
1919
|
+
this.requireInputManager().firePointerMove(x, y, opts);
|
|
1920
|
+
}, "pointerMove"),
|
|
1921
|
+
/**
|
|
1922
|
+
* Inject a synthetic pointer-down. With `opts.id` and `opts.type: "touch"`
|
|
1923
|
+
* this drives a multi-touch contact, exercising `getPointers()`,
|
|
1924
|
+
* per-pointer event hooks, and the any-pointer aggregate for `MouseLeft`.
|
|
1925
|
+
*/
|
|
1926
|
+
pointerDown: /* @__PURE__ */ __name((button = 0, opts) => {
|
|
1927
|
+
this.requireInputManager().firePointerDown(button, opts);
|
|
1928
|
+
}, "pointerDown"),
|
|
1929
|
+
pointerUp: /* @__PURE__ */ __name((button = 0, opts) => {
|
|
1930
|
+
this.requireInputManager().firePointerUp(button, opts);
|
|
1931
|
+
}, "pointerUp"),
|
|
1932
|
+
gamepadButton: /* @__PURE__ */ __name((code, pressed) => {
|
|
1933
|
+
this.requireInputManager().fireGamepadButton(code, pressed);
|
|
1934
|
+
}, "gamepadButton"),
|
|
1935
|
+
gamepadAxis: /* @__PURE__ */ __name((side, value) => {
|
|
1936
|
+
this.requireInputManager().fireGamepadAxis(side, value);
|
|
1937
|
+
}, "gamepadAxis"),
|
|
1938
|
+
tap: /* @__PURE__ */ __name((code, frames = 1) => {
|
|
1939
|
+
this.assertNonNegativeInteger(frames, "Inspector.input.tap(frames)");
|
|
1940
|
+
const input = this.requireInputManager();
|
|
1941
|
+
input.fireKeyDown(code);
|
|
1942
|
+
try {
|
|
1943
|
+
this.time.step(frames);
|
|
1944
|
+
} finally {
|
|
1945
|
+
input.fireKeyUp(code);
|
|
1946
|
+
}
|
|
1947
|
+
}, "tap"),
|
|
1948
|
+
hold: /* @__PURE__ */ __name((code, frames) => {
|
|
1949
|
+
this.assertNonNegativeInteger(frames, "Inspector.input.hold(frames)");
|
|
1950
|
+
const input = this.requireInputManager();
|
|
1951
|
+
input.fireKeyDown(code);
|
|
1952
|
+
try {
|
|
1953
|
+
this.time.step(frames);
|
|
1954
|
+
} finally {
|
|
1955
|
+
input.fireKeyUp(code);
|
|
1956
|
+
}
|
|
1957
|
+
}, "hold"),
|
|
1958
|
+
fireAction: /* @__PURE__ */ __name((name, frames = 1) => {
|
|
1959
|
+
this.assertNonNegativeInteger(
|
|
1960
|
+
frames,
|
|
1961
|
+
"Inspector.input.fireAction(frames)"
|
|
1962
|
+
);
|
|
1963
|
+
const input = this.requireInputManager();
|
|
1964
|
+
for (let i = 0; i < frames; i++) {
|
|
1965
|
+
input.fireAction(name);
|
|
1966
|
+
this.time.step(1);
|
|
1967
|
+
}
|
|
1968
|
+
}, "fireAction"),
|
|
1969
|
+
clearAll: /* @__PURE__ */ __name(() => {
|
|
1970
|
+
this.requireInputManager().clearAll();
|
|
1971
|
+
}, "clearAll")
|
|
1972
|
+
};
|
|
1973
|
+
events = {
|
|
1974
|
+
getLog: /* @__PURE__ */ __name(() => this.iterateLog().map(({ entry }) => ({ ...entry })), "getLog"),
|
|
1975
|
+
clearLog: /* @__PURE__ */ __name(() => {
|
|
1976
|
+
this.eventLog.length = 0;
|
|
1977
|
+
this.eventLogHead = 0;
|
|
1978
|
+
}, "clearLog"),
|
|
1979
|
+
setCapacity: /* @__PURE__ */ __name((n) => {
|
|
1980
|
+
this.assertNonNegativeInteger(
|
|
1981
|
+
n,
|
|
1982
|
+
"Inspector.events.setCapacity(capacity)"
|
|
1983
|
+
);
|
|
1984
|
+
const ordered = n === 0 ? [] : this.iterateLog().slice(-n);
|
|
1985
|
+
this.eventCapacity = n;
|
|
1986
|
+
this.eventLog = ordered;
|
|
1987
|
+
this.eventLogHead = 0;
|
|
1988
|
+
}, "setCapacity"),
|
|
1989
|
+
waitFor: /* @__PURE__ */ __name((pattern, options) => {
|
|
1990
|
+
const existing = this.findMatchingEvent(pattern, options?.source);
|
|
1991
|
+
if (existing) return Promise.resolve(existing);
|
|
1992
|
+
const withinFrames = options?.withinFrames;
|
|
1993
|
+
if (withinFrames !== void 0 && (!Number.isInteger(withinFrames) || withinFrames < 0)) {
|
|
1994
|
+
throw new Error(
|
|
1995
|
+
"Inspector.events.waitFor(withinFrames) requires a non-negative integer."
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
return new Promise((resolve, reject) => {
|
|
1999
|
+
const waiter = {
|
|
2000
|
+
pattern,
|
|
2001
|
+
source: options?.source,
|
|
2002
|
+
withinFrames,
|
|
2003
|
+
deadlineFrame: withinFrames !== void 0 ? this.time.getFrame() + withinFrames : void 0,
|
|
2004
|
+
resolve,
|
|
2005
|
+
reject
|
|
2006
|
+
};
|
|
2007
|
+
this.eventWaiters.add(waiter);
|
|
2008
|
+
});
|
|
2009
|
+
}, "waitFor")
|
|
2010
|
+
};
|
|
2011
|
+
capture = {
|
|
2012
|
+
png: /* @__PURE__ */ __name(async () => {
|
|
2013
|
+
const base64 = await this.capture.pngBase64();
|
|
2014
|
+
return decodeBase64(base64);
|
|
2015
|
+
}, "png"),
|
|
2016
|
+
dataURL: /* @__PURE__ */ __name(async () => {
|
|
2017
|
+
const renderer = this.engine.context.tryResolve(RendererRuntimeKey);
|
|
2018
|
+
if (!renderer) {
|
|
2019
|
+
throw new Error(
|
|
2020
|
+
"Inspector.capture requires RendererPlugin to be active."
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
const canvas = renderer.application.renderer.extract.canvas(
|
|
2024
|
+
renderer.application.stage
|
|
2025
|
+
);
|
|
2026
|
+
return canvas.toDataURL("image/png");
|
|
2027
|
+
}, "dataURL"),
|
|
2028
|
+
pngBase64: /* @__PURE__ */ __name(async () => {
|
|
2029
|
+
const dataUrl = await this.capture.dataURL();
|
|
2030
|
+
const comma = dataUrl.indexOf(",");
|
|
2031
|
+
return comma === -1 ? dataUrl : dataUrl.slice(comma + 1);
|
|
2032
|
+
}, "pngBase64")
|
|
2033
|
+
};
|
|
2034
|
+
constructor(engine) {
|
|
2035
|
+
this.engine = engine;
|
|
1779
2036
|
}
|
|
1780
|
-
/**
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
for (let i = idx + 1; i < stack.length; i++) {
|
|
1789
|
-
if (stack[i].pauseBelow) return true;
|
|
2037
|
+
/** Register a namespaced extension API for plugin-specific debug helpers. */
|
|
2038
|
+
addExtension(namespace, api) {
|
|
2039
|
+
this.assertNonEmptyString(
|
|
2040
|
+
namespace,
|
|
2041
|
+
"Inspector.addExtension(namespace)"
|
|
2042
|
+
);
|
|
2043
|
+
if (!api || typeof api !== "object") {
|
|
2044
|
+
throw new Error("Inspector.addExtension(api) requires an object.");
|
|
1790
2045
|
}
|
|
1791
|
-
|
|
2046
|
+
if (this.extensions.has(namespace)) {
|
|
2047
|
+
throw new Error(
|
|
2048
|
+
`Inspector.addExtension(): namespace "${namespace}" is already registered.`
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
this.extensions.set(namespace, api);
|
|
2052
|
+
return api;
|
|
1792
2053
|
}
|
|
1793
|
-
/**
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
2054
|
+
/** Look up a previously registered extension API by namespace. */
|
|
2055
|
+
getExtension(namespace) {
|
|
2056
|
+
this.assertNonEmptyString(
|
|
2057
|
+
namespace,
|
|
2058
|
+
"Inspector.getExtension(namespace)"
|
|
2059
|
+
);
|
|
2060
|
+
return this.extensions.get(namespace);
|
|
1797
2061
|
}
|
|
1798
|
-
/**
|
|
1799
|
-
|
|
1800
|
-
|
|
2062
|
+
/** Remove a previously registered extension namespace. */
|
|
2063
|
+
removeExtension(namespace) {
|
|
2064
|
+
this.assertNonEmptyString(
|
|
2065
|
+
namespace,
|
|
2066
|
+
"Inspector.removeExtension(namespace)"
|
|
2067
|
+
);
|
|
2068
|
+
this.extensions.delete(namespace);
|
|
1801
2069
|
}
|
|
1802
|
-
/**
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
resolved ??= this._context.resolve(key);
|
|
1819
|
-
resolved[prop] = value;
|
|
1820
|
-
return true;
|
|
1821
|
-
}, "set")
|
|
1822
|
-
});
|
|
2070
|
+
/** Full deterministic state snapshot (stable ordering, serializable). */
|
|
2071
|
+
snapshot() {
|
|
2072
|
+
const scenes = this.engine.scenes.all.map(
|
|
2073
|
+
(scene) => this.sceneToWorldSnapshot(scene)
|
|
2074
|
+
);
|
|
2075
|
+
return {
|
|
2076
|
+
version: 1,
|
|
2077
|
+
frame: this.time.getFrame(),
|
|
2078
|
+
sceneStack: this.getSceneStack(),
|
|
2079
|
+
entityCount: this.countEntities(),
|
|
2080
|
+
systemCount: this.getSystems().length,
|
|
2081
|
+
errors: this.getErrors(),
|
|
2082
|
+
scenes,
|
|
2083
|
+
camera: this.buildCameraSnapshot(),
|
|
2084
|
+
input: this.buildInputSnapshot()
|
|
2085
|
+
};
|
|
1823
2086
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
2087
|
+
/** Stable JSON form of {@link snapshot}. */
|
|
2088
|
+
snapshotJSON() {
|
|
2089
|
+
return stableStringify(this.snapshot());
|
|
2090
|
+
}
|
|
2091
|
+
/** Snapshot one scene by inspector scene id. */
|
|
2092
|
+
snapshotScene(id) {
|
|
2093
|
+
const scene = this.engine.scenes.all.find(
|
|
2094
|
+
(candidate) => this.getSceneId(candidate) === id
|
|
2095
|
+
);
|
|
2096
|
+
if (!scene) {
|
|
2097
|
+
throw new Error(`Inspector.snapshotScene(): unknown scene id "${id}".`);
|
|
2098
|
+
}
|
|
2099
|
+
return this.sceneToWorldSnapshot(scene);
|
|
2100
|
+
}
|
|
2101
|
+
/** Find entity by name in the active scene. */
|
|
2102
|
+
getEntityByName(name) {
|
|
2103
|
+
const entity = this.findActiveEntity(name);
|
|
2104
|
+
if (!entity) return void 0;
|
|
2105
|
+
return this.entityToQuerySnapshot(entity);
|
|
2106
|
+
}
|
|
2107
|
+
/** Get entity position (from Transform component). */
|
|
2108
|
+
getEntityPosition(name) {
|
|
2109
|
+
const entity = this.findActiveEntity(name);
|
|
2110
|
+
if (!entity) return void 0;
|
|
2111
|
+
const transform = this.getTransform(entity);
|
|
2112
|
+
if (!transform) return void 0;
|
|
2113
|
+
return { x: transform.position.x, y: transform.position.y };
|
|
2114
|
+
}
|
|
2115
|
+
/** Check if an entity has a component by class name string. */
|
|
2116
|
+
hasComponent(entityName, componentClass) {
|
|
2117
|
+
return this.findComponentByName(entityName, componentClass) !== void 0;
|
|
2118
|
+
}
|
|
2119
|
+
/** Get component data (serializable subset) by class name string. */
|
|
2120
|
+
getComponentData(entityName, componentClass) {
|
|
2121
|
+
const comp = this.findComponentByName(entityName, componentClass);
|
|
2122
|
+
if (!comp) return void 0;
|
|
2123
|
+
if (typeof comp.serialize === "function") {
|
|
2124
|
+
const data = trySerialize(comp);
|
|
2125
|
+
if (data !== void 0) return data;
|
|
2126
|
+
}
|
|
2127
|
+
return this.serializeComponentOwnProperties(comp);
|
|
2128
|
+
}
|
|
2129
|
+
/** Get all entities in the active scene as lightweight snapshots. */
|
|
2130
|
+
getEntities() {
|
|
2131
|
+
const scene = this.engine.scenes.active;
|
|
2132
|
+
if (!scene) return [];
|
|
2133
|
+
const result = [];
|
|
2134
|
+
for (const entity of scene.getEntities()) {
|
|
2135
|
+
if (!entity.isDestroyed) {
|
|
2136
|
+
result.push(this.entityToQuerySnapshot(entity));
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
return result;
|
|
2140
|
+
}
|
|
2141
|
+
/** Get scene stack info. */
|
|
2142
|
+
getSceneStack() {
|
|
2143
|
+
return this.engine.scenes.all.map((scene) => ({
|
|
2144
|
+
name: scene.name,
|
|
2145
|
+
entityCount: scene.getEntities().size,
|
|
2146
|
+
paused: scene.isPaused
|
|
2147
|
+
}));
|
|
2148
|
+
}
|
|
2149
|
+
/** Get active system info. */
|
|
2150
|
+
getSystems() {
|
|
2151
|
+
const scheduler = this.engine.context.tryResolve(SystemSchedulerKey);
|
|
2152
|
+
if (!scheduler) return [];
|
|
2153
|
+
return scheduler.getAllSystems().map((sys) => ({
|
|
2154
|
+
name: sys.constructor.name,
|
|
2155
|
+
phase: sys.phase,
|
|
2156
|
+
priority: sys.priority,
|
|
2157
|
+
enabled: sys.enabled
|
|
2158
|
+
}));
|
|
2159
|
+
}
|
|
2160
|
+
/** Get disabled components/systems from error boundary. */
|
|
2161
|
+
getErrors() {
|
|
2162
|
+
const boundary = this.engine.context.tryResolve(ErrorBoundaryKey);
|
|
2163
|
+
if (!boundary) return { disabledSystems: [], disabledComponents: [] };
|
|
2164
|
+
const disabled = boundary.getDisabled();
|
|
2165
|
+
return {
|
|
2166
|
+
disabledSystems: disabled.systems.map(
|
|
2167
|
+
(s) => s.system.constructor.name
|
|
2168
|
+
),
|
|
2169
|
+
disabledComponents: disabled.components.map((c) => ({
|
|
2170
|
+
entity: c.component.entity?.name ?? "unknown",
|
|
2171
|
+
component: c.component.constructor.name,
|
|
2172
|
+
error: c.error
|
|
2173
|
+
}))
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
/** Create a new scene-scoped RNG instance using the current inspector seed policy. */
|
|
2177
|
+
createSceneRandom() {
|
|
2178
|
+
const seed = this.sceneSeedOverride ?? this.defaultSceneSeed ?? createDefaultRandomSeed();
|
|
2179
|
+
return createRandomService(seed);
|
|
2180
|
+
}
|
|
2181
|
+
/** Force every current and future scene RNG to the provided seed. */
|
|
2182
|
+
setSeed(seed) {
|
|
2183
|
+
const normalized = normalizeSeed(seed);
|
|
2184
|
+
this.sceneSeedOverride = normalized;
|
|
2185
|
+
for (const scene of this.engine.scenes.all) {
|
|
2186
|
+
this.resolveInternalRandom(scene)?.setSeed(normalized);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
/** @internal DebugPlugin installs a deterministic default seed through this hook. */
|
|
2190
|
+
setDefaultSceneSeed(seed) {
|
|
2191
|
+
this.defaultSceneSeed = seed === void 0 ? void 0 : normalizeSeed(seed);
|
|
2192
|
+
if (this.sceneSeedOverride !== void 0 || this.defaultSceneSeed === void 0) {
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
for (const scene of this.engine.scenes.all) {
|
|
2196
|
+
this.resolveInternalRandom(scene)?.setSeed(this.defaultSceneSeed);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
resolveInternalRandom(scene) {
|
|
2200
|
+
return scene._resolveScoped(RandomKey);
|
|
2201
|
+
}
|
|
2202
|
+
/** @internal DebugPlugin attaches the frozen-time controller through this hook. */
|
|
2203
|
+
attachTimeController(controller) {
|
|
2204
|
+
this.timeController = controller;
|
|
2205
|
+
}
|
|
2206
|
+
/** @internal Clear a previously attached time controller. */
|
|
2207
|
+
detachTimeController(controller) {
|
|
2208
|
+
if (!controller || this.timeController === controller) {
|
|
2209
|
+
this.timeController = null;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
/** @internal Enable or disable event log recording. */
|
|
2213
|
+
setEventLogEnabled(enabled) {
|
|
2214
|
+
if (this.eventLogEnabled === enabled) return;
|
|
2215
|
+
this.eventLogEnabled = enabled;
|
|
2216
|
+
if (enabled) {
|
|
2217
|
+
if (!this.detachBusTap && this.engine.events?.tap) {
|
|
2218
|
+
this.detachBusTap = this.engine.events.tap(this.busEventObserver);
|
|
2219
|
+
}
|
|
2220
|
+
} else {
|
|
2221
|
+
this.detachBusTap?.();
|
|
2222
|
+
this.detachBusTap = null;
|
|
2223
|
+
}
|
|
2224
|
+
for (const scene of this.engine.scenes.all) {
|
|
2225
|
+
if (enabled) {
|
|
2226
|
+
this.attachSceneEventObserver(scene);
|
|
2227
|
+
} else {
|
|
2228
|
+
this.detachSceneEventObserver(scene);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
/** @internal Install entity-event observation for one scene. No-op if disabled. */
|
|
2233
|
+
attachSceneEventObserver(scene) {
|
|
2234
|
+
if (!this.eventLogEnabled) return;
|
|
2235
|
+
scene._setEntityEventObserver(this.sceneEventObserver);
|
|
2236
|
+
}
|
|
2237
|
+
/** @internal Clear entity-event observation for one scene. */
|
|
2238
|
+
detachSceneEventObserver(scene) {
|
|
2239
|
+
scene._setEntityEventObserver(void 0);
|
|
2240
|
+
}
|
|
2241
|
+
/** @internal Scene hooks forward entity events through this method. */
|
|
2242
|
+
recordEntityEvent(eventName, data, entity) {
|
|
2243
|
+
if (!this.eventLogEnabled) return;
|
|
2244
|
+
const scene = entity.tryScene;
|
|
2245
|
+
this.appendEvent(
|
|
2246
|
+
{
|
|
2247
|
+
frame: this.time.getFrame(),
|
|
2248
|
+
source: "entity",
|
|
2249
|
+
type: eventName,
|
|
2250
|
+
targetId: String(entity.id),
|
|
2251
|
+
payload: serializeEventPayload(data)
|
|
2252
|
+
},
|
|
2253
|
+
scene ? this.getSceneId(scene) : void 0
|
|
2254
|
+
);
|
|
2255
|
+
}
|
|
2256
|
+
/** @internal Engine teardown releases the event-bus tap through this hook. */
|
|
2257
|
+
dispose() {
|
|
2258
|
+
this.detachBusTap?.();
|
|
2259
|
+
this.detachBusTap = null;
|
|
2260
|
+
for (const scene of this.engine.scenes.all) {
|
|
2261
|
+
scene._setEntityEventObserver(void 0);
|
|
2262
|
+
}
|
|
2263
|
+
this.extensions.clear();
|
|
2264
|
+
}
|
|
2265
|
+
requireTimeController() {
|
|
2266
|
+
if (!this.timeController) {
|
|
2267
|
+
throw new Error(
|
|
2268
|
+
"Inspector.time requires DebugPlugin to be active."
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
return this.timeController;
|
|
2272
|
+
}
|
|
2273
|
+
requireInputManager() {
|
|
2274
|
+
const input = this.engine.context.tryResolve(InputManagerRuntimeKey);
|
|
2275
|
+
if (!input) {
|
|
2276
|
+
throw new Error(
|
|
2277
|
+
"Inspector.input requires InputPlugin to be active."
|
|
2278
|
+
);
|
|
2279
|
+
}
|
|
2280
|
+
return input;
|
|
2281
|
+
}
|
|
2282
|
+
recordBusEvent(type, data) {
|
|
2283
|
+
if (!this.eventLogEnabled) return;
|
|
2284
|
+
this.appendEvent(
|
|
2285
|
+
{
|
|
2286
|
+
frame: this.time.getFrame(),
|
|
2287
|
+
source: "bus",
|
|
2288
|
+
type,
|
|
2289
|
+
payload: serializeEventPayload(data)
|
|
2290
|
+
},
|
|
2291
|
+
this.inferSceneIdFromPayload(data)
|
|
2292
|
+
);
|
|
2293
|
+
}
|
|
2294
|
+
appendEvent(entry, sceneId) {
|
|
2295
|
+
if (this.eventCapacity === 0) {
|
|
2296
|
+
this.flushMatchingWaiter(entry);
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
const logged = { entry, sceneId };
|
|
2300
|
+
if (this.eventLog.length < this.eventCapacity) {
|
|
2301
|
+
this.eventLog.push(logged);
|
|
2302
|
+
} else {
|
|
2303
|
+
this.eventLog[this.eventLogHead] = logged;
|
|
2304
|
+
this.eventLogHead = (this.eventLogHead + 1) % this.eventCapacity;
|
|
2305
|
+
}
|
|
2306
|
+
this.flushMatchingWaiter(entry);
|
|
2307
|
+
}
|
|
2308
|
+
/** Resolve waiters whose deadline has passed without a match. */
|
|
2309
|
+
expireDeadlineWaiters() {
|
|
2310
|
+
if (this.eventWaiters.size === 0) return;
|
|
2311
|
+
const frame = this.time.getFrame();
|
|
2312
|
+
for (const waiter of [...this.eventWaiters]) {
|
|
2313
|
+
if (waiter.deadlineFrame !== void 0 && frame > waiter.deadlineFrame) {
|
|
2314
|
+
this.eventWaiters.delete(waiter);
|
|
2315
|
+
waiter.reject(
|
|
2316
|
+
new Error(
|
|
2317
|
+
`Inspector.events.waitFor() timed out after ${waiter.withinFrames} frames.`
|
|
2318
|
+
)
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
/** Resolve any waiter that matches the just-appended entry. */
|
|
2324
|
+
flushMatchingWaiter(entry) {
|
|
2325
|
+
if (this.eventWaiters.size === 0) return;
|
|
2326
|
+
for (const waiter of [...this.eventWaiters]) {
|
|
2327
|
+
if (this.eventMatches(entry, waiter.pattern, waiter.source)) {
|
|
2328
|
+
this.eventWaiters.delete(waiter);
|
|
2329
|
+
waiter.resolve(entry);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Walk the ring buffer in chronological order. We avoid materializing the
|
|
2335
|
+
* ordered array on every event append; instead, every consumer that needs
|
|
2336
|
+
* order calls this helper.
|
|
2337
|
+
*/
|
|
2338
|
+
iterateLog() {
|
|
2339
|
+
if (this.eventLog.length < this.eventCapacity || this.eventLogHead === 0) {
|
|
2340
|
+
return this.eventLog;
|
|
2341
|
+
}
|
|
2342
|
+
return [
|
|
2343
|
+
...this.eventLog.slice(this.eventLogHead),
|
|
2344
|
+
...this.eventLog.slice(0, this.eventLogHead)
|
|
2345
|
+
];
|
|
2346
|
+
}
|
|
2347
|
+
findMatchingEvent(pattern, source) {
|
|
2348
|
+
for (const { entry } of this.iterateLog()) {
|
|
2349
|
+
if (this.eventMatches(entry, pattern, source)) {
|
|
2350
|
+
return { ...entry };
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
return void 0;
|
|
2354
|
+
}
|
|
2355
|
+
eventMatches(entry, pattern, source) {
|
|
2356
|
+
if (source && entry.source !== source) return false;
|
|
2357
|
+
return typeof pattern === "string" ? entry.type === pattern : pattern.test(entry.type);
|
|
2358
|
+
}
|
|
2359
|
+
sceneToWorldSnapshot(scene) {
|
|
2360
|
+
const random = scene._resolveScoped(RandomKey);
|
|
2361
|
+
const physicsManager = this.engine.context.tryResolve(
|
|
2362
|
+
PhysicsWorldManagerRuntimeKey
|
|
2363
|
+
);
|
|
2364
|
+
return {
|
|
2365
|
+
id: this.getSceneId(scene),
|
|
2366
|
+
name: scene.name,
|
|
2367
|
+
paused: scene.isPaused,
|
|
2368
|
+
timeScale: scene.timeScale,
|
|
2369
|
+
seed: random?.getSeed() ?? 0,
|
|
2370
|
+
entities: this.getSceneEntities(scene),
|
|
2371
|
+
ui: this.buildUISnapshot(scene),
|
|
2372
|
+
physics: physicsManager?.getContext(scene)?.world.snapshot() ?? {
|
|
2373
|
+
bodies: [],
|
|
2374
|
+
contacts: []
|
|
2375
|
+
},
|
|
2376
|
+
events: this.getSceneEvents(scene)
|
|
2377
|
+
};
|
|
2378
|
+
}
|
|
2379
|
+
getSceneEntities(scene) {
|
|
2380
|
+
return [...scene.getEntities()].filter((entity) => !entity.isDestroyed).sort((a, b) => a.id - b.id).map((entity) => this.entityToWorldSnapshot(entity));
|
|
2381
|
+
}
|
|
2382
|
+
entityToWorldSnapshot(entity) {
|
|
2383
|
+
const transform = entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
2384
|
+
const worldPosition = transform?.worldPosition;
|
|
2385
|
+
const worldScale = transform?.worldScale;
|
|
2386
|
+
const components = [...entity.getAll()].map((component) => this.componentToSnapshot(component)).sort((a, b) => a.type < b.type ? -1 : a.type > b.type ? 1 : 0);
|
|
2387
|
+
return {
|
|
2388
|
+
id: String(entity.id),
|
|
2389
|
+
type: entity.constructor.name,
|
|
2390
|
+
parent: entity.parent ? String(entity.parent.id) : null,
|
|
2391
|
+
transform: {
|
|
2392
|
+
x: worldPosition?.x ?? 0,
|
|
2393
|
+
y: worldPosition?.y ?? 0,
|
|
2394
|
+
rotation: transform?.worldRotation ?? 0,
|
|
2395
|
+
scaleX: worldScale?.x ?? 1,
|
|
2396
|
+
scaleY: worldScale?.y ?? 1
|
|
2397
|
+
},
|
|
2398
|
+
components
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
componentToSnapshot(component) {
|
|
2402
|
+
return {
|
|
2403
|
+
type: component.constructor.name,
|
|
2404
|
+
state: typeof component.serialize === "function" ? trySerialize(component) ?? null : null
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
buildUISnapshot(scene) {
|
|
2408
|
+
const roots = [...scene.getEntities()].filter((entity) => !entity.isDestroyed).flatMap(
|
|
2409
|
+
(entity) => [...entity.getAll()].filter(
|
|
2410
|
+
(component) => component.constructor.name === "UIPanel" && "_node" in component
|
|
2411
|
+
).map(
|
|
2412
|
+
(component, index) => this.buildUINodeSnapshot(
|
|
2413
|
+
component._node,
|
|
2414
|
+
`entity-${entity.id}:UIPanel:${index}`
|
|
2415
|
+
)
|
|
2416
|
+
)
|
|
2417
|
+
);
|
|
2418
|
+
if (roots.length === 0) return null;
|
|
2419
|
+
if (roots.length === 1) {
|
|
2420
|
+
return { root: roots[0] };
|
|
2421
|
+
}
|
|
2422
|
+
return {
|
|
2423
|
+
root: {
|
|
2424
|
+
id: `scene-${this.getSceneId(scene)}:ui`,
|
|
2425
|
+
type: "UIRoot",
|
|
2426
|
+
layout: { x: 0, y: 0, width: 0, height: 0 },
|
|
2427
|
+
children: roots,
|
|
2428
|
+
state: null
|
|
2429
|
+
}
|
|
2430
|
+
};
|
|
2431
|
+
}
|
|
2432
|
+
buildUINodeSnapshot(node, id) {
|
|
2433
|
+
const layout = node.yogaNode?.getComputedLayout();
|
|
2434
|
+
const children = (node.children ?? []).map(
|
|
2435
|
+
(child, index) => this.buildUINodeSnapshot(child, `${id}/${index}`)
|
|
2436
|
+
);
|
|
2437
|
+
return {
|
|
2438
|
+
id,
|
|
2439
|
+
type: node.constructor.name,
|
|
2440
|
+
layout: {
|
|
2441
|
+
x: layout?.left ?? 0,
|
|
2442
|
+
y: layout?.top ?? 0,
|
|
2443
|
+
width: layout?.width ?? 0,
|
|
2444
|
+
height: layout?.height ?? 0
|
|
2445
|
+
},
|
|
2446
|
+
children,
|
|
2447
|
+
state: null
|
|
2448
|
+
};
|
|
2449
|
+
}
|
|
2450
|
+
buildCameraSnapshot() {
|
|
2451
|
+
const match = this.findTopmostCamera();
|
|
2452
|
+
if (!match) return null;
|
|
2453
|
+
const { scene, camera } = match;
|
|
2454
|
+
return {
|
|
2455
|
+
sceneId: this.getSceneId(scene),
|
|
2456
|
+
sceneName: scene.name,
|
|
2457
|
+
name: camera.cameraName ?? null,
|
|
2458
|
+
priority: camera.priority ?? 0,
|
|
2459
|
+
position: {
|
|
2460
|
+
x: camera.position.x,
|
|
2461
|
+
y: camera.position.y
|
|
2462
|
+
},
|
|
2463
|
+
zoom: camera.zoom,
|
|
2464
|
+
rotation: camera.rotation
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
findTopmostCamera() {
|
|
2468
|
+
const stack = this.engine.scenes.all;
|
|
2469
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
2470
|
+
const scene = stack[i];
|
|
2471
|
+
if (!scene) continue;
|
|
2472
|
+
let highest;
|
|
2473
|
+
for (const entity of scene.getEntities()) {
|
|
2474
|
+
if (entity.isDestroyed) continue;
|
|
2475
|
+
for (const component of entity.getAll()) {
|
|
2476
|
+
if (component.constructor.name !== "CameraComponent") continue;
|
|
2477
|
+
const camera = component;
|
|
2478
|
+
if (camera.enabled && (!highest || (camera.priority ?? 0) > (highest.priority ?? 0))) {
|
|
2479
|
+
highest = camera;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
if (highest) {
|
|
2484
|
+
return { scene, camera: highest };
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
return void 0;
|
|
2488
|
+
}
|
|
2489
|
+
buildInputSnapshot() {
|
|
2490
|
+
const input = this.engine.context.tryResolve(InputManagerRuntimeKey);
|
|
2491
|
+
return input?.snapshotState() ?? {
|
|
2492
|
+
keys: [],
|
|
2493
|
+
actions: [],
|
|
2494
|
+
mouse: { x: 0, y: 0, buttons: [], down: false },
|
|
2495
|
+
pointers: [],
|
|
2496
|
+
gamepad: { buttons: [], axes: [] }
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
getSceneEvents(scene) {
|
|
2500
|
+
const sceneId = this.getSceneId(scene);
|
|
2501
|
+
return this.iterateLog().filter((entry) => entry.sceneId === sceneId).map(({ entry }) => ({ ...entry }));
|
|
2502
|
+
}
|
|
2503
|
+
inferSceneIdFromPayload(data) {
|
|
2504
|
+
if (!data || typeof data !== "object") return void 0;
|
|
2505
|
+
const record = data;
|
|
2506
|
+
const scene = this.extractScene(record["scene"]) ?? this.extractSceneFromEntity(record["entity"]) ?? this.extractSceneFromEntity(record["oldScene"]) ?? this.extractSceneFromEntity(record["newScene"]);
|
|
2507
|
+
return scene ? this.getSceneId(scene) : void 0;
|
|
2508
|
+
}
|
|
2509
|
+
extractScene(value) {
|
|
2510
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2511
|
+
return this.engine.scenes.all.find((scene) => scene === value);
|
|
2512
|
+
}
|
|
2513
|
+
extractSceneFromEntity(value) {
|
|
2514
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2515
|
+
const maybeEntity = value;
|
|
2516
|
+
return maybeEntity.tryScene ?? this.extractScene(value);
|
|
2517
|
+
}
|
|
2518
|
+
findActiveEntity(name) {
|
|
2519
|
+
return this.engine.scenes.active?.findEntity(name);
|
|
2520
|
+
}
|
|
2521
|
+
findComponentByName(entityName, componentClass) {
|
|
2522
|
+
const entity = this.findActiveEntity(entityName);
|
|
2523
|
+
if (!entity) return void 0;
|
|
2524
|
+
for (const comp of entity.getAll()) {
|
|
2525
|
+
if (comp.constructor.name === componentClass) return comp;
|
|
2526
|
+
}
|
|
2527
|
+
return void 0;
|
|
2528
|
+
}
|
|
2529
|
+
entityToQuerySnapshot(entity) {
|
|
2530
|
+
const transform = this.getTransform(entity);
|
|
2531
|
+
const snapshot = {
|
|
2532
|
+
id: entity.id,
|
|
2533
|
+
name: entity.name,
|
|
2534
|
+
tags: [...entity.tags].sort((a, b) => a < b ? -1 : a > b ? 1 : 0),
|
|
2535
|
+
components: [...entity.getAll()].map((component) => component.constructor.name).sort((a, b) => a < b ? -1 : a > b ? 1 : 0)
|
|
2536
|
+
};
|
|
2537
|
+
if (transform) {
|
|
2538
|
+
snapshot.position = {
|
|
2539
|
+
x: transform.position.x,
|
|
2540
|
+
y: transform.position.y
|
|
2541
|
+
};
|
|
2542
|
+
}
|
|
2543
|
+
return snapshot;
|
|
2544
|
+
}
|
|
2545
|
+
getTransform(entity) {
|
|
2546
|
+
return entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
2547
|
+
}
|
|
2548
|
+
serializeComponentOwnProperties(comp) {
|
|
2549
|
+
const result = {};
|
|
2550
|
+
for (const key of Object.getOwnPropertyNames(comp)) {
|
|
2551
|
+
if (key === "entity") continue;
|
|
2552
|
+
if (key.startsWith("_")) continue;
|
|
2553
|
+
const value = comp[key];
|
|
2554
|
+
if (!isSerializableValue(value)) continue;
|
|
2555
|
+
result[key] = value;
|
|
2556
|
+
}
|
|
2557
|
+
return result;
|
|
2558
|
+
}
|
|
2559
|
+
countEntities() {
|
|
2560
|
+
let count = 0;
|
|
2561
|
+
for (const scene of this.engine.scenes.all) {
|
|
2562
|
+
for (const entity of scene.getEntities()) {
|
|
2563
|
+
if (!entity.isDestroyed) count++;
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
return count;
|
|
2567
|
+
}
|
|
2568
|
+
getSceneId(scene) {
|
|
2569
|
+
let id = this.sceneIds.get(scene);
|
|
2570
|
+
if (!id) {
|
|
2571
|
+
this.nextSceneId++;
|
|
2572
|
+
id = `scene-${this.nextSceneId}`;
|
|
2573
|
+
this.sceneIds.set(scene, id);
|
|
2574
|
+
}
|
|
2575
|
+
return id;
|
|
2576
|
+
}
|
|
2577
|
+
assertNonNegativeInteger(value, name) {
|
|
2578
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
2579
|
+
throw new Error(`${name} requires a non-negative integer.`);
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
assertNonEmptyString(value, name) {
|
|
2583
|
+
if (value.trim().length === 0) {
|
|
2584
|
+
throw new Error(`${name} requires a non-empty string.`);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
};
|
|
2588
|
+
function isSerializableValue(value) {
|
|
2589
|
+
if (value === null || value === void 0) return true;
|
|
2590
|
+
const t = typeof value;
|
|
2591
|
+
if (t === "function") return false;
|
|
2592
|
+
if (t !== "object") return true;
|
|
2593
|
+
if (Array.isArray(value)) return true;
|
|
2594
|
+
const proto = Object.getPrototypeOf(value);
|
|
2595
|
+
return proto === Object.prototype || proto === null;
|
|
2596
|
+
}
|
|
2597
|
+
__name(isSerializableValue, "isSerializableValue");
|
|
2598
|
+
function safeClone(value) {
|
|
2599
|
+
try {
|
|
2600
|
+
return JSON.parse(JSON.stringify(value));
|
|
2601
|
+
} catch {
|
|
2602
|
+
return void 0;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
__name(safeClone, "safeClone");
|
|
2606
|
+
function trySerialize(component) {
|
|
2607
|
+
try {
|
|
2608
|
+
return safeClone(component.serialize?.());
|
|
2609
|
+
} catch {
|
|
2610
|
+
return void 0;
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
__name(trySerialize, "trySerialize");
|
|
2614
|
+
function serializeEventPayload(payload) {
|
|
2615
|
+
if (payload === void 0) return null;
|
|
2616
|
+
const cloned = safeClone(payload);
|
|
2617
|
+
return cloned === void 0 ? { _unserializable: true } : cloned;
|
|
2618
|
+
}
|
|
2619
|
+
__name(serializeEventPayload, "serializeEventPayload");
|
|
2620
|
+
function stableStringify(value) {
|
|
2621
|
+
return JSON.stringify(sortJsonValue(value));
|
|
2622
|
+
}
|
|
2623
|
+
__name(stableStringify, "stableStringify");
|
|
2624
|
+
function sortJsonValue(value) {
|
|
2625
|
+
if (Array.isArray(value)) {
|
|
2626
|
+
return value.map((item) => sortJsonValue(item));
|
|
2627
|
+
}
|
|
2628
|
+
if (value && typeof value === "object") {
|
|
2629
|
+
const entries = Object.entries(value).sort(
|
|
2630
|
+
([left], [right]) => left < right ? -1 : left > right ? 1 : 0
|
|
2631
|
+
);
|
|
2632
|
+
const result = {};
|
|
2633
|
+
for (const [key, child] of entries) {
|
|
2634
|
+
result[key] = sortJsonValue(child);
|
|
2635
|
+
}
|
|
2636
|
+
return result;
|
|
2637
|
+
}
|
|
2638
|
+
return value;
|
|
2639
|
+
}
|
|
2640
|
+
__name(sortJsonValue, "sortJsonValue");
|
|
2641
|
+
function decodeBase64(base64) {
|
|
2642
|
+
if (typeof atob === "function") {
|
|
2643
|
+
const binary = atob(base64);
|
|
2644
|
+
const bytes = new Uint8Array(binary.length);
|
|
2645
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2646
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2647
|
+
}
|
|
2648
|
+
return bytes;
|
|
2649
|
+
}
|
|
2650
|
+
const bufferCtor = globalThis.Buffer;
|
|
2651
|
+
if (bufferCtor) {
|
|
2652
|
+
return bufferCtor.from(base64, "base64");
|
|
2653
|
+
}
|
|
2654
|
+
throw new Error("Inspector.capture.png() is not supported in this environment.");
|
|
2655
|
+
}
|
|
2656
|
+
__name(decodeBase64, "decodeBase64");
|
|
2657
|
+
|
|
2658
|
+
// src/Scene.ts
|
|
2659
|
+
var Scene = class {
|
|
2660
|
+
static {
|
|
2661
|
+
__name(this, "Scene");
|
|
2662
|
+
}
|
|
2663
|
+
/** Whether scenes below this one in the stack should be paused. Default: true. */
|
|
2664
|
+
pauseBelow = true;
|
|
2665
|
+
/** Whether scenes below this one should still render. Default: false. */
|
|
2666
|
+
transparentBelow = false;
|
|
2667
|
+
/** Asset handles to load before onEnter(). Override in subclasses. */
|
|
2668
|
+
preload;
|
|
2669
|
+
/** Default transition used when this scene is the destination of a push/pop/replace. */
|
|
2670
|
+
defaultTransition;
|
|
2671
|
+
/** Manual pause flag. Set by game code to pause this scene regardless of stack position. */
|
|
2672
|
+
paused = false;
|
|
2673
|
+
/** Time scale multiplier for this scene. 1.0 = normal, 0.5 = half speed. Default: 1. */
|
|
2674
|
+
timeScale = 1;
|
|
2675
|
+
entities = /* @__PURE__ */ new Set();
|
|
2676
|
+
destroyQueue = [];
|
|
2677
|
+
_context;
|
|
2678
|
+
entityCallbacks;
|
|
2679
|
+
queryCache;
|
|
2680
|
+
bus;
|
|
2681
|
+
_entityEventHandlers;
|
|
2682
|
+
_entityEventObserver;
|
|
2683
|
+
_scopedServices;
|
|
2684
|
+
/** Access the EngineContext. */
|
|
2685
|
+
get context() {
|
|
2686
|
+
return this._context;
|
|
2687
|
+
}
|
|
2688
|
+
/** Whether this scene is effectively paused (manual pause or paused by stack). */
|
|
2689
|
+
get isPaused() {
|
|
2690
|
+
if (this.paused) return true;
|
|
2691
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
2692
|
+
if (!sm) return false;
|
|
2693
|
+
const stack = sm.all;
|
|
2694
|
+
const idx = stack.indexOf(this);
|
|
2695
|
+
if (idx === -1) return false;
|
|
2696
|
+
for (let i = idx + 1; i < stack.length; i++) {
|
|
2697
|
+
if (stack[i].pauseBelow) return true;
|
|
2698
|
+
}
|
|
2699
|
+
return false;
|
|
2700
|
+
}
|
|
2701
|
+
/** Whether a scene transition is currently running. */
|
|
2702
|
+
get isTransitioning() {
|
|
2703
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
2704
|
+
return sm?.isTransitioning ?? false;
|
|
2705
|
+
}
|
|
2706
|
+
/** Convenience accessor for the AssetManager. */
|
|
2707
|
+
get assets() {
|
|
2708
|
+
return this._context.resolve(AssetManagerKey);
|
|
2709
|
+
}
|
|
2710
|
+
/**
|
|
2711
|
+
* Lazy proxy-based service resolution. Can be used at field-declaration time:
|
|
2712
|
+
* ```ts
|
|
2713
|
+
* readonly layers = this.service(RenderLayerManagerKey);
|
|
2714
|
+
* ```
|
|
2715
|
+
* The actual resolution is deferred until first property access.
|
|
2716
|
+
*/
|
|
2717
|
+
service(key) {
|
|
2718
|
+
let resolved;
|
|
2719
|
+
return new Proxy({}, {
|
|
2720
|
+
get: /* @__PURE__ */ __name((_target, prop) => {
|
|
2721
|
+
resolved ??= this._context.resolve(key);
|
|
2722
|
+
const value = resolved[prop];
|
|
2723
|
+
return typeof value === "function" ? value.bind(resolved) : value;
|
|
2724
|
+
}, "get"),
|
|
2725
|
+
set: /* @__PURE__ */ __name((_target, prop, value) => {
|
|
2726
|
+
resolved ??= this._context.resolve(key);
|
|
2727
|
+
resolved[prop] = value;
|
|
2728
|
+
return true;
|
|
2729
|
+
}, "set")
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
spawn(nameOrBlueprintOrClass, params) {
|
|
2733
|
+
if (typeof nameOrBlueprintOrClass === "function") {
|
|
2734
|
+
const entity2 = new nameOrBlueprintOrClass();
|
|
2735
|
+
entity2._setScene(this, this.entityCallbacks);
|
|
2736
|
+
this.entities.add(entity2);
|
|
2737
|
+
this.bus?.emit("entity:created", { entity: entity2 });
|
|
2738
|
+
entity2.setup?.(params);
|
|
2739
|
+
return entity2;
|
|
1832
2740
|
}
|
|
1833
2741
|
const isBlueprint = typeof nameOrBlueprintOrClass === "object" && nameOrBlueprintOrClass !== null && "build" in nameOrBlueprintOrClass;
|
|
1834
2742
|
const name = isBlueprint ? nameOrBlueprintOrClass.name : nameOrBlueprintOrClass;
|
|
@@ -1918,6 +2826,14 @@ var Scene = class {
|
|
|
1918
2826
|
}
|
|
1919
2827
|
}
|
|
1920
2828
|
}
|
|
2829
|
+
/**
|
|
2830
|
+
* Observe entity-scoped event emissions after they dispatch locally and
|
|
2831
|
+
* bubble to the scene. Tooling only; game code should keep using `on()`.
|
|
2832
|
+
* @internal
|
|
2833
|
+
*/
|
|
2834
|
+
_observeEntityEvent(eventName, data, entity) {
|
|
2835
|
+
this._entityEventObserver?.(eventName, data, entity);
|
|
2836
|
+
}
|
|
1921
2837
|
// ---- Internal methods ----
|
|
1922
2838
|
/**
|
|
1923
2839
|
* Register a scene-scoped service. Called from a plugin's `beforeEnter`
|
|
@@ -1929,6 +2845,13 @@ var Scene = class {
|
|
|
1929
2845
|
this._scopedServices ??= /* @__PURE__ */ new Map();
|
|
1930
2846
|
this._scopedServices.set(key.id, value);
|
|
1931
2847
|
}
|
|
2848
|
+
/**
|
|
2849
|
+
* Install or clear a tooling-only observer for bubbled entity events.
|
|
2850
|
+
* @internal
|
|
2851
|
+
*/
|
|
2852
|
+
_setEntityEventObserver(observer) {
|
|
2853
|
+
this._entityEventObserver = observer;
|
|
2854
|
+
}
|
|
1932
2855
|
/**
|
|
1933
2856
|
* Resolve a scene-scoped service, or `undefined` if none was registered.
|
|
1934
2857
|
* @internal
|
|
@@ -1995,6 +2918,128 @@ var Scene = class {
|
|
|
1995
2918
|
}
|
|
1996
2919
|
};
|
|
1997
2920
|
|
|
2921
|
+
// src/Process.ts
|
|
2922
|
+
var Process = class _Process {
|
|
2923
|
+
static {
|
|
2924
|
+
__name(this, "Process");
|
|
2925
|
+
}
|
|
2926
|
+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
2927
|
+
updateFn;
|
|
2928
|
+
onCompleteFn;
|
|
2929
|
+
duration;
|
|
2930
|
+
loop;
|
|
2931
|
+
/** Tags for filtering/grouping. */
|
|
2932
|
+
tags;
|
|
2933
|
+
elapsed = 0;
|
|
2934
|
+
_completed = false;
|
|
2935
|
+
_paused = false;
|
|
2936
|
+
_cancelled = false;
|
|
2937
|
+
resolvePromise;
|
|
2938
|
+
/** Create a timer that fires `onComplete` after `duration` ms. */
|
|
2939
|
+
static delay(duration, onComplete, tags) {
|
|
2940
|
+
const opts = { duration };
|
|
2941
|
+
if (onComplete !== void 0) opts.onComplete = onComplete;
|
|
2942
|
+
if (tags !== void 0) opts.tags = tags;
|
|
2943
|
+
return new _Process(opts);
|
|
2944
|
+
}
|
|
2945
|
+
constructor(options) {
|
|
2946
|
+
this.updateFn = options.update ?? (() => {
|
|
2947
|
+
});
|
|
2948
|
+
this.onCompleteFn = options.onComplete;
|
|
2949
|
+
this.duration = options.duration;
|
|
2950
|
+
this.loop = options.loop ?? false;
|
|
2951
|
+
this.tags = options.tags ?? [];
|
|
2952
|
+
}
|
|
2953
|
+
/** Whether the process has completed. */
|
|
2954
|
+
get completed() {
|
|
2955
|
+
return this._completed;
|
|
2956
|
+
}
|
|
2957
|
+
/** Whether the process is paused. */
|
|
2958
|
+
get paused() {
|
|
2959
|
+
return this._paused;
|
|
2960
|
+
}
|
|
2961
|
+
/** Pause the process. */
|
|
2962
|
+
pause() {
|
|
2963
|
+
this._paused = true;
|
|
2964
|
+
}
|
|
2965
|
+
/** Resume the process. */
|
|
2966
|
+
resume() {
|
|
2967
|
+
this._paused = false;
|
|
2968
|
+
}
|
|
2969
|
+
/** Cancel the process. */
|
|
2970
|
+
cancel() {
|
|
2971
|
+
this._cancelled = true;
|
|
2972
|
+
this._completed = true;
|
|
2973
|
+
this.resolvePromise?.();
|
|
2974
|
+
}
|
|
2975
|
+
/** Returns a promise that resolves when the process completes or is cancelled. */
|
|
2976
|
+
toPromise() {
|
|
2977
|
+
if (this._completed) return Promise.resolve();
|
|
2978
|
+
return new Promise((resolve) => {
|
|
2979
|
+
this.resolvePromise = resolve;
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
/**
|
|
2983
|
+
* Advance the process by dt milliseconds.
|
|
2984
|
+
* @internal
|
|
2985
|
+
*/
|
|
2986
|
+
_update(dt) {
|
|
2987
|
+
if (this._completed || this._paused || this._cancelled) return;
|
|
2988
|
+
this.elapsed += dt;
|
|
2989
|
+
if (this.duration !== void 0 && this.elapsed >= this.duration) {
|
|
2990
|
+
const result2 = this.updateFn(dt, this.elapsed);
|
|
2991
|
+
if (this.loop && result2 !== true) {
|
|
2992
|
+
this.elapsed = this.elapsed % this.duration;
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
this.complete();
|
|
2996
|
+
return;
|
|
2997
|
+
}
|
|
2998
|
+
const result = this.updateFn(dt, this.elapsed);
|
|
2999
|
+
if (result === true) {
|
|
3000
|
+
if (this.loop) {
|
|
3001
|
+
this.elapsed = 0;
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
this.complete();
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
/**
|
|
3008
|
+
* Reset the process to its initial state so it can be re-run.
|
|
3009
|
+
* @internal Used by Sequence for loop/repeat with direct instances.
|
|
3010
|
+
*/
|
|
3011
|
+
_reset() {
|
|
3012
|
+
this.elapsed = 0;
|
|
3013
|
+
this._completed = false;
|
|
3014
|
+
this._paused = false;
|
|
3015
|
+
this._cancelled = false;
|
|
3016
|
+
delete this.resolvePromise;
|
|
3017
|
+
}
|
|
3018
|
+
complete() {
|
|
3019
|
+
this._completed = true;
|
|
3020
|
+
this.onCompleteFn?.();
|
|
3021
|
+
this.resolvePromise?.();
|
|
3022
|
+
}
|
|
3023
|
+
};
|
|
3024
|
+
var easeLinear = /* @__PURE__ */ __name((t) => t, "easeLinear");
|
|
3025
|
+
var easeInQuad = /* @__PURE__ */ __name((t) => t * t, "easeInQuad");
|
|
3026
|
+
var easeOutQuad = /* @__PURE__ */ __name((t) => t * (2 - t), "easeOutQuad");
|
|
3027
|
+
var easeInOutQuad = /* @__PURE__ */ __name((t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, "easeInOutQuad");
|
|
3028
|
+
var easeOutBounce = /* @__PURE__ */ __name((t) => {
|
|
3029
|
+
if (t < 1 / 2.75) {
|
|
3030
|
+
return 7.5625 * t * t;
|
|
3031
|
+
} else if (t < 2 / 2.75) {
|
|
3032
|
+
const t2 = t - 1.5 / 2.75;
|
|
3033
|
+
return 7.5625 * t2 * t2 + 0.75;
|
|
3034
|
+
} else if (t < 2.5 / 2.75) {
|
|
3035
|
+
const t2 = t - 2.25 / 2.75;
|
|
3036
|
+
return 7.5625 * t2 * t2 + 0.9375;
|
|
3037
|
+
} else {
|
|
3038
|
+
const t2 = t - 2.625 / 2.75;
|
|
3039
|
+
return 7.5625 * t2 * t2 + 0.984375;
|
|
3040
|
+
}
|
|
3041
|
+
}, "easeOutBounce");
|
|
3042
|
+
|
|
1998
3043
|
// src/LoadingScene.ts
|
|
1999
3044
|
var LoadingScene = class extends Scene {
|
|
2000
3045
|
static {
|
|
@@ -2019,6 +3064,7 @@ var LoadingScene = class extends Scene {
|
|
|
2019
3064
|
_active = true;
|
|
2020
3065
|
_continueRequested = false;
|
|
2021
3066
|
_continueGate;
|
|
3067
|
+
_pendingWaits = /* @__PURE__ */ new Set();
|
|
2022
3068
|
// Bumped on every `_run` attempt. `AssetManager.loadAll` uses `Promise.all`
|
|
2023
3069
|
// under the hood, so individual loaders from a failed attempt can still
|
|
2024
3070
|
// resolve and fire `onProgress` after the attempt rejects. Without this
|
|
@@ -2073,28 +3119,32 @@ var LoadingScene = class extends Scene {
|
|
|
2073
3119
|
onExit() {
|
|
2074
3120
|
this._active = false;
|
|
2075
3121
|
this._continueGate?.();
|
|
3122
|
+
for (const wait of this._pendingWaits) {
|
|
3123
|
+
wait.cancel();
|
|
3124
|
+
}
|
|
3125
|
+
this._pendingWaits.clear();
|
|
2076
3126
|
}
|
|
2077
3127
|
async _run() {
|
|
2078
|
-
await
|
|
3128
|
+
await Promise.resolve();
|
|
2079
3129
|
if (!this._active) return;
|
|
2080
3130
|
const attempt = ++this._attempt;
|
|
2081
3131
|
const target = typeof this.target === "function" ? this.target() : this.target;
|
|
2082
|
-
const startedAt = performance.now();
|
|
2083
3132
|
const bus = this.context.resolve(EventBusKey);
|
|
3133
|
+
const minDuration = this._createEngineTimeDelay(this.minDuration);
|
|
2084
3134
|
try {
|
|
2085
3135
|
await this.assets.loadAll(target.preload ?? [], (ratio) => {
|
|
2086
3136
|
if (!this._active || attempt !== this._attempt) return;
|
|
2087
3137
|
this._progress = ratio;
|
|
2088
3138
|
bus.emit("scene:loading:progress", { scene: this, ratio });
|
|
2089
3139
|
});
|
|
2090
|
-
if (!this._active || attempt !== this._attempt)
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
if (remaining > 0) {
|
|
2094
|
-
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
2095
|
-
if (!this._active || attempt !== this._attempt) return;
|
|
3140
|
+
if (!this._active || attempt !== this._attempt) {
|
|
3141
|
+
minDuration.cancel();
|
|
3142
|
+
return;
|
|
2096
3143
|
}
|
|
3144
|
+
await minDuration.promise;
|
|
3145
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
2097
3146
|
} catch (err) {
|
|
3147
|
+
minDuration.cancel();
|
|
2098
3148
|
if (!this._active || attempt !== this._attempt) return;
|
|
2099
3149
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
2100
3150
|
this._started = false;
|
|
@@ -2118,6 +3168,26 @@ var LoadingScene = class extends Scene {
|
|
|
2118
3168
|
this.transition ? { transition: this.transition } : void 0
|
|
2119
3169
|
);
|
|
2120
3170
|
}
|
|
3171
|
+
_createEngineTimeDelay(ms) {
|
|
3172
|
+
if (ms <= 0) {
|
|
3173
|
+
return {
|
|
3174
|
+
promise: Promise.resolve(),
|
|
3175
|
+
cancel: /* @__PURE__ */ __name(() => {
|
|
3176
|
+
}, "cancel")
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
const wait = Process.delay(ms);
|
|
3180
|
+
this._pendingWaits.add(wait);
|
|
3181
|
+
this.context.resolve(ProcessSystemKey).add(wait);
|
|
3182
|
+
return {
|
|
3183
|
+
promise: wait.toPromise().finally(() => {
|
|
3184
|
+
this._pendingWaits.delete(wait);
|
|
3185
|
+
}),
|
|
3186
|
+
cancel: /* @__PURE__ */ __name(() => {
|
|
3187
|
+
wait.cancel();
|
|
3188
|
+
}, "cancel")
|
|
3189
|
+
};
|
|
3190
|
+
}
|
|
2121
3191
|
};
|
|
2122
3192
|
|
|
2123
3193
|
// src/SceneTransition.ts
|
|
@@ -2526,174 +3596,52 @@ var SceneManager = class {
|
|
|
2526
3596
|
toScene: run.toScene
|
|
2527
3597
|
};
|
|
2528
3598
|
}
|
|
2529
|
-
_snapshotPauseStates() {
|
|
2530
|
-
return new Map(
|
|
2531
|
-
this.stack.map((scene) => [scene, scene.isPaused])
|
|
2532
|
-
);
|
|
2533
|
-
}
|
|
2534
|
-
_assertNotMutating(method) {
|
|
2535
|
-
if (this._mutationDepth === 0) return;
|
|
2536
|
-
throw new Error(
|
|
2537
|
-
`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().`
|
|
2538
|
-
);
|
|
2539
|
-
}
|
|
2540
|
-
async _withMutation(work) {
|
|
2541
|
-
this._mutationDepth++;
|
|
2542
|
-
try {
|
|
2543
|
-
return await work();
|
|
2544
|
-
} finally {
|
|
2545
|
-
this._mutationDepth--;
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
_withMutationSync(work) {
|
|
2549
|
-
this._mutationDepth++;
|
|
2550
|
-
try {
|
|
2551
|
-
return work();
|
|
2552
|
-
} finally {
|
|
2553
|
-
this._mutationDepth--;
|
|
2554
|
-
}
|
|
2555
|
-
}
|
|
2556
|
-
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
2557
|
-
_firePauseTransitions(wasPaused) {
|
|
2558
|
-
for (const scene of this.stack) {
|
|
2559
|
-
const was = wasPaused.get(scene) ?? false;
|
|
2560
|
-
if (scene.isPaused && !was) {
|
|
2561
|
-
scene.onPause?.();
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
}
|
|
2565
|
-
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
2566
|
-
_fireResumeTransitions(wasPaused) {
|
|
2567
|
-
for (const scene of this.stack) {
|
|
2568
|
-
const was = wasPaused.get(scene) ?? false;
|
|
2569
|
-
if (!scene.isPaused && was) {
|
|
2570
|
-
scene.onResume?.();
|
|
2571
|
-
}
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
};
|
|
2575
|
-
|
|
2576
|
-
// src/Process.ts
|
|
2577
|
-
var Process = class _Process {
|
|
2578
|
-
static {
|
|
2579
|
-
__name(this, "Process");
|
|
2580
|
-
}
|
|
2581
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
2582
|
-
updateFn;
|
|
2583
|
-
onCompleteFn;
|
|
2584
|
-
duration;
|
|
2585
|
-
loop;
|
|
2586
|
-
/** Tags for filtering/grouping. */
|
|
2587
|
-
tags;
|
|
2588
|
-
elapsed = 0;
|
|
2589
|
-
_completed = false;
|
|
2590
|
-
_paused = false;
|
|
2591
|
-
_cancelled = false;
|
|
2592
|
-
resolvePromise;
|
|
2593
|
-
/** Create a timer that fires `onComplete` after `duration` ms. */
|
|
2594
|
-
static delay(duration, onComplete, tags) {
|
|
2595
|
-
const opts = { duration };
|
|
2596
|
-
if (onComplete !== void 0) opts.onComplete = onComplete;
|
|
2597
|
-
if (tags !== void 0) opts.tags = tags;
|
|
2598
|
-
return new _Process(opts);
|
|
2599
|
-
}
|
|
2600
|
-
constructor(options) {
|
|
2601
|
-
this.updateFn = options.update ?? (() => {
|
|
2602
|
-
});
|
|
2603
|
-
this.onCompleteFn = options.onComplete;
|
|
2604
|
-
this.duration = options.duration;
|
|
2605
|
-
this.loop = options.loop ?? false;
|
|
2606
|
-
this.tags = options.tags ?? [];
|
|
2607
|
-
}
|
|
2608
|
-
/** Whether the process has completed. */
|
|
2609
|
-
get completed() {
|
|
2610
|
-
return this._completed;
|
|
2611
|
-
}
|
|
2612
|
-
/** Whether the process is paused. */
|
|
2613
|
-
get paused() {
|
|
2614
|
-
return this._paused;
|
|
2615
|
-
}
|
|
2616
|
-
/** Pause the process. */
|
|
2617
|
-
pause() {
|
|
2618
|
-
this._paused = true;
|
|
2619
|
-
}
|
|
2620
|
-
/** Resume the process. */
|
|
2621
|
-
resume() {
|
|
2622
|
-
this._paused = false;
|
|
3599
|
+
_snapshotPauseStates() {
|
|
3600
|
+
return new Map(
|
|
3601
|
+
this.stack.map((scene) => [scene, scene.isPaused])
|
|
3602
|
+
);
|
|
2623
3603
|
}
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
3604
|
+
_assertNotMutating(method) {
|
|
3605
|
+
if (this._mutationDepth === 0) return;
|
|
3606
|
+
throw new Error(
|
|
3607
|
+
`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().`
|
|
3608
|
+
);
|
|
2629
3609
|
}
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
3610
|
+
async _withMutation(work) {
|
|
3611
|
+
this._mutationDepth++;
|
|
3612
|
+
try {
|
|
3613
|
+
return await work();
|
|
3614
|
+
} finally {
|
|
3615
|
+
this._mutationDepth--;
|
|
3616
|
+
}
|
|
2636
3617
|
}
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
this.elapsed += dt;
|
|
2644
|
-
if (this.duration !== void 0 && this.elapsed >= this.duration) {
|
|
2645
|
-
const result2 = this.updateFn(dt, this.elapsed);
|
|
2646
|
-
if (this.loop && result2 !== true) {
|
|
2647
|
-
this.elapsed = this.elapsed % this.duration;
|
|
2648
|
-
return;
|
|
2649
|
-
}
|
|
2650
|
-
this.complete();
|
|
2651
|
-
return;
|
|
3618
|
+
_withMutationSync(work) {
|
|
3619
|
+
this._mutationDepth++;
|
|
3620
|
+
try {
|
|
3621
|
+
return work();
|
|
3622
|
+
} finally {
|
|
3623
|
+
this._mutationDepth--;
|
|
2652
3624
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
3625
|
+
}
|
|
3626
|
+
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
3627
|
+
_firePauseTransitions(wasPaused) {
|
|
3628
|
+
for (const scene of this.stack) {
|
|
3629
|
+
const was = wasPaused.get(scene) ?? false;
|
|
3630
|
+
if (scene.isPaused && !was) {
|
|
3631
|
+
scene.onPause?.();
|
|
2658
3632
|
}
|
|
2659
|
-
this.complete();
|
|
2660
3633
|
}
|
|
2661
3634
|
}
|
|
2662
|
-
/**
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
this._cancelled = false;
|
|
2671
|
-
delete this.resolvePromise;
|
|
2672
|
-
}
|
|
2673
|
-
complete() {
|
|
2674
|
-
this._completed = true;
|
|
2675
|
-
this.onCompleteFn?.();
|
|
2676
|
-
this.resolvePromise?.();
|
|
3635
|
+
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
3636
|
+
_fireResumeTransitions(wasPaused) {
|
|
3637
|
+
for (const scene of this.stack) {
|
|
3638
|
+
const was = wasPaused.get(scene) ?? false;
|
|
3639
|
+
if (!scene.isPaused && was) {
|
|
3640
|
+
scene.onResume?.();
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
2677
3643
|
}
|
|
2678
3644
|
};
|
|
2679
|
-
var easeLinear = /* @__PURE__ */ __name((t) => t, "easeLinear");
|
|
2680
|
-
var easeInQuad = /* @__PURE__ */ __name((t) => t * t, "easeInQuad");
|
|
2681
|
-
var easeOutQuad = /* @__PURE__ */ __name((t) => t * (2 - t), "easeOutQuad");
|
|
2682
|
-
var easeInOutQuad = /* @__PURE__ */ __name((t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, "easeInOutQuad");
|
|
2683
|
-
var easeOutBounce = /* @__PURE__ */ __name((t) => {
|
|
2684
|
-
if (t < 1 / 2.75) {
|
|
2685
|
-
return 7.5625 * t * t;
|
|
2686
|
-
} else if (t < 2 / 2.75) {
|
|
2687
|
-
const t2 = t - 1.5 / 2.75;
|
|
2688
|
-
return 7.5625 * t2 * t2 + 0.75;
|
|
2689
|
-
} else if (t < 2.5 / 2.75) {
|
|
2690
|
-
const t2 = t - 2.25 / 2.75;
|
|
2691
|
-
return 7.5625 * t2 * t2 + 0.9375;
|
|
2692
|
-
} else {
|
|
2693
|
-
const t2 = t - 2.625 / 2.75;
|
|
2694
|
-
return 7.5625 * t2 * t2 + 0.984375;
|
|
2695
|
-
}
|
|
2696
|
-
}, "easeOutBounce");
|
|
2697
3645
|
|
|
2698
3646
|
// src/Tween.ts
|
|
2699
3647
|
var Tween = {
|
|
@@ -2920,7 +3868,9 @@ var ProcessSlot = class {
|
|
|
2920
3868
|
};
|
|
2921
3869
|
|
|
2922
3870
|
// src/ProcessComponent.ts
|
|
2923
|
-
var
|
|
3871
|
+
var _ProcessComponent_decorators, _init2, _a2;
|
|
3872
|
+
_ProcessComponent_decorators = [serializable];
|
|
3873
|
+
var ProcessComponent = class extends (_a2 = Component) {
|
|
2924
3874
|
static {
|
|
2925
3875
|
__name(this, "ProcessComponent");
|
|
2926
3876
|
}
|
|
@@ -2990,10 +3940,18 @@ var ProcessComponent = class extends Component {
|
|
|
2990
3940
|
onDestroy() {
|
|
2991
3941
|
this.cancel();
|
|
2992
3942
|
}
|
|
3943
|
+
serialize() {
|
|
3944
|
+
return null;
|
|
3945
|
+
}
|
|
2993
3946
|
};
|
|
3947
|
+
_init2 = __decoratorStart(_a2);
|
|
3948
|
+
ProcessComponent = __decorateElement(_init2, 0, "ProcessComponent", _ProcessComponent_decorators, ProcessComponent);
|
|
3949
|
+
__runInitializers(_init2, 1, ProcessComponent);
|
|
2994
3950
|
|
|
2995
3951
|
// src/KeyframeAnimator.ts
|
|
2996
|
-
var
|
|
3952
|
+
var _KeyframeAnimator_decorators, _init3, _a3;
|
|
3953
|
+
_KeyframeAnimator_decorators = [serializable];
|
|
3954
|
+
var KeyframeAnimator = class extends (_a3 = Component) {
|
|
2997
3955
|
static {
|
|
2998
3956
|
__name(this, "KeyframeAnimator");
|
|
2999
3957
|
}
|
|
@@ -3045,6 +4003,9 @@ var KeyframeAnimator = class extends Component {
|
|
|
3045
4003
|
onDestroy() {
|
|
3046
4004
|
this.stopAll();
|
|
3047
4005
|
}
|
|
4006
|
+
serialize() {
|
|
4007
|
+
return null;
|
|
4008
|
+
}
|
|
3048
4009
|
stopInternal(name, complete) {
|
|
3049
4010
|
const process = this.active.get(name);
|
|
3050
4011
|
if (!process) return;
|
|
@@ -3053,6 +4014,9 @@ var KeyframeAnimator = class extends Component {
|
|
|
3053
4014
|
this.defs[name]?.onExit?.(complete);
|
|
3054
4015
|
}
|
|
3055
4016
|
};
|
|
4017
|
+
_init3 = __decoratorStart(_a3);
|
|
4018
|
+
KeyframeAnimator = __decorateElement(_init3, 0, "KeyframeAnimator", _KeyframeAnimator_decorators, KeyframeAnimator);
|
|
4019
|
+
__runInitializers(_init3, 1, KeyframeAnimator);
|
|
3056
4020
|
|
|
3057
4021
|
// src/Sequence.ts
|
|
3058
4022
|
var Sequence = class {
|
|
@@ -3204,36 +4168,94 @@ var ProcessSystem = class extends System {
|
|
|
3204
4168
|
/** Global time scale multiplier. Stacks multiplicatively with per-scene timeScale. */
|
|
3205
4169
|
timeScale = 1;
|
|
3206
4170
|
sceneManager;
|
|
3207
|
-
|
|
4171
|
+
globalProcesses = /* @__PURE__ */ new Set();
|
|
4172
|
+
scenePools = /* @__PURE__ */ new Map();
|
|
4173
|
+
_unregisterSceneHook = null;
|
|
3208
4174
|
onRegister(context) {
|
|
3209
4175
|
this.sceneManager = context.resolve(SceneManagerKey);
|
|
4176
|
+
const hooks = context.tryResolve(SceneHookRegistryKey);
|
|
4177
|
+
this._unregisterSceneHook = hooks?.register({
|
|
4178
|
+
afterExit: /* @__PURE__ */ __name((scene) => this.cancelForScene(scene), "afterExit")
|
|
4179
|
+
}) ?? null;
|
|
4180
|
+
}
|
|
4181
|
+
onUnregister() {
|
|
4182
|
+
this._unregisterSceneHook?.();
|
|
4183
|
+
this._unregisterSceneHook = null;
|
|
4184
|
+
for (const p of this.globalProcesses) {
|
|
4185
|
+
if (!p.completed) p.cancel();
|
|
4186
|
+
}
|
|
4187
|
+
this.globalProcesses.clear();
|
|
4188
|
+
for (const pool of this.scenePools.values()) {
|
|
4189
|
+
for (const p of pool) {
|
|
4190
|
+
if (!p.completed) p.cancel();
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
this.scenePools.clear();
|
|
3210
4194
|
}
|
|
3211
|
-
/**
|
|
4195
|
+
/**
|
|
4196
|
+
* Add an engine-global process. Ticked under the global timeScale only;
|
|
4197
|
+
* NOT gated by per-scene pause or scaled by per-scene timeScale. Use this
|
|
4198
|
+
* for cross-scene effects (e.g. screen-scope filter fades on `app.stage`)
|
|
4199
|
+
* or processes that have no owning scene.
|
|
4200
|
+
*/
|
|
3212
4201
|
add(process) {
|
|
3213
|
-
this.
|
|
4202
|
+
this.globalProcesses.add(process);
|
|
4203
|
+
return process;
|
|
4204
|
+
}
|
|
4205
|
+
/**
|
|
4206
|
+
* Add a process bound to a specific scene's lifecycle. Ticked only while
|
|
4207
|
+
* the scene is active (not paused) and scaled by the scene's `timeScale`,
|
|
4208
|
+
* exactly like an entity-owned `ProcessComponent`. Use this for layer or
|
|
4209
|
+
* scene-scope effect fades that should pause with the scene.
|
|
4210
|
+
*/
|
|
4211
|
+
addForScene(scene, process) {
|
|
4212
|
+
let pool = this.scenePools.get(scene);
|
|
4213
|
+
if (!pool) {
|
|
4214
|
+
pool = /* @__PURE__ */ new Set();
|
|
4215
|
+
this.scenePools.set(scene, pool);
|
|
4216
|
+
}
|
|
4217
|
+
pool.add(process);
|
|
3214
4218
|
return process;
|
|
3215
4219
|
}
|
|
3216
|
-
/** Cancel
|
|
4220
|
+
/** Cancel engine-global processes, optionally by tag. */
|
|
3217
4221
|
cancel(tag) {
|
|
3218
|
-
for (const p of this.
|
|
4222
|
+
for (const p of this.globalProcesses) {
|
|
3219
4223
|
if (tag === void 0 || p.tags.includes(tag)) {
|
|
3220
4224
|
p.cancel();
|
|
4225
|
+
this.globalProcesses.delete(p);
|
|
3221
4226
|
}
|
|
3222
4227
|
}
|
|
3223
|
-
|
|
3224
|
-
|
|
4228
|
+
}
|
|
4229
|
+
/** Cancel every scene-bound process for `scene`, optionally by tag. */
|
|
4230
|
+
cancelForScene(scene, tag) {
|
|
4231
|
+
const pool = this.scenePools.get(scene);
|
|
4232
|
+
if (!pool) return;
|
|
4233
|
+
for (const p of pool) {
|
|
4234
|
+
if (tag === void 0 || p.tags.includes(tag)) {
|
|
4235
|
+
p.cancel();
|
|
4236
|
+
pool.delete(p);
|
|
4237
|
+
}
|
|
3225
4238
|
}
|
|
4239
|
+
if (pool.size === 0) this.scenePools.delete(scene);
|
|
3226
4240
|
}
|
|
3227
4241
|
update(dt) {
|
|
3228
4242
|
const globalScaledDt = dt * this.timeScale;
|
|
3229
|
-
for (const p of this.
|
|
4243
|
+
for (const p of this.globalProcesses) {
|
|
3230
4244
|
p._update(globalScaledDt);
|
|
3231
4245
|
if (p.completed) {
|
|
3232
|
-
this.
|
|
4246
|
+
this.globalProcesses.delete(p);
|
|
3233
4247
|
}
|
|
3234
4248
|
}
|
|
3235
4249
|
for (const scene of this.sceneManager.activeScenes) {
|
|
3236
4250
|
const effectiveDt = globalScaledDt * scene.timeScale;
|
|
4251
|
+
const pool = this.scenePools.get(scene);
|
|
4252
|
+
if (pool) {
|
|
4253
|
+
for (const p of pool) {
|
|
4254
|
+
p._update(effectiveDt);
|
|
4255
|
+
if (p.completed) pool.delete(p);
|
|
4256
|
+
}
|
|
4257
|
+
if (pool.size === 0) this.scenePools.delete(scene);
|
|
4258
|
+
}
|
|
3237
4259
|
for (const entity of scene.getEntities()) {
|
|
3238
4260
|
if (entity.isDestroyed) continue;
|
|
3239
4261
|
const pc = entity.tryGet(ProcessComponent);
|
|
@@ -3244,145 +4266,45 @@ var ProcessSystem = class extends System {
|
|
|
3244
4266
|
}
|
|
3245
4267
|
};
|
|
3246
4268
|
|
|
3247
|
-
// src/
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
this.engine = engine;
|
|
3255
|
-
}
|
|
3256
|
-
/** Full state snapshot (serializable). */
|
|
3257
|
-
snapshot() {
|
|
3258
|
-
return {
|
|
3259
|
-
frameCount: this.engine.loop.frameCount,
|
|
3260
|
-
sceneStack: this.getSceneStack(),
|
|
3261
|
-
entityCount: this.countEntities(),
|
|
3262
|
-
systemCount: this.getSystems().length,
|
|
3263
|
-
errors: this.getErrors()
|
|
3264
|
-
};
|
|
3265
|
-
}
|
|
3266
|
-
/** Find entity by name in the active scene. */
|
|
3267
|
-
getEntityByName(name) {
|
|
3268
|
-
const entity = this.findActiveEntity(name);
|
|
3269
|
-
if (!entity) return void 0;
|
|
3270
|
-
return this.entityToSnapshot(entity);
|
|
3271
|
-
}
|
|
3272
|
-
/** Get entity position (from Transform component). */
|
|
3273
|
-
getEntityPosition(name) {
|
|
3274
|
-
const entity = this.findActiveEntity(name);
|
|
3275
|
-
if (!entity) return void 0;
|
|
3276
|
-
const transform = this.getTransform(entity);
|
|
3277
|
-
if (!transform) return void 0;
|
|
3278
|
-
return { x: transform.position.x, y: transform.position.y };
|
|
3279
|
-
}
|
|
3280
|
-
/** Check if an entity has a component by class name string. */
|
|
3281
|
-
hasComponent(entityName, componentClass) {
|
|
3282
|
-
return this.findComponentByName(entityName, componentClass) !== void 0;
|
|
3283
|
-
}
|
|
3284
|
-
/** Get component data (serializable subset) by class name string. */
|
|
3285
|
-
getComponentData(entityName, componentClass) {
|
|
3286
|
-
const comp = this.findComponentByName(entityName, componentClass);
|
|
3287
|
-
if (!comp) return void 0;
|
|
3288
|
-
return this.serializeComponent(comp);
|
|
3289
|
-
}
|
|
3290
|
-
/** Get all entities in the active scene as snapshots. */
|
|
3291
|
-
getEntities() {
|
|
3292
|
-
const scene = this.engine.scenes.active;
|
|
3293
|
-
if (!scene) return [];
|
|
3294
|
-
const result = [];
|
|
3295
|
-
for (const entity of scene.getEntities()) {
|
|
3296
|
-
if (!entity.isDestroyed) {
|
|
3297
|
-
result.push(this.entityToSnapshot(entity));
|
|
4269
|
+
// src/ProcessQueue.ts
|
|
4270
|
+
function makeQueue(route) {
|
|
4271
|
+
const ours = /* @__PURE__ */ new Set();
|
|
4272
|
+
return {
|
|
4273
|
+
run(p) {
|
|
4274
|
+
for (const old of ours) {
|
|
4275
|
+
if (old.completed) ours.delete(old);
|
|
3298
4276
|
}
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
entityCount: scene.getEntities().size,
|
|
3307
|
-
paused: scene.isPaused
|
|
3308
|
-
}));
|
|
3309
|
-
}
|
|
3310
|
-
/** Get active system info. */
|
|
3311
|
-
getSystems() {
|
|
3312
|
-
const scheduler = this.engine.context.tryResolve(SystemSchedulerKey);
|
|
3313
|
-
if (!scheduler) return [];
|
|
3314
|
-
return scheduler.getAllSystems().map((sys) => ({
|
|
3315
|
-
name: sys.constructor.name,
|
|
3316
|
-
phase: sys.phase,
|
|
3317
|
-
priority: sys.priority,
|
|
3318
|
-
enabled: sys.enabled
|
|
3319
|
-
}));
|
|
3320
|
-
}
|
|
3321
|
-
/** Get disabled components/systems from error boundary. */
|
|
3322
|
-
getErrors() {
|
|
3323
|
-
const boundary = this.engine.context.tryResolve(ErrorBoundaryKey);
|
|
3324
|
-
if (!boundary) return { disabledSystems: [], disabledComponents: [] };
|
|
3325
|
-
const disabled = boundary.getDisabled();
|
|
3326
|
-
return {
|
|
3327
|
-
disabledSystems: disabled.systems.map(
|
|
3328
|
-
(s) => s.system.constructor.name
|
|
3329
|
-
),
|
|
3330
|
-
disabledComponents: disabled.components.map((c) => ({
|
|
3331
|
-
entity: c.component.entity?.name ?? "unknown",
|
|
3332
|
-
component: c.component.constructor.name,
|
|
3333
|
-
error: c.error
|
|
3334
|
-
}))
|
|
3335
|
-
};
|
|
3336
|
-
}
|
|
3337
|
-
findActiveEntity(name) {
|
|
3338
|
-
return this.engine.scenes.active?.findEntity(name);
|
|
3339
|
-
}
|
|
3340
|
-
findComponentByName(entityName, componentClass) {
|
|
3341
|
-
const entity = this.findActiveEntity(entityName);
|
|
3342
|
-
if (!entity) return void 0;
|
|
3343
|
-
for (const comp of entity.getAll()) {
|
|
3344
|
-
if (comp.constructor.name === componentClass) return comp;
|
|
3345
|
-
}
|
|
3346
|
-
return void 0;
|
|
3347
|
-
}
|
|
3348
|
-
entityToSnapshot(entity) {
|
|
3349
|
-
const transform = this.getTransform(entity);
|
|
3350
|
-
const snapshot = {
|
|
3351
|
-
id: entity.id,
|
|
3352
|
-
name: entity.name,
|
|
3353
|
-
tags: [...entity.tags],
|
|
3354
|
-
components: [...entity.getAll()].map((c) => c.constructor.name)
|
|
3355
|
-
};
|
|
3356
|
-
if (transform) {
|
|
3357
|
-
snapshot.position = {
|
|
3358
|
-
x: transform.position.x,
|
|
3359
|
-
y: transform.position.y
|
|
3360
|
-
};
|
|
3361
|
-
}
|
|
3362
|
-
return snapshot;
|
|
3363
|
-
}
|
|
3364
|
-
getTransform(entity) {
|
|
3365
|
-
return entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
3366
|
-
}
|
|
3367
|
-
serializeComponent(comp) {
|
|
3368
|
-
const result = {};
|
|
3369
|
-
for (const key of Object.getOwnPropertyNames(comp)) {
|
|
3370
|
-
if (key === "entity") continue;
|
|
3371
|
-
const value = comp[key];
|
|
3372
|
-
if (typeof value !== "function") {
|
|
3373
|
-
result[key] = value;
|
|
4277
|
+
route(p);
|
|
4278
|
+
ours.add(p);
|
|
4279
|
+
return p;
|
|
4280
|
+
},
|
|
4281
|
+
cancelAll() {
|
|
4282
|
+
for (const p of ours) {
|
|
4283
|
+
if (!p.completed) p.cancel();
|
|
3374
4284
|
}
|
|
4285
|
+
ours.clear();
|
|
3375
4286
|
}
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
4287
|
+
};
|
|
4288
|
+
}
|
|
4289
|
+
__name(makeQueue, "makeQueue");
|
|
4290
|
+
function makeEntityScopedQueue(entity) {
|
|
4291
|
+
return makeQueue((p) => {
|
|
4292
|
+
let pc = entity.tryGet(ProcessComponent);
|
|
4293
|
+
if (!pc) {
|
|
4294
|
+
pc = entity.add(new ProcessComponent());
|
|
4295
|
+
}
|
|
4296
|
+
pc.run(p);
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4299
|
+
__name(makeEntityScopedQueue, "makeEntityScopedQueue");
|
|
4300
|
+
function makeSceneScopedQueue(processSystem, scene) {
|
|
4301
|
+
return makeQueue((p) => processSystem.addForScene(scene, p));
|
|
4302
|
+
}
|
|
4303
|
+
__name(makeSceneScopedQueue, "makeSceneScopedQueue");
|
|
4304
|
+
function makeGlobalScopedQueue(processSystem) {
|
|
4305
|
+
return makeQueue((p) => processSystem.add(p));
|
|
4306
|
+
}
|
|
4307
|
+
__name(makeGlobalScopedQueue, "makeGlobalScopedQueue");
|
|
3386
4308
|
|
|
3387
4309
|
// src/Engine.ts
|
|
3388
4310
|
var Engine = class {
|
|
@@ -3436,6 +4358,15 @@ var Engine = class {
|
|
|
3436
4358
|
this.context.register(SystemSchedulerKey, this.scheduler);
|
|
3437
4359
|
this.context.register(AssetManagerKey, this.assets);
|
|
3438
4360
|
this.context.register(SceneHookRegistryKey, this.sceneHooks);
|
|
4361
|
+
this.sceneHooks.register({
|
|
4362
|
+
beforeEnter: /* @__PURE__ */ __name((scene) => {
|
|
4363
|
+
scene._registerScoped(RandomKey, this.inspector.createSceneRandom());
|
|
4364
|
+
this.inspector.attachSceneEventObserver(scene);
|
|
4365
|
+
}, "beforeEnter"),
|
|
4366
|
+
afterExit: /* @__PURE__ */ __name((scene) => {
|
|
4367
|
+
this.inspector.detachSceneEventObserver(scene);
|
|
4368
|
+
}, "afterExit")
|
|
4369
|
+
});
|
|
3439
4370
|
this.scenes._setContext(this.context);
|
|
3440
4371
|
this.registerBuiltInSystems();
|
|
3441
4372
|
this.loop.setCallbacks({
|
|
@@ -3517,6 +4448,7 @@ var Engine = class {
|
|
|
3517
4448
|
if (this.debug && typeof globalThis !== "undefined" && "__yage__" in globalThis) {
|
|
3518
4449
|
delete globalThis["__yage__"];
|
|
3519
4450
|
}
|
|
4451
|
+
this.inspector.dispose();
|
|
3520
4452
|
this.events.clear();
|
|
3521
4453
|
this.started = false;
|
|
3522
4454
|
}
|
|
@@ -3584,6 +4516,21 @@ var RendererAdapterKey = new ServiceKey(
|
|
|
3584
4516
|
"rendererAdapter"
|
|
3585
4517
|
);
|
|
3586
4518
|
|
|
4519
|
+
// src/ui-consume-registry.ts
|
|
4520
|
+
var registry2 = /* @__PURE__ */ new WeakSet();
|
|
4521
|
+
function markPointerConsumeContainer(container) {
|
|
4522
|
+
registry2.add(container);
|
|
4523
|
+
}
|
|
4524
|
+
__name(markPointerConsumeContainer, "markPointerConsumeContainer");
|
|
4525
|
+
function unmarkPointerConsumeContainer(container) {
|
|
4526
|
+
registry2.delete(container);
|
|
4527
|
+
}
|
|
4528
|
+
__name(unmarkPointerConsumeContainer, "unmarkPointerConsumeContainer");
|
|
4529
|
+
function isPointerConsumeContainer(container) {
|
|
4530
|
+
return registry2.has(container);
|
|
4531
|
+
}
|
|
4532
|
+
__name(isPointerConsumeContainer, "isPointerConsumeContainer");
|
|
4533
|
+
|
|
3587
4534
|
// src/test-utils.ts
|
|
3588
4535
|
var _TestScene = class extends Scene {
|
|
3589
4536
|
static {
|
|
@@ -3614,6 +4561,7 @@ function createMockScene(name = "mock-scene") {
|
|
|
3614
4561
|
ctx.register(ErrorBoundaryKey, boundary);
|
|
3615
4562
|
const scene = new _TestScene(name);
|
|
3616
4563
|
scene._setContext(ctx);
|
|
4564
|
+
scene._registerScoped(RandomKey, createRandomService(1234));
|
|
3617
4565
|
return { scene, context: ctx };
|
|
3618
4566
|
}
|
|
3619
4567
|
__name(createMockScene, "createMockScene");
|
|
@@ -3668,6 +4616,7 @@ var VERSION = "0.0.0";
|
|
|
3668
4616
|
QueryCache,
|
|
3669
4617
|
QueryCacheKey,
|
|
3670
4618
|
QueryResult,
|
|
4619
|
+
RandomKey,
|
|
3671
4620
|
RendererAdapterKey,
|
|
3672
4621
|
SERIALIZABLE_KEY,
|
|
3673
4622
|
Scene,
|
|
@@ -3689,9 +4638,11 @@ var VERSION = "0.0.0";
|
|
|
3689
4638
|
Vec2,
|
|
3690
4639
|
_resetEntityIdCounter,
|
|
3691
4640
|
advanceFrames,
|
|
4641
|
+
createDefaultRandomSeed,
|
|
3692
4642
|
createKeyframeTrack,
|
|
3693
4643
|
createMockEntity,
|
|
3694
4644
|
createMockScene,
|
|
4645
|
+
createRandomService,
|
|
3695
4646
|
createTestEngine,
|
|
3696
4647
|
defineBlueprint,
|
|
3697
4648
|
defineEvent,
|
|
@@ -3703,10 +4654,18 @@ var VERSION = "0.0.0";
|
|
|
3703
4654
|
easeOutQuad,
|
|
3704
4655
|
filterEntities,
|
|
3705
4656
|
getSerializableType,
|
|
4657
|
+
globalRandom,
|
|
3706
4658
|
interpolate,
|
|
4659
|
+
isPointerConsumeContainer,
|
|
3707
4660
|
isSerializable,
|
|
4661
|
+
makeEntityScopedQueue,
|
|
4662
|
+
makeGlobalScopedQueue,
|
|
4663
|
+
makeSceneScopedQueue,
|
|
4664
|
+
markPointerConsumeContainer,
|
|
4665
|
+
normalizeSeed,
|
|
3708
4666
|
resolveTransition,
|
|
3709
4667
|
serializable,
|
|
3710
|
-
trait
|
|
4668
|
+
trait,
|
|
4669
|
+
unmarkPointerConsumeContainer
|
|
3711
4670
|
});
|
|
3712
4671
|
//# sourceMappingURL=index.cjs.map
|