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

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 +23 -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 +2070 -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
@@ -1,32 +1,30 @@
1
1
  import { Color3, Scene, TransformNode } from '@babylonjs/core';
2
2
  import { ClusteredLightContainer } from '@babylonjs/core/Lights/Clustered/clusteredLightContainer';
3
- import { NodeSimulation } from "../entities/nodeSimulation.d.ts";
4
- import { LoggerInterface } from "../interfaces/logger.d.ts";
5
- import { ColorConfig, LevelConfig, LevelContext, LevelInstance, LevelLoadOptions, PropertyHandler } from "../interfaces/level.d.ts";
6
- import { GameEngine } from "./gameEngine.d.ts";
7
- import { OutlineManager } from "../managers/outline.d.ts";
3
+ import { NodeEntityRegistry, NodeSimulation } from "../entities/nodeSimulation.js";
4
+ import { MeshLoader } from "../entities/nodeSimulationContext.js";
5
+ import { LoggerInterface } from "../interfaces/logger.js";
6
+ import { ColorConfig, LevelConfig, LevelContext, LevelInstance, LevelLoadOptions, PropertyHandler } from "../interfaces/level.js";
7
+ import { OutlineManager } from "../managers/outline.js";
8
8
  /**
9
- * Abstract base class for game levels.
9
+ * The base a game subclasses when it builds a level's scene in code. A subclass writes one method,
10
+ * `buildScene`, and reaches every dependency it has through `this.context`; the base drives the load,
11
+ * publishes the level's own events, and owns the scene, the simulation built against it, and the teardown
12
+ * (design/levels.md#the-base-class).
10
13
  *
11
- * Levels are responsible for creating and configuring their own scenes via the
12
- * `buildScene()` method. All runtime dependencies (eventBus, sceneEngine, simulation)
13
- * are injected via the constructor and are immediately available.
14
- *
15
- * Users should not instantiate Level subclasses directly. Instead, use LevelManager
16
- * which acts as a factory and injects the required dependencies:
14
+ * The level manager is the factory: it constructs a subclass with the configuration and the context, so a
15
+ * game registers a class and loads a level rather than constructing one itself.
17
16
  *
18
17
  * ```typescript
19
- * // Register a custom level class
20
18
  * levelManager.registerLevelClass('my-level', MyCustomLevel);
21
19
  *
22
- * // Load the level - manager creates instance with deps injected
23
20
  * const level = await levelManager.loadLevel({ name: 'Level1', class: 'my-level' });
24
21
  * ```
25
22
  */
