@yagejs/core 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1504 -446
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +895 -485
- package/dist/index.d.ts +895 -485
- package/dist/index.js +1496 -447
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -99,6 +99,8 @@ __export(index_exports, {
|
|
|
99
99
|
QueryCache: () => QueryCache,
|
|
100
100
|
QueryCacheKey: () => QueryCacheKey,
|
|
101
101
|
QueryResult: () => QueryResult,
|
|
102
|
+
RandomKey: () => RandomKey,
|
|
103
|
+
RendererAdapterKey: () => RendererAdapterKey,
|
|
102
104
|
SERIALIZABLE_KEY: () => SERIALIZABLE_KEY,
|
|
103
105
|
Scene: () => Scene,
|
|
104
106
|
SceneHookRegistry: () => SceneHookRegistry,
|
|
@@ -119,9 +121,11 @@ __export(index_exports, {
|
|
|
119
121
|
Vec2: () => Vec2,
|
|
120
122
|
_resetEntityIdCounter: () => _resetEntityIdCounter,
|
|
121
123
|
advanceFrames: () => advanceFrames,
|
|
124
|
+
createDefaultRandomSeed: () => createDefaultRandomSeed,
|
|
122
125
|
createKeyframeTrack: () => createKeyframeTrack,
|
|
123
126
|
createMockEntity: () => createMockEntity,
|
|
124
127
|
createMockScene: () => createMockScene,
|
|
128
|
+
createRandomService: () => createRandomService,
|
|
125
129
|
createTestEngine: () => createTestEngine,
|
|
126
130
|
defineBlueprint: () => defineBlueprint,
|
|
127
131
|
defineEvent: () => defineEvent,
|
|
@@ -133,8 +137,13 @@ __export(index_exports, {
|
|
|
133
137
|
easeOutQuad: () => easeOutQuad,
|
|
134
138
|
filterEntities: () => filterEntities,
|
|
135
139
|
getSerializableType: () => getSerializableType,
|
|
140
|
+
globalRandom: () => globalRandom,
|
|
136
141
|
interpolate: () => interpolate,
|
|
137
142
|
isSerializable: () => isSerializable,
|
|
143
|
+
makeEntityScopedQueue: () => makeEntityScopedQueue,
|
|
144
|
+
makeGlobalScopedQueue: () => makeGlobalScopedQueue,
|
|
145
|
+
makeSceneScopedQueue: () => makeSceneScopedQueue,
|
|
146
|
+
normalizeSeed: () => normalizeSeed,
|
|
138
147
|
resolveTransition: () => resolveTransition,
|
|
139
148
|
serializable: () => serializable,
|
|
140
149
|
trait: () => trait
|
|
@@ -265,14 +274,52 @@ var Vec2 = class _Vec2 {
|
|
|
265
274
|
static lerp(a, b, t) {
|
|
266
275
|
return new _Vec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
|
|
267
276
|
}
|
|
277
|
+
/** Move current toward target by at most maxDelta without overshooting. */
|
|
278
|
+
static moveTowards(current, target, maxDelta) {
|
|
279
|
+
const dx = target.x - current.x;
|
|
280
|
+
const dy = target.y - current.y;
|
|
281
|
+
const distanceSq = dx * dx + dy * dy;
|
|
282
|
+
if (distanceSq < EPSILON * EPSILON) {
|
|
283
|
+
return new _Vec2(target.x, target.y);
|
|
284
|
+
}
|
|
285
|
+
if (maxDelta <= 0) {
|
|
286
|
+
return new _Vec2(current.x, current.y);
|
|
287
|
+
}
|
|
288
|
+
const distance = Math.sqrt(distanceSq);
|
|
289
|
+
if (distance <= maxDelta) {
|
|
290
|
+
return new _Vec2(target.x, target.y);
|
|
291
|
+
}
|
|
292
|
+
const scale = maxDelta / distance;
|
|
293
|
+
return new _Vec2(current.x + dx * scale, current.y + dy * scale);
|
|
294
|
+
}
|
|
268
295
|
};
|
|
269
296
|
|
|
270
297
|
// src/MathUtils.ts
|
|
298
|
+
var TAU = Math.PI * 2;
|
|
299
|
+
var MIN_SMOOTH_TIME = 1e-4;
|
|
300
|
+
function normalizeAngle(radians) {
|
|
301
|
+
const wrapped = ((radians + Math.PI) % TAU + TAU) % TAU - Math.PI;
|
|
302
|
+
return wrapped === -Math.PI && radians > 0 ? Math.PI : wrapped;
|
|
303
|
+
}
|
|
304
|
+
__name(normalizeAngle, "normalizeAngle");
|
|
271
305
|
var MathUtils = {
|
|
272
306
|
/** Linear interpolation between a and b. */
|
|
273
307
|
lerp(a, b, t) {
|
|
274
308
|
return a + (b - a) * t;
|
|
275
309
|
},
|
|
310
|
+
/** Return the clamped interpolation factor that produces v between a and b. */
|
|
311
|
+
inverseLerp(a, b, v) {
|
|
312
|
+
if (a === b) return 0;
|
|
313
|
+
return MathUtils.clamp((v - a) / (b - a), 0, 1);
|
|
314
|
+
},
|
|
315
|
+
/** Interpolate between angles in radians along the shortest path. */
|
|
316
|
+
lerpAngle(a, b, t) {
|
|
317
|
+
return normalizeAngle(a + MathUtils.shortestAngleBetween(a, b) * t);
|
|
318
|
+
},
|
|
319
|
+
/** Signed shortest angular delta from a to b, in radians. */
|
|
320
|
+
shortestAngleBetween(a, b) {
|
|
321
|
+
return normalizeAngle(b - a);
|
|
322
|
+
},
|
|
276
323
|
/** Clamp a value between min and max. */
|
|
277
324
|
clamp(value, min, max) {
|
|
278
325
|
return Math.max(min, Math.min(max, value));
|
|
@@ -282,13 +329,11 @@ var MathUtils = {
|
|
|
282
329
|
const t = (value - inMin) / (inMax - inMin);
|
|
283
330
|
return outMin + (outMax - outMin) * t;
|
|
284
331
|
},
|
|
285
|
-
/**
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
randomInt(min, max) {
|
|
291
|
-
return Math.floor(min + Math.random() * (max - min + 1));
|
|
332
|
+
/** Bounce t between 0 and length. */
|
|
333
|
+
pingPong(t, length) {
|
|
334
|
+
if (length <= 0) return 0;
|
|
335
|
+
const wrapped = MathUtils.wrap(t, 0, length * 2);
|
|
336
|
+
return length - Math.abs(wrapped - length);
|
|
292
337
|
},
|
|
293
338
|
/** Convert degrees to radians. */
|
|
294
339
|
degToRad(degrees) {
|
|
@@ -305,6 +350,34 @@ var MathUtils = {
|
|
|
305
350
|
}
|
|
306
351
|
return Math.max(current - step, target);
|
|
307
352
|
},
|
|
353
|
+
/**
|
|
354
|
+
* Smoothly damp current toward target without overshooting.
|
|
355
|
+
* Pass the returned velocity back into the next call.
|
|
356
|
+
*/
|
|
357
|
+
smoothDamp(current, target, velocity, smoothTime, deltaTime, maxSpeed = Infinity) {
|
|
358
|
+
if (deltaTime <= 0) {
|
|
359
|
+
return { value: current, velocity };
|
|
360
|
+
}
|
|
361
|
+
const safeSmoothTime = Math.max(MIN_SMOOTH_TIME, smoothTime);
|
|
362
|
+
const omega = 2 / safeSmoothTime;
|
|
363
|
+
const x = omega * deltaTime;
|
|
364
|
+
const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
|
|
365
|
+
const originalTarget = target;
|
|
366
|
+
const maxChange = maxSpeed * safeSmoothTime;
|
|
367
|
+
const change = MathUtils.clamp(current - target, -maxChange, maxChange);
|
|
368
|
+
const adjustedTarget = current - change;
|
|
369
|
+
const temp = (velocity + omega * change) * deltaTime;
|
|
370
|
+
const nextVelocity = (velocity - omega * temp) * exp;
|
|
371
|
+
let value = adjustedTarget + (change + temp) * exp;
|
|
372
|
+
let resultVelocity = nextVelocity;
|
|
373
|
+
const targetIsAboveCurrent = originalTarget - current > 0;
|
|
374
|
+
const valuePassedTarget = targetIsAboveCurrent ? value > originalTarget : value < originalTarget;
|
|
375
|
+
if (valuePassedTarget) {
|
|
376
|
+
value = originalTarget;
|
|
377
|
+
resultVelocity = 0;
|
|
378
|
+
}
|
|
379
|
+
return { value, velocity: resultVelocity };
|
|
380
|
+
},
|
|
308
381
|
/** Wrap value into the range [min, max). */
|
|
309
382
|
wrap(value, min, max) {
|
|
310
383
|
const range = max - min;
|
|
@@ -312,12 +385,138 @@ var MathUtils = {
|
|
|
312
385
|
}
|
|
313
386
|
};
|
|
314
387
|
|
|
388
|
+
// src/EngineContext.ts
|
|
389
|
+
var ServiceKey = class {
|
|
390
|
+
constructor(id, options) {
|
|
391
|
+
this.id = id;
|
|
392
|
+
this.scope = options?.scope ?? "engine";
|
|
393
|
+
}
|
|
394
|
+
id;
|
|
395
|
+
static {
|
|
396
|
+
__name(this, "ServiceKey");
|
|
397
|
+
}
|
|
398
|
+
/** Declared scope (engine or scene). Defaults to `"engine"`. */
|
|
399
|
+
scope;
|
|
400
|
+
};
|
|
401
|
+
var EngineContext = class {
|
|
402
|
+
static {
|
|
403
|
+
__name(this, "EngineContext");
|
|
404
|
+
}
|
|
405
|
+
services = /* @__PURE__ */ new Map();
|
|
406
|
+
/** Register a service. Throws if the key is already registered. */
|
|
407
|
+
register(key, service) {
|
|
408
|
+
if (this.services.has(key.id)) {
|
|
409
|
+
throw new Error(`Service "${key.id}" is already registered.`);
|
|
410
|
+
}
|
|
411
|
+
this.services.set(key.id, service);
|
|
412
|
+
}
|
|
413
|
+
/** Resolve a service. Throws if not registered. */
|
|
414
|
+
resolve(key) {
|
|
415
|
+
if (!this.services.has(key.id)) {
|
|
416
|
+
throw new Error(`Service "${key.id}" is not registered.`);
|
|
417
|
+
}
|
|
418
|
+
return this.services.get(key.id);
|
|
419
|
+
}
|
|
420
|
+
/** Resolve a service, returning undefined if not registered. */
|
|
421
|
+
tryResolve(key) {
|
|
422
|
+
return this.services.get(key.id);
|
|
423
|
+
}
|
|
424
|
+
/** Remove a registered service. No-op if not registered. */
|
|
425
|
+
unregister(key) {
|
|
426
|
+
this.services.delete(key.id);
|
|
427
|
+
}
|
|
428
|
+
/** Check if a service is registered. */
|
|
429
|
+
has(key) {
|
|
430
|
+
return this.services.has(key.id);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var EngineKey = new ServiceKey("engine");
|
|
434
|
+
var EventBusKey = new ServiceKey("eventBus");
|
|
435
|
+
var SceneManagerKey = new ServiceKey("sceneManager");
|
|
436
|
+
var LoggerKey = new ServiceKey("logger");
|
|
437
|
+
var InspectorKey = new ServiceKey("inspector");
|
|
438
|
+
var QueryCacheKey = new ServiceKey("queryCache");
|
|
439
|
+
var ErrorBoundaryKey = new ServiceKey("errorBoundary");
|
|
440
|
+
var GameLoopKey = new ServiceKey("gameLoop");
|
|
441
|
+
var SystemSchedulerKey = new ServiceKey(
|
|
442
|
+
"systemScheduler"
|
|
443
|
+
);
|
|
444
|
+
var ProcessSystemKey = new ServiceKey("processSystem");
|
|
445
|
+
var AssetManagerKey = new ServiceKey("assetManager");
|
|
446
|
+
|
|
447
|
+
// src/Random.ts
|
|
448
|
+
var RandomKey = new ServiceKey("random", {
|
|
449
|
+
scope: "scene"
|
|
450
|
+
});
|
|
451
|
+
var UINT32_MAX = 4294967296;
|
|
452
|
+
function normalizeSeed(seed) {
|
|
453
|
+
return seed >>> 0;
|
|
454
|
+
}
|
|
455
|
+
__name(normalizeSeed, "normalizeSeed");
|
|
456
|
+
function createDefaultRandomSeed() {
|
|
457
|
+
return normalizeSeed(Date.now() ^ Math.floor(Math.random() * 1e9));
|
|
458
|
+
}
|
|
459
|
+
__name(createDefaultRandomSeed, "createDefaultRandomSeed");
|
|
460
|
+
var Mulberry32Random = class {
|
|
461
|
+
static {
|
|
462
|
+
__name(this, "Mulberry32Random");
|
|
463
|
+
}
|
|
464
|
+
seed;
|
|
465
|
+
state;
|
|
466
|
+
constructor(seed) {
|
|
467
|
+
const normalized = normalizeSeed(seed);
|
|
468
|
+
this.seed = normalized;
|
|
469
|
+
this.state = normalized;
|
|
470
|
+
}
|
|
471
|
+
float() {
|
|
472
|
+
let t = this.state += 1831565813;
|
|
473
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
474
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
475
|
+
return ((t ^ t >>> 14) >>> 0) / UINT32_MAX;
|
|
476
|
+
}
|
|
477
|
+
range(min, max) {
|
|
478
|
+
return min + this.float() * (max - min);
|
|
479
|
+
}
|
|
480
|
+
int(min, max) {
|
|
481
|
+
return Math.floor(this.range(min, max + 1));
|
|
482
|
+
}
|
|
483
|
+
pick(arr) {
|
|
484
|
+
if (arr.length === 0) {
|
|
485
|
+
throw new Error("RandomService.pick() requires a non-empty array.");
|
|
486
|
+
}
|
|
487
|
+
return arr[this.int(0, arr.length - 1)];
|
|
488
|
+
}
|
|
489
|
+
shuffle(arr) {
|
|
490
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
491
|
+
const j = this.int(0, i);
|
|
492
|
+
const tmp = arr[i];
|
|
493
|
+
arr[i] = arr[j];
|
|
494
|
+
arr[j] = tmp;
|
|
495
|
+
}
|
|
496
|
+
return arr;
|
|
497
|
+
}
|
|
498
|
+
setSeed(seed) {
|
|
499
|
+
const normalized = normalizeSeed(seed);
|
|
500
|
+
this.seed = normalized;
|
|
501
|
+
this.state = normalized;
|
|
502
|
+
}
|
|
503
|
+
getSeed() {
|
|
504
|
+
return this.seed;
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
function createRandomService(seed = createDefaultRandomSeed()) {
|
|
508
|
+
return new Mulberry32Random(seed);
|
|
509
|
+
}
|
|
510
|
+
__name(createRandomService, "createRandomService");
|
|
511
|
+
var globalRandom = createRandomService();
|
|
512
|
+
|
|
315
513
|
// src/EventBus.ts
|
|
316
514
|
var EventBus = class {
|
|
317
515
|
static {
|
|
318
516
|
__name(this, "EventBus");
|
|
319
517
|
}
|
|
320
518
|
handlers = /* @__PURE__ */ new Map();
|
|
519
|
+
observers = /* @__PURE__ */ new Set();
|
|
321
520
|
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
322
521
|
on(event, handler) {
|
|
323
522
|
let list = this.handlers.get(event);
|
|
@@ -344,6 +543,12 @@ var EventBus = class {
|
|
|
344
543
|
}
|
|
345
544
|
/** Emit an event. Handlers are called synchronously in registration order. */
|
|
346
545
|
emit(event, data) {
|
|
546
|
+
if (this.observers.size > 0) {
|
|
547
|
+
const observers = [...this.observers];
|
|
548
|
+
for (const observer of observers) {
|
|
549
|
+
observer(event, data);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
347
552
|
const list = this.handlers.get(event);
|
|
348
553
|
if (!list) return;
|
|
349
554
|
const snapshot = [...list];
|
|
@@ -351,6 +556,16 @@ var EventBus = class {
|
|
|
351
556
|
handler(data);
|
|
352
557
|
}
|
|
353
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* Observe every emitted event without affecting handler order or control
|
|
561
|
+
* flow. Used by tooling such as the Inspector event log.
|
|
562
|
+
*/
|
|
563
|
+
tap(observer) {
|
|
564
|
+
this.observers.add(observer);
|
|
565
|
+
return () => {
|
|
566
|
+
this.observers.delete(observer);
|
|
567
|
+
};
|
|
568
|
+
}
|
|
354
569
|
/** Remove all handlers for an event, or all handlers if no event specified. */
|
|
355
570
|
clear(event) {
|
|
356
571
|
if (event !== void 0) {
|
|
@@ -460,63 +675,6 @@ var Logger = class {
|
|
|
460
675
|
}
|
|
461
676
|
};
|
|
462
677
|
|
|
463
|
-
// src/EngineContext.ts
|
|
464
|
-
var ServiceKey = class {
|
|
465
|
-
constructor(id, options) {
|
|
466
|
-
this.id = id;
|
|
467
|
-
this.scope = options?.scope ?? "engine";
|
|
468
|
-
}
|
|
469
|
-
id;
|
|
470
|
-
static {
|
|
471
|
-
__name(this, "ServiceKey");
|
|
472
|
-
}
|
|
473
|
-
/** Declared scope (engine or scene). Defaults to `"engine"`. */
|
|
474
|
-
scope;
|
|
475
|
-
};
|
|
476
|
-
var EngineContext = class {
|
|
477
|
-
static {
|
|
478
|
-
__name(this, "EngineContext");
|
|
479
|
-
}
|
|
480
|
-
services = /* @__PURE__ */ new Map();
|
|
481
|
-
/** Register a service. Throws if the key is already registered. */
|
|
482
|
-
register(key, service) {
|
|
483
|
-
if (this.services.has(key.id)) {
|
|
484
|
-
throw new Error(`Service "${key.id}" is already registered.`);
|
|
485
|
-
}
|
|
486
|
-
this.services.set(key.id, service);
|
|
487
|
-
}
|
|
488
|
-
/** Resolve a service. Throws if not registered. */
|
|
489
|
-
resolve(key) {
|
|
490
|
-
if (!this.services.has(key.id)) {
|
|
491
|
-
throw new Error(`Service "${key.id}" is not registered.`);
|
|
492
|
-
}
|
|
493
|
-
return this.services.get(key.id);
|
|
494
|
-
}
|
|
495
|
-
/** Resolve a service, returning undefined if not registered. */
|
|
496
|
-
tryResolve(key) {
|
|
497
|
-
return this.services.get(key.id);
|
|
498
|
-
}
|
|
499
|
-
/** Remove a registered service. No-op if not registered. */
|
|
500
|
-
unregister(key) {
|
|
501
|
-
this.services.delete(key.id);
|
|
502
|
-
}
|
|
503
|
-
/** Check if a service is registered. */
|
|
504
|
-
has(key) {
|
|
505
|
-
return this.services.has(key.id);
|
|
506
|
-
}
|
|
507
|
-
};
|
|
508
|
-
var EngineKey = new ServiceKey("engine");
|
|
509
|
-
var EventBusKey = new ServiceKey("eventBus");
|
|
510
|
-
var SceneManagerKey = new ServiceKey("sceneManager");
|
|
511
|
-
var LoggerKey = new ServiceKey("logger");
|
|
512
|
-
var InspectorKey = new ServiceKey("inspector");
|
|
513
|
-
var QueryCacheKey = new ServiceKey("queryCache");
|
|
514
|
-
var ErrorBoundaryKey = new ServiceKey("errorBoundary");
|
|
515
|
-
var GameLoopKey = new ServiceKey("gameLoop");
|
|
516
|
-
var SystemSchedulerKey = new ServiceKey("systemScheduler");
|
|
517
|
-
var ProcessSystemKey = new ServiceKey("processSystem");
|
|
518
|
-
var AssetManagerKey = new ServiceKey("assetManager");
|
|
519
|
-
|
|
520
678
|
// src/SceneHooks.ts
|
|
521
679
|
var SceneHookRegistry = class {
|
|
522
680
|
static {
|
|
@@ -1253,6 +1411,7 @@ var Entity = class {
|
|
|
1253
1411
|
}
|
|
1254
1412
|
}
|
|
1255
1413
|
this._scene?._onEntityEvent(token.name, data, this);
|
|
1414
|
+
this._scene?._observeEntityEvent(token.name, data, this);
|
|
1256
1415
|
}
|
|
1257
1416
|
/** Get all components as an iterable. */
|
|
1258
1417
|
getAll() {
|
|
@@ -1675,84 +1834,883 @@ var GameLoop = class {
|
|
|
1675
1834
|
}
|
|
1676
1835
|
};
|
|
1677
1836
|
|
|
1678
|
-
// src/
|
|
1679
|
-
var
|
|
1837
|
+
// src/Inspector.ts
|
|
1838
|
+
var InputManagerRuntimeKey = new ServiceKey("inputManager");
|
|
1839
|
+
var PhysicsWorldManagerRuntimeKey = new ServiceKey(
|
|
1840
|
+
"physicsWorldManager"
|
|
1841
|
+
);
|
|
1842
|
+
var RendererRuntimeKey = new ServiceKey("renderer");
|
|
1843
|
+
var Inspector = class {
|
|
1680
1844
|
static {
|
|
1681
|
-
__name(this, "
|
|
1845
|
+
__name(this, "Inspector");
|
|
1682
1846
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1847
|
+
engine;
|
|
1848
|
+
extensions = /* @__PURE__ */ new Map();
|
|
1849
|
+
sceneIds = /* @__PURE__ */ new WeakMap();
|
|
1850
|
+
nextSceneId = 0;
|
|
1851
|
+
defaultSceneSeed;
|
|
1852
|
+
sceneSeedOverride;
|
|
1853
|
+
timeController = null;
|
|
1854
|
+
eventLogEnabled = false;
|
|
1855
|
+
eventCapacity = 500;
|
|
1856
|
+
/**
|
|
1857
|
+
* Ring buffer of recent events. `eventLogHead` points at the oldest slot;
|
|
1858
|
+
* a full ring contains exactly `eventCapacity` entries. We avoid `splice` to
|
|
1859
|
+
* keep `appendEvent` O(1) — the previous shift-on-overflow approach was
|
|
1860
|
+
* O(n) per event once the buffer was full.
|
|
1861
|
+
*/
|
|
1862
|
+
eventLog = [];
|
|
1863
|
+
eventLogHead = 0;
|
|
1864
|
+
eventWaiters = /* @__PURE__ */ new Set();
|
|
1865
|
+
detachBusTap = null;
|
|
1866
|
+
busEventObserver = /* @__PURE__ */ __name((event, data) => {
|
|
1867
|
+
this.recordBusEvent(String(event), data);
|
|
1868
|
+
}, "busEventObserver");
|
|
1869
|
+
sceneEventObserver = /* @__PURE__ */ __name((eventName, data, entity) => {
|
|
1870
|
+
this.recordEntityEvent(eventName, data, entity);
|
|
1871
|
+
}, "sceneEventObserver");
|
|
1872
|
+
time = {
|
|
1873
|
+
freeze: /* @__PURE__ */ __name(() => {
|
|
1874
|
+
this.requireTimeController().freeze();
|
|
1875
|
+
}, "freeze"),
|
|
1876
|
+
thaw: /* @__PURE__ */ __name(() => {
|
|
1877
|
+
this.requireTimeController().thaw();
|
|
1878
|
+
}, "thaw"),
|
|
1879
|
+
step: /* @__PURE__ */ __name((frames = 1) => {
|
|
1880
|
+
this.assertNonNegativeInteger(frames, "Inspector.time.step(frames)");
|
|
1881
|
+
if (frames === 0) return;
|
|
1882
|
+
this.requireTimeController().stepFrames(frames);
|
|
1883
|
+
this.expireDeadlineWaiters();
|
|
1884
|
+
}, "step"),
|
|
1885
|
+
setDelta: /* @__PURE__ */ __name((ms) => {
|
|
1886
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
1887
|
+
throw new Error("Inspector.time.setDelta(ms) requires a positive number.");
|
|
1888
|
+
}
|
|
1889
|
+
this.requireTimeController().setDelta(ms);
|
|
1890
|
+
}, "setDelta"),
|
|
1891
|
+
isFrozen: /* @__PURE__ */ __name(() => this.timeController?.isFrozen ?? false, "isFrozen"),
|
|
1892
|
+
getFrame: /* @__PURE__ */ __name(() => this.timeController?.getFrame() ?? this.engine.loop.frameCount, "getFrame")
|
|
1893
|
+
};
|
|
1894
|
+
input = {
|
|
1895
|
+
keyDown: /* @__PURE__ */ __name((code) => {
|
|
1896
|
+
this.requireInputManager().fireKeyDown(code);
|
|
1897
|
+
}, "keyDown"),
|
|
1898
|
+
keyUp: /* @__PURE__ */ __name((code) => {
|
|
1899
|
+
this.requireInputManager().fireKeyUp(code);
|
|
1900
|
+
}, "keyUp"),
|
|
1901
|
+
mouseMove: /* @__PURE__ */ __name((x, y) => {
|
|
1902
|
+
this.requireInputManager().firePointerMove(x, y);
|
|
1903
|
+
}, "mouseMove"),
|
|
1904
|
+
mouseDown: /* @__PURE__ */ __name((button = 0) => {
|
|
1905
|
+
this.requireInputManager().firePointerDown(button);
|
|
1906
|
+
}, "mouseDown"),
|
|
1907
|
+
mouseUp: /* @__PURE__ */ __name((button = 0) => {
|
|
1908
|
+
this.requireInputManager().firePointerUp(button);
|
|
1909
|
+
}, "mouseUp"),
|
|
1910
|
+
gamepadButton: /* @__PURE__ */ __name((idx, pressed) => {
|
|
1911
|
+
this.requireInputManager().fireGamepadButton(idx, pressed);
|
|
1912
|
+
}, "gamepadButton"),
|
|
1913
|
+
gamepadAxis: /* @__PURE__ */ __name((idx, value) => {
|
|
1914
|
+
this.requireInputManager().fireGamepadAxis(idx, value);
|
|
1915
|
+
}, "gamepadAxis"),
|
|
1916
|
+
tap: /* @__PURE__ */ __name((code, frames = 1) => {
|
|
1917
|
+
this.assertNonNegativeInteger(frames, "Inspector.input.tap(frames)");
|
|
1918
|
+
const input = this.requireInputManager();
|
|
1919
|
+
input.fireKeyDown(code);
|
|
1920
|
+
try {
|
|
1921
|
+
this.time.step(frames);
|
|
1922
|
+
} finally {
|
|
1923
|
+
input.fireKeyUp(code);
|
|
1924
|
+
}
|
|
1925
|
+
}, "tap"),
|
|
1926
|
+
hold: /* @__PURE__ */ __name((code, frames) => {
|
|
1927
|
+
this.assertNonNegativeInteger(frames, "Inspector.input.hold(frames)");
|
|
1928
|
+
const input = this.requireInputManager();
|
|
1929
|
+
input.fireKeyDown(code);
|
|
1930
|
+
try {
|
|
1931
|
+
this.time.step(frames);
|
|
1932
|
+
} finally {
|
|
1933
|
+
input.fireKeyUp(code);
|
|
1934
|
+
}
|
|
1935
|
+
}, "hold"),
|
|
1936
|
+
fireAction: /* @__PURE__ */ __name((name, frames = 1) => {
|
|
1937
|
+
this.assertNonNegativeInteger(
|
|
1938
|
+
frames,
|
|
1939
|
+
"Inspector.input.fireAction(frames)"
|
|
1940
|
+
);
|
|
1941
|
+
const input = this.requireInputManager();
|
|
1942
|
+
for (let i = 0; i < frames; i++) {
|
|
1943
|
+
input.fireAction(name);
|
|
1944
|
+
this.time.step(1);
|
|
1945
|
+
}
|
|
1946
|
+
}, "fireAction"),
|
|
1947
|
+
clearAll: /* @__PURE__ */ __name(() => {
|
|
1948
|
+
this.requireInputManager().clearAll();
|
|
1949
|
+
}, "clearAll")
|
|
1950
|
+
};
|
|
1951
|
+
events = {
|
|
1952
|
+
getLog: /* @__PURE__ */ __name(() => this.iterateLog().map(({ entry }) => ({ ...entry })), "getLog"),
|
|
1953
|
+
clearLog: /* @__PURE__ */ __name(() => {
|
|
1954
|
+
this.eventLog.length = 0;
|
|
1955
|
+
this.eventLogHead = 0;
|
|
1956
|
+
}, "clearLog"),
|
|
1957
|
+
setCapacity: /* @__PURE__ */ __name((n) => {
|
|
1958
|
+
this.assertNonNegativeInteger(
|
|
1959
|
+
n,
|
|
1960
|
+
"Inspector.events.setCapacity(capacity)"
|
|
1961
|
+
);
|
|
1962
|
+
const ordered = n === 0 ? [] : this.iterateLog().slice(-n);
|
|
1963
|
+
this.eventCapacity = n;
|
|
1964
|
+
this.eventLog = ordered;
|
|
1965
|
+
this.eventLogHead = 0;
|
|
1966
|
+
}, "setCapacity"),
|
|
1967
|
+
waitFor: /* @__PURE__ */ __name((pattern, options) => {
|
|
1968
|
+
const existing = this.findMatchingEvent(pattern, options?.source);
|
|
1969
|
+
if (existing) return Promise.resolve(existing);
|
|
1970
|
+
const withinFrames = options?.withinFrames;
|
|
1971
|
+
if (withinFrames !== void 0 && (!Number.isInteger(withinFrames) || withinFrames < 0)) {
|
|
1972
|
+
throw new Error(
|
|
1973
|
+
"Inspector.events.waitFor(withinFrames) requires a non-negative integer."
|
|
1974
|
+
);
|
|
1975
|
+
}
|
|
1976
|
+
return new Promise((resolve, reject) => {
|
|
1977
|
+
const waiter = {
|
|
1978
|
+
pattern,
|
|
1979
|
+
source: options?.source,
|
|
1980
|
+
withinFrames,
|
|
1981
|
+
deadlineFrame: withinFrames !== void 0 ? this.time.getFrame() + withinFrames : void 0,
|
|
1982
|
+
resolve,
|
|
1983
|
+
reject
|
|
1984
|
+
};
|
|
1985
|
+
this.eventWaiters.add(waiter);
|
|
1986
|
+
});
|
|
1987
|
+
}, "waitFor")
|
|
1988
|
+
};
|
|
1989
|
+
capture = {
|
|
1990
|
+
png: /* @__PURE__ */ __name(async () => {
|
|
1991
|
+
const base64 = await this.capture.pngBase64();
|
|
1992
|
+
return decodeBase64(base64);
|
|
1993
|
+
}, "png"),
|
|
1994
|
+
dataURL: /* @__PURE__ */ __name(async () => {
|
|
1995
|
+
const renderer = this.engine.context.tryResolve(RendererRuntimeKey);
|
|
1996
|
+
if (!renderer) {
|
|
1997
|
+
throw new Error(
|
|
1998
|
+
"Inspector.capture requires RendererPlugin to be active."
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
const canvas = renderer.application.renderer.extract.canvas(
|
|
2002
|
+
renderer.application.stage
|
|
2003
|
+
);
|
|
2004
|
+
return canvas.toDataURL("image/png");
|
|
2005
|
+
}, "dataURL"),
|
|
2006
|
+
pngBase64: /* @__PURE__ */ __name(async () => {
|
|
2007
|
+
const dataUrl = await this.capture.dataURL();
|
|
2008
|
+
const comma = dataUrl.indexOf(",");
|
|
2009
|
+
return comma === -1 ? dataUrl : dataUrl.slice(comma + 1);
|
|
2010
|
+
}, "pngBase64")
|
|
2011
|
+
};
|
|
2012
|
+
constructor(engine) {
|
|
2013
|
+
this.engine = engine;
|
|
1706
2014
|
}
|
|
1707
|
-
/**
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
for (let i = idx + 1; i < stack.length; i++) {
|
|
1716
|
-
if (stack[i].pauseBelow) return true;
|
|
2015
|
+
/** Register a namespaced extension API for plugin-specific debug helpers. */
|
|
2016
|
+
addExtension(namespace, api) {
|
|
2017
|
+
this.assertNonEmptyString(
|
|
2018
|
+
namespace,
|
|
2019
|
+
"Inspector.addExtension(namespace)"
|
|
2020
|
+
);
|
|
2021
|
+
if (!api || typeof api !== "object") {
|
|
2022
|
+
throw new Error("Inspector.addExtension(api) requires an object.");
|
|
1717
2023
|
}
|
|
1718
|
-
|
|
2024
|
+
if (this.extensions.has(namespace)) {
|
|
2025
|
+
throw new Error(
|
|
2026
|
+
`Inspector.addExtension(): namespace "${namespace}" is already registered.`
|
|
2027
|
+
);
|
|
2028
|
+
}
|
|
2029
|
+
this.extensions.set(namespace, api);
|
|
2030
|
+
return api;
|
|
2031
|
+
}
|
|
2032
|
+
/** Look up a previously registered extension API by namespace. */
|
|
2033
|
+
getExtension(namespace) {
|
|
2034
|
+
this.assertNonEmptyString(
|
|
2035
|
+
namespace,
|
|
2036
|
+
"Inspector.getExtension(namespace)"
|
|
2037
|
+
);
|
|
2038
|
+
return this.extensions.get(namespace);
|
|
1719
2039
|
}
|
|
1720
|
-
/**
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
2040
|
+
/** Remove a previously registered extension namespace. */
|
|
2041
|
+
removeExtension(namespace) {
|
|
2042
|
+
this.assertNonEmptyString(
|
|
2043
|
+
namespace,
|
|
2044
|
+
"Inspector.removeExtension(namespace)"
|
|
2045
|
+
);
|
|
2046
|
+
this.extensions.delete(namespace);
|
|
1724
2047
|
}
|
|
1725
|
-
/**
|
|
1726
|
-
|
|
1727
|
-
|
|
2048
|
+
/** Full deterministic state snapshot (stable ordering, serializable). */
|
|
2049
|
+
snapshot() {
|
|
2050
|
+
const scenes = this.engine.scenes.all.map(
|
|
2051
|
+
(scene) => this.sceneToWorldSnapshot(scene)
|
|
2052
|
+
);
|
|
2053
|
+
return {
|
|
2054
|
+
version: 1,
|
|
2055
|
+
frame: this.time.getFrame(),
|
|
2056
|
+
sceneStack: this.getSceneStack(),
|
|
2057
|
+
entityCount: this.countEntities(),
|
|
2058
|
+
systemCount: this.getSystems().length,
|
|
2059
|
+
errors: this.getErrors(),
|
|
2060
|
+
scenes,
|
|
2061
|
+
camera: this.buildCameraSnapshot(),
|
|
2062
|
+
input: this.buildInputSnapshot()
|
|
2063
|
+
};
|
|
1728
2064
|
}
|
|
1729
|
-
/**
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
* readonly layers = this.service(RenderLayerManagerKey);
|
|
1733
|
-
* ```
|
|
1734
|
-
* The actual resolution is deferred until first property access.
|
|
1735
|
-
*/
|
|
1736
|
-
service(key) {
|
|
1737
|
-
let resolved;
|
|
1738
|
-
return new Proxy({}, {
|
|
1739
|
-
get: /* @__PURE__ */ __name((_target, prop) => {
|
|
1740
|
-
resolved ??= this._context.resolve(key);
|
|
1741
|
-
const value = resolved[prop];
|
|
1742
|
-
return typeof value === "function" ? value.bind(resolved) : value;
|
|
1743
|
-
}, "get"),
|
|
1744
|
-
set: /* @__PURE__ */ __name((_target, prop, value) => {
|
|
1745
|
-
resolved ??= this._context.resolve(key);
|
|
1746
|
-
resolved[prop] = value;
|
|
1747
|
-
return true;
|
|
1748
|
-
}, "set")
|
|
1749
|
-
});
|
|
2065
|
+
/** Stable JSON form of {@link snapshot}. */
|
|
2066
|
+
snapshotJSON() {
|
|
2067
|
+
return stableStringify(this.snapshot());
|
|
1750
2068
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
2069
|
+
/** Snapshot one scene by inspector scene id. */
|
|
2070
|
+
snapshotScene(id) {
|
|
2071
|
+
const scene = this.engine.scenes.all.find(
|
|
2072
|
+
(candidate) => this.getSceneId(candidate) === id
|
|
2073
|
+
);
|
|
2074
|
+
if (!scene) {
|
|
2075
|
+
throw new Error(`Inspector.snapshotScene(): unknown scene id "${id}".`);
|
|
2076
|
+
}
|
|
2077
|
+
return this.sceneToWorldSnapshot(scene);
|
|
2078
|
+
}
|
|
2079
|
+
/** Find entity by name in the active scene. */
|
|
2080
|
+
getEntityByName(name) {
|
|
2081
|
+
const entity = this.findActiveEntity(name);
|
|
2082
|
+
if (!entity) return void 0;
|
|
2083
|
+
return this.entityToQuerySnapshot(entity);
|
|
2084
|
+
}
|
|
2085
|
+
/** Get entity position (from Transform component). */
|
|
2086
|
+
getEntityPosition(name) {
|
|
2087
|
+
const entity = this.findActiveEntity(name);
|
|
2088
|
+
if (!entity) return void 0;
|
|
2089
|
+
const transform = this.getTransform(entity);
|
|
2090
|
+
if (!transform) return void 0;
|
|
2091
|
+
return { x: transform.position.x, y: transform.position.y };
|
|
2092
|
+
}
|
|
2093
|
+
/** Check if an entity has a component by class name string. */
|
|
2094
|
+
hasComponent(entityName, componentClass) {
|
|
2095
|
+
return this.findComponentByName(entityName, componentClass) !== void 0;
|
|
2096
|
+
}
|
|
2097
|
+
/** Get component data (serializable subset) by class name string. */
|
|
2098
|
+
getComponentData(entityName, componentClass) {
|
|
2099
|
+
const comp = this.findComponentByName(entityName, componentClass);
|
|
2100
|
+
if (!comp) return void 0;
|
|
2101
|
+
if (typeof comp.serialize === "function") {
|
|
2102
|
+
const data = trySerialize(comp);
|
|
2103
|
+
if (data !== void 0) return data;
|
|
2104
|
+
}
|
|
2105
|
+
return this.serializeComponentOwnProperties(comp);
|
|
2106
|
+
}
|
|
2107
|
+
/** Get all entities in the active scene as lightweight snapshots. */
|
|
2108
|
+
getEntities() {
|
|
2109
|
+
const scene = this.engine.scenes.active;
|
|
2110
|
+
if (!scene) return [];
|
|
2111
|
+
const result = [];
|
|
2112
|
+
for (const entity of scene.getEntities()) {
|
|
2113
|
+
if (!entity.isDestroyed) {
|
|
2114
|
+
result.push(this.entityToQuerySnapshot(entity));
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
return result;
|
|
2118
|
+
}
|
|
2119
|
+
/** Get scene stack info. */
|
|
2120
|
+
getSceneStack() {
|
|
2121
|
+
return this.engine.scenes.all.map((scene) => ({
|
|
2122
|
+
name: scene.name,
|
|
2123
|
+
entityCount: scene.getEntities().size,
|
|
2124
|
+
paused: scene.isPaused
|
|
2125
|
+
}));
|
|
2126
|
+
}
|
|
2127
|
+
/** Get active system info. */
|
|
2128
|
+
getSystems() {
|
|
2129
|
+
const scheduler = this.engine.context.tryResolve(SystemSchedulerKey);
|
|
2130
|
+
if (!scheduler) return [];
|
|
2131
|
+
return scheduler.getAllSystems().map((sys) => ({
|
|
2132
|
+
name: sys.constructor.name,
|
|
2133
|
+
phase: sys.phase,
|
|
2134
|
+
priority: sys.priority,
|
|
2135
|
+
enabled: sys.enabled
|
|
2136
|
+
}));
|
|
2137
|
+
}
|
|
2138
|
+
/** Get disabled components/systems from error boundary. */
|
|
2139
|
+
getErrors() {
|
|
2140
|
+
const boundary = this.engine.context.tryResolve(ErrorBoundaryKey);
|
|
2141
|
+
if (!boundary) return { disabledSystems: [], disabledComponents: [] };
|
|
2142
|
+
const disabled = boundary.getDisabled();
|
|
2143
|
+
return {
|
|
2144
|
+
disabledSystems: disabled.systems.map(
|
|
2145
|
+
(s) => s.system.constructor.name
|
|
2146
|
+
),
|
|
2147
|
+
disabledComponents: disabled.components.map((c) => ({
|
|
2148
|
+
entity: c.component.entity?.name ?? "unknown",
|
|
2149
|
+
component: c.component.constructor.name,
|
|
2150
|
+
error: c.error
|
|
2151
|
+
}))
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
/** Create a new scene-scoped RNG instance using the current inspector seed policy. */
|
|
2155
|
+
createSceneRandom() {
|
|
2156
|
+
const seed = this.sceneSeedOverride ?? this.defaultSceneSeed ?? createDefaultRandomSeed();
|
|
2157
|
+
return createRandomService(seed);
|
|
2158
|
+
}
|
|
2159
|
+
/** Force every current and future scene RNG to the provided seed. */
|
|
2160
|
+
setSeed(seed) {
|
|
2161
|
+
const normalized = normalizeSeed(seed);
|
|
2162
|
+
this.sceneSeedOverride = normalized;
|
|
2163
|
+
for (const scene of this.engine.scenes.all) {
|
|
2164
|
+
this.resolveInternalRandom(scene)?.setSeed(normalized);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
/** @internal DebugPlugin installs a deterministic default seed through this hook. */
|
|
2168
|
+
setDefaultSceneSeed(seed) {
|
|
2169
|
+
this.defaultSceneSeed = seed === void 0 ? void 0 : normalizeSeed(seed);
|
|
2170
|
+
if (this.sceneSeedOverride !== void 0 || this.defaultSceneSeed === void 0) {
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
for (const scene of this.engine.scenes.all) {
|
|
2174
|
+
this.resolveInternalRandom(scene)?.setSeed(this.defaultSceneSeed);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
resolveInternalRandom(scene) {
|
|
2178
|
+
return scene._resolveScoped(RandomKey);
|
|
2179
|
+
}
|
|
2180
|
+
/** @internal DebugPlugin attaches the frozen-time controller through this hook. */
|
|
2181
|
+
attachTimeController(controller) {
|
|
2182
|
+
this.timeController = controller;
|
|
2183
|
+
}
|
|
2184
|
+
/** @internal Clear a previously attached time controller. */
|
|
2185
|
+
detachTimeController(controller) {
|
|
2186
|
+
if (!controller || this.timeController === controller) {
|
|
2187
|
+
this.timeController = null;
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
/** @internal Enable or disable event log recording. */
|
|
2191
|
+
setEventLogEnabled(enabled) {
|
|
2192
|
+
if (this.eventLogEnabled === enabled) return;
|
|
2193
|
+
this.eventLogEnabled = enabled;
|
|
2194
|
+
if (enabled) {
|
|
2195
|
+
if (!this.detachBusTap && this.engine.events?.tap) {
|
|
2196
|
+
this.detachBusTap = this.engine.events.tap(this.busEventObserver);
|
|
2197
|
+
}
|
|
2198
|
+
} else {
|
|
2199
|
+
this.detachBusTap?.();
|
|
2200
|
+
this.detachBusTap = null;
|
|
2201
|
+
}
|
|
2202
|
+
for (const scene of this.engine.scenes.all) {
|
|
2203
|
+
if (enabled) {
|
|
2204
|
+
this.attachSceneEventObserver(scene);
|
|
2205
|
+
} else {
|
|
2206
|
+
this.detachSceneEventObserver(scene);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
/** @internal Install entity-event observation for one scene. No-op if disabled. */
|
|
2211
|
+
attachSceneEventObserver(scene) {
|
|
2212
|
+
if (!this.eventLogEnabled) return;
|
|
2213
|
+
scene._setEntityEventObserver(this.sceneEventObserver);
|
|
2214
|
+
}
|
|
2215
|
+
/** @internal Clear entity-event observation for one scene. */
|
|
2216
|
+
detachSceneEventObserver(scene) {
|
|
2217
|
+
scene._setEntityEventObserver(void 0);
|
|
2218
|
+
}
|
|
2219
|
+
/** @internal Scene hooks forward entity events through this method. */
|
|
2220
|
+
recordEntityEvent(eventName, data, entity) {
|
|
2221
|
+
if (!this.eventLogEnabled) return;
|
|
2222
|
+
const scene = entity.tryScene;
|
|
2223
|
+
this.appendEvent(
|
|
2224
|
+
{
|
|
2225
|
+
frame: this.time.getFrame(),
|
|
2226
|
+
source: "entity",
|
|
2227
|
+
type: eventName,
|
|
2228
|
+
targetId: String(entity.id),
|
|
2229
|
+
payload: serializeEventPayload(data)
|
|
2230
|
+
},
|
|
2231
|
+
scene ? this.getSceneId(scene) : void 0
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
/** @internal Engine teardown releases the event-bus tap through this hook. */
|
|
2235
|
+
dispose() {
|
|
2236
|
+
this.detachBusTap?.();
|
|
2237
|
+
this.detachBusTap = null;
|
|
2238
|
+
for (const scene of this.engine.scenes.all) {
|
|
2239
|
+
scene._setEntityEventObserver(void 0);
|
|
2240
|
+
}
|
|
2241
|
+
this.extensions.clear();
|
|
2242
|
+
}
|
|
2243
|
+
requireTimeController() {
|
|
2244
|
+
if (!this.timeController) {
|
|
2245
|
+
throw new Error(
|
|
2246
|
+
"Inspector.time requires DebugPlugin to be active."
|
|
2247
|
+
);
|
|
2248
|
+
}
|
|
2249
|
+
return this.timeController;
|
|
2250
|
+
}
|
|
2251
|
+
requireInputManager() {
|
|
2252
|
+
const input = this.engine.context.tryResolve(InputManagerRuntimeKey);
|
|
2253
|
+
if (!input) {
|
|
2254
|
+
throw new Error(
|
|
2255
|
+
"Inspector.input requires InputPlugin to be active."
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
return input;
|
|
2259
|
+
}
|
|
2260
|
+
recordBusEvent(type, data) {
|
|
2261
|
+
if (!this.eventLogEnabled) return;
|
|
2262
|
+
this.appendEvent(
|
|
2263
|
+
{
|
|
2264
|
+
frame: this.time.getFrame(),
|
|
2265
|
+
source: "bus",
|
|
2266
|
+
type,
|
|
2267
|
+
payload: serializeEventPayload(data)
|
|
2268
|
+
},
|
|
2269
|
+
this.inferSceneIdFromPayload(data)
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
appendEvent(entry, sceneId) {
|
|
2273
|
+
if (this.eventCapacity === 0) {
|
|
2274
|
+
this.flushMatchingWaiter(entry);
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
const logged = { entry, sceneId };
|
|
2278
|
+
if (this.eventLog.length < this.eventCapacity) {
|
|
2279
|
+
this.eventLog.push(logged);
|
|
2280
|
+
} else {
|
|
2281
|
+
this.eventLog[this.eventLogHead] = logged;
|
|
2282
|
+
this.eventLogHead = (this.eventLogHead + 1) % this.eventCapacity;
|
|
2283
|
+
}
|
|
2284
|
+
this.flushMatchingWaiter(entry);
|
|
2285
|
+
}
|
|
2286
|
+
/** Resolve waiters whose deadline has passed without a match. */
|
|
2287
|
+
expireDeadlineWaiters() {
|
|
2288
|
+
if (this.eventWaiters.size === 0) return;
|
|
2289
|
+
const frame = this.time.getFrame();
|
|
2290
|
+
for (const waiter of [...this.eventWaiters]) {
|
|
2291
|
+
if (waiter.deadlineFrame !== void 0 && frame > waiter.deadlineFrame) {
|
|
2292
|
+
this.eventWaiters.delete(waiter);
|
|
2293
|
+
waiter.reject(
|
|
2294
|
+
new Error(
|
|
2295
|
+
`Inspector.events.waitFor() timed out after ${waiter.withinFrames} frames.`
|
|
2296
|
+
)
|
|
2297
|
+
);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
/** Resolve any waiter that matches the just-appended entry. */
|
|
2302
|
+
flushMatchingWaiter(entry) {
|
|
2303
|
+
if (this.eventWaiters.size === 0) return;
|
|
2304
|
+
for (const waiter of [...this.eventWaiters]) {
|
|
2305
|
+
if (this.eventMatches(entry, waiter.pattern, waiter.source)) {
|
|
2306
|
+
this.eventWaiters.delete(waiter);
|
|
2307
|
+
waiter.resolve(entry);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Walk the ring buffer in chronological order. We avoid materializing the
|
|
2313
|
+
* ordered array on every event append; instead, every consumer that needs
|
|
2314
|
+
* order calls this helper.
|
|
2315
|
+
*/
|
|
2316
|
+
iterateLog() {
|
|
2317
|
+
if (this.eventLog.length < this.eventCapacity || this.eventLogHead === 0) {
|
|
2318
|
+
return this.eventLog;
|
|
2319
|
+
}
|
|
2320
|
+
return [
|
|
2321
|
+
...this.eventLog.slice(this.eventLogHead),
|
|
2322
|
+
...this.eventLog.slice(0, this.eventLogHead)
|
|
2323
|
+
];
|
|
2324
|
+
}
|
|
2325
|
+
findMatchingEvent(pattern, source) {
|
|
2326
|
+
for (const { entry } of this.iterateLog()) {
|
|
2327
|
+
if (this.eventMatches(entry, pattern, source)) {
|
|
2328
|
+
return { ...entry };
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
return void 0;
|
|
2332
|
+
}
|
|
2333
|
+
eventMatches(entry, pattern, source) {
|
|
2334
|
+
if (source && entry.source !== source) return false;
|
|
2335
|
+
return typeof pattern === "string" ? entry.type === pattern : pattern.test(entry.type);
|
|
2336
|
+
}
|
|
2337
|
+
sceneToWorldSnapshot(scene) {
|
|
2338
|
+
const random = scene._resolveScoped(RandomKey);
|
|
2339
|
+
const physicsManager = this.engine.context.tryResolve(
|
|
2340
|
+
PhysicsWorldManagerRuntimeKey
|
|
2341
|
+
);
|
|
2342
|
+
return {
|
|
2343
|
+
id: this.getSceneId(scene),
|
|
2344
|
+
name: scene.name,
|
|
2345
|
+
paused: scene.isPaused,
|
|
2346
|
+
timeScale: scene.timeScale,
|
|
2347
|
+
seed: random?.getSeed() ?? 0,
|
|
2348
|
+
entities: this.getSceneEntities(scene),
|
|
2349
|
+
ui: this.buildUISnapshot(scene),
|
|
2350
|
+
physics: physicsManager?.getContext(scene)?.world.snapshot() ?? {
|
|
2351
|
+
bodies: [],
|
|
2352
|
+
contacts: []
|
|
2353
|
+
},
|
|
2354
|
+
events: this.getSceneEvents(scene)
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
getSceneEntities(scene) {
|
|
2358
|
+
return [...scene.getEntities()].filter((entity) => !entity.isDestroyed).sort((a, b) => a.id - b.id).map((entity) => this.entityToWorldSnapshot(entity));
|
|
2359
|
+
}
|
|
2360
|
+
entityToWorldSnapshot(entity) {
|
|
2361
|
+
const transform = entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
2362
|
+
const worldPosition = transform?.worldPosition;
|
|
2363
|
+
const worldScale = transform?.worldScale;
|
|
2364
|
+
const components = [...entity.getAll()].map((component) => this.componentToSnapshot(component)).sort((a, b) => a.type < b.type ? -1 : a.type > b.type ? 1 : 0);
|
|
2365
|
+
return {
|
|
2366
|
+
id: String(entity.id),
|
|
2367
|
+
type: entity.constructor.name,
|
|
2368
|
+
parent: entity.parent ? String(entity.parent.id) : null,
|
|
2369
|
+
transform: {
|
|
2370
|
+
x: worldPosition?.x ?? 0,
|
|
2371
|
+
y: worldPosition?.y ?? 0,
|
|
2372
|
+
rotation: transform?.worldRotation ?? 0,
|
|
2373
|
+
scaleX: worldScale?.x ?? 1,
|
|
2374
|
+
scaleY: worldScale?.y ?? 1
|
|
2375
|
+
},
|
|
2376
|
+
components
|
|
2377
|
+
};
|
|
2378
|
+
}
|
|
2379
|
+
componentToSnapshot(component) {
|
|
2380
|
+
return {
|
|
2381
|
+
type: component.constructor.name,
|
|
2382
|
+
state: typeof component.serialize === "function" ? trySerialize(component) ?? null : null
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
buildUISnapshot(scene) {
|
|
2386
|
+
const roots = [...scene.getEntities()].filter((entity) => !entity.isDestroyed).flatMap(
|
|
2387
|
+
(entity) => [...entity.getAll()].filter(
|
|
2388
|
+
(component) => component.constructor.name === "UIPanel" && "_node" in component
|
|
2389
|
+
).map(
|
|
2390
|
+
(component, index) => this.buildUINodeSnapshot(
|
|
2391
|
+
component._node,
|
|
2392
|
+
`entity-${entity.id}:UIPanel:${index}`
|
|
2393
|
+
)
|
|
2394
|
+
)
|
|
2395
|
+
);
|
|
2396
|
+
if (roots.length === 0) return null;
|
|
2397
|
+
if (roots.length === 1) {
|
|
2398
|
+
return { root: roots[0] };
|
|
2399
|
+
}
|
|
2400
|
+
return {
|
|
2401
|
+
root: {
|
|
2402
|
+
id: `scene-${this.getSceneId(scene)}:ui`,
|
|
2403
|
+
type: "UIRoot",
|
|
2404
|
+
layout: { x: 0, y: 0, width: 0, height: 0 },
|
|
2405
|
+
children: roots,
|
|
2406
|
+
state: null
|
|
2407
|
+
}
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
buildUINodeSnapshot(node, id) {
|
|
2411
|
+
const layout = node.yogaNode?.getComputedLayout();
|
|
2412
|
+
const children = (node.children ?? []).map(
|
|
2413
|
+
(child, index) => this.buildUINodeSnapshot(child, `${id}/${index}`)
|
|
2414
|
+
);
|
|
2415
|
+
return {
|
|
2416
|
+
id,
|
|
2417
|
+
type: node.constructor.name,
|
|
2418
|
+
layout: {
|
|
2419
|
+
x: layout?.left ?? 0,
|
|
2420
|
+
y: layout?.top ?? 0,
|
|
2421
|
+
width: layout?.width ?? 0,
|
|
2422
|
+
height: layout?.height ?? 0
|
|
2423
|
+
},
|
|
2424
|
+
children,
|
|
2425
|
+
state: null
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2428
|
+
buildCameraSnapshot() {
|
|
2429
|
+
const match = this.findTopmostCamera();
|
|
2430
|
+
if (!match) return null;
|
|
2431
|
+
const { scene, camera } = match;
|
|
2432
|
+
return {
|
|
2433
|
+
sceneId: this.getSceneId(scene),
|
|
2434
|
+
sceneName: scene.name,
|
|
2435
|
+
name: camera.cameraName ?? null,
|
|
2436
|
+
priority: camera.priority ?? 0,
|
|
2437
|
+
position: {
|
|
2438
|
+
x: camera.position.x,
|
|
2439
|
+
y: camera.position.y
|
|
2440
|
+
},
|
|
2441
|
+
zoom: camera.zoom,
|
|
2442
|
+
rotation: camera.rotation
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
findTopmostCamera() {
|
|
2446
|
+
const stack = this.engine.scenes.all;
|
|
2447
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
2448
|
+
const scene = stack[i];
|
|
2449
|
+
if (!scene) continue;
|
|
2450
|
+
let highest;
|
|
2451
|
+
for (const entity of scene.getEntities()) {
|
|
2452
|
+
if (entity.isDestroyed) continue;
|
|
2453
|
+
for (const component of entity.getAll()) {
|
|
2454
|
+
if (component.constructor.name !== "CameraComponent") continue;
|
|
2455
|
+
const camera = component;
|
|
2456
|
+
if (camera.enabled && (!highest || (camera.priority ?? 0) > (highest.priority ?? 0))) {
|
|
2457
|
+
highest = camera;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
if (highest) {
|
|
2462
|
+
return { scene, camera: highest };
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
return void 0;
|
|
2466
|
+
}
|
|
2467
|
+
buildInputSnapshot() {
|
|
2468
|
+
const input = this.engine.context.tryResolve(InputManagerRuntimeKey);
|
|
2469
|
+
return input?.snapshotState() ?? {
|
|
2470
|
+
keys: [],
|
|
2471
|
+
actions: [],
|
|
2472
|
+
mouse: { x: 0, y: 0, buttons: [], down: false },
|
|
2473
|
+
gamepad: { buttons: [], axes: [] }
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
getSceneEvents(scene) {
|
|
2477
|
+
const sceneId = this.getSceneId(scene);
|
|
2478
|
+
return this.iterateLog().filter((entry) => entry.sceneId === sceneId).map(({ entry }) => ({ ...entry }));
|
|
2479
|
+
}
|
|
2480
|
+
inferSceneIdFromPayload(data) {
|
|
2481
|
+
if (!data || typeof data !== "object") return void 0;
|
|
2482
|
+
const record = data;
|
|
2483
|
+
const scene = this.extractScene(record["scene"]) ?? this.extractSceneFromEntity(record["entity"]) ?? this.extractSceneFromEntity(record["oldScene"]) ?? this.extractSceneFromEntity(record["newScene"]);
|
|
2484
|
+
return scene ? this.getSceneId(scene) : void 0;
|
|
2485
|
+
}
|
|
2486
|
+
extractScene(value) {
|
|
2487
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2488
|
+
return this.engine.scenes.all.find((scene) => scene === value);
|
|
2489
|
+
}
|
|
2490
|
+
extractSceneFromEntity(value) {
|
|
2491
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2492
|
+
const maybeEntity = value;
|
|
2493
|
+
return maybeEntity.tryScene ?? this.extractScene(value);
|
|
2494
|
+
}
|
|
2495
|
+
findActiveEntity(name) {
|
|
2496
|
+
return this.engine.scenes.active?.findEntity(name);
|
|
2497
|
+
}
|
|
2498
|
+
findComponentByName(entityName, componentClass) {
|
|
2499
|
+
const entity = this.findActiveEntity(entityName);
|
|
2500
|
+
if (!entity) return void 0;
|
|
2501
|
+
for (const comp of entity.getAll()) {
|
|
2502
|
+
if (comp.constructor.name === componentClass) return comp;
|
|
2503
|
+
}
|
|
2504
|
+
return void 0;
|
|
2505
|
+
}
|
|
2506
|
+
entityToQuerySnapshot(entity) {
|
|
2507
|
+
const transform = this.getTransform(entity);
|
|
2508
|
+
const snapshot = {
|
|
2509
|
+
id: entity.id,
|
|
2510
|
+
name: entity.name,
|
|
2511
|
+
tags: [...entity.tags].sort((a, b) => a < b ? -1 : a > b ? 1 : 0),
|
|
2512
|
+
components: [...entity.getAll()].map((component) => component.constructor.name).sort((a, b) => a < b ? -1 : a > b ? 1 : 0)
|
|
2513
|
+
};
|
|
2514
|
+
if (transform) {
|
|
2515
|
+
snapshot.position = {
|
|
2516
|
+
x: transform.position.x,
|
|
2517
|
+
y: transform.position.y
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
return snapshot;
|
|
2521
|
+
}
|
|
2522
|
+
getTransform(entity) {
|
|
2523
|
+
return entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
2524
|
+
}
|
|
2525
|
+
serializeComponentOwnProperties(comp) {
|
|
2526
|
+
const result = {};
|
|
2527
|
+
for (const key of Object.getOwnPropertyNames(comp)) {
|
|
2528
|
+
if (key === "entity") continue;
|
|
2529
|
+
if (key.startsWith("_")) continue;
|
|
2530
|
+
const value = comp[key];
|
|
2531
|
+
if (!isSerializableValue(value)) continue;
|
|
2532
|
+
result[key] = value;
|
|
2533
|
+
}
|
|
2534
|
+
return result;
|
|
2535
|
+
}
|
|
2536
|
+
countEntities() {
|
|
2537
|
+
let count = 0;
|
|
2538
|
+
for (const scene of this.engine.scenes.all) {
|
|
2539
|
+
for (const entity of scene.getEntities()) {
|
|
2540
|
+
if (!entity.isDestroyed) count++;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
return count;
|
|
2544
|
+
}
|
|
2545
|
+
getSceneId(scene) {
|
|
2546
|
+
let id = this.sceneIds.get(scene);
|
|
2547
|
+
if (!id) {
|
|
2548
|
+
this.nextSceneId++;
|
|
2549
|
+
id = `scene-${this.nextSceneId}`;
|
|
2550
|
+
this.sceneIds.set(scene, id);
|
|
2551
|
+
}
|
|
2552
|
+
return id;
|
|
2553
|
+
}
|
|
2554
|
+
assertNonNegativeInteger(value, name) {
|
|
2555
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
2556
|
+
throw new Error(`${name} requires a non-negative integer.`);
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
assertNonEmptyString(value, name) {
|
|
2560
|
+
if (value.trim().length === 0) {
|
|
2561
|
+
throw new Error(`${name} requires a non-empty string.`);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
};
|
|
2565
|
+
function isSerializableValue(value) {
|
|
2566
|
+
if (value === null || value === void 0) return true;
|
|
2567
|
+
const t = typeof value;
|
|
2568
|
+
if (t === "function") return false;
|
|
2569
|
+
if (t !== "object") return true;
|
|
2570
|
+
if (Array.isArray(value)) return true;
|
|
2571
|
+
const proto = Object.getPrototypeOf(value);
|
|
2572
|
+
return proto === Object.prototype || proto === null;
|
|
2573
|
+
}
|
|
2574
|
+
__name(isSerializableValue, "isSerializableValue");
|
|
2575
|
+
function safeClone(value) {
|
|
2576
|
+
try {
|
|
2577
|
+
return JSON.parse(JSON.stringify(value));
|
|
2578
|
+
} catch {
|
|
2579
|
+
return void 0;
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
__name(safeClone, "safeClone");
|
|
2583
|
+
function trySerialize(component) {
|
|
2584
|
+
try {
|
|
2585
|
+
return safeClone(component.serialize?.());
|
|
2586
|
+
} catch {
|
|
2587
|
+
return void 0;
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
__name(trySerialize, "trySerialize");
|
|
2591
|
+
function serializeEventPayload(payload) {
|
|
2592
|
+
if (payload === void 0) return null;
|
|
2593
|
+
const cloned = safeClone(payload);
|
|
2594
|
+
return cloned === void 0 ? { _unserializable: true } : cloned;
|
|
2595
|
+
}
|
|
2596
|
+
__name(serializeEventPayload, "serializeEventPayload");
|
|
2597
|
+
function stableStringify(value) {
|
|
2598
|
+
return JSON.stringify(sortJsonValue(value));
|
|
2599
|
+
}
|
|
2600
|
+
__name(stableStringify, "stableStringify");
|
|
2601
|
+
function sortJsonValue(value) {
|
|
2602
|
+
if (Array.isArray(value)) {
|
|
2603
|
+
return value.map((item) => sortJsonValue(item));
|
|
2604
|
+
}
|
|
2605
|
+
if (value && typeof value === "object") {
|
|
2606
|
+
const entries = Object.entries(value).sort(
|
|
2607
|
+
([left], [right]) => left < right ? -1 : left > right ? 1 : 0
|
|
2608
|
+
);
|
|
2609
|
+
const result = {};
|
|
2610
|
+
for (const [key, child] of entries) {
|
|
2611
|
+
result[key] = sortJsonValue(child);
|
|
2612
|
+
}
|
|
2613
|
+
return result;
|
|
2614
|
+
}
|
|
2615
|
+
return value;
|
|
2616
|
+
}
|
|
2617
|
+
__name(sortJsonValue, "sortJsonValue");
|
|
2618
|
+
function decodeBase64(base64) {
|
|
2619
|
+
if (typeof atob === "function") {
|
|
2620
|
+
const binary = atob(base64);
|
|
2621
|
+
const bytes = new Uint8Array(binary.length);
|
|
2622
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2623
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2624
|
+
}
|
|
2625
|
+
return bytes;
|
|
2626
|
+
}
|
|
2627
|
+
const bufferCtor = globalThis.Buffer;
|
|
2628
|
+
if (bufferCtor) {
|
|
2629
|
+
return bufferCtor.from(base64, "base64");
|
|
2630
|
+
}
|
|
2631
|
+
throw new Error("Inspector.capture.png() is not supported in this environment.");
|
|
2632
|
+
}
|
|
2633
|
+
__name(decodeBase64, "decodeBase64");
|
|
2634
|
+
|
|
2635
|
+
// src/Scene.ts
|
|
2636
|
+
var Scene = class {
|
|
2637
|
+
static {
|
|
2638
|
+
__name(this, "Scene");
|
|
2639
|
+
}
|
|
2640
|
+
/** Whether scenes below this one in the stack should be paused. Default: true. */
|
|
2641
|
+
pauseBelow = true;
|
|
2642
|
+
/** Whether scenes below this one should still render. Default: false. */
|
|
2643
|
+
transparentBelow = false;
|
|
2644
|
+
/** Asset handles to load before onEnter(). Override in subclasses. */
|
|
2645
|
+
preload;
|
|
2646
|
+
/** Default transition used when this scene is the destination of a push/pop/replace. */
|
|
2647
|
+
defaultTransition;
|
|
2648
|
+
/** Manual pause flag. Set by game code to pause this scene regardless of stack position. */
|
|
2649
|
+
paused = false;
|
|
2650
|
+
/** Time scale multiplier for this scene. 1.0 = normal, 0.5 = half speed. Default: 1. */
|
|
2651
|
+
timeScale = 1;
|
|
2652
|
+
entities = /* @__PURE__ */ new Set();
|
|
2653
|
+
destroyQueue = [];
|
|
2654
|
+
_context;
|
|
2655
|
+
entityCallbacks;
|
|
2656
|
+
queryCache;
|
|
2657
|
+
bus;
|
|
2658
|
+
_entityEventHandlers;
|
|
2659
|
+
_entityEventObserver;
|
|
2660
|
+
_scopedServices;
|
|
2661
|
+
/** Access the EngineContext. */
|
|
2662
|
+
get context() {
|
|
2663
|
+
return this._context;
|
|
2664
|
+
}
|
|
2665
|
+
/** Whether this scene is effectively paused (manual pause or paused by stack). */
|
|
2666
|
+
get isPaused() {
|
|
2667
|
+
if (this.paused) return true;
|
|
2668
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
2669
|
+
if (!sm) return false;
|
|
2670
|
+
const stack = sm.all;
|
|
2671
|
+
const idx = stack.indexOf(this);
|
|
2672
|
+
if (idx === -1) return false;
|
|
2673
|
+
for (let i = idx + 1; i < stack.length; i++) {
|
|
2674
|
+
if (stack[i].pauseBelow) return true;
|
|
2675
|
+
}
|
|
2676
|
+
return false;
|
|
2677
|
+
}
|
|
2678
|
+
/** Whether a scene transition is currently running. */
|
|
2679
|
+
get isTransitioning() {
|
|
2680
|
+
const sm = this._context?.tryResolve(SceneManagerKey);
|
|
2681
|
+
return sm?.isTransitioning ?? false;
|
|
2682
|
+
}
|
|
2683
|
+
/** Convenience accessor for the AssetManager. */
|
|
2684
|
+
get assets() {
|
|
2685
|
+
return this._context.resolve(AssetManagerKey);
|
|
2686
|
+
}
|
|
2687
|
+
/**
|
|
2688
|
+
* Lazy proxy-based service resolution. Can be used at field-declaration time:
|
|
2689
|
+
* ```ts
|
|
2690
|
+
* readonly layers = this.service(RenderLayerManagerKey);
|
|
2691
|
+
* ```
|
|
2692
|
+
* The actual resolution is deferred until first property access.
|
|
2693
|
+
*/
|
|
2694
|
+
service(key) {
|
|
2695
|
+
let resolved;
|
|
2696
|
+
return new Proxy({}, {
|
|
2697
|
+
get: /* @__PURE__ */ __name((_target, prop) => {
|
|
2698
|
+
resolved ??= this._context.resolve(key);
|
|
2699
|
+
const value = resolved[prop];
|
|
2700
|
+
return typeof value === "function" ? value.bind(resolved) : value;
|
|
2701
|
+
}, "get"),
|
|
2702
|
+
set: /* @__PURE__ */ __name((_target, prop, value) => {
|
|
2703
|
+
resolved ??= this._context.resolve(key);
|
|
2704
|
+
resolved[prop] = value;
|
|
2705
|
+
return true;
|
|
2706
|
+
}, "set")
|
|
2707
|
+
});
|
|
2708
|
+
}
|
|
2709
|
+
spawn(nameOrBlueprintOrClass, params) {
|
|
2710
|
+
if (typeof nameOrBlueprintOrClass === "function") {
|
|
2711
|
+
const entity2 = new nameOrBlueprintOrClass();
|
|
2712
|
+
entity2._setScene(this, this.entityCallbacks);
|
|
2713
|
+
this.entities.add(entity2);
|
|
1756
2714
|
this.bus?.emit("entity:created", { entity: entity2 });
|
|
1757
2715
|
entity2.setup?.(params);
|
|
1758
2716
|
return entity2;
|
|
@@ -1845,6 +2803,14 @@ var Scene = class {
|
|
|
1845
2803
|
}
|
|
1846
2804
|
}
|
|
1847
2805
|
}
|
|
2806
|
+
/**
|
|
2807
|
+
* Observe entity-scoped event emissions after they dispatch locally and
|
|
2808
|
+
* bubble to the scene. Tooling only; game code should keep using `on()`.
|
|
2809
|
+
* @internal
|
|
2810
|
+
*/
|
|
2811
|
+
_observeEntityEvent(eventName, data, entity) {
|
|
2812
|
+
this._entityEventObserver?.(eventName, data, entity);
|
|
2813
|
+
}
|
|
1848
2814
|
// ---- Internal methods ----
|
|
1849
2815
|
/**
|
|
1850
2816
|
* Register a scene-scoped service. Called from a plugin's `beforeEnter`
|
|
@@ -1856,6 +2822,13 @@ var Scene = class {
|
|
|
1856
2822
|
this._scopedServices ??= /* @__PURE__ */ new Map();
|
|
1857
2823
|
this._scopedServices.set(key.id, value);
|
|
1858
2824
|
}
|
|
2825
|
+
/**
|
|
2826
|
+
* Install or clear a tooling-only observer for bubbled entity events.
|
|
2827
|
+
* @internal
|
|
2828
|
+
*/
|
|
2829
|
+
_setEntityEventObserver(observer) {
|
|
2830
|
+
this._entityEventObserver = observer;
|
|
2831
|
+
}
|
|
1859
2832
|
/**
|
|
1860
2833
|
* Resolve a scene-scoped service, or `undefined` if none was registered.
|
|
1861
2834
|
* @internal
|
|
@@ -1922,6 +2895,128 @@ var Scene = class {
|
|
|
1922
2895
|
}
|
|
1923
2896
|
};
|
|
1924
2897
|
|
|
2898
|
+
// src/Process.ts
|
|
2899
|
+
var Process = class _Process {
|
|
2900
|
+
static {
|
|
2901
|
+
__name(this, "Process");
|
|
2902
|
+
}
|
|
2903
|
+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
2904
|
+
updateFn;
|
|
2905
|
+
onCompleteFn;
|
|
2906
|
+
duration;
|
|
2907
|
+
loop;
|
|
2908
|
+
/** Tags for filtering/grouping. */
|
|
2909
|
+
tags;
|
|
2910
|
+
elapsed = 0;
|
|
2911
|
+
_completed = false;
|
|
2912
|
+
_paused = false;
|
|
2913
|
+
_cancelled = false;
|
|
2914
|
+
resolvePromise;
|
|
2915
|
+
/** Create a timer that fires `onComplete` after `duration` ms. */
|
|
2916
|
+
static delay(duration, onComplete, tags) {
|
|
2917
|
+
const opts = { duration };
|
|
2918
|
+
if (onComplete !== void 0) opts.onComplete = onComplete;
|
|
2919
|
+
if (tags !== void 0) opts.tags = tags;
|
|
2920
|
+
return new _Process(opts);
|
|
2921
|
+
}
|
|
2922
|
+
constructor(options) {
|
|
2923
|
+
this.updateFn = options.update ?? (() => {
|
|
2924
|
+
});
|
|
2925
|
+
this.onCompleteFn = options.onComplete;
|
|
2926
|
+
this.duration = options.duration;
|
|
2927
|
+
this.loop = options.loop ?? false;
|
|
2928
|
+
this.tags = options.tags ?? [];
|
|
2929
|
+
}
|
|
2930
|
+
/** Whether the process has completed. */
|
|
2931
|
+
get completed() {
|
|
2932
|
+
return this._completed;
|
|
2933
|
+
}
|
|
2934
|
+
/** Whether the process is paused. */
|
|
2935
|
+
get paused() {
|
|
2936
|
+
return this._paused;
|
|
2937
|
+
}
|
|
2938
|
+
/** Pause the process. */
|
|
2939
|
+
pause() {
|
|
2940
|
+
this._paused = true;
|
|
2941
|
+
}
|
|
2942
|
+
/** Resume the process. */
|
|
2943
|
+
resume() {
|
|
2944
|
+
this._paused = false;
|
|
2945
|
+
}
|
|
2946
|
+
/** Cancel the process. */
|
|
2947
|
+
cancel() {
|
|
2948
|
+
this._cancelled = true;
|
|
2949
|
+
this._completed = true;
|
|
2950
|
+
this.resolvePromise?.();
|
|
2951
|
+
}
|
|
2952
|
+
/** Returns a promise that resolves when the process completes or is cancelled. */
|
|
2953
|
+
toPromise() {
|
|
2954
|
+
if (this._completed) return Promise.resolve();
|
|
2955
|
+
return new Promise((resolve) => {
|
|
2956
|
+
this.resolvePromise = resolve;
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
/**
|
|
2960
|
+
* Advance the process by dt milliseconds.
|
|
2961
|
+
* @internal
|
|
2962
|
+
*/
|
|
2963
|
+
_update(dt) {
|
|
2964
|
+
if (this._completed || this._paused || this._cancelled) return;
|
|
2965
|
+
this.elapsed += dt;
|
|
2966
|
+
if (this.duration !== void 0 && this.elapsed >= this.duration) {
|
|
2967
|
+
const result2 = this.updateFn(dt, this.elapsed);
|
|
2968
|
+
if (this.loop && result2 !== true) {
|
|
2969
|
+
this.elapsed = this.elapsed % this.duration;
|
|
2970
|
+
return;
|
|
2971
|
+
}
|
|
2972
|
+
this.complete();
|
|
2973
|
+
return;
|
|
2974
|
+
}
|
|
2975
|
+
const result = this.updateFn(dt, this.elapsed);
|
|
2976
|
+
if (result === true) {
|
|
2977
|
+
if (this.loop) {
|
|
2978
|
+
this.elapsed = 0;
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
this.complete();
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
/**
|
|
2985
|
+
* Reset the process to its initial state so it can be re-run.
|
|
2986
|
+
* @internal Used by Sequence for loop/repeat with direct instances.
|
|
2987
|
+
*/
|
|
2988
|
+
_reset() {
|
|
2989
|
+
this.elapsed = 0;
|
|
2990
|
+
this._completed = false;
|
|
2991
|
+
this._paused = false;
|
|
2992
|
+
this._cancelled = false;
|
|
2993
|
+
delete this.resolvePromise;
|
|
2994
|
+
}
|
|
2995
|
+
complete() {
|
|
2996
|
+
this._completed = true;
|
|
2997
|
+
this.onCompleteFn?.();
|
|
2998
|
+
this.resolvePromise?.();
|
|
2999
|
+
}
|
|
3000
|
+
};
|
|
3001
|
+
var easeLinear = /* @__PURE__ */ __name((t) => t, "easeLinear");
|
|
3002
|
+
var easeInQuad = /* @__PURE__ */ __name((t) => t * t, "easeInQuad");
|
|
3003
|
+
var easeOutQuad = /* @__PURE__ */ __name((t) => t * (2 - t), "easeOutQuad");
|
|
3004
|
+
var easeInOutQuad = /* @__PURE__ */ __name((t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, "easeInOutQuad");
|
|
3005
|
+
var easeOutBounce = /* @__PURE__ */ __name((t) => {
|
|
3006
|
+
if (t < 1 / 2.75) {
|
|
3007
|
+
return 7.5625 * t * t;
|
|
3008
|
+
} else if (t < 2 / 2.75) {
|
|
3009
|
+
const t2 = t - 1.5 / 2.75;
|
|
3010
|
+
return 7.5625 * t2 * t2 + 0.75;
|
|
3011
|
+
} else if (t < 2.5 / 2.75) {
|
|
3012
|
+
const t2 = t - 2.25 / 2.75;
|
|
3013
|
+
return 7.5625 * t2 * t2 + 0.9375;
|
|
3014
|
+
} else {
|
|
3015
|
+
const t2 = t - 2.625 / 2.75;
|
|
3016
|
+
return 7.5625 * t2 * t2 + 0.984375;
|
|
3017
|
+
}
|
|
3018
|
+
}, "easeOutBounce");
|
|
3019
|
+
|
|
1925
3020
|
// src/LoadingScene.ts
|
|
1926
3021
|
var LoadingScene = class extends Scene {
|
|
1927
3022
|
static {
|
|
@@ -1946,6 +3041,7 @@ var LoadingScene = class extends Scene {
|
|
|
1946
3041
|
_active = true;
|
|
1947
3042
|
_continueRequested = false;
|
|
1948
3043
|
_continueGate;
|
|
3044
|
+
_pendingWaits = /* @__PURE__ */ new Set();
|
|
1949
3045
|
// Bumped on every `_run` attempt. `AssetManager.loadAll` uses `Promise.all`
|
|
1950
3046
|
// under the hood, so individual loaders from a failed attempt can still
|
|
1951
3047
|
// resolve and fire `onProgress` after the attempt rejects. Without this
|
|
@@ -2000,28 +3096,32 @@ var LoadingScene = class extends Scene {
|
|
|
2000
3096
|
onExit() {
|
|
2001
3097
|
this._active = false;
|
|
2002
3098
|
this._continueGate?.();
|
|
3099
|
+
for (const wait of this._pendingWaits) {
|
|
3100
|
+
wait.cancel();
|
|
3101
|
+
}
|
|
3102
|
+
this._pendingWaits.clear();
|
|
2003
3103
|
}
|
|
2004
3104
|
async _run() {
|
|
2005
|
-
await
|
|
3105
|
+
await Promise.resolve();
|
|
2006
3106
|
if (!this._active) return;
|
|
2007
3107
|
const attempt = ++this._attempt;
|
|
2008
3108
|
const target = typeof this.target === "function" ? this.target() : this.target;
|
|
2009
|
-
const startedAt = performance.now();
|
|
2010
3109
|
const bus = this.context.resolve(EventBusKey);
|
|
3110
|
+
const minDuration = this._createEngineTimeDelay(this.minDuration);
|
|
2011
3111
|
try {
|
|
2012
3112
|
await this.assets.loadAll(target.preload ?? [], (ratio) => {
|
|
2013
3113
|
if (!this._active || attempt !== this._attempt) return;
|
|
2014
3114
|
this._progress = ratio;
|
|
2015
3115
|
bus.emit("scene:loading:progress", { scene: this, ratio });
|
|
2016
3116
|
});
|
|
2017
|
-
if (!this._active || attempt !== this._attempt)
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
if (remaining > 0) {
|
|
2021
|
-
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
2022
|
-
if (!this._active || attempt !== this._attempt) return;
|
|
3117
|
+
if (!this._active || attempt !== this._attempt) {
|
|
3118
|
+
minDuration.cancel();
|
|
3119
|
+
return;
|
|
2023
3120
|
}
|
|
3121
|
+
await minDuration.promise;
|
|
3122
|
+
if (!this._active || attempt !== this._attempt) return;
|
|
2024
3123
|
} catch (err) {
|
|
3124
|
+
minDuration.cancel();
|
|
2025
3125
|
if (!this._active || attempt !== this._attempt) return;
|
|
2026
3126
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
2027
3127
|
this._started = false;
|
|
@@ -2045,6 +3145,26 @@ var LoadingScene = class extends Scene {
|
|
|
2045
3145
|
this.transition ? { transition: this.transition } : void 0
|
|
2046
3146
|
);
|
|
2047
3147
|
}
|
|
3148
|
+
_createEngineTimeDelay(ms) {
|
|
3149
|
+
if (ms <= 0) {
|
|
3150
|
+
return {
|
|
3151
|
+
promise: Promise.resolve(),
|
|
3152
|
+
cancel: /* @__PURE__ */ __name(() => {
|
|
3153
|
+
}, "cancel")
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
const wait = Process.delay(ms);
|
|
3157
|
+
this._pendingWaits.add(wait);
|
|
3158
|
+
this.context.resolve(ProcessSystemKey).add(wait);
|
|
3159
|
+
return {
|
|
3160
|
+
promise: wait.toPromise().finally(() => {
|
|
3161
|
+
this._pendingWaits.delete(wait);
|
|
3162
|
+
}),
|
|
3163
|
+
cancel: /* @__PURE__ */ __name(() => {
|
|
3164
|
+
wait.cancel();
|
|
3165
|
+
}, "cancel")
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
2048
3168
|
};
|
|
2049
3169
|
|
|
2050
3170
|
// src/SceneTransition.ts
|
|
@@ -2069,6 +3189,29 @@ var SceneManager = class {
|
|
|
2069
3189
|
_pendingChain = Promise.resolve();
|
|
2070
3190
|
_mutationDepth = 0;
|
|
2071
3191
|
_destroyed = false;
|
|
3192
|
+
_autoPauseOnBlur = false;
|
|
3193
|
+
_isBlurred = false;
|
|
3194
|
+
_visibilityPausedScenes = /* @__PURE__ */ new Set();
|
|
3195
|
+
_visibilityListenerCleanup;
|
|
3196
|
+
/**
|
|
3197
|
+
* Pause all non-paused scenes when `document.hidden` becomes `true`; restore
|
|
3198
|
+
* them on focus. Default: `false`. Only scenes paused by this mechanism are
|
|
3199
|
+
* restored — user-paused scenes (manual `scene.paused = true` or `pauseBelow`
|
|
3200
|
+
* cascade) are never touched.
|
|
3201
|
+
*/
|
|
3202
|
+
get autoPauseOnBlur() {
|
|
3203
|
+
return this._autoPauseOnBlur;
|
|
3204
|
+
}
|
|
3205
|
+
set autoPauseOnBlur(value) {
|
|
3206
|
+
if (this._autoPauseOnBlur === value) return;
|
|
3207
|
+
this._autoPauseOnBlur = value;
|
|
3208
|
+
if (!this._isBlurred) return;
|
|
3209
|
+
if (value) {
|
|
3210
|
+
this._applyBlurPause();
|
|
3211
|
+
} else if (this._visibilityPausedScenes.size > 0) {
|
|
3212
|
+
this._restoreBlurPause();
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
2072
3215
|
/**
|
|
2073
3216
|
* Set the engine context.
|
|
2074
3217
|
* @internal
|
|
@@ -2079,6 +3222,40 @@ var SceneManager = class {
|
|
|
2079
3222
|
this.assetManager = context.tryResolve(AssetManagerKey);
|
|
2080
3223
|
this.hookRegistry = context.tryResolve(SceneHookRegistryKey);
|
|
2081
3224
|
this.logger = context.tryResolve(LoggerKey);
|
|
3225
|
+
if (this._visibilityListenerCleanup || typeof document === "undefined") {
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
const onVisibilityChange = /* @__PURE__ */ __name(() => {
|
|
3229
|
+
this._handleVisibilityChange(document.hidden);
|
|
3230
|
+
}, "onVisibilityChange");
|
|
3231
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
3232
|
+
this._visibilityListenerCleanup = () => document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
3233
|
+
}
|
|
3234
|
+
/**
|
|
3235
|
+
* React to a visibility change. Parameterised on `hidden` so unit tests can
|
|
3236
|
+
* drive it without a real `document`.
|
|
3237
|
+
* @internal
|
|
3238
|
+
*/
|
|
3239
|
+
_handleVisibilityChange(hidden) {
|
|
3240
|
+
if (hidden && !this._isBlurred) {
|
|
3241
|
+
this._isBlurred = true;
|
|
3242
|
+
if (this._autoPauseOnBlur) this._applyBlurPause();
|
|
3243
|
+
} else if (!hidden && this._isBlurred) {
|
|
3244
|
+
this._isBlurred = false;
|
|
3245
|
+
if (this._visibilityPausedScenes.size > 0) this._restoreBlurPause();
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
_applyBlurPause() {
|
|
3249
|
+
for (const scene of this.activeScenes) {
|
|
3250
|
+
scene.paused = true;
|
|
3251
|
+
this._visibilityPausedScenes.add(scene);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
_restoreBlurPause() {
|
|
3255
|
+
for (const scene of this._visibilityPausedScenes) {
|
|
3256
|
+
scene.paused = false;
|
|
3257
|
+
}
|
|
3258
|
+
this._visibilityPausedScenes.clear();
|
|
2082
3259
|
}
|
|
2083
3260
|
/** The topmost (active) scene. */
|
|
2084
3261
|
get active() {
|
|
@@ -2205,6 +3382,9 @@ var SceneManager = class {
|
|
|
2205
3382
|
this._cleanupRun(this._currentRun);
|
|
2206
3383
|
}
|
|
2207
3384
|
this._pendingChain = Promise.resolve();
|
|
3385
|
+
this._visibilityListenerCleanup?.();
|
|
3386
|
+
this._visibilityListenerCleanup = void 0;
|
|
3387
|
+
this._visibilityPausedScenes.clear();
|
|
2208
3388
|
this._withMutationSync(() => {
|
|
2209
3389
|
while (this.stack.length > 0) {
|
|
2210
3390
|
const scene = this.stack.pop();
|
|
@@ -2324,6 +3504,7 @@ var SceneManager = class {
|
|
|
2324
3504
|
scene._destroyAllEntities();
|
|
2325
3505
|
this.hookRegistry?.runAfterExit(scene);
|
|
2326
3506
|
scene._clearScopedServices();
|
|
3507
|
+
this._visibilityPausedScenes.delete(scene);
|
|
2327
3508
|
}
|
|
2328
3509
|
async _runTransition(kind, transition, fromScene, toScene) {
|
|
2329
3510
|
if (this._destroyed) return;
|
|
@@ -2392,174 +3573,52 @@ var SceneManager = class {
|
|
|
2392
3573
|
toScene: run.toScene
|
|
2393
3574
|
};
|
|
2394
3575
|
}
|
|
2395
|
-
_snapshotPauseStates() {
|
|
2396
|
-
return new Map(
|
|
2397
|
-
this.stack.map((scene) => [scene, scene.isPaused])
|
|
2398
|
-
);
|
|
2399
|
-
}
|
|
2400
|
-
_assertNotMutating(method) {
|
|
2401
|
-
if (this._mutationDepth === 0) return;
|
|
2402
|
-
throw new Error(
|
|
2403
|
-
`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().`
|
|
2404
|
-
);
|
|
2405
|
-
}
|
|
2406
|
-
async _withMutation(work) {
|
|
2407
|
-
this._mutationDepth++;
|
|
2408
|
-
try {
|
|
2409
|
-
return await work();
|
|
2410
|
-
} finally {
|
|
2411
|
-
this._mutationDepth--;
|
|
2412
|
-
}
|
|
2413
|
-
}
|
|
2414
|
-
_withMutationSync(work) {
|
|
2415
|
-
this._mutationDepth++;
|
|
2416
|
-
try {
|
|
2417
|
-
return work();
|
|
2418
|
-
} finally {
|
|
2419
|
-
this._mutationDepth--;
|
|
2420
|
-
}
|
|
2421
|
-
}
|
|
2422
|
-
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
2423
|
-
_firePauseTransitions(wasPaused) {
|
|
2424
|
-
for (const scene of this.stack) {
|
|
2425
|
-
const was = wasPaused.get(scene) ?? false;
|
|
2426
|
-
if (scene.isPaused && !was) {
|
|
2427
|
-
scene.onPause?.();
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
2432
|
-
_fireResumeTransitions(wasPaused) {
|
|
2433
|
-
for (const scene of this.stack) {
|
|
2434
|
-
const was = wasPaused.get(scene) ?? false;
|
|
2435
|
-
if (!scene.isPaused && was) {
|
|
2436
|
-
scene.onResume?.();
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
}
|
|
2440
|
-
};
|
|
2441
|
-
|
|
2442
|
-
// src/Process.ts
|
|
2443
|
-
var Process = class _Process {
|
|
2444
|
-
static {
|
|
2445
|
-
__name(this, "Process");
|
|
2446
|
-
}
|
|
2447
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
2448
|
-
updateFn;
|
|
2449
|
-
onCompleteFn;
|
|
2450
|
-
duration;
|
|
2451
|
-
loop;
|
|
2452
|
-
/** Tags for filtering/grouping. */
|
|
2453
|
-
tags;
|
|
2454
|
-
elapsed = 0;
|
|
2455
|
-
_completed = false;
|
|
2456
|
-
_paused = false;
|
|
2457
|
-
_cancelled = false;
|
|
2458
|
-
resolvePromise;
|
|
2459
|
-
/** Create a timer that fires `onComplete` after `duration` ms. */
|
|
2460
|
-
static delay(duration, onComplete, tags) {
|
|
2461
|
-
const opts = { duration };
|
|
2462
|
-
if (onComplete !== void 0) opts.onComplete = onComplete;
|
|
2463
|
-
if (tags !== void 0) opts.tags = tags;
|
|
2464
|
-
return new _Process(opts);
|
|
2465
|
-
}
|
|
2466
|
-
constructor(options) {
|
|
2467
|
-
this.updateFn = options.update ?? (() => {
|
|
2468
|
-
});
|
|
2469
|
-
this.onCompleteFn = options.onComplete;
|
|
2470
|
-
this.duration = options.duration;
|
|
2471
|
-
this.loop = options.loop ?? false;
|
|
2472
|
-
this.tags = options.tags ?? [];
|
|
2473
|
-
}
|
|
2474
|
-
/** Whether the process has completed. */
|
|
2475
|
-
get completed() {
|
|
2476
|
-
return this._completed;
|
|
2477
|
-
}
|
|
2478
|
-
/** Whether the process is paused. */
|
|
2479
|
-
get paused() {
|
|
2480
|
-
return this._paused;
|
|
2481
|
-
}
|
|
2482
|
-
/** Pause the process. */
|
|
2483
|
-
pause() {
|
|
2484
|
-
this._paused = true;
|
|
3576
|
+
_snapshotPauseStates() {
|
|
3577
|
+
return new Map(
|
|
3578
|
+
this.stack.map((scene) => [scene, scene.isPaused])
|
|
3579
|
+
);
|
|
2485
3580
|
}
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
3581
|
+
_assertNotMutating(method) {
|
|
3582
|
+
if (this._mutationDepth === 0) return;
|
|
3583
|
+
throw new Error(
|
|
3584
|
+
`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().`
|
|
3585
|
+
);
|
|
2489
3586
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
3587
|
+
async _withMutation(work) {
|
|
3588
|
+
this._mutationDepth++;
|
|
3589
|
+
try {
|
|
3590
|
+
return await work();
|
|
3591
|
+
} finally {
|
|
3592
|
+
this._mutationDepth--;
|
|
3593
|
+
}
|
|
2495
3594
|
}
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
3595
|
+
_withMutationSync(work) {
|
|
3596
|
+
this._mutationDepth++;
|
|
3597
|
+
try {
|
|
3598
|
+
return work();
|
|
3599
|
+
} finally {
|
|
3600
|
+
this._mutationDepth--;
|
|
3601
|
+
}
|
|
2502
3602
|
}
|
|
2503
|
-
/**
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
this.elapsed += dt;
|
|
2510
|
-
if (this.duration !== void 0 && this.elapsed >= this.duration) {
|
|
2511
|
-
const result2 = this.updateFn(dt, this.elapsed);
|
|
2512
|
-
if (this.loop && result2 !== true) {
|
|
2513
|
-
this.elapsed = this.elapsed % this.duration;
|
|
2514
|
-
return;
|
|
3603
|
+
/** Fire onPause() for scenes that transitioned from not-paused to paused. */
|
|
3604
|
+
_firePauseTransitions(wasPaused) {
|
|
3605
|
+
for (const scene of this.stack) {
|
|
3606
|
+
const was = wasPaused.get(scene) ?? false;
|
|
3607
|
+
if (scene.isPaused && !was) {
|
|
3608
|
+
scene.onPause?.();
|
|
2515
3609
|
}
|
|
2516
|
-
this.complete();
|
|
2517
|
-
return;
|
|
2518
3610
|
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
3611
|
+
}
|
|
3612
|
+
/** Fire onResume() for scenes that transitioned from paused to not-paused. */
|
|
3613
|
+
_fireResumeTransitions(wasPaused) {
|
|
3614
|
+
for (const scene of this.stack) {
|
|
3615
|
+
const was = wasPaused.get(scene) ?? false;
|
|
3616
|
+
if (!scene.isPaused && was) {
|
|
3617
|
+
scene.onResume?.();
|
|
2524
3618
|
}
|
|
2525
|
-
this.complete();
|
|
2526
3619
|
}
|
|
2527
3620
|
}
|
|
2528
|
-
/**
|
|
2529
|
-
* Reset the process to its initial state so it can be re-run.
|
|
2530
|
-
* @internal Used by Sequence for loop/repeat with direct instances.
|
|
2531
|
-
*/
|
|
2532
|
-
_reset() {
|
|
2533
|
-
this.elapsed = 0;
|
|
2534
|
-
this._completed = false;
|
|
2535
|
-
this._paused = false;
|
|
2536
|
-
this._cancelled = false;
|
|
2537
|
-
delete this.resolvePromise;
|
|
2538
|
-
}
|
|
2539
|
-
complete() {
|
|
2540
|
-
this._completed = true;
|
|
2541
|
-
this.onCompleteFn?.();
|
|
2542
|
-
this.resolvePromise?.();
|
|
2543
|
-
}
|
|
2544
3621
|
};
|
|
2545
|
-
var easeLinear = /* @__PURE__ */ __name((t) => t, "easeLinear");
|
|
2546
|
-
var easeInQuad = /* @__PURE__ */ __name((t) => t * t, "easeInQuad");
|
|
2547
|
-
var easeOutQuad = /* @__PURE__ */ __name((t) => t * (2 - t), "easeOutQuad");
|
|
2548
|
-
var easeInOutQuad = /* @__PURE__ */ __name((t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, "easeInOutQuad");
|
|
2549
|
-
var easeOutBounce = /* @__PURE__ */ __name((t) => {
|
|
2550
|
-
if (t < 1 / 2.75) {
|
|
2551
|
-
return 7.5625 * t * t;
|
|
2552
|
-
} else if (t < 2 / 2.75) {
|
|
2553
|
-
const t2 = t - 1.5 / 2.75;
|
|
2554
|
-
return 7.5625 * t2 * t2 + 0.75;
|
|
2555
|
-
} else if (t < 2.5 / 2.75) {
|
|
2556
|
-
const t2 = t - 2.25 / 2.75;
|
|
2557
|
-
return 7.5625 * t2 * t2 + 0.9375;
|
|
2558
|
-
} else {
|
|
2559
|
-
const t2 = t - 2.625 / 2.75;
|
|
2560
|
-
return 7.5625 * t2 * t2 + 0.984375;
|
|
2561
|
-
}
|
|
2562
|
-
}, "easeOutBounce");
|
|
2563
3622
|
|
|
2564
3623
|
// src/Tween.ts
|
|
2565
3624
|
var Tween = {
|
|
@@ -2786,7 +3845,9 @@ var ProcessSlot = class {
|
|
|
2786
3845
|
};
|
|
2787
3846
|
|
|
2788
3847
|
// src/ProcessComponent.ts
|
|
2789
|
-
var
|
|
3848
|
+
var _ProcessComponent_decorators, _init2, _a2;
|
|
3849
|
+
_ProcessComponent_decorators = [serializable];
|
|
3850
|
+
var ProcessComponent = class extends (_a2 = Component) {
|
|
2790
3851
|
static {
|
|
2791
3852
|
__name(this, "ProcessComponent");
|
|
2792
3853
|
}
|
|
@@ -2856,10 +3917,18 @@ var ProcessComponent = class extends Component {
|
|
|
2856
3917
|
onDestroy() {
|
|
2857
3918
|
this.cancel();
|
|
2858
3919
|
}
|
|
3920
|
+
serialize() {
|
|
3921
|
+
return null;
|
|
3922
|
+
}
|
|
2859
3923
|
};
|
|
3924
|
+
_init2 = __decoratorStart(_a2);
|
|
3925
|
+
ProcessComponent = __decorateElement(_init2, 0, "ProcessComponent", _ProcessComponent_decorators, ProcessComponent);
|
|
3926
|
+
__runInitializers(_init2, 1, ProcessComponent);
|
|
2860
3927
|
|
|
2861
3928
|
// src/KeyframeAnimator.ts
|
|
2862
|
-
var
|
|
3929
|
+
var _KeyframeAnimator_decorators, _init3, _a3;
|
|
3930
|
+
_KeyframeAnimator_decorators = [serializable];
|
|
3931
|
+
var KeyframeAnimator = class extends (_a3 = Component) {
|
|
2863
3932
|
static {
|
|
2864
3933
|
__name(this, "KeyframeAnimator");
|
|
2865
3934
|
}
|
|
@@ -2911,6 +3980,9 @@ var KeyframeAnimator = class extends Component {
|
|
|
2911
3980
|
onDestroy() {
|
|
2912
3981
|
this.stopAll();
|
|
2913
3982
|
}
|
|
3983
|
+
serialize() {
|
|
3984
|
+
return null;
|
|
3985
|
+
}
|
|
2914
3986
|
stopInternal(name, complete) {
|
|
2915
3987
|
const process = this.active.get(name);
|
|
2916
3988
|
if (!process) return;
|
|
@@ -2919,6 +3991,9 @@ var KeyframeAnimator = class extends Component {
|
|
|
2919
3991
|
this.defs[name]?.onExit?.(complete);
|
|
2920
3992
|
}
|
|
2921
3993
|
};
|
|
3994
|
+
_init3 = __decoratorStart(_a3);
|
|
3995
|
+
KeyframeAnimator = __decorateElement(_init3, 0, "KeyframeAnimator", _KeyframeAnimator_decorators, KeyframeAnimator);
|
|
3996
|
+
__runInitializers(_init3, 1, KeyframeAnimator);
|
|
2922
3997
|
|
|
2923
3998
|
// src/Sequence.ts
|
|
2924
3999
|
var Sequence = class {
|
|
@@ -3070,36 +4145,94 @@ var ProcessSystem = class extends System {
|
|
|
3070
4145
|
/** Global time scale multiplier. Stacks multiplicatively with per-scene timeScale. */
|
|
3071
4146
|
timeScale = 1;
|
|
3072
4147
|
sceneManager;
|
|
3073
|
-
|
|
4148
|
+
globalProcesses = /* @__PURE__ */ new Set();
|
|
4149
|
+
scenePools = /* @__PURE__ */ new Map();
|
|
4150
|
+
_unregisterSceneHook = null;
|
|
3074
4151
|
onRegister(context) {
|
|
3075
4152
|
this.sceneManager = context.resolve(SceneManagerKey);
|
|
4153
|
+
const hooks = context.tryResolve(SceneHookRegistryKey);
|
|
4154
|
+
this._unregisterSceneHook = hooks?.register({
|
|
4155
|
+
afterExit: /* @__PURE__ */ __name((scene) => this.cancelForScene(scene), "afterExit")
|
|
4156
|
+
}) ?? null;
|
|
4157
|
+
}
|
|
4158
|
+
onUnregister() {
|
|
4159
|
+
this._unregisterSceneHook?.();
|
|
4160
|
+
this._unregisterSceneHook = null;
|
|
4161
|
+
for (const p of this.globalProcesses) {
|
|
4162
|
+
if (!p.completed) p.cancel();
|
|
4163
|
+
}
|
|
4164
|
+
this.globalProcesses.clear();
|
|
4165
|
+
for (const pool of this.scenePools.values()) {
|
|
4166
|
+
for (const p of pool) {
|
|
4167
|
+
if (!p.completed) p.cancel();
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
this.scenePools.clear();
|
|
3076
4171
|
}
|
|
3077
|
-
/**
|
|
4172
|
+
/**
|
|
4173
|
+
* Add an engine-global process. Ticked under the global timeScale only;
|
|
4174
|
+
* NOT gated by per-scene pause or scaled by per-scene timeScale. Use this
|
|
4175
|
+
* for cross-scene effects (e.g. screen-scope filter fades on `app.stage`)
|
|
4176
|
+
* or processes that have no owning scene.
|
|
4177
|
+
*/
|
|
3078
4178
|
add(process) {
|
|
3079
|
-
this.
|
|
4179
|
+
this.globalProcesses.add(process);
|
|
4180
|
+
return process;
|
|
4181
|
+
}
|
|
4182
|
+
/**
|
|
4183
|
+
* Add a process bound to a specific scene's lifecycle. Ticked only while
|
|
4184
|
+
* the scene is active (not paused) and scaled by the scene's `timeScale`,
|
|
4185
|
+
* exactly like an entity-owned `ProcessComponent`. Use this for layer or
|
|
4186
|
+
* scene-scope effect fades that should pause with the scene.
|
|
4187
|
+
*/
|
|
4188
|
+
addForScene(scene, process) {
|
|
4189
|
+
let pool = this.scenePools.get(scene);
|
|
4190
|
+
if (!pool) {
|
|
4191
|
+
pool = /* @__PURE__ */ new Set();
|
|
4192
|
+
this.scenePools.set(scene, pool);
|
|
4193
|
+
}
|
|
4194
|
+
pool.add(process);
|
|
3080
4195
|
return process;
|
|
3081
4196
|
}
|
|
3082
|
-
/** Cancel
|
|
4197
|
+
/** Cancel engine-global processes, optionally by tag. */
|
|
3083
4198
|
cancel(tag) {
|
|
3084
|
-
for (const p of this.
|
|
4199
|
+
for (const p of this.globalProcesses) {
|
|
3085
4200
|
if (tag === void 0 || p.tags.includes(tag)) {
|
|
3086
4201
|
p.cancel();
|
|
4202
|
+
this.globalProcesses.delete(p);
|
|
3087
4203
|
}
|
|
3088
4204
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
4205
|
+
}
|
|
4206
|
+
/** Cancel every scene-bound process for `scene`, optionally by tag. */
|
|
4207
|
+
cancelForScene(scene, tag) {
|
|
4208
|
+
const pool = this.scenePools.get(scene);
|
|
4209
|
+
if (!pool) return;
|
|
4210
|
+
for (const p of pool) {
|
|
4211
|
+
if (tag === void 0 || p.tags.includes(tag)) {
|
|
4212
|
+
p.cancel();
|
|
4213
|
+
pool.delete(p);
|
|
4214
|
+
}
|
|
3091
4215
|
}
|
|
4216
|
+
if (pool.size === 0) this.scenePools.delete(scene);
|
|
3092
4217
|
}
|
|
3093
4218
|
update(dt) {
|
|
3094
4219
|
const globalScaledDt = dt * this.timeScale;
|
|
3095
|
-
for (const p of this.
|
|
4220
|
+
for (const p of this.globalProcesses) {
|
|
3096
4221
|
p._update(globalScaledDt);
|
|
3097
4222
|
if (p.completed) {
|
|
3098
|
-
this.
|
|
4223
|
+
this.globalProcesses.delete(p);
|
|
3099
4224
|
}
|
|
3100
4225
|
}
|
|
3101
4226
|
for (const scene of this.sceneManager.activeScenes) {
|
|
3102
4227
|
const effectiveDt = globalScaledDt * scene.timeScale;
|
|
4228
|
+
const pool = this.scenePools.get(scene);
|
|
4229
|
+
if (pool) {
|
|
4230
|
+
for (const p of pool) {
|
|
4231
|
+
p._update(effectiveDt);
|
|
4232
|
+
if (p.completed) pool.delete(p);
|
|
4233
|
+
}
|
|
4234
|
+
if (pool.size === 0) this.scenePools.delete(scene);
|
|
4235
|
+
}
|
|
3103
4236
|
for (const entity of scene.getEntities()) {
|
|
3104
4237
|
if (entity.isDestroyed) continue;
|
|
3105
4238
|
const pc = entity.tryGet(ProcessComponent);
|
|
@@ -3110,145 +4243,45 @@ var ProcessSystem = class extends System {
|
|
|
3110
4243
|
}
|
|
3111
4244
|
};
|
|
3112
4245
|
|
|
3113
|
-
// src/
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
this.engine = engine;
|
|
3121
|
-
}
|
|
3122
|
-
/** Full state snapshot (serializable). */
|
|
3123
|
-
snapshot() {
|
|
3124
|
-
return {
|
|
3125
|
-
frameCount: this.engine.loop.frameCount,
|
|
3126
|
-
sceneStack: this.getSceneStack(),
|
|
3127
|
-
entityCount: this.countEntities(),
|
|
3128
|
-
systemCount: this.getSystems().length,
|
|
3129
|
-
errors: this.getErrors()
|
|
3130
|
-
};
|
|
3131
|
-
}
|
|
3132
|
-
/** Find entity by name in the active scene. */
|
|
3133
|
-
getEntityByName(name) {
|
|
3134
|
-
const entity = this.findActiveEntity(name);
|
|
3135
|
-
if (!entity) return void 0;
|
|
3136
|
-
return this.entityToSnapshot(entity);
|
|
3137
|
-
}
|
|
3138
|
-
/** Get entity position (from Transform component). */
|
|
3139
|
-
getEntityPosition(name) {
|
|
3140
|
-
const entity = this.findActiveEntity(name);
|
|
3141
|
-
if (!entity) return void 0;
|
|
3142
|
-
const transform = this.getTransform(entity);
|
|
3143
|
-
if (!transform) return void 0;
|
|
3144
|
-
return { x: transform.position.x, y: transform.position.y };
|
|
3145
|
-
}
|
|
3146
|
-
/** Check if an entity has a component by class name string. */
|
|
3147
|
-
hasComponent(entityName, componentClass) {
|
|
3148
|
-
return this.findComponentByName(entityName, componentClass) !== void 0;
|
|
3149
|
-
}
|
|
3150
|
-
/** Get component data (serializable subset) by class name string. */
|
|
3151
|
-
getComponentData(entityName, componentClass) {
|
|
3152
|
-
const comp = this.findComponentByName(entityName, componentClass);
|
|
3153
|
-
if (!comp) return void 0;
|
|
3154
|
-
return this.serializeComponent(comp);
|
|
3155
|
-
}
|
|
3156
|
-
/** Get all entities in the active scene as snapshots. */
|
|
3157
|
-
getEntities() {
|
|
3158
|
-
const scene = this.engine.scenes.active;
|
|
3159
|
-
if (!scene) return [];
|
|
3160
|
-
const result = [];
|
|
3161
|
-
for (const entity of scene.getEntities()) {
|
|
3162
|
-
if (!entity.isDestroyed) {
|
|
3163
|
-
result.push(this.entityToSnapshot(entity));
|
|
4246
|
+
// src/ProcessQueue.ts
|
|
4247
|
+
function makeQueue(route) {
|
|
4248
|
+
const ours = /* @__PURE__ */ new Set();
|
|
4249
|
+
return {
|
|
4250
|
+
run(p) {
|
|
4251
|
+
for (const old of ours) {
|
|
4252
|
+
if (old.completed) ours.delete(old);
|
|
3164
4253
|
}
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
entityCount: scene.getEntities().size,
|
|
3173
|
-
paused: scene.isPaused
|
|
3174
|
-
}));
|
|
3175
|
-
}
|
|
3176
|
-
/** Get active system info. */
|
|
3177
|
-
getSystems() {
|
|
3178
|
-
const scheduler = this.engine.context.tryResolve(SystemSchedulerKey);
|
|
3179
|
-
if (!scheduler) return [];
|
|
3180
|
-
return scheduler.getAllSystems().map((sys) => ({
|
|
3181
|
-
name: sys.constructor.name,
|
|
3182
|
-
phase: sys.phase,
|
|
3183
|
-
priority: sys.priority,
|
|
3184
|
-
enabled: sys.enabled
|
|
3185
|
-
}));
|
|
3186
|
-
}
|
|
3187
|
-
/** Get disabled components/systems from error boundary. */
|
|
3188
|
-
getErrors() {
|
|
3189
|
-
const boundary = this.engine.context.tryResolve(ErrorBoundaryKey);
|
|
3190
|
-
if (!boundary) return { disabledSystems: [], disabledComponents: [] };
|
|
3191
|
-
const disabled = boundary.getDisabled();
|
|
3192
|
-
return {
|
|
3193
|
-
disabledSystems: disabled.systems.map(
|
|
3194
|
-
(s) => s.system.constructor.name
|
|
3195
|
-
),
|
|
3196
|
-
disabledComponents: disabled.components.map((c) => ({
|
|
3197
|
-
entity: c.component.entity?.name ?? "unknown",
|
|
3198
|
-
component: c.component.constructor.name,
|
|
3199
|
-
error: c.error
|
|
3200
|
-
}))
|
|
3201
|
-
};
|
|
3202
|
-
}
|
|
3203
|
-
findActiveEntity(name) {
|
|
3204
|
-
return this.engine.scenes.active?.findEntity(name);
|
|
3205
|
-
}
|
|
3206
|
-
findComponentByName(entityName, componentClass) {
|
|
3207
|
-
const entity = this.findActiveEntity(entityName);
|
|
3208
|
-
if (!entity) return void 0;
|
|
3209
|
-
for (const comp of entity.getAll()) {
|
|
3210
|
-
if (comp.constructor.name === componentClass) return comp;
|
|
3211
|
-
}
|
|
3212
|
-
return void 0;
|
|
3213
|
-
}
|
|
3214
|
-
entityToSnapshot(entity) {
|
|
3215
|
-
const transform = this.getTransform(entity);
|
|
3216
|
-
const snapshot = {
|
|
3217
|
-
id: entity.id,
|
|
3218
|
-
name: entity.name,
|
|
3219
|
-
tags: [...entity.tags],
|
|
3220
|
-
components: [...entity.getAll()].map((c) => c.constructor.name)
|
|
3221
|
-
};
|
|
3222
|
-
if (transform) {
|
|
3223
|
-
snapshot.position = {
|
|
3224
|
-
x: transform.position.x,
|
|
3225
|
-
y: transform.position.y
|
|
3226
|
-
};
|
|
3227
|
-
}
|
|
3228
|
-
return snapshot;
|
|
3229
|
-
}
|
|
3230
|
-
getTransform(entity) {
|
|
3231
|
-
return entity.has(Transform) ? entity.get(Transform) : void 0;
|
|
3232
|
-
}
|
|
3233
|
-
serializeComponent(comp) {
|
|
3234
|
-
const result = {};
|
|
3235
|
-
for (const key of Object.getOwnPropertyNames(comp)) {
|
|
3236
|
-
if (key === "entity") continue;
|
|
3237
|
-
const value = comp[key];
|
|
3238
|
-
if (typeof value !== "function") {
|
|
3239
|
-
result[key] = value;
|
|
4254
|
+
route(p);
|
|
4255
|
+
ours.add(p);
|
|
4256
|
+
return p;
|
|
4257
|
+
},
|
|
4258
|
+
cancelAll() {
|
|
4259
|
+
for (const p of ours) {
|
|
4260
|
+
if (!p.completed) p.cancel();
|
|
3240
4261
|
}
|
|
4262
|
+
ours.clear();
|
|
3241
4263
|
}
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
4264
|
+
};
|
|
4265
|
+
}
|
|
4266
|
+
__name(makeQueue, "makeQueue");
|
|
4267
|
+
function makeEntityScopedQueue(entity) {
|
|
4268
|
+
return makeQueue((p) => {
|
|
4269
|
+
let pc = entity.tryGet(ProcessComponent);
|
|
4270
|
+
if (!pc) {
|
|
4271
|
+
pc = entity.add(new ProcessComponent());
|
|
3248
4272
|
}
|
|
3249
|
-
|
|
3250
|
-
}
|
|
3251
|
-
}
|
|
4273
|
+
pc.run(p);
|
|
4274
|
+
});
|
|
4275
|
+
}
|
|
4276
|
+
__name(makeEntityScopedQueue, "makeEntityScopedQueue");
|
|
4277
|
+
function makeSceneScopedQueue(processSystem, scene) {
|
|
4278
|
+
return makeQueue((p) => processSystem.addForScene(scene, p));
|
|
4279
|
+
}
|
|
4280
|
+
__name(makeSceneScopedQueue, "makeSceneScopedQueue");
|
|
4281
|
+
function makeGlobalScopedQueue(processSystem) {
|
|
4282
|
+
return makeQueue((p) => processSystem.add(p));
|
|
4283
|
+
}
|
|
4284
|
+
__name(makeGlobalScopedQueue, "makeGlobalScopedQueue");
|
|
3252
4285
|
|
|
3253
4286
|
// src/Engine.ts
|
|
3254
4287
|
var Engine = class {
|
|
@@ -3302,6 +4335,15 @@ var Engine = class {
|
|
|
3302
4335
|
this.context.register(SystemSchedulerKey, this.scheduler);
|
|
3303
4336
|
this.context.register(AssetManagerKey, this.assets);
|
|
3304
4337
|
this.context.register(SceneHookRegistryKey, this.sceneHooks);
|
|
4338
|
+
this.sceneHooks.register({
|
|
4339
|
+
beforeEnter: /* @__PURE__ */ __name((scene) => {
|
|
4340
|
+
scene._registerScoped(RandomKey, this.inspector.createSceneRandom());
|
|
4341
|
+
this.inspector.attachSceneEventObserver(scene);
|
|
4342
|
+
}, "beforeEnter"),
|
|
4343
|
+
afterExit: /* @__PURE__ */ __name((scene) => {
|
|
4344
|
+
this.inspector.detachSceneEventObserver(scene);
|
|
4345
|
+
}, "afterExit")
|
|
4346
|
+
});
|
|
3305
4347
|
this.scenes._setContext(this.context);
|
|
3306
4348
|
this.registerBuiltInSystems();
|
|
3307
4349
|
this.loop.setCallbacks({
|
|
@@ -3383,6 +4425,7 @@ var Engine = class {
|
|
|
3383
4425
|
if (this.debug && typeof globalThis !== "undefined" && "__yage__" in globalThis) {
|
|
3384
4426
|
delete globalThis["__yage__"];
|
|
3385
4427
|
}
|
|
4428
|
+
this.inspector.dispose();
|
|
3386
4429
|
this.events.clear();
|
|
3387
4430
|
this.started = false;
|
|
3388
4431
|
}
|
|
@@ -3445,6 +4488,11 @@ var Engine = class {
|
|
|
3445
4488
|
}
|
|
3446
4489
|
};
|
|
3447
4490
|
|
|
4491
|
+
// src/RendererAdapter.ts
|
|
4492
|
+
var RendererAdapterKey = new ServiceKey(
|
|
4493
|
+
"rendererAdapter"
|
|
4494
|
+
);
|
|
4495
|
+
|
|
3448
4496
|
// src/test-utils.ts
|
|
3449
4497
|
var _TestScene = class extends Scene {
|
|
3450
4498
|
static {
|
|
@@ -3475,6 +4523,7 @@ function createMockScene(name = "mock-scene") {
|
|
|
3475
4523
|
ctx.register(ErrorBoundaryKey, boundary);
|
|
3476
4524
|
const scene = new _TestScene(name);
|
|
3477
4525
|
scene._setContext(ctx);
|
|
4526
|
+
scene._registerScoped(RandomKey, createRandomService(1234));
|
|
3478
4527
|
return { scene, context: ctx };
|
|
3479
4528
|
}
|
|
3480
4529
|
__name(createMockScene, "createMockScene");
|
|
@@ -3529,6 +4578,8 @@ var VERSION = "0.0.0";
|
|
|
3529
4578
|
QueryCache,
|
|
3530
4579
|
QueryCacheKey,
|
|
3531
4580
|
QueryResult,
|
|
4581
|
+
RandomKey,
|
|
4582
|
+
RendererAdapterKey,
|
|
3532
4583
|
SERIALIZABLE_KEY,
|
|
3533
4584
|
Scene,
|
|
3534
4585
|
SceneHookRegistry,
|
|
@@ -3549,9 +4600,11 @@ var VERSION = "0.0.0";
|
|
|
3549
4600
|
Vec2,
|
|
3550
4601
|
_resetEntityIdCounter,
|
|
3551
4602
|
advanceFrames,
|
|
4603
|
+
createDefaultRandomSeed,
|
|
3552
4604
|
createKeyframeTrack,
|
|
3553
4605
|
createMockEntity,
|
|
3554
4606
|
createMockScene,
|
|
4607
|
+
createRandomService,
|
|
3555
4608
|
createTestEngine,
|
|
3556
4609
|
defineBlueprint,
|
|
3557
4610
|
defineEvent,
|
|
@@ -3563,8 +4616,13 @@ var VERSION = "0.0.0";
|
|
|
3563
4616
|
easeOutQuad,
|
|
3564
4617
|
filterEntities,
|
|
3565
4618
|
getSerializableType,
|
|
4619
|
+
globalRandom,
|
|
3566
4620
|
interpolate,
|
|
3567
4621
|
isSerializable,
|
|
4622
|
+
makeEntityScopedQueue,
|
|
4623
|
+
makeGlobalScopedQueue,
|
|
4624
|
+
makeSceneScopedQueue,
|
|
4625
|
+
normalizeSeed,
|
|
3568
4626
|
resolveTransition,
|
|
3569
4627
|
serializable,
|
|
3570
4628
|
trait
|