@skewedaspect/sage 0.10.0-beta.2 → 0.10.0-rc.1

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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/dist/behaviors/characterController.d.ts +58 -0
  4. package/dist/behaviors/physicsBody.d.ts +38 -0
  5. package/dist/behaviors/sound.d.ts +2 -2
  6. package/dist/classes/debugConsole.d.ts +8 -16
  7. package/dist/classes/gameEngine.d.ts +56 -94
  8. package/dist/classes/gameLevel.d.ts +23 -24
  9. package/dist/classes/level.d.ts +44 -53
  10. package/dist/classes/loggers/consoleBackend.d.ts +1 -1
  11. package/dist/classes/loggers/nullBackend.d.ts +1 -1
  12. package/dist/debug/builtins.d.ts +4 -6
  13. package/dist/engines/audio.d.ts +4 -7
  14. package/dist/engines/scene.d.ts +14 -73
  15. package/dist/entities/defineEntity.d.ts +7 -5
  16. package/dist/entities/entityPicking.d.ts +1 -1
  17. package/dist/entities/meshConfig.d.ts +11 -3
  18. package/dist/entities/nodeEntity.d.ts +15 -6
  19. package/dist/entities/nodePool.d.ts +1 -1
  20. package/dist/entities/nodeSimulation.d.ts +22 -11
  21. package/dist/entities/nodeSimulationContext.d.ts +4 -4
  22. package/dist/entities/standardMeshSource.d.ts +2 -2
  23. package/dist/events/payloads.d.ts +18 -23
  24. package/dist/handlers/collider.d.ts +7 -12
  25. package/dist/handlers/index.d.ts +13 -14
  26. package/dist/handlers/lod.d.ts +11 -9
  27. package/dist/handlers/occlusion.d.ts +8 -0
  28. package/dist/handlers/postProcessing.d.ts +7 -2
  29. package/dist/handlers/postProcessingFrameGraph.d.ts +7 -2
  30. package/dist/handlers/postProcessingPipeline.d.ts +4 -3
  31. package/dist/handlers/sound.d.ts +2 -4
  32. package/dist/handlers/trigger.d.ts +2 -4
  33. package/dist/handlers/visible.d.ts +4 -5
  34. package/dist/input/actionContext.d.ts +3 -1
  35. package/dist/input/bindings/delta.d.ts +2 -2
  36. package/dist/input/bindings/toggle.d.ts +2 -2
  37. package/dist/input/bindings/trigger.d.ts +2 -2
  38. package/dist/input/bindings/value.d.ts +2 -2
  39. package/dist/input/configuration.d.ts +3 -3
  40. package/dist/input/deliverAction.d.ts +1 -1
  41. package/dist/input/deviceReaders/gamepad.d.ts +2 -2
  42. package/dist/input/deviceReaders/keyboard.d.ts +2 -2
  43. package/dist/input/deviceReaders/mouse.d.ts +2 -2
  44. package/dist/input/inputCapture.d.ts +1 -1
  45. package/dist/input/readerFor.d.ts +3 -0
  46. package/dist/input/resolveBinding.d.ts +3 -3
  47. package/dist/interfaces/game.d.ts +19 -31
  48. package/dist/interfaces/level.d.ts +48 -51
  49. package/dist/interfaces/lifecycle.d.ts +3 -41
  50. package/dist/interfaces/logger.d.ts +0 -10
  51. package/dist/managers/asset.d.ts +10 -17
  52. package/dist/managers/audio.d.ts +10 -21
  53. package/dist/managers/colliderDebug.d.ts +7 -4
  54. package/dist/managers/game.d.ts +22 -83
  55. package/dist/managers/input.d.ts +11 -10
  56. package/dist/managers/level.d.ts +69 -132
  57. package/dist/managers/outline.d.ts +2 -2
  58. package/dist/managers/simulationSave.d.ts +16 -16
  59. package/dist/postProcessingFrameGraph-Dx3e6D33.js +93 -0
  60. package/dist/postProcessingFrameGraph-Dx3e6D33.js.map +1 -0
  61. package/dist/sage.d.ts +85 -81
  62. package/dist/sage.es.js +2067 -1829
  63. package/dist/sage.es.js.map +1 -1
  64. package/dist/utils/graphics.d.ts +3 -3
  65. package/dist/utils/logger.d.ts +3 -7
  66. package/dist/utils/physics.d.ts +9 -1
  67. package/dist/utils/timer.d.ts +1 -1
  68. package/dist/utils/vectors.d.ts +13 -10
  69. package/package.json +7 -8
  70. package/dist/classes/eventBus.d.ts +0 -141
  71. package/dist/events/index.d.ts +0 -2
  72. package/dist/events/types.d.ts +0 -17
  73. package/dist/handlers/occluder.d.ts +0 -13
  74. package/dist/postProcessingFrameGraph-C3Og4kaS.js +0 -78
  75. package/dist/postProcessingFrameGraph-C3Og4kaS.js.map +0 -1
  76. package/dist/sage.umd.js +0 -2
  77. package/dist/sage.umd.js.map +0 -1
  78. package/dist/utils/id.d.ts +0 -11
  79. package/dist/utils/stateMachine.d.ts +0 -43
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christopher Case
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # @skewedaspect/sage
2
+
3
+ The Babylon.js game engine: `NodeEntity` and `NodeSimulation` on top of `@skewedaspect/sage-core`'s entity
4
+ system, levels and their scenes, physics through Havok, the input system, audio, pooling, the parent-child
5
+ hierarchy, debug tooling, and the save manager.
6
+
7
+ ```
8
+ npm install @skewedaspect/sage @babylonjs/core @babylonjs/havok @babylonjs/loaders
9
+ ```
10
+
11
+ Documentation: the site built from `site/docs` in https://gitlab.com/skewed-aspect/sage/sage
@@ -0,0 +1,58 @@
1
+ import { Vector3, CharacterSurfaceInfo } from '@babylonjs/core';
2
+ import { Behavior } from '@skewedaspect/sage-core';
3
+ import { NodeBehaviorHooks, NodeEntity, Vector3State } from "../entities/nodeEntity.js";
4
+ /** A capsule height and a speed under a name (design/character.md#stances). */
5
+ export interface StanceConfig {
6
+ height: number;
7
+ radius: number;
8
+ /** Metres per second. */
9
+ speed: number;
10
+ }
11
+ export interface CharacterControllerState {
12
+ /** How hard a game is asking to move right, -1 to 1. */
13
+ moveX: number;
14
+ /** How hard a game is asking to move forward, -1 to 1. */
15
+ moveZ: number;
16
+ wantsJump: boolean;
17
+ wantsSprint: boolean;
18
+ /** The stance a game is asking for, by name. */
19
+ wantsStance: string;
20
+ stances: Record<string, StanceConfig>;
21
+ /** Take the tallest stance's capsule from the mesh instead. */
22
+ measureCapsule: boolean;
23
+ /** What sprinting multiplies the current stance's speed by. */
24
+ sprintScale: number;
25
+ /** Upward metres per second a jump starts with. */
26
+ jumpSpeed: number;
27
+ /** How long a jump request survives, in fixed steps. */
28
+ jumpBufferSteps: number;
29
+ /** How long after leaving the ground a jump is still allowed, in fixed steps. */
30
+ coyoteSteps: number;
31
+ /** Metres per second. */
32
+ velocity: Vector3State;
33
+ /** The character's foot, not the middle of its capsule (design/character.md#state). */
34
+ position: Vector3State;
35
+ /** True only when the last step's support query answered `SUPPORTED`. */
36
+ grounded: boolean;
37
+ /** The stance the character is actually in, which names an entry in `stances`. */
38
+ stance: string;
39
+ }
40
+ export declare class CharacterControllerBehavior extends Behavior<CharacterControllerState, object, NodeEntity> implements NodeBehaviorHooks {
41
+ #private;
42
+ readonly defaults: CharacterControllerState;
43
+ onCreate(): void;
44
+ onMeshLoaded(): void;
45
+ onUpdate(duration: number): void;
46
+ onDestroy(): void;
47
+ /**
48
+ * The velocity the character wants this step, in world space, before the surface it is standing on is added
49
+ * (design/character.md#what-a-game-overrides). The jump is not this method's: an override answers for
50
+ * intent, and the behavior takes the jump around it.
51
+ */
52
+ protected desiredVelocity(duration: number, surface: CharacterSurfaceInfo): Vector3;
53
+ /**
54
+ * Whether that stance's capsule fits where the character is standing
55
+ * (design/character.md#what-a-game-overrides).
56
+ */
57
+ protected stanceFits(stance: string): boolean;
58
+ }
@@ -0,0 +1,38 @@
1
+ import { Behavior } from '@skewedaspect/sage-core';
2
+ import { NodeBehaviorHooks, NodeEntity, QuaternionState, Vector3State } from "../entities/nodeEntity.js";
3
+ /** What a body's outline can be (design/physics.md#shapes). */
4
+ export type BodyShape = 'box' | 'sphere' | 'cylinder' | 'convex' | 'mesh' | 'compound';
5
+ /** Who decides where a body goes (design/physics.md#static-dynamic-and-kinematic). */
6
+ export type BodyMotion = 'static' | 'dynamic' | 'kinematic';
7
+ /** One child of a compound shape, placed relative to the entity's node. */
8
+ export interface CompoundPart {
9
+ shape: 'box' | 'sphere' | 'cylinder';
10
+ position: Vector3State;
11
+ rotation: QuaternionState;
12
+ /** Full size: a box's width, height, depth; a sphere's diameter in x; a cylinder's diameter in x, height in y. */
13
+ extents: Vector3State;
14
+ }
15
+ export interface PhysicsBodyState {
16
+ shape: BodyShape;
17
+ /** The children of a `compound` shape. Unused by every other shape. */
18
+ parts: CompoundPart[];
19
+ motion: BodyMotion;
20
+ /** In kilograms. */
21
+ mass: number;
22
+ friction: number;
23
+ restitution: number;
24
+ position: Vector3State;
25
+ rotation: QuaternionState;
26
+ /** Metres per second. */
27
+ velocity: Vector3State;
28
+ /** Radians per second. */
29
+ angularVelocity: Vector3State;
30
+ }
31
+ export declare class HavokPhysicsBodyBehavior extends Behavior<PhysicsBodyState, object, NodeEntity> implements NodeBehaviorHooks {
32
+ #private;
33
+ static readonly ops: readonly ["applyImpulse"];
34
+ readonly defaults: PhysicsBodyState;
35
+ onMeshLoaded(): void;
36
+ onDestroy(): void;
37
+ applyImpulse(impulse: Vector3State, location?: Vector3State): void;
38
+ }
@@ -1,5 +1,5 @@
1
1
  import { Behavior } from '@skewedaspect/sage-core';