26
- export declare abstract class Level implements LevelInstance {
23
+ export declare abstract class Level<Registry extends NodeEntityRegistry = NodeEntityRegistry> implements LevelInstance {
24
+ #private;
27
25
  readonly name: string;
28
26
  protected readonly _log: LoggerInterface;
29
- protected readonly _context: LevelContext;
27
+ protected readonly _context: LevelContext<Registry>;
30
28
  protected _scene: Scene | null;
31
29
  protected _forRestore: boolean;
32
30
  /**
@@ -34,45 +32,42 @@ export declare abstract class Level implements LevelInstance {
34
32
  * disposing this one, so by the time this level despawns what it spawned, the engine already holds the
35
33
  * next level's simulation, minting the same IDs.
36
34
  */
37
- protected _simulation: NodeSimulation | null;
35
+ protected _simulation: NodeSimulation<Registry> | null;
38
36
  clusteredLights: ClusteredLightContainer | null;
39
37
  outlines: OutlineManager | null;
40
- /**
41
- * Creates a new Level instance.
42
- *
43
- * @param config
44
- * @param context
45
- */
46
- constructor(config: LevelConfig, context: LevelContext);
38
+ constructor(config: LevelConfig, context: LevelContext<Registry>);
47
39
  get scene(): Scene | null;
48
40
  get isLoaded(): boolean;
49
- get gameEngine(): GameEngine;
41
+ protected get context(): LevelContext<Registry>;
50
42
  get propertyHandlers(): Map<string, PropertyHandler>;
51
- /**
52
- * Emit a progress event during loading
53
- *
54
- * @param progress - Percentage (0-100)
55
- * @param message
56
- */
57
43
  /**
58
44
  * The simulation this level owns, or nothing for a level that never built one. The level manager binds
59
45
  * the engine to the current level's simulation (design/simulation.md#which-simulation-the-engine-holds).
60
46
  */
61
- get $simulation(): NodeSimulation | null;
47
+ get $simulation(): NodeSimulation<Registry> | null;
48
+ /**
49
+ * Takes over a simulation built to replace this level's own, as a save load does once it has restored
50
+ * into a fresh one (design/simulation.md#saved-games).
51
+ */
52
+ $adoptSimulation(simulation: NodeSimulation<Registry>): void;
53
+ /**
54
+ * Builds this level's simulation for the scene it just created, named after the level, and keeps it. A
55
+ * subclass calls this from `buildScene` before it spawns anything, since a NodeEntity constructs its node
56
+ * in its simulation's scene (design/levels.md#the-scene-and-its-simulation).
57
+ */
58
+ protected rebuildSimulation(scene: Scene, meshSource?: MeshLoader): NodeSimulation<Registry>;
62
59
  /**
63
- * Take over a simulation built to replace this level's own, as a save load does once it has restored
64
- * into a fresh one.
65
- *
66
- * @param simulation
60
+ * Publishes one more `level:progress` at the number and the message a subclass names, which is how a
61
+ * level reports its own progress between the 0 and the 100 the base publishes
62
+ * (design/levels.md#the-base-class).
67
63
  */
68
- $adoptSimulation(simulation: NodeSimulation): void;
69
64
  protected $emitProgress(progress: number, message: string): void;
70
65
  protected _toColor3(color: ColorConfig): Color3;
71
66
  /**
72
- * Get normalized metadata from a node, handling glTF extras.
73
- *
74
- * Babylon.js stores glTF custom properties in node.metadata.gltf.extras,
75
- * so we need to extract them for easier access.
67
+ * A node's metadata as one flat record: its own top-level keys with its glTF extras merged onto them, extras
68
+ * winning where both name one. The `gltf` container itself is left out, since it is the loader's record and
69
+ * never an authored property, so a node whose extras are empty and whose own keys are too normalizes to an
70
+ * empty record and the walk skips it (design/authoring.md#the-walk).
76
71
  */
77
72
  protected _getNormalizedMetadata(node: TransformNode): Record<string, unknown> | null;
78
73
  /**
@@ -80,16 +75,14 @@ export declare abstract class Level implements LevelInstance {
80
75
  */
81
76
  protected _runPropertyHandlers(node: TransformNode): Promise<void>;
82
77
  /**
83
- * Load and create the scene for this level.
84
- * Progress events will be emitted during loading.
78
+ * Builds the scene through `buildScene`, publishing `level:progress` at 0 and at 100 around it and
79
+ * `level:complete` after, or `level:error` before rethrowing when building fails. A level already holding a
80
+ * scene warns and answers with the one it has.
85
81
  */
86
82
  load(options?: LevelLoadOptions): Promise<Scene>;
87
83
  /**
88
- * Abstract method for building the scene content.
89
- * Concrete levels must implement this to create their specific content.
90
- *
91
- * All dependencies are available via `this.gameEngine`.
92
- *
84
+ * Builds the scene: a subclass answers with the scene it built, reaching what it needs through `this.context`
85
+ * (design/levels.md#the-base-class).
93
86
  */
94
87
  protected abstract buildScene(): Promise<Scene>;
95
88
  /**
@@ -105,10 +98,8 @@ export declare abstract class Level implements LevelInstance {
105
98
  */
106
99
  onDeactivate?(): Promise<void> | void;
107
100
  /**
108
- * Dispose of this level's resources and scene.
109
- *
110
- * The `$` prefix indicates this is an internal lifecycle method, consistent with
111
- * other engine lifecycle methods like `$teardown()`.
101
+ * Releases what the base owns: the outline manager, the clustered light container, and the scene. Nothing for
102
+ * a level that never loaded one, and nothing the second time (design/architecture.md#teardown).
112
103
  */
113
- $dispose(): Promise<void>;
104
+ dispose(): Promise<void>;
114
105
  }
@@ -1,4 +1,4 @@
1
- import { LoggingBackend } from "../../interfaces/logger.d.ts";
1
+ import { LoggingBackend } from "../../interfaces/logger.js";
2
2
  /**
3
3
  * A logging backend that logs to the console.
4
4
  */
@@ -1,4 +1,4 @@
1
- import { LoggingBackend } from "../../interfaces/logger.d.ts";
1
+ import { LoggingBackend } from "../../interfaces/logger.js";
2
2
  /**
3
3
  * A logging backend that does nothing (discards all logs).
4
4
  * Useful for production environments or when you want to completely disable logging.
@@ -1,6 +1,4 @@
1
- import { DebugConsole } from "../classes/debugConsole.d.ts";
2
- import { GameEngine } from "../classes/gameEngine.d.ts";
3
- /**
4
- * Registers all default references and commands onto the debug console namespace.
5
- */
6
- export declare function registerBuiltins(dc: DebugConsole, ge: GameEngine): void;
1
+ import { DebugConsole } from "../classes/debugConsole.js";
2
+ import { GameEngine } from "../classes/gameEngine.js";
3
+ import { NodeEntityRegistry } from "../entities/nodeSimulation.js";
4
+ export declare function registerBuiltins<Registry extends NodeEntityRegistry>(dc: DebugConsole, ge: GameEngine<Registry>): void;
@@ -1,14 +1,11 @@
1
1
  import { AudioBus, IStaticSoundOptions, StaticSound } from '@babylonjs/core';
2
- import { Disposable } from "../interfaces/lifecycle.d.ts";
3
- import { LoggingUtility } from "../utils/logger.d.ts";
2
+ import { Disposable } from "../interfaces/lifecycle.js";
3
+ import { LoggingUtility } from "../utils/logger.js";
4
4
  export declare class AudioEngine implements Disposable {
5
- private _engine;
6
- private _mainBus;
7
- private _buses;
8
- private _log;
5
+ #private;
9
6
  constructor(logger: LoggingUtility);
10
7
  initialize(): Promise<void>;
11
- $teardown(): Promise<void>;
8
+ dispose(): Promise<void>;
12
9
  createBus(name: string): Promise<AudioBus>;
13
10
  getBus(name: string): AudioBus | undefined;
14
11
  createSound(name: string, url: string, bus?: AudioBus, options?: Partial<IStaticSoundOptions>): Promise<StaticSound>;
@@ -1,78 +1,43 @@
1
- import { AbstractEngine, AbstractMesh, AssetContainer, DirectionalLight, FreeCamera, HemisphericLight, Light, Mesh, PhysicsAggregate, PhysicsShapeType, PointLight, RectAreaLight, Scene, SpotLight, Vector3 } from '@babylonjs/core';
1
+ import { AbstractEngine, AbstractMesh, AssetContainer, DirectionalLight, HemisphericLight, Light, Mesh, PointLight, RectAreaLight, Scene, SpotLight, Vector3 } from '@babylonjs/core';
2
2
  import { HavokPhysicsWithBindings } from '@babylonjs/havok';
3
- import { GameCanvas } from "../interfaces/game.d.ts";
4
- import { Disposable } from "../interfaces/lifecycle.d.ts";
5
- import { LoggingUtility } from "../utils/logger.d.ts";
3
+ import { Disposable } from "../interfaces/lifecycle.js";
4
+ import { LoggingUtility } from "../utils/logger.js";
6
5
  export declare class SceneEngine implements Disposable {
7
- private _canvas;
8
- private _engine;
9
- private _havok;
10
- private _log;
11
- constructor(canvas: GameCanvas, engine: AbstractEngine, havok: HavokPhysicsWithBindings, logger: LoggingUtility);
6
+ #private;
7
+ constructor(engine: AbstractEngine, logger: LoggingUtility, havokBinary?: BufferSource);
8
+ /** Whether the Havok module has been loaded, which nothing does until a scene asks for physics. */
9
+ get $havokLoaded(): boolean;
10
+ /** The one loaded module, for a test asserting every scene's plugin was built over the same one. */
11
+ get $havokModule(): HavokPhysicsWithBindings | undefined;
12
12
  /**
13
- * Creates a new scene with physics enabled
13
+ * Creates an empty scene on the render engine. It belongs to whoever asked for it.
14
14
  */
15
15
  createScene(): Scene;
16
- enablePhysics(scene: Scene, gravityVector?: Vector3, floatingOriginWorldRadius?: number): void;
17
- /**
18
- * Creates a free camera at the specified position, targeting the origin. Attaches controls to the provided
19
- * canvas, or falls back to the engine's default canvas.
20
- * @param name
21
- * @param position
22
- * @param scene
23
- * @param canvas - Overrides the default canvas for input controls
24
- */
25
- createFreeCamera(name: string, position: Vector3, scene: Scene, canvas?: GameCanvas): FreeCamera;
16
+ enablePhysics(scene: Scene, gravityVector?: Vector3, floatingOriginWorldRadius?: number): Promise<void>;
26
17
  /**
27
18
  * Creates a hemispheric light (ambient lighting)
28
- * @param name
29
- * @param direction
30
- * @param scene
31
- * @param intensity - Default: 0.7
32
19
  */
33
20
  createHemisphericLight(name: string, direction: Vector3, scene: Scene, intensity?: number): HemisphericLight;
34
21
  /**
35
22
  * Creates a directional light (sun-like lighting)
36
- * @param name
37
- * @param direction
38
- * @param scene
39
- * @param intensity - Default: 1.0
40
23
  */
41
24
  createDirectionalLight(name: string, direction: Vector3, scene: Scene, intensity?: number): DirectionalLight;
42
25
  /**
43
26
  * Creates a point light (bulb-like lighting)
44
- * @param name
45
- * @param position
46
- * @param scene
47
- * @param intensity - Default: 1.0
48
27
  */
49
28
  createPointLight(name: string, position: Vector3, scene: Scene, intensity?: number): PointLight;
50
29
  /**
51
30
  * Creates a spotlight (flashlight-like lighting)
52
- * @param name
53
- * @param position
54
- * @param direction
55
31
  * @param angle - Cone angle in radians
56
32
  * @param exponent - Controls light falloff from center to edge of cone
57
- * @param scene
58
- * @param intensity - Default: 1.0
59
33
  */
60
34
  createSpotLight(name: string, position: Vector3, direction: Vector3, angle: number, exponent: number, scene: Scene, intensity?: number): SpotLight;
61
35
  /**
62
36
  * Creates a rectangular area light (emits from a flat rectangle in the -Z direction)
63
- * @param name
64
- * @param position
65
- * @param width
66
- * @param height
67
- * @param scene
68
- * @param intensity - Default: 1.0
69
37
  */
70
38
  createRectAreaLight(name: string, position: Vector3, width: number, height: number, scene: Scene, intensity?: number): RectAreaLight;
71
39
  /**
72
40
  * Creates a sphere mesh. Defaults: diameter 1, 32 segments.
73
- * @param name
74
- * @param options
75
- * @param scene
76
41
  */
77
42
  createSphere(name: string, options: {
78
43
  diameter?: number;
@@ -80,9 +45,6 @@ export declare class SceneEngine implements Disposable {
80
45
  } | undefined, scene: Scene): Mesh;
81
46
  /**
82
47
  * Creates a box mesh. Default size: 1.
83
- * @param name
84
- * @param options
85
- * @param scene
86
48
  */
87
49
  createBox(name: string, options: {
88
50
  size?: number;
@@ -92,9 +54,6 @@ export declare class SceneEngine implements Disposable {
92
54
  } | undefined, scene: Scene): Mesh;
93
55
  /**
94
56
  * Creates a ground mesh. Defaults: 6x6 with 2 subdivisions.
95
- * @param name
96
- * @param options
97
- * @param scene
98
57
  */
99
58
  createGround(name: string, options: {
100
59
  width?: number;
@@ -103,38 +62,19 @@ export declare class SceneEngine implements Disposable {
103
62
  } | undefined, scene: Scene): Mesh;
104
63
  /**
105
64
  * Creates a cylinder mesh. Defaults: height 2, diameter 1 (top and bottom).
106
- * @param name
107
- * @param options
108
- * @param scene
109
65
  */
110
66
  createCylinder(name: string, options: {
111
67
  height?: number;
112
68
  diameterTop?: number;
113
69
  diameterBottom?: number;
114
70
  } | undefined, scene: Scene): Mesh;
115
- /**
116
- * Adds physics to a mesh. Defaults: mass 1, restitution 0.75, friction 0.5.
117
- * @param mesh
118
- * @param shapeType
119
- * @param physicsProps
120
- * @param scene
121
- */
122
- addPhysics(mesh: Mesh, shapeType: PhysicsShapeType, physicsProps: {
123
- mass?: number;
124
- restitution?: number;
125
- friction?: number;
126
- } | undefined, scene: Scene): PhysicsAggregate;
127
71
  /**
128
72
  * Loads a 3D model/asset into an AssetContainer without adding it to the scene automatically.
129
- * @param sceneFilename
130
- * @param scene
131
73
  */
132
74
  loadModel(sceneFilename: string, scene: Scene): Promise<AssetContainer>;
133
75
  /**
134
76
  * Imports meshes from a file directly into the scene.
135
77
  * @param meshNames - Specific meshes to import; empty array imports all
136
- * @param sceneFilename
137
- * @param scene
138
78
  */
139
79
  importMeshes(meshNames: string[], sceneFilename: string, scene: Scene): Promise<{
140
80
  meshes: AbstractMesh[];
@@ -146,7 +86,8 @@ export declare class SceneEngine implements Disposable {
146
86
  lights: Light[];
147
87
  }>;
148
88
  /**
149
- * Tears down the scene engine and cleans up any resources
89
+ * Releases the Havok module and forgets a load of it still in flight. The scenes this engine created
90
+ * belong to their levels and are left alone.
150
91
  */
151
- $teardown(): Promise<void>;
92
+ dispose(): Promise<void>;
152
93
  }
@@ -1,6 +1,7 @@
1
1
  import { Action, BehaviorClass, StateFor, ValidTuple } from '@skewedaspect/sage-core';
2
- import { NodeEntity } from "./nodeEntity.d.ts";
3
- import { MeshConfig } from "./meshConfig.d.ts";
2
+ import { NodeEntity } from "./nodeEntity.js";
3
+ import { MeshConfig } from "./meshConfig.js";
4
+ import { NodeSimulationContext } from "./nodeSimulationContext.js";
4
5
  export interface GameEntityDefinition<Behaviors extends readonly BehaviorClass[]> {
5
6
  type: string;
6
7
  tags?: string[];
@@ -9,9 +10,10 @@ export interface GameEntityDefinition<Behaviors extends readonly BehaviorClass[]
9
10
  mesh?: MeshConfig;
10
11
  actions?: readonly Action[];
11
12
  }
12
- export type GameEntityDefinitionClass<Behaviors extends readonly BehaviorClass[]> = typeof NodeEntity & {
13
+ export type GameEntityDefinitionClass<Behaviors extends readonly BehaviorClass[]> = Omit<typeof NodeEntity, 'prototype'> & {
13
14
  readonly behaviors: Behaviors;
14
- };
15
+ readonly prototype: NodeEntity<Behaviors>;
16
+ } & (abstract new (id: string, context: NodeSimulationContext, providedState: object, runCreateHooks: boolean, name?: string) => NodeEntity<Behaviors>);
15
17
  export declare function defineEntity<Behaviors extends readonly BehaviorClass[]>(definition: GameEntityDefinition<Behaviors> & {
16
- behaviors: ValidTuple<Behaviors>;
18
+ behaviors: ValidTuple<Behaviors, NodeEntity<Behaviors>>;
17
19
  }): GameEntityDefinitionClass<Behaviors>;
@@ -1,6 +1,6 @@
1
1
  import { Vector3, AbstractMesh, PickingInfo } from '@babylonjs/core';
2
2
  import { GameEntity } from '@skewedaspect/sage-core';
3
- import { NodeEntity } from "./nodeEntity.d.ts";
3
+ import { NodeEntity } from "./nodeEntity.js";
4
4
  export interface EntityPickFilter {
5
5
  tags?: readonly string[];
6
6
  }
@@ -1,11 +1,19 @@
1
- import { ColorConfig } from "../interfaces/level.d.ts";
2
- export interface MeshMaterialConfig {
3
- type?: 'standard' | 'pbr';
1
+ import { ColorConfig } from "../interfaces/level.js";
2
+ export interface StandardMaterialConfig {
3
+ type?: 'standard';
4
+ color?: ColorConfig;
5
+ emissive?: ColorConfig;
6
+ metallic?: never;
7
+ roughness?: never;
8
+ }
9
+ export interface PBRMaterialConfig {
10
+ type: 'pbr';
4
11
  color?: ColorConfig;
5
12
  emissive?: ColorConfig;
6
13
  metallic?: number;
7
14
  roughness?: number;
8
15
  }
16
+ export type MeshMaterialConfig = StandardMaterialConfig | PBRMaterialConfig;
9
17
  export interface MeshConfig {
10
18
  source: string;
11
19
  params?: Record<string, unknown>;
@@ -1,8 +1,9 @@
1
1
  import { TransformNode, AbstractMesh } from '@babylonjs/core';
2
- import { GameEntity, Action, GameEntityState, GameSimulationContext } from '@skewedaspect/sage-core';
3
- import { AudioManager } from "../managers/audio.d.ts";
4
- import { ColliderDebugConfig } from "../managers/colliderDebug.d.ts";
5
- import { MeshConfig } from "./meshConfig.d.ts";
2
+ import { GameEntity, Action, BehaviorClass, ComposedOf, DeepReadonly, GameEntityState, GameEntityType, GameSimulationContext, OpsFor, StateFor, ValidTuple } from '@skewedaspect/sage-core';
3
+ import { AudioManager } from "../managers/audio.js";
4
+ import { ColliderDebugConfig } from "../managers/colliderDebug.js";
5
+ import { NodeSimulationContext } from "./nodeSimulationContext.js";
6
+ import { MeshConfig } from "./meshConfig.js";
6
7
  export interface Vector3State {
7
8
  x: number;
8
9
  y: number;
@@ -25,19 +26,27 @@ export interface NodeBehaviorHooks {
25
26
  onMeshLoaded?(): void;
26
27
  onDestroy?(): void;
27
28
  }
28
- export declare abstract class NodeEntity extends GameEntity {
29
+ export declare abstract class NodeEntity<Behaviors extends readonly BehaviorClass[] = readonly BehaviorClass[]> extends GameEntity {
29
30
  #private;
30
31
  static readonly poolable: boolean;
31
32
  static readonly poolSize?: number;
32
33
  static readonly meshConfig?: MeshConfig;
33
34
  static readonly actions?: readonly Action[];
35
+ readonly state: DeepReadonly<StateFor<Behaviors>>;
36
+ readonly ops: OpsFor<Behaviors>;
37
+ static create<Type extends GameEntityType>(this: Type & {
38
+ behaviors: ValidTuple<Type['behaviors'], Type['prototype']>;
39
+ }, id: string, context: NodeSimulationContext, state?: Partial<StateFor<Type['behaviors']>>, name?: string): ComposedOf<Type>;
40
+ static restore<Type extends GameEntityType>(this: Type & {
41
+ behaviors: ValidTuple<Type['behaviors'], Type['prototype']>;
42
+ }, id: string, context: NodeSimulationContext, state: StateFor<Type['behaviors']>, name?: string): ComposedOf<Type>;
34
43
  get node(): TransformNode;
35
44
  get mesh(): AbstractMesh | undefined;
36
45
  set mesh(mesh: AbstractMesh | undefined);
37
46
  get audio(): AudioManager | undefined;
38
47
  showCollider(config?: ColliderDebugConfig, color?: string): void;
39
48
  hideCollider(name?: string): void;
49
+ protected declareComputedState(suppliedState: object): void;
40
50
  protected constructor(id: string, context: GameSimulationContext, providedState: object, runCreateHooks: boolean, name?: string);
41
- update(dt: number): void;
42
51
  destroy(): void;
43
52
  }
@@ -1,4 +1,4 @@
1
- import { NodeSet } from "./nodeSimulationContext.d.ts";
1
+ import { NodeSet } from "./nodeSimulationContext.js";
2
2
  export declare class NodePool {
3
3
  #private;
4
4
  take(name: string): NodeSet | undefined;
@@ -1,30 +1,42 @@
1
- import { GameSimulationManager, GameSimulationOptions, StateBag } from '@skewedaspect/sage-core';
1
+ import { GameSimulationManager, GameEntityRegistry, GameSimulationOptions, GameSimulationSnapshot, StateBag, ValidEntityRegistry } from '@skewedaspect/sage-core';
2
2
  import { AbstractMesh, Camera, Ray, Scene } from '@babylonjs/core';
3
- import { EntityPickFilter, EntityPickResult } from "./entityPicking.d.ts";
4
- import { NodeEntity } from "./nodeEntity.d.ts";
5
- import { NodePool } from "./nodePool.d.ts";
6
- import { MeshLoader, NodeSimulationContext } from "./nodeSimulationContext.d.ts";
7
- import { AudioManager } from "../managers/audio.d.ts";
8
- import { ColliderDebugManager } from "../managers/colliderDebug.d.ts";
9
- export interface NodeSimulationOptions extends GameSimulationOptions {
3
+ import { EntityPickFilter, EntityPickResult } from "./entityPicking.js";
4
+ import { NodeEntity } from "./nodeEntity.js";
5
+ import { NodePool } from "./nodePool.js";
6
+ import { MeshLoader, NodeSimulationContext } from "./nodeSimulationContext.js";
7
+ import { AudioManager } from "../managers/audio.js";
8
+ import { ColliderDebugManager } from "../managers/colliderDebug.js";
9
+ export type NodeEntityRegistry = GameEntityRegistry<NodeSimulationContext>;
10
+ export interface NodeSimulationOptions<Registry extends NodeEntityRegistry = NodeEntityRegistry> extends GameSimulationOptions<Registry> {
10
11
  scene: Scene;
11
12
  loadMesh: MeshLoader;
12
13
  pool: NodePool;
13
14
  audio?: AudioManager;
14
15
  colliders: ColliderDebugManager;
15
16
  }
16
- export declare class NodeSimulation extends GameSimulationManager implements NodeSimulationContext {
17
+ export declare class NodeSimulation<Registry extends NodeEntityRegistry = NodeEntityRegistry> extends GameSimulationManager<Registry, NodeSimulationContext> implements NodeSimulationContext {
17
18
  #private;
18
19
  readonly scene: Scene;
19
20
  readonly pool: NodePool;
20
21
  readonly loadMesh: MeshLoader;
21
22
  readonly audio: AudioManager | undefined;
22
23
  readonly colliders: ColliderDebugManager;
23
- constructor(options: NodeSimulationOptions);
24
+ constructor(options: NodeSimulationOptions<Registry> & {
25
+ entityClasses: ValidEntityRegistry<Registry>;
26
+ });
27
+ /**
28
+ * Advances the scene's physics engine by one fixed step.
29
+ *
30
+ * A scene with no physics engine has nothing to advance and does nothing, which is what a game that
31
+ * never asked for physics gets (design/physics.md#physics-is-optional).
32
+ */
33
+ protected advanceFixedStep(duration: number): void;
24
34
  prewarm(name: string, count: number, state?: StateBag): Promise<void>;
25
35
  pickEntityWithRay(ray: Ray, filter?: EntityPickFilter): EntityPickResult | undefined;
26
36
  pickEntity(x: number, y: number, filter?: EntityPickFilter): EntityPickResult | undefined;
27
37
  pickEntityForward(camera: Camera, range: number, filter?: EntityPickFilter): EntityPickResult | undefined;
28
38
  getEntityForMesh(mesh: AbstractMesh): NodeEntity | undefined;
29
- protected buildReplacementSimulation(options: GameSimulationOptions): NodeSimulation;
39
+ spawnByName(name: string, state?: StateBag, id?: string): NodeEntity;
40
+ buildFromSnapshot(snapshot: GameSimulationSnapshot): NodeSimulation<Registry>;
41
+ protected buildReplacementSimulation(options: GameSimulationOptions<Registry>): NodeSimulation<Registry>;
30
42
  }
@@ -1,13 +1,13 @@
1
1
  import { AbstractMesh, Scene, TransformNode } from '@babylonjs/core';
2
2
  import { GameSimulationContext, StateBag } from '@skewedaspect/sage-core';
3
- import { AudioManager } from "../managers/audio.d.ts";
4
- import { ColliderDebugManager } from "../managers/colliderDebug.d.ts";
5
- import { NodePool } from "./nodePool.d.ts";
3
+ import { AudioManager } from "../managers/audio.js";
4
+ import { ColliderDebugManager } from "../managers/colliderDebug.js";
5
+ import { NodePool } from "./nodePool.js";
6
6
  export interface NodeSet {
7
7
  node: TransformNode;
8
8
  mesh: AbstractMesh | undefined;
9
9
  }
10
- export type MeshLoader = (name: string, state: Readonly<StateBag>) => Promise<AbstractMesh | undefined>;
10
+ export type MeshLoader = (scene: Scene, name: string, state: Readonly<StateBag>) => Promise<AbstractMesh | undefined>;
11
11
  export interface NodeSimulationContext extends GameSimulationContext {
12
12
  readonly scene: Scene;
13
13
  readonly pool: NodePool;
@@ -1,8 +1,8 @@
1
1
  import { AbstractMesh, AssetContainer, Mesh } from '@babylonjs/core';
2
2
  import { GameEntityType } from '@skewedaspect/sage-core';
3
- import { EntityMeshLoader } from "../interfaces/game.d.ts";
3
+ import { MeshLoader } from "./nodeSimulationContext.js";
4
4
  export interface MeshAssetSource {
5
5
  load(path: string): Promise<AssetContainer | AbstractMesh>;
6
6
  clone(path: string): Mesh;
7
7
  }
8
- export declare function buildStandardMeshSource(entityClasses: Readonly<Record<string, GameEntityType>>, assets: MeshAssetSource): EntityMeshLoader;
8
+ export declare function buildStandardMeshSource(entityClasses: Readonly<Record<string, GameEntityType>>, assets: MeshAssetSource): MeshLoader;
@@ -1,4 +1,5 @@
1
1
  import { AbstractMesh } from '@babylonjs/core';
2
+ import { LevelInstance } from "../interfaces/level.js";
2
3
  /** Payload for level:progress events */
3
4
  export interface LevelProgressPayload {
4
5
  levelName: string;
@@ -10,7 +11,7 @@ export interface LevelCompletePayload {
10
11
  levelName: string;
11
12
  message?: string;
12
13
  /** The loaded level instance (available for accessing scene, entities, etc.) */
13
- level: import('../interfaces/level.ts').LevelInstance;
14
+ level: LevelInstance;
14
15
  }
15
16
  /** Payload for level:error events */
16
17
  export interface LevelErrorPayload {
@@ -68,26 +69,20 @@ export interface GameResumedPayload {
68
69
  /** Optional reason for resuming */
69
70
  reason?: string;
70
71
  }
71
- /**
72
- * Maps library event type strings to their payload types.
73
- * This is the source of truth for type safety of library-emitted events.
74
- *
75
- * The index signature allows this interface to satisfy Record<string, unknown>
76
- * while still providing type safety for known event types.
77
- */
78
- export interface LibraryEventPayloadMap {
79
- 'level:progress': LevelProgressPayload;
80
- 'level:complete': LevelCompletePayload;
81
- 'level:error': LevelErrorPayload;
82
- 'level:transition:start': LevelTransitionStartPayload;
83
- 'level:transition:progress': LevelTransitionProgressPayload;
84
- 'level:transition:complete': LevelTransitionCompletePayload;
85
- 'level:transition:error': LevelTransitionErrorPayload;
86
- 'asset:progress': AssetProgressPayload;
87
- 'asset:complete': AssetCompletePayload;
88
- 'trigger:enter': TriggerEnterPayload;
89
- 'trigger:exit': TriggerExitPayload;
90
- 'game:paused': GamePausedPayload;
91
- 'game:resumed': GameResumedPayload;
92
- [key: string]: unknown;
72
+ declare module '@skewedaspect/sage-core' {
73
+ interface EventMap {
74
+ 'level:progress': LevelProgressPayload;
75
+ 'level:complete': LevelCompletePayload;
76
+ 'level:error': LevelErrorPayload;
77
+ 'level:transition:start': LevelTransitionStartPayload;
78
+ 'level:transition:progress': LevelTransitionProgressPayload;
79
+ 'level:transition:complete': LevelTransitionCompletePayload;
80
+ 'level:transition:error': LevelTransitionErrorPayload;
81
+ 'asset:progress': AssetProgressPayload;
82
+ 'asset:complete': AssetCompletePayload;
83
+ 'trigger:enter': TriggerEnterPayload;
84
+ 'trigger:exit': TriggerExitPayload;
85
+ 'game:paused': GamePausedPayload;
86
+ 'game:resumed': GameResumedPayload;
87
+ }
93
88
  }
@@ -1,17 +1,12 @@
1
- import { LevelManager } from "../managers/level.d.ts";
1
+ import { LevelManager } from "../managers/level.js";
2
2
  /**
3
3
  * Register the collider property handler.
4
4
  *
5
5
  * Supports the following properties on scene nodes:
6
- * - `collider` (string): Collider type - "box", "sphere", "mesh", or "none"
7
- * - `collider_mesh` (boolean): On a child node, marks it as the collision source for parent's "mesh" collider
8
- * - `collider_mass` (number): Mass for the physics body. Defaults to 0 (static). Set > 0 for dynamic bodies.
9
- * - `collider_kinematic` (boolean): When true, sets `disablePreStep = false` so the physics body
10
- * tracks mesh transform each frame. Required for animated colliders (doors, elevators, platforms).
11
- *
12
- * For "mesh" colliders, if a child node has `collider_mesh: true`, that child's geometry
13
- * is used for collision and the child is made invisible.
14
- *
15
- * @param levelManager
6
+ * - `collider` (string): the shape, one of "box", "sphere", "mesh", or "none"
7
+ * - `collider_mesh` (boolean): on a child node, marks it as the collision source for a parent's "mesh" collider
8
+ * - `collider_motion` (string): "static", "dynamic", or "kinematic". Defaults to "static".
9
+ * - `collider_mass` (number): mass in kilograms, defaulting to 1. It sets the mass and nothing else; it does
10
+ * not decide whether the body moves.
16
11
  */
17
- export declare function registerColliderHandler(levelManager: LevelManager): void;
12
+ export declare function registerColliderHandler(levelManager: Pick<LevelManager, 'registerPropertyHandler'>): void;