2
- import { NodeBehaviorHooks } from "../entities/nodeEntity.d.ts";
2
+ import { NodeBehaviorHooks, NodeEntity } from "../entities/nodeEntity.js";
3
3
  export interface SoundConfig {
4
4
  url: string;
5
5
  volume?: number;
@@ -11,7 +11,7 @@ export interface SoundConfig {
11
11
  export interface SoundState {
12
12
  sounds: Record<string, SoundConfig>;
13
13
  }
14
- export declare class SoundBehavior extends Behavior<SoundState> implements NodeBehaviorHooks {
14
+ export declare class SoundBehavior extends Behavior<SoundState, object, NodeEntity> implements NodeBehaviorHooks {
15
15
  #private;
16
16
  static readonly ops: readonly ["play", "stop"];
17
17
  readonly defaults: SoundState;
@@ -1,23 +1,15 @@
1
- import { Disposable } from "../interfaces/lifecycle.d.ts";
2
- interface DynamicGetter {
1
+ /** What `expose` takes to define a property that resolves fresh on every read. */
2
+ export interface DynamicGetter {
3
3
  get: () => unknown;
4
4
  }
5
- export declare class DebugConsole implements Disposable {
6
- private _namespaceName;
7
- private _entries;
8
- private _disposed;
5
+ export declare class DebugConsole {
6
+ #private;
9
7
  constructor(namespaceName?: string);
10
- expose(name: string, value: unknown | DynamicGetter): void;
8
+ /** Defines a property that resolves fresh on every read (design/debugging.md#debug-tooling). */
9
+ expose(name: string, getter: DynamicGetter): void;
10
+ /** Puts a value, a callable command included, on the namespace (design/debugging.md#debug-tooling). */
11
+ expose(name: string, value: unknown): void;
11
12
  setNamespace(name: string): void;
12
13
  get namespaceName(): string;
13
14
  dispose(): void;
14
- $teardown(): Promise<void>;
15
- private _isDynamicGetter;
16
- private _getNamespace;
17
- private _assertNamespaceFree;
18
- private _installNamespace;
19
- private _removeNamespace;
20
- private _defineGetter;
21
- private _defineValue;
22
15
  }
23
- export {};
@@ -1,33 +1,32 @@
1
- import { AbstractEngine, HavokPlugin, Scene } from '@babylonjs/core';
2
- import { EntityMeshLoader, GameCanvas } from "../interfaces/game.d.ts";
3
- import { Disposable } from "../interfaces/lifecycle.d.ts";
4
- import { NodeSimulation } from "../entities/nodeSimulation.d.ts";
5
- import { SceneEngine } from "../engines/scene.d.ts";
6
- import { AudioEngine } from "../engines/audio.d.ts";
7
- import { AssetManager } from "../managers/asset.d.ts";
8
- import { ColliderDebugManager } from "../managers/colliderDebug.d.ts";
9
- import { DebugConsole } from "./debugConsole.d.ts";
10
- import { GameManager } from "../managers/game.d.ts";
11
- import { InputManager } from "../managers/input.d.ts";
12
- import { LevelManager } from "../managers/level.d.ts";
13
- import { SimulationSaveManager } from "../managers/simulationSave.d.ts";
14
- import { AudioManager } from "../managers/audio.d.ts";
15
- import { GameEventBus, GameEventCallback, Unsubscribe } from "./eventBus.d.ts";
16
- import { LibraryEventPayloadMap } from "../events/payloads.d.ts";
17
- import { WildcardPattern } from "../events/types.d.ts";
18
- import { LoggingUtility } from "../utils/logger.d.ts";
19
- import { GameTimer } from "../utils/timer.d.ts";
1
+ import { AbstractEngine, Scene } from '@babylonjs/core';
2
+ import { EventBus, Unsubscribe } from '@skewedaspect/sage-core';
3
+ import { GameCanvas } from "../interfaces/game.js";
4
+ import { Disposable } from "../interfaces/lifecycle.js";
5
+ import { NodeEntityRegistry, NodeSimulation } from "../entities/nodeSimulation.js";
6
+ import { MeshLoader } from "../entities/nodeSimulationContext.js";
7
+ import { SceneEngine } from "../engines/scene.js";
8
+ import { AudioEngine } from "../engines/audio.js";
9
+ import { AssetManager } from "../managers/asset.js";
10
+ import { ColliderDebugManager } from "../managers/colliderDebug.js";
11
+ import { DebugConsole } from "./debugConsole.js";
12
+ import { GameManager } from "../managers/game.js";
13
+ import { InputManager } from "../managers/input.js";
14
+ import { LevelManager } from "../managers/level.js";
15
+ import { SimulationSaveManager } from "../managers/simulationSave.js";
16
+ import { AudioManager } from "../managers/audio.js";
17
+ import { LoggingUtility } from "../utils/logger.js";
18
+ import { GameTimer } from "../utils/timer.js";
20
19
  /**
21
20
  * Type definition for game lifecycle hook functions
22
21
  */
23
- export type GameHook = (gameEngine: GameEngine) => Promise<void>;
24
- /** Builds a NodeSimulation bound to a scene and a mesh source -- a fresh one is built for every level that loads. */
25
- export type SimulationFactory = (scene: Scene, meshSource?: EntityMeshLoader) => NodeSimulation;
22
+ export type GameHook<Registry extends NodeEntityRegistry = NodeEntityRegistry> = (gameEngine: GameEngine<Registry>) => Promise<void>;
23
+ /** Builds a NodeSimulation named and bound to a scene and a mesh source -- a fresh one for every level that loads. */
24
+ export type SimulationFactory<Registry extends NodeEntityRegistry = NodeEntityRegistry> = (scene: Scene, name: string, meshSource?: MeshLoader) => NodeSimulation<Registry>;
26
25
  /**
27
26
  * Interface representing the engines used in the game.
28
27
  * All engines must implement Disposable for proper cleanup.
29
28
  */
30
- interface Engines extends Record<string, Disposable | undefined> {
29
+ export interface Engines extends Record<string, Disposable | undefined> {
31
30
  sceneEngine: SceneEngine;
32
31
  audioEngine?: AudioEngine;
33
32
  }
@@ -35,35 +34,34 @@ interface Engines extends Record<string, Disposable | undefined> {
35
34
  * Interface representing the managers used in the game.
36
35
  * All managers must implement Disposable for proper cleanup.
37
36
  */
38
- interface Managers extends Record<string, Disposable | undefined> {
37
+ export interface Managers<Registry extends NodeEntityRegistry = NodeEntityRegistry> extends Record<string, Disposable | undefined> {
39
38
  assetManager: AssetManager;
40
- gameManager: GameManager;
39
+ gameManager: GameManager<Registry>;
41
40
  inputManager: InputManager;
42
- levelManager: LevelManager;
43
- saveManager: SimulationSaveManager;
41
+ levelManager: LevelManager<Registry>;
42
+ saveManager: SimulationSaveManager<Registry>;
44
43
  audioManager?: AudioManager;
45
44
  }
46
45
  /**
47
46
  * Interface representing the debug tools available on the engine.
48
47
  */
49
- interface DebugTools {
48
+ export interface DebugTools {
50
49
  colliders: ColliderDebugManager;
51
50
  console: DebugConsole | null;
52
51
  expose(name: string, value: unknown): void;
53
52
  }
54
53
  /**
55
- * Central hub that owns the render loop, physics, event bus, and all managers.
54
+ * Central hub that owns the render loop, the event bus, and all managers.
56
55
  * Created via `createGameEngine()` in sage.ts.
57
56
  */
58
- export declare class GameEngine {
57
+ export declare class GameEngine<Registry extends NodeEntityRegistry = NodeEntityRegistry> {
58
+ #private;
59
59
  canvas: GameCanvas;
60
60
  renderEngine: AbstractEngine;
61
- physics: HavokPlugin;
62
- managers: Managers;
61
+ managers: Managers<Registry>;
63
62
  engines: Engines;
64
- eventBus: GameEventBus;
63
+ bus: EventBus;
65
64
  logger: LoggingUtility;
66
- simulation: NodeSimulation | null;
67
65
  timer: GameTimer;
68
66
  debug: DebugTools;
69
67
  largeWorldRendering: boolean;
@@ -71,78 +69,42 @@ export declare class GameEngine {
71
69
  private _stopped;
72
70
  private _log;
73
71
  private _buildSimulation;
74
- private _beforeStartHook;
75
- private _onStartHook;
76
- private _onTeardownHook;
77
72
  /**
78
73
  * Creates an instance of GameEngine.
79
- * @param canvas
80
- * @param renderEngine
81
- * @param physics
82
- * @param eventBus
83
- * @param logger
84
- * @param timer
85
74
  * @param buildSimulation - Builds a fresh simulation for a scene, once rebuildSimulation is called with one.
86
- * @param engines
87
- * @param managers
88
75
  */
89
- constructor(canvas: GameCanvas, renderEngine: AbstractEngine, physics: HavokPlugin, eventBus: GameEventBus, logger: LoggingUtility, timer: GameTimer, buildSimulation: SimulationFactory, engines: Engines, managers: Managers, largeWorldRendering?: boolean, debug?: DebugTools);
76
+ constructor(canvas: GameCanvas, renderEngine: AbstractEngine, bus: EventBus, logger: LoggingUtility, timer: GameTimer, buildSimulation: SimulationFactory<Registry>, engines: Engines, managers: Managers<Registry>, largeWorldRendering: boolean | undefined, debug: DebugTools);
77
+ /** The simulation the engine holds: attached to the bus, stepped by the frame loop, and what a view reads. */
78
+ get simulation(): NodeSimulation<Registry> | null;
90
79
  /**
91
- * Builds a fresh NodeSimulation bound to the given scene and mesh source, and swaps it in. A level calls
92
- * this once its own scene is ready, so every NodeEntity it spawns gets a node in that scene and loads its
93
- * mesh through that source. Left unset, a spawned entity gets a node with no mesh.
80
+ * Builds a fresh NodeSimulation named `name` for the given scene and mesh source, handed this engine's bus and
81
+ * not yet bound. A level calls this once its own scene exists, naming the simulation after itself, so every
82
+ * NodeEntity it spawns gets a node in that scene and loads its mesh through that source; left unset, a spawned
83
+ * entity gets a node with no mesh (design/simulation.md#the-simulations-name).
94
84
  */
95
- rebuildSimulation(scene: Scene, meshSource?: EntityMeshLoader): NodeSimulation;
85
+ rebuildSimulation(scene: Scene, name: string, meshSource?: MeshLoader): NodeSimulation<Registry>;
96
86
  /**
97
- * Register a function to be called before the game engine starts
98
- * @param hook
99
- * @throws Error if a hook is already registered
87
+ * Binds the engine to a simulation, or to none. The one bound before detaches from the bus and the new one
88
+ * attaches, carrying its entities' subscriptions and held emissions across
89
+ * (design/events.md#one-bus-per-engine).
100
90
  */
101
- onBeforeStart(hook: GameHook): void;
91
+ bindSimulation(simulation: NodeSimulation<Registry> | null): void;
92
+ /** Runs before the frame loop starts, awaited, in registration order (design/engine.md#lifecycle-hooks). */
93
+ onBeforeStart(hook: GameHook<Registry>): Unsubscribe;
94
+ /** Runs after the frame loop has started, awaited, in registration order. */
95
+ onStart(hook: GameHook<Registry>): Unsubscribe;
96
+ /** Runs before anything is disposed, awaited; a hook that throws is logged and does not stop the teardown. */
97
+ onTeardown(hook: GameHook<Registry>): Unsubscribe;
102
98
  /**
103
- * Register a function to be called after the game engine starts
104
- * @param hook
105
- * @throws Error if a hook is already registered
106
- */
107
- onStart(hook: GameHook): void;
108
- /**
109
- * Register a function to be called when the game engine stops
110
- * @param hook
111
- * @throws Error if a hook is already registered
112
- */
113
- onTeardown(hook: GameHook): void;
114
- /**
115
- * Subscribe to an event on the event bus.
116
- *
117
- * This is a convenience method that forwards to `eventBus.subscribe()`.
118
- *
119
- * @param eventType - Exact event type string, wildcard pattern (e.g. 'level:*'), or RegExp
120
- * @param callback
121
- * @returns A function that removes this subscription when called
122
- *
123
- * @example
124
- * // Subscribe to a specific event
125
- * const unsub = engine.subscribe('level:complete', (event) => {
126
- * console.log('Level complete:', event.payload.levelName);
127
- * });
128
- *
129
- * // Subscribe to every level event
130
- * engine.subscribe('level:*', (event) => {
131
- * console.log('Level event:', event.type);
132
- * });
133
- */
134
- subscribe<T extends keyof LibraryEventPayloadMap & string>(eventType: T | WildcardPattern | RegExp, callback: GameEventCallback<T, LibraryEventPayloadMap>): Unsubscribe;
135
- /**
136
- * Start the engine: runs beforeStart hook, starts the game manager, then runs onStart hook.
99
+ * Starts the frame loop, with the before-start hooks ahead of it and the start hooks after. An engine that has
100
+ * stopped does not start again (design/architecture.md#teardown).
137
101
  */
138
102
  start(): Promise<void>;
139
103
  /**
140
- * Stop the engine: runs the teardown hook, then tears down all managers and engines in order.
141
- *
142
- * Managers claim what they need when they are built, not when the engine starts -- the input manager's
143
- * window listeners, the debug console's namespace -- so an engine that never started still has
144
- * everything to release. Tearing down twice is the no-op.
104
+ * Tears the engine down in one order: the frame loop stops, the teardown hooks run, then the debug console, the
105
+ * managers, and the engines dispose. A failure in one disposal does not stop the rest, and the first one raised
106
+ * is raised again once everything else is disposed. Stopping twice changes nothing
107
+ * (design/architecture.md#teardown).
145
108
  */
146
109
  stop(): Promise<void>;
147
110
  }
148
- export {};
@@ -1,8 +1,8 @@
1
1
  import { Quaternion, Scene, StaticSound, TransformNode, Vector3 } from '@babylonjs/core';
2
- import { LevelConfig, LevelContext } from "../interfaces/level.d.ts";
3
- import { NodeEntity } from "../entities/nodeEntity.d.ts";
4
- import { NodeSimulation } from "../entities/nodeSimulation.d.ts";
5
- import { Level } from "./level.d.ts";
2
+ import { LevelConfig, LevelContext } from "../interfaces/level.js";
3
+ import { NodeEntity } from "../entities/nodeEntity.js";
4
+ import { NodeEntityRegistry, NodeSimulation } from "../entities/nodeSimulation.js";
5
+ import { Level } from "./level.js";
6
6
  /**
7
7
  * Metadata collected from a spawn point node.
8
8
  * Transforms are normalized to canonical Babylon space — the source level's __root__
@@ -23,10 +23,12 @@ interface EntityNodeData {
23
23
  node: TransformNode;
24
24
  }
25
25
  /**
26
- * Default Level implementation that loads from YAML configuration.
27
- * Supports property handlers for processing scene node metadata.
26
+ * The level a game gets when it describes a scene in configuration instead of writing a `Level` subclass: it
27
+ * builds the scene its configuration names, in one fixed order, and spawns what the scene's own markers name
28
+ * (design/levels.md#gamelevel).
28
29
  */
29
- export declare class GameLevel extends Level {
30
+ export declare class GameLevel<Registry extends NodeEntityRegistry = NodeEntityRegistry> extends Level<Registry> {
31
+ #private;
30
32
  /** The level configuration */
31
33
  protected _config: LevelConfig;
32
34
  /** Collected spawn points from the scene */
@@ -39,20 +41,14 @@ export declare class GameLevel extends Level {
39
41
  protected _levelSounds: Map<string, StaticSound>;
40
42
  /** Sounds that were playing before deactivation (for resume on activate) */
41
43
  private _playingSoundsBeforeDeactivate;
42
- /**
43
- * Create a GameLevel from a configuration object
44
- *
45
- * @param config
46
- * @param context
47
- */
48
- constructor(config: LevelConfig, context: LevelContext);
44
+ constructor(config: LevelConfig, context: LevelContext<Registry>);
49
45
  get config(): LevelConfig;
50
46
  get spawnedEntities(): readonly NodeEntity[];
51
47
  /**
52
48
  * The simulation this level built its scene against: the one its own spawns and despawns go through,
53
49
  * whichever simulation the engine holds at the time. Throws before `buildScene` has bound one.
54
50
  */
55
- protected get simulation(): NodeSimulation;
51
+ protected get simulation(): NodeSimulation<Registry>;
56
52
  /**
57
53
  * Build the scene by loading assets, processing node metadata, and spawning entities.
58
54
  */
@@ -78,13 +74,15 @@ export declare class GameLevel extends Level {
78
74
  */
79
75
  private _createEnvironmentTexture;
80
76
  private _getFileExtension;
77
+ private _attachableCanvas;
81
78
  /**
82
- * Process cameras: apply YAML overrides to imported cameras, create new ones, and set the active camera.
83
- * If no YAML cameras config exists, the first imported camera (if any) is activated.
79
+ * An entry whose key names an imported camera overrides it, and one whose key names none creates a camera
80
+ * of the type it declares. With no camera configuration at all, the first imported camera activates.
84
81
  */
85
82
  private _processCameras;
86
83
  /**
87
- * Create a new camera from a YAML definition.
84
+ * Create a camera of the type an entry declares, or nothing for an entry that cannot be built, which is
85
+ * logged and skipped (design/levels.md#the-load-sequence).
88
86
  */
89
87
  private _createCamera;
90
88
  /**
@@ -92,12 +90,12 @@ export declare class GameLevel extends Level {
92
90
  */
93
91
  private _applyCameraConfig;
94
92
  /**
95
- * Process lights: apply YAML overrides to imported lights and create new ones.
96
- * If no YAML lights config exists, imported lights are left unchanged.
93
+ * Lights override and create by key the way cameras do. With no light configuration at all, imported
94
+ * lights are left as they are.
97
95
  */
98
96
  private _processLights;
99
97
  /**
100
- * Create a new light from a YAML definition.
98
+ * Create a light of the type an entry declares.
101
99
  */
102
100
  private _createLight;
103
101
  /**
@@ -117,6 +115,7 @@ export declare class GameLevel extends Level {
117
115
  * Walk all transform nodes and meshes, normalizing glTF metadata and dispatching to property handlers.
118
116
  */
119
117
  private _processNodeProperties;
118
+ private _warnUnhandledProperties;
120
119
  /**
121
120
  * Check a node's metadata for spawn/entity markers and run any registered property handlers.
122
121
  */
@@ -150,8 +149,8 @@ export declare class GameLevel extends Level {
150
149
  */
151
150
  private _spawnEntityNode;
152
151
  /**
153
- * Create level-scoped sounds from YAML config. Sounds are tracked for lifecycle management
154
- * (pause on deactivate, resume on activate, dispose on unload).
152
+ * Create the sounds the configuration names, keeping each by name so it follows the level: paused on
153
+ * deactivation, played again on activation, disposed with the level (design/levels.md#level-sounds).
155
154
  */
156
155
  private _processLevelSounds;
157
156
  /**
@@ -167,6 +166,6 @@ export declare class GameLevel extends Level {
167
166
  /**
168
167
  * Dispose of this level's resources
169
168
  */
170
- $dispose(): Promise<void>;
169
+ dispose(): Promise<void>;
171
170
  }
172
171
  export {};