@skewedaspect/sage 0.10.0-beta.1 → 0.10.0-beta.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 (36) hide show
  1. package/dist/behaviors/sound.d.ts +22 -0
  2. package/dist/classes/gameEngine.d.ts +7 -0
  3. package/dist/classes/gameLevel.d.ts +6 -0
  4. package/dist/classes/level.d.ts +19 -0
  5. package/dist/debug/builtins.d.ts +0 -4
  6. package/dist/entities/entityPicking.d.ts +1 -0
  7. package/dist/entities/nodeEntity.d.ts +10 -0
  8. package/dist/entities/nodeSimulation.d.ts +9 -1
  9. package/dist/entities/nodeSimulationContext.d.ts +4 -0
  10. package/dist/events/payloads.d.ts +0 -12
  11. package/dist/input/actionContext.d.ts +2 -0
  12. package/dist/input/binding.d.ts +3 -1
  13. package/dist/input/bindings/delta.d.ts +23 -0
  14. package/dist/input/bindings/toggle.d.ts +5 -1
  15. package/dist/input/bindings/trigger.d.ts +5 -1
  16. package/dist/input/bindings/value.d.ts +7 -4
  17. package/dist/input/deliverAction.d.ts +3 -2
  18. package/dist/input/deviceReaders/mouse.d.ts +1 -1
  19. package/dist/input/deviceState.d.ts +5 -0
  20. package/dist/interfaces/game.d.ts +1 -1
  21. package/dist/interfaces/level.d.ts +2 -2
  22. package/dist/managers/asset.d.ts +17 -3
  23. package/dist/managers/game.d.ts +8 -1
  24. package/dist/managers/input.d.ts +34 -0
  25. package/dist/managers/level.d.ts +14 -0
  26. package/dist/managers/simulationSave.d.ts +10 -2
  27. package/dist/{postProcessingFrameGraph-rr2DbgPi.js → postProcessingFrameGraph-C3Og4kaS.js} +34 -26
  28. package/dist/postProcessingFrameGraph-C3Og4kaS.js.map +1 -0
  29. package/dist/sage.d.ts +13 -5
  30. package/dist/sage.es.js +919 -429
  31. package/dist/sage.es.js.map +1 -1
  32. package/dist/sage.umd.js +1 -1
  33. package/dist/sage.umd.js.map +1 -1
  34. package/dist/utils/bounds.d.ts +11 -0
  35. package/package.json +1 -1
  36. package/dist/postProcessingFrameGraph-rr2DbgPi.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"sage.es.js","names":[],"sources":["../src/classes/debugConsole.ts","../src/utils/version.ts","../src/classes/gameEngine.ts","../src/classes/loggers/consoleBackend.ts","../src/classes/loggers/nullBackend.ts","../src/utils/logger.ts","../src/classes/eventBus.ts","../../sage-core/dist/sage-core.js","../src/entities/nodeEntity.ts","../src/entities/entityPicking.ts","../src/entities/nodeSimulation.ts","../src/entities/nodePool.ts","../src/engines/audio.ts","../src/engines/scene.ts","../src/managers/asset.ts","../src/utils/capabilities.ts","../src/managers/game.ts","../src/handlers/postProcessingPipeline.ts","../src/handlers/postProcessing.ts","../src/utils/vectors.ts","../src/classes/level.ts","../src/managers/outline.ts","../src/classes/gameLevel.ts","../src/managers/level.ts","../src/managers/audio.ts","../src/managers/simulationSave.ts","../src/managers/colliderDebug.ts","../src/utils/graphics.ts","../src/utils/physics.ts","../src/utils/timer.ts","../src/debug/builtins.ts","../src/interfaces/logger.ts","../src/handlers/collider.ts","../src/handlers/lod.ts","../src/handlers/occluder.ts","../src/utils/metadata.ts","../src/handlers/sound.ts","../src/handlers/trigger.ts","../src/handlers/visible.ts","../src/handlers/index.ts","../src/entities/defineEntity.ts","../src/entities/standardMeshSource.ts","../src/input/actionRegistry.ts","../src/input/actionContext.ts","../src/input/deviceReaders/keyboard.ts","../src/input/deviceReaders/mouse.ts","../src/input/deviceReaders/gamepad.ts","../src/input/bindings/trigger.ts","../src/input/bindings/toggle.ts","../src/input/bindings/value.ts","../src/input/resolveBinding.ts","../src/input/deliverAction.ts","../src/input/inputCapture.ts","../src/input/configuration.ts","../../../node_modules/hexoid/dist/index.mjs","../src/utils/id.ts","../src/utils/stateMachine.ts","../src/sage.ts"],"sourcesContent":["//----------------------------------------------------------------------------------------------------------------------\n// Debug Console\n//\n// Manages a namespace object on `window` that exposes game engine internals to the browser dev console.\n// Supports direct values, callable commands, and dynamic getters via Object.defineProperty.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Disposable } from '../interfaces/lifecycle.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface DynamicGetter\n{\n get : () => unknown;\n}\n\ninterface ExposedEntry\n{\n getter ?: () => unknown;\n value ?: unknown;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class DebugConsole implements Disposable\n{\n private _namespaceName : string;\n private _entries = new Map<string, ExposedEntry>();\n private _disposed = false;\n\n constructor(namespaceName = 'sage')\n {\n this._namespaceName = namespaceName;\n this._installNamespace();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n expose(name : string, value : unknown | DynamicGetter) : void\n {\n if(this._disposed)\n {\n return;\n }\n\n if(this._isDynamicGetter(value))\n {\n this._entries.set(name, { getter: value.get });\n this._defineGetter(name, value.get);\n }\n else\n {\n this._entries.set(name, { value });\n this._defineValue(name, value);\n }\n }\n\n setNamespace(name : string) : void\n {\n if(this._disposed || name === this._namespaceName)\n {\n return;\n }\n\n // Checked before the old namespace comes down, so a collision leaves the console where it was\n this._assertNamespaceFree(name);\n\n this._removeNamespace();\n this._namespaceName = name;\n this._installNamespace();\n\n for(const [ entryName, entry ] of this._entries)\n {\n if(entry.getter)\n {\n this._defineGetter(entryName, entry.getter);\n }\n else\n {\n this._defineValue(entryName, entry.value);\n }\n }\n }\n\n get namespaceName() : string\n {\n return this._namespaceName;\n }\n\n dispose() : void\n {\n if(this._disposed)\n {\n return;\n }\n\n this._removeNamespace();\n this._entries.clear();\n this._disposed = true;\n }\n\n async $teardown() : Promise<void>\n {\n this.dispose();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private\n //------------------------------------------------------------------------------------------------------------------\n\n private _isDynamicGetter(value : unknown) : value is DynamicGetter\n {\n return typeof value === 'object'\n && value !== null\n && 'get' in value\n && typeof (value as DynamicGetter).get === 'function';\n }\n\n private _getNamespace() : Record<string, unknown> | undefined\n {\n return Reflect.get(window, this._namespaceName) as Record<string, unknown> | undefined;\n }\n\n private _assertNamespaceFree(name : string) : void\n {\n if(typeof window === 'undefined')\n {\n return;\n }\n\n if(Reflect.has(window, name))\n {\n throw new Error(\n `window.${ name } is already occupied, and the debug console will not overwrite it. `\n + 'Pick a different namespace with the debug.namespace option.'\n );\n }\n }\n\n private _installNamespace() : void\n {\n if(typeof window === 'undefined')\n {\n return;\n }\n\n this._assertNamespaceFree(this._namespaceName);\n\n Reflect.set(window, this._namespaceName, {});\n }\n\n private _removeNamespace() : void\n {\n if(typeof window === 'undefined')\n {\n return;\n }\n\n Reflect.deleteProperty(window, this._namespaceName);\n }\n\n private _defineGetter(name : string, getter : () => unknown) : void\n {\n const ns = this._getNamespace();\n if(!ns)\n {\n return;\n }\n\n Object.defineProperty(ns, name, {\n get: getter,\n enumerable: true,\n configurable: true,\n });\n }\n\n private _defineValue(name : string, value : unknown) : void\n {\n const ns = this._getNamespace();\n if(!ns)\n {\n return;\n }\n\n Object.defineProperty(ns, name, {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Version utility for SAGE\n//----------------------------------------------------------------------------------------------------------------------\n\ndeclare const __APP_VERSION__ : string;\n\nexport const VERSION = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0-dev';\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Game Engine\n//\n// WARNING: This module must be imported as `import type` everywhere except sage.ts. A regular import\n// would create a circular runtime dependency (managers import GameEngine, GameEngine imports managers).\n// The `import type` is erased at compile time, keeping the runtime graph acyclic.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractEngine, HavokPlugin } from '@babylonjs/core';\n\n// Interfaces\nimport type { Scene } from '@babylonjs/core';\nimport type { EntityMeshLoader, GameCanvas } from '../interfaces/game.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Entities\nimport type { NodeSimulation } from '../entities/nodeSimulation.ts';\n\n// Engines\nimport { SceneEngine } from '../engines/scene.ts';\nimport type { AudioEngine } from '../engines/audio.ts';\n\n// Managers\nimport { AssetManager } from '../managers/asset.ts';\nimport type { ColliderDebugManager } from '../managers/colliderDebug.ts';\nimport type { DebugConsole } from './debugConsole.ts';\nimport { GameManager } from '../managers/game.ts';\nimport { LevelManager } from '../managers/level.ts';\nimport type { SimulationSaveManager } from '../managers/simulationSave.ts';\nimport type { AudioManager } from '../managers/audio.ts';\n\n// Utils\nimport { GameEventBus, type GameEventCallback, type Unsubscribe } from './eventBus.ts';\nimport type { LibraryEventPayloadMap } from '../events/payloads.ts';\nimport type { WildcardPattern } from '../events/types.ts';\nimport { LoggingUtility } from '../utils/logger.ts';\nimport { GameTimer } from '../utils/timer.ts';\nimport { VERSION } from '../utils/version.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type definition for game lifecycle hook functions\n */\n\nexport type GameHook = (gameEngine : GameEngine) => Promise<void>;\n\n/** Builds a NodeSimulation bound to a scene and a mesh source -- a fresh one is built for every level that loads. */\nexport type SimulationFactory = (scene : Scene, meshSource ?: EntityMeshLoader) => NodeSimulation;\n\n/**\n * Interface representing the engines used in the game.\n * All engines must implement Disposable for proper cleanup.\n */\ninterface Engines extends Record<string, Disposable | undefined>\n{\n sceneEngine : SceneEngine;\n audioEngine ?: AudioEngine;\n}\n\n/**\n * Interface representing the managers used in the game.\n * All managers must implement Disposable for proper cleanup.\n */\ninterface Managers extends Record<string, Disposable | undefined>\n{\n assetManager : AssetManager;\n gameManager : GameManager;\n levelManager : LevelManager;\n saveManager : SimulationSaveManager;\n audioManager ?: AudioManager;\n}\n\n/**\n * Interface representing the debug tools available on the engine.\n */\ninterface DebugTools\n{\n colliders : ColliderDebugManager;\n console : DebugConsole | null;\n expose(name : string, value : unknown) : void;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Central hub that owns the render loop, physics, event bus, and all managers.\n * Created via `createGameEngine()` in sage.ts.\n */\nexport class GameEngine\n{\n public canvas : GameCanvas;\n public renderEngine : AbstractEngine;\n public physics : HavokPlugin;\n public managers : Managers;\n public engines : Engines;\n public eventBus : GameEventBus;\n public logger : LoggingUtility;\n // Null until something gives the engine a scene to build one against: rebuildSimulation, called by a level\n // once its own scene exists, or by the game directly for a game that never loads one.\n public simulation : NodeSimulation | null = null;\n public timer : GameTimer;\n public debug : DebugTools;\n public largeWorldRendering : boolean;\n public started = false;\n\n private _log : LoggerInterface;\n private _buildSimulation : SimulationFactory;\n\n // Lifecycle hooks\n private _beforeStartHook : GameHook | null = null;\n private _onStartHook : GameHook | null = null;\n private _onTeardownHook : GameHook | null = null;\n\n /**\n * Creates an instance of GameEngine.\n * @param canvas\n * @param renderEngine\n * @param physics\n * @param eventBus\n * @param logger\n * @param timer\n * @param buildSimulation - Builds a fresh simulation for a scene, once rebuildSimulation is called with one.\n * @param engines\n * @param managers\n */\n constructor(\n canvas : GameCanvas,\n renderEngine : AbstractEngine,\n physics : HavokPlugin,\n eventBus : GameEventBus,\n logger : LoggingUtility,\n timer : GameTimer,\n buildSimulation : SimulationFactory,\n engines : Engines,\n managers : Managers,\n largeWorldRendering = false,\n debug ?: DebugTools\n )\n {\n this.canvas = canvas;\n this.renderEngine = renderEngine;\n this.physics = physics;\n this.eventBus = eventBus;\n this.logger = logger;\n this.timer = timer;\n this._buildSimulation = buildSimulation;\n this.engines = engines;\n this.managers = managers;\n this.debug = debug ?? {\n colliders: undefined as unknown as ColliderDebugManager,\n console: null,\n expose: () => { /* no-op */ },\n };\n this.largeWorldRendering = largeWorldRendering;\n\n this._log = logger.getLogger('GameEngine');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Builds a fresh NodeSimulation bound to the given scene and mesh source, and swaps it in. A level calls\n * this once its own scene is ready, so every NodeEntity it spawns gets a node in that scene and loads its\n * mesh through that source. Left unset, a spawned entity gets a node with no mesh.\n */\n rebuildSimulation(scene : Scene, meshSource ?: EntityMeshLoader) : NodeSimulation\n {\n this.simulation = this._buildSimulation(scene, meshSource);\n\n return this.simulation;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a function to be called before the game engine starts\n * @param hook\n * @throws Error if a hook is already registered\n */\n onBeforeStart(hook : GameHook) : void\n {\n if(this._beforeStartHook !== null)\n {\n throw new Error('A beforeStart hook is already registered. Only one hook is allowed per lifecycle event.');\n }\n this._beforeStartHook = hook;\n }\n\n /**\n * Register a function to be called after the game engine starts\n * @param hook\n * @throws Error if a hook is already registered\n */\n onStart(hook : GameHook) : void\n {\n if(this._onStartHook !== null)\n {\n throw new Error('An onStart hook is already registered. Only one hook is allowed per lifecycle event.');\n }\n this._onStartHook = hook;\n }\n\n /**\n * Register a function to be called when the game engine stops\n * @param hook\n * @throws Error if a hook is already registered\n */\n onTeardown(hook : GameHook) : void\n {\n if(this._onTeardownHook !== null)\n {\n throw new Error('An onTeardown hook is already registered. Only one hook is allowed per lifecycle event.');\n }\n this._onTeardownHook = hook;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Event Subscriptions\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Subscribe to an event on the event bus.\n *\n * This is a convenience method that forwards to `eventBus.subscribe()`.\n *\n * @param eventType - Exact event type string, wildcard pattern (e.g. 'level:*'), or RegExp\n * @param callback\n * @returns A function that removes this subscription when called\n *\n * @example\n * // Subscribe to a specific event\n * const unsub = engine.subscribe('level:complete', (event) => {\n * console.log('Level complete:', event.payload.levelName);\n * });\n *\n * // Subscribe to every level event\n * engine.subscribe('level:*', (event) => {\n * console.log('Level event:', event.type);\n * });\n */\n subscribe<T extends keyof LibraryEventPayloadMap & string>(\n eventType : T | WildcardPattern | RegExp,\n callback : GameEventCallback<T, LibraryEventPayloadMap>\n ) : Unsubscribe\n {\n return this.eventBus.subscribe(eventType, callback);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Start the engine: runs beforeStart hook, starts the game manager, then runs onStart hook.\n */\n async start() : Promise<void>\n {\n if(!this.started)\n {\n this._log.info(`Starting SkewedAspect Game Engine (Version: ${ VERSION })...`);\n\n // Execute beforeStart hook if registered\n if(this._beforeStartHook)\n {\n this._log.debug('Executing beforeStart hook...');\n await this._beforeStartHook(this);\n }\n\n // Start the game manager\n await this.managers.gameManager.start();\n\n this._log.info('Game engine started successfully');\n\n // Execute onStart hook if registered\n if(this._onStartHook)\n {\n this._log.debug('Executing onStart hook...');\n await this._onStartHook(this);\n }\n\n // Mark game as started\n this.started = true;\n }\n else\n {\n this._log.warn('Game engine is already started. Skipping start.');\n }\n }\n\n /**\n * Stop the engine: runs the teardown hook, then tears down all managers and engines in order.\n */\n async stop() : Promise<void>\n {\n if(this.started)\n {\n this._log.info(`Stopping SkewedAspect Game Engine (Version: ${ VERSION })...`);\n\n let teardownError : Error | null = null;\n\n // Execute onTeardown hook if registered\n if(this._onTeardownHook)\n {\n try\n {\n this._log.debug('Executing onTeardown hook...');\n await this._onTeardownHook(this);\n }\n catch (err)\n {\n this._log.error(`Error in onTeardown hook: ${ err }`);\n teardownError = teardownError || (err as Error);\n }\n }\n\n // Dispose debug console before tearing down managers\n if(this.debug.console)\n {\n this.debug.console.dispose();\n }\n\n // Teardown all managers\n for(const managerKey of Object.keys(this.managers) as (keyof Managers)[])\n {\n const manager = this.managers[managerKey];\n if(manager)\n {\n try\n {\n this._log.debug(`Tearing down manager: ${ managerKey }`);\n // eslint-disable-next-line no-await-in-loop\n await manager.$teardown();\n }\n catch (err)\n {\n this._log.error(`Error tearing down manager ${ managerKey }: ${ err }`);\n teardownError = teardownError || (err as Error);\n }\n }\n }\n\n // Teardown all engines\n for(const engineKey of Object.keys(this.engines) as (keyof Engines)[])\n {\n const engine = this.engines[engineKey];\n if(engine)\n {\n try\n {\n this._log.debug(`Tearing down engine: ${ engineKey }`);\n // eslint-disable-next-line no-await-in-loop\n await engine.$teardown();\n }\n catch (err)\n {\n this._log.error(`Error tearing down engine ${ engineKey }: ${ err }`);\n teardownError = teardownError || (err as Error);\n }\n }\n }\n\n this.started = false;\n this._log.info('Game engine stopped successfully');\n\n // If we encountered any errors during teardown, throw the first one\n if(teardownError)\n {\n throw teardownError;\n }\n }\n else\n {\n this._log.warn('Game engine is not started. Skipping stop.');\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Console Logger Backend\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LogLevel, LoggingBackend } from '../../interfaces/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ntype ColorLevel = LogLevel | 'timer';\n\n/**\n * A logging backend that logs to the console.\n */\nexport class ConsoleBackend implements LoggingBackend\n{\n private timers : Map<string, number>;\n\n constructor()\n {\n this.timers = new Map();\n }\n\n /**\n * Get the CSS style for a specific log level\n */\n private getStyleForLevel(level : ColorLevel) : string\n {\n // CSS styles for browser console\n const styles : Record<ColorLevel, string> = {\n trace: 'color: #999999', // Dim gray\n debug: 'color: #00AAAA', // Cyan\n info: 'color: #00AA00', // Green\n warn: 'color: #AAAA00', // Yellow\n error: 'color: #AA0000', // Red\n timer: 'color: #AA00AA', // Magenta\n none: 'color: inherit', // Default\n };\n\n return styles[level] || styles.none;\n }\n\n /**\n * Format a log message with category, timestamp and log level\n */\n private formatMessage(category : string, level : ColorLevel) : [string, string, string, string]\n {\n // Format time as HH:MM:SS AM/PM\n const now = new Date();\n const hours = now.getHours() % 12 || 12; // Convert 0 to 12 for 12 AM\n const minutes = now.getMinutes().toString()\n .padStart(2, '0');\n const seconds = now.getSeconds().toString()\n .padStart(2, '0');\n const ampm = now.getHours() >= 12 ? 'PM' : 'AM';\n const timestamp = `[${ hours }:${ minutes }:${ seconds } ${ ampm }]`;\n\n const upperLevel = level.toUpperCase();\n\n // Return format strings and styling that will be used with console methods\n return [\n `${ timestamp } %c${ upperLevel }%c (${ category }): `, // Format string with placeholders\n this.getStyleForLevel(level), // Level style\n '', // Default style\n '', // Reset style\n ];\n }\n\n trace(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'trace');\n console.debug(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n debug(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'debug');\n console.debug(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n info(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'info');\n console.info(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n warn(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'warn');\n console.warn(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n error(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'error');\n console.error(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n /**\n * Start a timer with the specified label.\n */\n time(category : string, label : string) : void\n {\n const timerKey = `${ category }:${ label }`;\n this.timers.set(timerKey, performance.now());\n }\n\n /**\n * End a timer and log the elapsed time.\n */\n timeEnd(category : string, label : string) : void\n {\n const timerKey = `${ category }:${ label }`;\n const startTime = this.timers.get(timerKey);\n\n if(startTime !== undefined)\n {\n const duration = performance.now() - startTime;\n this.timers.delete(timerKey);\n\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'timer');\n console.info(\n `${ format } Timer '${ label }' completed in ${ duration.toFixed(2) }ms`,\n defaultStyle,\n levelStyle,\n resetStyle\n );\n }\n else\n {\n console.warn(`[${ category }]: Timer '${ label }' does not exist`);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Null Logger Backend\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LoggingBackend } from '../../interfaces/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A logging backend that does nothing (discards all logs).\n * Useful for production environments or when you want to completely disable logging.\n */\nexport class NullBackend implements LoggingBackend\n{\n trace(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n debug(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n info(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n warn(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n error(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n time(_category : string, _label : string) : void\n {\n // Do nothing\n }\n\n timeEnd(_category : string, _label : string) : void\n {\n // Do nothing\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Logger Utility\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type {\n LogLevel,\n LoggerInterface,\n LoggingBackend,\n} from '../interfaces/logger.ts';\n\nimport { ConsoleBackend } from '../classes/loggers/consoleBackend.ts';\nimport { NullBackend } from '../classes/loggers/nullBackend.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Implementation of Logger interface.\n * Each instance is bound to a specific category and backend.\n */\nexport class SAGELogger implements LoggerInterface\n{\n private category : string;\n private backend : LoggingBackend;\n private minLevel : LogLevel;\n\n constructor(category : string, minLevel : LogLevel = 'none', backend : LoggingBackend = new NullBackend())\n {\n this.category = category;\n this.backend = backend;\n this.minLevel = minLevel;\n }\n\n /**\n * Update the logger's backend and minimum level\n */\n public updateSettings(backend : LoggingBackend, minLevel : LogLevel) : void\n {\n this.backend = backend;\n this.minLevel = minLevel;\n }\n\n trace(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('trace'))\n {\n this.backend.trace(this.category, message, ...args);\n }\n }\n\n debug(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('debug'))\n {\n this.backend.debug(this.category, message, ...args);\n }\n }\n\n info(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('info'))\n {\n this.backend.info(this.category, message, ...args);\n }\n }\n\n warn(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('warn'))\n {\n this.backend.warn(this.category, message, ...args);\n }\n }\n\n error(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('error'))\n {\n this.backend.error(this.category, message, ...args);\n }\n }\n\n time(label : string) : void\n {\n if(this.minLevel !== 'none')\n {\n this.backend.time(this.category, label);\n }\n }\n\n timeEnd(label : string) : void\n {\n if(this.minLevel !== 'none')\n {\n this.backend.timeEnd(this.category, label);\n }\n }\n\n /**\n * Determine if a message at the given level should be logged based on the current minimum level.\n */\n private shouldLog(level : LogLevel) : boolean\n {\n if(this.minLevel === 'none')\n {\n return false;\n }\n\n switch (this.minLevel)\n {\n case 'trace':\n return true;\n case 'debug':\n return level !== 'trace';\n case 'info':\n return level === 'info' || level === 'warn' || level === 'error';\n case 'warn':\n return level === 'warn' || level === 'error';\n case 'error':\n return level === 'error';\n default:\n return false;\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A utility for creating loggers and managing logging configuration.\n */\nexport class LoggingUtility\n{\n private backend : LoggingBackend;\n private level : LogLevel;\n private loggers : Map<string, LoggerInterface>;\n\n /**\n * Create a new LoggingUtility.\n *\n * @param level - The minimum logging level (defaults to INFO)\n * @param backend - The logging backend to use (defaults to ConsoleBackend)\n */\n constructor(level : LogLevel = 'debug', backend : LoggingBackend = new ConsoleBackend())\n {\n this.backend = backend;\n this.level = level;\n this.loggers = new Map();\n }\n\n /**\n * Set the logging backend. Updates all existing loggers to use the new backend.\n *\n * @param backend\n */\n public setBackend(backend : LoggingBackend) : void\n {\n this.backend = backend;\n\n // Update all existing loggers to use the new backend\n this.loggers.forEach((logger) =>\n {\n if(logger instanceof SAGELogger)\n {\n logger.updateSettings(this.backend, this.level);\n }\n });\n }\n\n /**\n * Set the minimum logging level. Updates all existing loggers.\n *\n * @param level\n */\n public setLevel(level : LogLevel) : void\n {\n this.level = level;\n\n // Update all existing loggers to use the new level\n this.loggers.forEach((logger) =>\n {\n if(logger instanceof SAGELogger)\n {\n logger.updateSettings(this.backend, this.level);\n }\n });\n }\n\n /**\n * Get the current minimum logging level.\n */\n public getLevel() : LogLevel\n {\n return this.level;\n }\n\n /**\n * Get or create a logger for the specified category. Returns an existing logger if one was already created.\n *\n * @param category - Typically the class or module name\n */\n public getLogger(category : string) : LoggerInterface\n {\n let logger = this.loggers.get(category);\n if(!logger)\n {\n logger = new SAGELogger(category, this.level, this.backend);\n this.loggers.set(category, logger);\n }\n\n return logger;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport { ConsoleBackend } from '../classes/loggers/consoleBackend.ts';\nexport { NullBackend } from '../classes/loggers/nullBackend.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Event Bus\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LibraryEventPayloadMap } from '../events/payloads.ts';\nimport type { WildcardPattern } from '../events/types.ts';\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A typed game event with compile-time payload checking.\n *\n * @template T - The event type string (e.g., 'input:changed')\n * @template TPayloadMap - The payload map to resolve types from\n */\nexport interface GameEvent<\n T extends string = string,\n TPayloadMap extends Record<string, unknown> = Record<string, unknown>,\n>\n{\n /** The string identifier/category of the event (e.g. \"input:keydown\"). */\n type : T;\n /** The optional ID of whoever sent this event. */\n senderID ?: string;\n /** The optional ID of who should receive this event. */\n targetID ?: string;\n /** The payload, typed based on the payload map. */\n payload : T extends keyof TPayloadMap ? TPayloadMap[T] : unknown;\n}\n\n/**\n * A callback function that receives a {@link GameEvent} with a given payload type.\n *\n * @template T - The event type string\n * @template TPayloadMap - The payload map to resolve types from\n */\nexport type GameEventCallback<\n T extends string = string,\n TPayloadMap extends Record<string, unknown> = Record<string, unknown>,\n> = (event : GameEvent<T, TPayloadMap>) => void | Promise<void>;\n\n/**\n * Represents a subscription record internally within the EventBus.\n *\n * We store the callback as if it handles `GameEvent<any>`\n * because we can't unify multiple different payload types in the same structure.\n */\ninterface Subscription\n{\n /** Precompiled RegExp if this subscription is pattern-based. */\n pattern ?: RegExp;\n /** The callback, but stored to accept any payload type (due to the union nature). */\n callback : (event : GameEvent<any, any>) => void | Promise<void>;\n}\n\n/**\n * An unsubscribe function, returned by any subscribe method.\n */\nexport type Unsubscribe = () => void;\n\n/**\n * Callback for wildcard/pattern subscriptions.\n * Payload is unknown since multiple event types may match.\n */\nexport type WildcardEventCallback\n = (event : GameEvent<string, Record<string, unknown>>) => void | Promise<void>;\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A high-performance, type-safe event bus that supports exact or pattern-based subscriptions.\n *\n * The generic parameter `TPayloadMap` maps event type strings to their payload types,\n * enabling compile-time checking of event/payload combinations.\n *\n * @template TPayloadMap - Map of event type strings to their payload types.\n * Defaults to LibraryEventPayloadMap for internal library usage.\n *\n * @example\n * // Using library events (default)\n * const bus = new GameEventBus();\n * bus.publish({ type: 'level:progress', payload: { levelName: 'Level1', progress: 50 } });\n *\n * @example\n * // User-defined events with full type safety\n * interface MyEvents {\n * 'game:score-updated': { score: number; player: string };\n * 'game:paused': { reason: string };\n * [key: string]: unknown; // Allow other events\n * }\n *\n * const bus = new GameEventBus<MyEvents>();\n * bus.publish({ type: 'game:score-updated', payload: { score: 100, player: 'alice' } });\n *\n * @example\n * // Merging user events with library events\n * const bus = new GameEventBus<MyEvents & LibraryEventPayloadMap>();\n */\nexport class GameEventBus<TPayloadMap extends Record<string, unknown> = LibraryEventPayloadMap> implements Disposable\n{\n /**\n * For exact event-type matches:\n * - Key = event type string (e.g. \"input:keydown\")\n * - Value = set of subscriptions for that exact type\n */\n private directMap = new Map<string, Set<Subscription>>();\n\n /**\n * For pattern-based (wildcard or RegExp) subscriptions.\n */\n private patternSubs = new Set<Subscription>();\n\n /**\n * Logger instance\n */\n private _log : LoggerInterface;\n\n /**\n * Creates a new GameEventBus instance\n *\n * @param logger\n */\n constructor(logger ?: LoggingUtility)\n {\n this._log = logger?.getLogger('EventBus') || new SAGELogger('EventBus');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Subscribe\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Subscribe with automatic detection:\n * - If `eventTypeOrPattern` is a `RegExp` or a string containing `*`, treat it as a pattern subscription.\n * - Otherwise, treat it as an exact subscription.\n *\n * @template T - The event type string (must be a key in TPayloadMap for type safety)\n * @param eventTypeOrPattern - Exact event type string, wildcard pattern, or RegExp\n * @param callback\n * @returns A function that removes this subscription when called\n */\n public subscribe<T extends keyof TPayloadMap & string>(\n eventTypeOrPattern : T | WildcardPattern | RegExp,\n callback : GameEventCallback<T, TPayloadMap>\n ) : Unsubscribe\n {\n this._log.trace(`Subscribe request for: ${ eventTypeOrPattern.toString() }`);\n\n // If it's explicitly a RegExp or includes '*', treat as pattern:\n if(\n eventTypeOrPattern instanceof RegExp\n || (typeof eventTypeOrPattern === 'string' && eventTypeOrPattern.includes('*'))\n )\n {\n return this.subscribePattern(\n eventTypeOrPattern as WildcardPattern | RegExp,\n callback as WildcardEventCallback\n );\n }\n else\n {\n return this.subscribeExact(eventTypeOrPattern as T, callback);\n }\n }\n\n /**\n * Subscribes to an exact event type (e.g. \"input:keydown\").\n *\n * @template T - The event type string (must be a key in TPayloadMap for type safety)\n * @param eventType\n * @param callback\n * @returns A function that removes this subscription when called\n */\n public subscribeExact<T extends keyof TPayloadMap & string>(\n eventType : T,\n callback : GameEventCallback<T, TPayloadMap>\n ) : Unsubscribe\n {\n this._log.debug(`Adding exact subscription for: ${ eventType }`);\n\n let subs = this.directMap.get(eventType);\n if(!subs)\n {\n subs = new Set();\n this.directMap.set(eventType, subs);\n }\n\n // Store as a Subscription that accepts `GameEvent<any>`\n const subscription : Subscription = {\n callback: (event) => callback(event as GameEvent<T, TPayloadMap>),\n };\n subs.add(subscription);\n\n return () =>\n {\n this._log.debug(`Removing exact subscription for: ${ eventType }`);\n subs.delete(subscription);\n if(subs.size === 0)\n {\n this.directMap.delete(eventType);\n }\n };\n }\n\n /**\n * Subscribes with either:\n * - a wildcard string (contains `*`) that gets converted to a RegExp\n * - a RegExp directly\n *\n * Note: Payload typing is not available for wildcards since multiple event types may match.\n *\n * @param patternOrRegExp - Wildcard string (e.g. `\"input:*\"`) or a RegExp\n * @param callback\n * @returns A function that removes this subscription when called\n */\n public subscribePattern(\n patternOrRegExp : WildcardPattern | RegExp,\n callback : WildcardEventCallback\n ) : Unsubscribe\n {\n let regex : RegExp;\n\n if(typeof patternOrRegExp === 'string')\n {\n // We replace '*' with '.*' for wildcard matching\n const escaped = patternOrRegExp\n .replace(/[-/\\\\^$+?.()|[\\]{}]/g, '\\\\$&') // escape special chars\n .replace(/\\*/g, '.*'); // replace '*' with '.*'\n\n // e.g. \"input:*\" => \"input:.*\" => new RegExp(\"^input:.*$\")\n regex = new RegExp(`^${ escaped }$`);\n this._log.debug(\n `Adding pattern subscription for string: ${ patternOrRegExp }, regex: ${ regex.toString() }`\n );\n }\n else\n {\n // If already a RegExp, use as-is\n regex = patternOrRegExp;\n this._log.debug(`Adding pattern subscription for regex: ${ regex.toString() }`);\n }\n\n const subscription : Subscription = {\n pattern: regex,\n callback: (event) => callback(event as GameEvent<string, Record<string, unknown>>),\n };\n this.patternSubs.add(subscription);\n\n return () =>\n {\n this._log.debug(`Removing pattern subscription: ${ regex.toString() }`);\n this.patternSubs.delete(subscription);\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Publish\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Publishes an event, invoking all matching callbacks.\n *\n * **Async Execution Model:**\n * Callbacks are invoked asynchronously via the microtask queue using `Promise.resolve().then()`.\n * This means `publish()` returns immediately, before any callbacks execute. This design:\n * - Prevents stack overflow from recursive event publishing\n * - Allows multiple events to be queued before processing\n * - Is ideal for fire-and-forget events (notifications, state changes)\n *\n * **Not suitable for request-response patterns.** If you need to wait for a callback's\n * result, consider using a different communication mechanism.\n *\n * @template T - The event type string (must be a key in TPayloadMap for type safety)\n * @param event\n */\n public publish<T extends keyof TPayloadMap & string>(\n event : GameEvent<T, TPayloadMap>\n ) : void\n {\n this._log.trace(`Publishing event: ${ event.type }`, event);\n\n let callbackCount = 0;\n\n // 1) Exact match\n const directSubs = this.directMap.get(event.type);\n if(directSubs)\n {\n callbackCount += directSubs.size;\n for(const sub of directSubs)\n {\n Promise.resolve()\n .then(() => sub.callback(event))\n .catch((err) =>\n {\n this._log.error(`Error in event handler for '${ event.type }':`, err);\n });\n }\n }\n\n // 2) Pattern subscriptions\n for(const sub of this.patternSubs)\n {\n if(sub.pattern && sub.pattern.test(event.type))\n {\n callbackCount++;\n Promise.resolve()\n .then(() => sub.callback(event))\n .catch((err) =>\n {\n this._log.error(`Error in pattern event handler for '${ event.type }':`, err);\n });\n }\n }\n\n if(callbackCount === 0)\n {\n this._log.debug(`No subscribers found for event: ${ event.type }`);\n }\n else\n {\n this._log.trace(`Event ${ event.type } dispatched to ${ callbackCount } subscribers`);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Tears down the event bus and clears all subscriptions.\n * After calling this method, no events will be delivered.\n */\n public async $teardown() : Promise<void>\n {\n this._log.debug('Tearing down EventBus');\n this.directMap.clear();\n this.patternSubs.clear();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//#region src/behavior.ts\nvar e = class {}, t = class extends e {\n\tdefaults = { displayName: \"\" };\n}, n = 32, r = 64, i = 5;\n//#endregion\n//#region src/events.ts\nfunction a(e) {\n\tlet t = e.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\\\\\*/g, \".*\");\n\treturn RegExp(`^${t}$`);\n}\n//#endregion\n//#region src/eventBus.ts\nvar o = class e {\n\t#e = [];\n\t#t = [];\n\t#n = /* @__PURE__ */ new Map();\n\t#r = /* @__PURE__ */ new Map();\n\tregister(e, t) {\n\t\tif (this.#n.has(e.id)) throw Error(`A subscriber is already registered under the ID \"${e.id}\".`);\n\t\tthis.#n.set(e.id, e);\n\t\tlet n = [], r = [];\n\t\tfor (let i of t) {\n\t\t\tlet t = i;\n\t\t\tif (t.endsWith(\":*\")) {\n\t\t\t\tlet n = {\n\t\t\t\t\tpattern: a(t),\n\t\t\t\t\tsubscriber: e\n\t\t\t\t};\n\t\t\t\tr.push(n), this.#t.push(n);\n\t\t\t} else n.includes(t) || (n.push(t), this.#i(t, e));\n\t\t}\n\t\treturn this.#o(e, n, r);\n\t}\n\temit(e) {\n\t\tthis.#e.push(e);\n\t}\n\tdrain() {\n\t\tlet e = [], t = 0;\n\t\tfor (; this.#e.length > 0;) {\n\t\t\tif (t += 1, t > 64) {\n\t\t\t\tthis.#e = [], e.push(/* @__PURE__ */ Error(\"The drain exceeded 64 passes.\"));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlet n = this.#e;\n\t\t\tthis.#e = [];\n\t\t\tfor (let t of n) this.#s(t, e);\n\t\t}\n\t\tif (e.length > 0) throw AggregateError(e, \"The drain raised.\");\n\t}\n\t#i(e, t) {\n\t\tlet n = this.#r.get(e) ?? [];\n\t\tn.push(t), this.#r.set(e, n);\n\t}\n\t#a(e, t) {\n\t\tlet n = this.#r.get(e);\n\t\tif (n === void 0) return;\n\t\tlet r = n.indexOf(t);\n\t\tr >= 0 && n.splice(r, 1), n.length === 0 && this.#r.delete(e);\n\t}\n\t#o(e, t, n) {\n\t\tlet r = !0;\n\t\treturn () => {\n\t\t\tif (!r) return;\n\t\t\tr = !1, this.#n.get(e.id) === e && this.#n.delete(e.id);\n\t\t\tfor (let n of t) this.#a(n, e);\n\t\t\tlet i = [];\n\t\t\tfor (let e of this.#t) n.includes(e) || i.push(e);\n\t\t\tthis.#t = i;\n\t\t};\n\t}\n\t#s(t, n) {\n\t\tif (t.targetID !== void 0) {\n\t\t\tlet r = this.#n.get(t.targetID);\n\t\t\tr !== void 0 && e.#l(r, t, n);\n\t\t\treturn;\n\t\t}\n\t\tlet r = /* @__PURE__ */ new Set();\n\t\tfor (let i of [...this.#r.get(t.type) ?? []]) e.#c(i, t, r, n);\n\t\tfor (let i of [...this.#t]) i.pattern.test(t.type) && e.#c(i.subscriber, t, r, n);\n\t}\n\tstatic #c(t, n, r, i) {\n\t\tr.has(t) || (r.add(t), e.#l(t, n, i));\n\t}\n\tstatic #l(e, t, n) {\n\t\ttry {\n\t\t\te.receive(t);\n\t\t} catch (e) {\n\t\t\tn.push(e);\n\t\t}\n\t}\n};\n//#endregion\n//#region src/gameEntity.ts\nfunction s(e) {\n\tif (Array.isArray(e)) {\n\t\tlet t = [];\n\t\tfor (let n of e) t.push(s(n));\n\t\treturn t;\n\t}\n\tif (typeof e == \"object\" && e) {\n\t\tlet t = Object.create(null);\n\t\tfor (let [n, r] of Object.entries(e)) t[n] = s(r);\n\t\treturn t;\n\t}\n\treturn e;\n}\nfunction c(e) {\n\treturn s(e);\n}\nvar l = class e {\n\tid;\n\tname;\n\tcontext;\n\tstate;\n\tops;\n\teventSubscriptions;\n\tstatic behaviors = [];\n\t#e = [];\n\t#t = [];\n\t#n;\n\t#r = 0;\n\t#i;\n\tstatic create(t, n, r, i) {\n\t\treturn e.#a(this, t, n, r ?? {}, !0, i);\n\t}\n\tstatic restore(t, n, r, i) {\n\t\treturn e.#a(this, t, n, r, !1, i);\n\t}\n\tstatic #a(e, t, n, r, i, a) {\n\t\treturn new e(t, n, r, i, a);\n\t}\n\tconstructor(t, n, r, i, a) {\n\t\tthis.id = t, this.name = a ?? new.target.name, this.context = n;\n\t\tlet o = new.target.behaviors;\n\t\tfor (let e of o) this.#e.push(new e());\n\t\tthis.state = e.#u(this.#e, r, t);\n\t\tlet s = Object.create(null);\n\t\tthis.ops = s;\n\t\tlet c = [];\n\t\tfor (let [e, t] of o.entries()) {\n\t\t\tlet n = this.#e[e];\n\t\t\tn !== void 0 && (Object.assign(n, {\n\t\t\t\tstate: this.state,\n\t\t\t\tops: s,\n\t\t\t\tentity: this\n\t\t\t}), this.#o(n, t, s), this.#s(n, t, c));\n\t\t}\n\t\tthis.eventSubscriptions = c, this.#n = n.bus.register(this, c), i && this.#l();\n\t}\n\thasBehavior(e) {\n\t\tfor (let t of this.#e) if (t instanceof e) return !0;\n\t\treturn !1;\n\t}\n\tgetEntity(e) {\n\t\treturn this.context.getEntity(e);\n\t}\n\temit(e, t, n) {\n\t\tlet r = {\n\t\t\ttype: e,\n\t\t\tsenderID: this.id,\n\t\t\ttargetID: n,\n\t\t\tpayload: t\n\t\t};\n\t\tif (this.#i !== void 0) {\n\t\t\tthis.#i.push(r);\n\t\t\treturn;\n\t\t}\n\t\tthis.context.bus.emit(r);\n\t}\n\tupdate(e) {\n\t\tfor (let t of this.#e) t.onUpdate?.(e);\n\t}\n\treceive(t) {\n\t\tlet n = t;\n\t\tfor (let r of this.#t) if (e.#g(r, n.type) && r.instance.onEvent?.(t) === !0) return !0;\n\t\treturn !1;\n\t}\n\tdestroy() {\n\t\tthis.#n();\n\t}\n\tattachedBehaviors() {\n\t\treturn [...this.#e];\n\t}\n\t#o(e, t, n) {\n\t\tfor (let r of t.ops ?? []) {\n\t\t\tif (Object.hasOwn(n, r)) throw Error(`Two behaviors on entity \"${this.id}\" expose the operation \"${String(r)}\".`);\n\t\t\tlet i = e[r];\n\t\t\tif (typeof i != \"function\") throw Error(`Behavior \"${t.name}\" declares the operation \"${String(r)}\" with no method behind it.`);\n\t\t\tn[r] = this.#c(r, i, e);\n\t\t}\n\t}\n\t#s(e, t, n) {\n\t\tlet r = [], i = [];\n\t\tfor (let e of t.events ?? []) {\n\t\t\tlet t = e;\n\t\t\tt.endsWith(\":*\") ? i.push(a(t)) : r.push(t), n.includes(e) || n.push(e);\n\t\t}\n\t\t(r.length > 0 || i.length > 0) && this.#t.push({\n\t\t\tinstance: e,\n\t\t\texact: r,\n\t\t\tpatterns: i\n\t\t});\n\t}\n\t#c(e, t, n) {\n\t\treturn (...r) => {\n\t\t\tthis.#r += 1;\n\t\t\ttry {\n\t\t\t\tif (this.#r > 32) throw RangeError(`Operation \"${String(e)}\" on entity \"${this.id}\" ran deeper than the operation call depth limit of 32.`);\n\t\t\t\treturn t.apply(n, r);\n\t\t\t} finally {\n\t\t\t\t--this.#r;\n\t\t\t}\n\t\t};\n\t}\n\t#l() {\n\t\tthis.#i = [];\n\t\ttry {\n\t\t\tfor (let e of this.#e) e.onCreate?.();\n\t\t} catch (e) {\n\t\t\tthrow this.#i = void 0, this.#n(), e;\n\t\t}\n\t\tlet e = this.#i;\n\t\tthis.#i = void 0;\n\t\tfor (let t of e) this.context.bus.emit(t);\n\t}\n\tstatic #u(t, n, r) {\n\t\tlet i = Object.create(null);\n\t\ti.tags = [];\n\t\tfor (let n of t) Object.assign(i, e.#d(n.defaults, r));\n\t\treturn Object.assign(i, e.#d(n, r)), e.#f(i, r), i;\n\t}\n\tstatic #d(t, n) {\n\t\tif (!e.#h(t, /* @__PURE__ */ new Set())) throw TypeError(`Entity \"${n}\" state holds a value that is not finite JSON-compatible data.`);\n\t\treturn c(t);\n\t}\n\tstatic #f(e, t) {\n\t\tlet n = e.tags;\n\t\tif (!Array.isArray(n)) throw TypeError(`Entity \"${t}\" tags must be a list of strings.`);\n\t\tlet r = [];\n\t\tfor (let e of n) {\n\t\t\tif (typeof e != \"string\") throw TypeError(`Entity \"${t}\" tags must be a list of strings.`);\n\t\t\tif (r.includes(e)) throw Error(`Entity \"${t}\" carries the tag \"${e}\" more than once.`);\n\t\t\tr.push(e);\n\t\t}\n\t}\n\tstatic #p(t, n) {\n\t\treturn t === null || typeof t == \"string\" || typeof t == \"boolean\" ? !0 : typeof t == \"number\" ? Number.isFinite(t) : Array.isArray(t) ? e.#m(t, n) : typeof t == \"object\" && e.#h(t, n);\n\t}\n\tstatic #m(t, n) {\n\t\tif (n.has(t)) return !1;\n\t\tn.add(t);\n\t\tfor (let r of t) if (!e.#p(r, n)) return n.delete(t), !1;\n\t\treturn n.delete(t), !0;\n\t}\n\tstatic #h(t, n) {\n\t\tlet r = Object.getPrototypeOf(t);\n\t\tif (r !== Object.prototype && r !== null || n.has(t)) return !1;\n\t\tn.add(t);\n\t\tfor (let r of Object.values(t)) if (!e.#p(r, n)) return n.delete(t), !1;\n\t\treturn n.delete(t), !0;\n\t}\n\tstatic #g(e, t) {\n\t\tif (e.exact.includes(t)) return !0;\n\t\tfor (let n of e.patterns) if (n.test(t)) return !0;\n\t\treturn !1;\n\t}\n}, u = class e {\n\t#e = new o();\n\t#t;\n\t#n;\n\t#r;\n\t#i = 0;\n\t#a = 0;\n\t#o = 0;\n\t#s = [];\n\tconstructor(e) {\n\t\tif (this.#t = e.fixedStepDuration, this.#n = e.entityClasses, this.#r = e.allocator, !Number.isFinite(this.#t) || this.#t <= 0) throw RangeError(\"A fixed step duration must be a finite number greater than zero.\");\n\t}\n\tget bus() {\n\t\treturn this.#e;\n\t}\n\tget tick() {\n\t\treturn this.#i;\n\t}\n\tget entities() {\n\t\treturn [...this.#s];\n\t}\n\tspawn(e, t, n) {\n\t\tlet r = this.#f(e), i = t;\n\t\tif (i === void 0 && (i = this.#d()), this.getEntity(i) !== void 0) throw Error(`The simulation already holds an entity under the ID \"${i}\".`);\n\t\tlet a = r.create(i, this, n ?? {}, e);\n\t\treturn this.#s.push(a), a;\n\t}\n\tdespawn(e) {\n\t\tlet t = this.#s.findIndex((t) => t.id === e);\n\t\tif (t < 0) return;\n\t\tlet n = this.#s[t];\n\t\tn !== void 0 && (n.destroy(), this.#s.splice(t, 1));\n\t}\n\tgetEntity(e) {\n\t\treturn this.#s.find((t) => t.id === e);\n\t}\n\tgetEntitiesByTag(t) {\n\t\tlet n = [];\n\t\tfor (let r of this.#s) e.#p(r).tags.includes(t) && n.push(r);\n\t\treturn n;\n\t}\n\trunStepsForElapsedTime(e) {\n\t\tif (!Number.isFinite(e) || e < 0) throw RangeError(\"Elapsed time must be a finite number of zero or more.\");\n\t\tthis.#o += e;\n\t\tlet t = 0;\n\t\tfor (; this.#l() && t < 5;) this.#u(), t += 1;\n\t\tt === 5 && this.#l() && (this.#o %= this.#t);\n\t}\n\tsnapshot() {\n\t\tlet e = [];\n\t\tfor (let t of this.#s) e.push({\n\t\t\tid: t.id,\n\t\t\tname: t.name,\n\t\t\tstate: c(t.state)\n\t\t});\n\t\treturn {\n\t\t\ttick: this.#i,\n\t\t\tidCounter: this.#a,\n\t\t\tentities: e\n\t\t};\n\t}\n\tfromSnapshot(e) {\n\t\tlet t = this.buildReplacementSimulation({\n\t\t\tfixedStepDuration: this.#t,\n\t\t\tentityClasses: this.#n,\n\t\t\tallocator: this.#r\n\t\t});\n\t\treturn t.#c(e), t;\n\t}\n\tbuildReplacementSimulation(t) {\n\t\treturn new e(t);\n\t}\n\t#c(e) {\n\t\tlet t = [];\n\t\ttry {\n\t\t\tfor (let n of e.entities) {\n\t\t\t\tlet e = this.#f(n.name).restore(n.id, this, n.state, n.name);\n\t\t\t\tt.push(e);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tfor (let e of t) e.destroy();\n\t\t\tthrow e;\n\t\t}\n\t\tthis.#s = t, this.#i = e.tick, this.#a = e.idCounter;\n\t}\n\t#l() {\n\t\treturn this.#o + 2 ** -52 >= this.#t;\n\t}\n\t#u() {\n\t\tfor (let e of [...this.#s]) e.update(this.#t);\n\t\ttry {\n\t\t\tthis.#e.drain();\n\t\t} finally {\n\t\t\tthis.#o -= this.#t, Math.abs(this.#o) <= 2 ** -52 && (this.#o = 0), this.#i += 1;\n\t\t}\n\t}\n\t#d() {\n\t\treturn this.#r === void 0 ? (this.#a += 1, `entity-${this.#a}`) : this.#r();\n\t}\n\t#f(e) {\n\t\tlet t = this.#n[e];\n\t\tif (t === void 0) throw Error(`The entity class table holds no class named \"${e}\".`);\n\t\treturn t;\n\t}\n\tstatic #p(e) {\n\t\treturn e.state;\n\t}\n};\n//#endregion\nexport { e as Behavior, o as EventBus, l as GameEntity, u as GameSimulationManager, t as NamedBehavior, i as dilationLimit, r as drainPassLimit, n as operationCallDepthLimit };\n\n//# sourceMappingURL=sage-core.js.map","//----------------------------------------------------------------------------------------------------------------------\n// NodeEntity\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Quaternion, TransformNode, Vector3 } from '@babylonjs/core';\nimport { GameEntity } from '@skewedaspect/sage-core';\n\nimport type { AbstractMesh } from '@babylonjs/core';\nimport type { Action, GameEntityState, GameSimulationContext, StateBag } from '@skewedaspect/sage-core';\n\nimport type { NodeSet, NodeSimulationContext } from './nodeSimulationContext.ts';\nimport type { MeshConfig } from './meshConfig.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface Vector3State\n{\n x : number;\n y : number;\n z : number;\n}\n\nexport interface QuaternionState\n{\n x : number;\n y : number;\n z : number;\n w : number;\n}\n\nexport interface NodeEntityState extends GameEntityState\n{\n parentID ?: string;\n nodeName ?: string;\n position : Vector3State;\n rotation : QuaternionState;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport abstract class NodeEntity extends GameEntity\n{\n static readonly poolable : boolean = false;\n static readonly poolSize ?: number;\n static readonly meshConfig ?: MeshConfig;\n static readonly actions ?: readonly Action[];\n\n // A poolable class's node and mesh outlive this entity: `destroy()` hands this exact object to the pool, so a\n // mesh that finishes loading after that -- from this entity's own load, still in flight -- attaches to\n // whichever entity is holding it by then, the same object either way (design/entities.md#pooling).\n readonly #set : NodeSet;\n\n get node() : TransformNode\n {\n return this.#set.node;\n }\n\n get mesh() : AbstractMesh | undefined\n {\n return this.#set.mesh;\n }\n\n set mesh(mesh : AbstractMesh | undefined)\n {\n this.#set.mesh = mesh;\n }\n\n protected constructor(\n id : string,\n context : GameSimulationContext,\n providedState : object,\n runCreateHooks : boolean,\n name ?: string\n )\n {\n super(id, context, providedState, runCreateHooks, name);\n\n const nodeContext = context as NodeSimulationContext;\n const entityClass = new.target as typeof NodeEntity;\n\n const spatial = this.state as NodeEntityState;\n\n if(spatial.position === undefined)\n {\n spatial.position = { x: 0, y: 0, z: 0 };\n }\n\n if(spatial.rotation === undefined)\n {\n spatial.rotation = { x: 0, y: 0, z: 0, w: 1 }; // eslint-disable-line id-length\n }\n\n if(spatial.nodeName !== undefined)\n {\n this.#set = { node: NodeEntity.#findAuthoredNode(nodeContext, spatial.nodeName), mesh: undefined };\n }\n else\n {\n const pooled = entityClass.poolable ? nodeContext.pool.take(this.name) : undefined;\n\n if(pooled !== undefined)\n {\n this.#set = pooled;\n this.#set.node.setEnabled(true);\n }\n else\n {\n this.#set = { node: new TransformNode(id, nodeContext.scene), mesh: undefined };\n\n const set = this.#set;\n\n nodeContext.loadMesh(this.name, this.state as StateBag).then((mesh) =>\n {\n if(mesh !== undefined)\n {\n mesh.parent = set.node;\n }\n\n set.mesh = mesh;\n });\n }\n }\n\n if(spatial.parentID !== undefined)\n {\n const parentEntity = nodeContext.getEntity(spatial.parentID);\n\n if(parentEntity instanceof NodeEntity)\n {\n this.node.parent = parentEntity.node;\n }\n }\n }\n\n override update(dt : number) : void\n {\n super.update(dt);\n\n const spatial = this.state as NodeEntityState;\n\n // An adopted node's transform came from the level, in the level's own space, under whatever parent\n // authoring gave it; writing state's unparented position and rotation over that would move it wrong\n // (design/authoring.md#position-rotation-and-scale).\n if(spatial.nodeName !== undefined)\n {\n return;\n }\n\n this.node.position = new Vector3(spatial.position.x, spatial.position.y, spatial.position.z);\n this.node.rotationQuaternion = new Quaternion(\n spatial.rotation.x,\n spatial.rotation.y,\n spatial.rotation.z,\n spatial.rotation.w\n );\n }\n\n override destroy() : void\n {\n super.destroy();\n\n const spatial = this.state as NodeEntityState;\n\n // The node is the level's content, not a resource this entity borrowed -- destroying the entity never\n // disposes, hides, or pools it (design/authoring.md#pooling-and-destruction).\n if(spatial.nodeName !== undefined)\n {\n return;\n }\n\n const entityClass = this.constructor as typeof NodeEntity;\n\n if(entityClass.poolable)\n {\n const nodeContext = this.context as NodeSimulationContext;\n nodeContext.pool.give(this.name, this.#set, entityClass.poolSize);\n }\n else\n {\n this.node.dispose();\n }\n }\n\n static #findAuthoredNode(nodeContext : NodeSimulationContext, nodeName : string) : TransformNode\n {\n const found = nodeContext.scene.getNodeByName(nodeName);\n\n if(!(found instanceof TransformNode))\n {\n throw new Error(`NodeEntity: no authored node named \"${ nodeName }\" exists in the scene.`);\n }\n\n return found;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Entity Picking\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Vector3 } from '@babylonjs/core';\n\nimport type { AbstractMesh, Node, PickingInfo } from '@babylonjs/core';\nimport type { GameEntity, GameEntityState } from '@skewedaspect/sage-core';\n\nimport { NodeEntity } from './nodeEntity.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface EntityPickFilter\n{\n tags ?: readonly string[];\n}\n\nexport interface EntityPickResult\n{\n entity : NodeEntity;\n mesh : AbstractMesh;\n point : Vector3;\n distance : number;\n normal : Vector3;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// Climbs the picked mesh's own ancestor chain -- itself, then its parent, and so on -- against the\n// simulation's current entity list, rather than a maintained node-to-entity lookup: nothing here needs\n// invalidating on spawn, despawn, or a pooled node changing hands.\nfunction resolveEntity(entities : readonly GameEntity[], mesh : AbstractMesh) : NodeEntity | undefined\n{\n const nodeEntities : NodeEntity[] = [];\n\n for(const entity of entities)\n {\n if(entity instanceof NodeEntity)\n {\n nodeEntities.push(entity);\n }\n }\n\n let current : Node | null = mesh;\n\n while(current !== null)\n {\n for(const entity of nodeEntities)\n {\n if(entity.node === current)\n {\n return entity;\n }\n }\n\n current = current.parent;\n }\n\n return undefined;\n}\n\nfunction passesFilter(entity : NodeEntity, filter : EntityPickFilter | undefined) : boolean\n{\n if(filter?.tags === undefined)\n {\n return true;\n }\n\n const tags = (entity.state as GameEntityState).tags;\n\n for(const tag of filter.tags)\n {\n if(!tags.includes(tag))\n {\n return false;\n }\n }\n\n return true;\n}\n\nexport function toEntityPickResult(\n entities : readonly GameEntity[],\n info : PickingInfo | null,\n filter ?: EntityPickFilter\n) : EntityPickResult | undefined\n{\n if(info === null || !info.hit || info.pickedMesh === null || info.pickedPoint === null)\n {\n return undefined;\n }\n\n const entity = resolveEntity(entities, info.pickedMesh);\n\n if(entity === undefined || !passesFilter(entity, filter))\n {\n return undefined;\n }\n\n const normal = info.getNormal(true, true);\n\n return {\n entity,\n mesh: info.pickedMesh,\n point: info.pickedPoint,\n distance: info.distance,\n normal: normal ?? Vector3.Zero(),\n };\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// NodeSimulation\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { TransformNode } from '@babylonjs/core';\nimport { GameSimulationManager } from '@skewedaspect/sage-core';\n\nimport type { GameSimulationOptions, StateBag } from '@skewedaspect/sage-core';\nimport type { Camera, Ray, Scene } from '@babylonjs/core';\n\nimport { toEntityPickResult } from './entityPicking.ts';\nimport type { EntityPickFilter, EntityPickResult } from './entityPicking.ts';\nimport type { NodeEntity } from './nodeEntity.ts';\nimport type { NodePool } from './nodePool.ts';\nimport type { MeshLoader, NodeSimulationContext } from './nodeSimulationContext.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface NodeSimulationOptions extends GameSimulationOptions\n{\n scene : Scene;\n loadMesh : MeshLoader;\n pool : NodePool;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class NodeSimulation extends GameSimulationManager implements NodeSimulationContext\n{\n readonly scene : Scene;\n readonly pool : NodePool;\n readonly loadMesh : MeshLoader;\n\n readonly #entityClasses : GameSimulationOptions['entityClasses'];\n\n constructor(options : NodeSimulationOptions)\n {\n super(options);\n\n this.scene = options.scene;\n this.pool = options.pool;\n this.loadMesh = options.loadMesh;\n this.#entityClasses = options.entityClasses;\n }\n\n async prewarm(name : string, count : number, state : StateBag = {}) : Promise<void>\n {\n const entityClass = this.#entityClasses[name] as typeof NodeEntity | undefined;\n\n const meshes = await Promise.all(\n Array.from({ length: count }, () => this.loadMesh(name, state))\n );\n\n for(const mesh of meshes)\n {\n const node = new TransformNode(name, this.scene);\n\n if(mesh !== undefined)\n {\n mesh.parent = node;\n }\n\n this.pool.give(name, { node, mesh }, entityClass?.poolSize);\n }\n }\n\n pickEntityWithRay(ray : Ray, filter ?: EntityPickFilter) : EntityPickResult | undefined\n {\n return toEntityPickResult(this.entities, this.scene.pickWithRay(ray), filter);\n }\n\n pickEntity(x : number, y : number, filter ?: EntityPickFilter) : EntityPickResult | undefined\n {\n return toEntityPickResult(this.entities, this.scene.pick(x, y), filter);\n }\n\n pickEntityForward(camera : Camera, range : number, filter ?: EntityPickFilter) : EntityPickResult | undefined\n {\n return this.pickEntityWithRay(camera.getForwardRay(range), filter);\n }\n\n protected override buildReplacementSimulation(options : GameSimulationOptions) : NodeSimulation\n {\n return new NodeSimulation({\n ...options,\n scene: this.scene,\n pool: this.pool,\n loadMesh: this.loadMesh,\n });\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// NodePool\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { poolSizeLimit } from './constants.ts';\n\nimport type { NodeSet } from './nodeSimulationContext.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// Node and mesh sets, keyed by entity name, capped per class at `poolSizeLimit`.\nexport class NodePool\n{\n readonly #sets = new Map<string, NodeSet[]>();\n\n take(name : string) : NodeSet | undefined\n {\n const sets = this.#sets.get(name);\n\n if(sets === undefined)\n {\n return undefined;\n }\n\n return sets.pop();\n }\n\n give(name : string, set : NodeSet, limit : number = poolSizeLimit) : void\n {\n set.node.parent = null;\n set.node.setEnabled(false);\n\n let sets = this.#sets.get(name);\n\n if(sets === undefined)\n {\n sets = [];\n this.#sets.set(name, sets);\n }\n\n if(sets.length >= limit)\n {\n set.node.dispose();\n return;\n }\n\n sets.push(set);\n }\n\n clear(name : string) : void\n {\n const sets = this.#sets.get(name);\n\n if(sets === undefined)\n {\n return;\n }\n\n for(const set of sets)\n {\n set.node.dispose();\n }\n\n this.#sets.delete(name);\n }\n\n clearAll() : void\n {\n for(const name of [ ...this.#sets.keys() ])\n {\n this.clear(name);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Audio Engine\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n type AudioBus,\n type AudioEngineV2,\n CreateAudioEngineAsync,\n type IStaticSoundOptions,\n type MainAudioBus,\n type PrimaryAudioBus,\n type StaticSound,\n} from '@babylonjs/core';\n\n// Interfaces\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Utils\nimport type { LoggingUtility } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class AudioEngine implements Disposable\n{\n private _engine : AudioEngineV2 | null = null;\n private _mainBus : MainAudioBus | null = null;\n private _buses = new Map<string, AudioBus>();\n private _log : LoggerInterface;\n\n constructor(logger : LoggingUtility)\n {\n this._log = logger.getLogger('AudioEngine');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n async initialize() : Promise<void>\n {\n this._log.info('Initializing audio engine...');\n\n this._engine = await CreateAudioEngineAsync();\n this._mainBus = await this._engine.createMainBusAsync('main');\n\n this._log.info('Audio engine initialized.');\n }\n\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down audio engine...');\n\n this._buses.clear();\n this._mainBus = null;\n\n if(this._engine)\n {\n this._engine.dispose();\n this._engine = null;\n }\n\n this._log.info('Audio engine torn down.');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Bus Management\n //------------------------------------------------------------------------------------------------------------------\n\n async createBus(name : string) : Promise<AudioBus>\n {\n if(!this._engine || !this._mainBus)\n {\n throw new Error('AudioEngine not initialized');\n }\n\n this._log.debug(`Creating audio bus: ${ name }`);\n const bus = await this._engine.createBusAsync(name, { outBus: this._mainBus });\n this._buses.set(name, bus);\n return bus;\n }\n\n getBus(name : string) : AudioBus | undefined\n {\n return this._buses.get(name);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Sound Creation\n //------------------------------------------------------------------------------------------------------------------\n\n async createSound(\n name : string,\n url : string,\n bus ?: AudioBus,\n options ?: Partial<IStaticSoundOptions>\n ) : Promise<StaticSound>\n {\n if(!this._engine)\n {\n throw new Error('AudioEngine not initialized');\n }\n\n const outBus : PrimaryAudioBus | undefined = bus ?? this._mainBus ?? undefined;\n\n // Spread user options and inject outBus; the cast is needed because\n // IStaticSoundOptions.outBus accepts Nullable<PrimaryAudioBus>\n const soundOptions = {\n ...options,\n ...(outBus ? { outBus } : {}),\n } as Partial<IStaticSoundOptions>;\n\n return this._engine.createSoundAsync(name, url, soundOptions);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Volume\n //------------------------------------------------------------------------------------------------------------------\n\n setMasterVolume(volume : number) : void\n {\n if(!this._engine)\n {\n throw new Error('AudioEngine not initialized');\n }\n\n this._engine.volume = volume;\n }\n\n getMasterVolume() : number\n {\n if(!this._engine)\n {\n return 1;\n }\n\n return this._engine.volume;\n }\n\n setBusVolume(bus : AudioBus, volume : number) : void\n {\n bus.volume = volume;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Scene Engine\n//\n// Wraps BabylonJS scene lifecycle, mesh loading, physics, cameras, and lights.\n// All built-in loaders (glTF, OBJ, STL, etc.) are registered via registerBuiltInLoaders()\n// and loaded on-demand when first used.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n AbstractEngine,\n AbstractMesh,\n AssetContainer,\n DirectionalLight,\n FreeCamera,\n HavokPlugin,\n HemisphericLight,\n ImportMeshAsync,\n Light,\n LoadAssetContainerAsync,\n Mesh,\n MeshBuilder,\n PhysicsAggregate,\n PhysicsShapeType,\n PointLight,\n RectAreaLight,\n Scene,\n SpotLight,\n Vector3,\n} from '@babylonjs/core';\n\n// Register all built-in loaders (glTF, OBJ, STL, etc.) -- loaded on-demand when first used\nimport { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic';\nregisterBuiltInLoaders();\nimport type { HavokPhysicsWithBindings } from '@babylonjs/havok';\n\n// Interfaces\nimport type { GameCanvas } from '../interfaces/game.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Utils\nimport { type LoggingUtility } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class SceneEngine implements Disposable\n{\n private _canvas : GameCanvas;\n private _engine : AbstractEngine;\n private _havok : HavokPhysicsWithBindings;\n private _log : LoggerInterface;\n\n constructor(canvas : GameCanvas, engine : AbstractEngine, havok : HavokPhysicsWithBindings, logger : LoggingUtility)\n {\n this._canvas = canvas;\n this._engine = engine;\n this._havok = havok;\n this._log = logger.getLogger('SceneEngine');\n }\n\n /**\n * Creates a new scene with physics enabled\n */\n public createScene() : Scene\n {\n return new Scene(this._engine);\n }\n\n public enablePhysics(\n scene : Scene,\n gravityVector : Vector3 = new Vector3(0, -9.8, 0),\n floatingOriginWorldRadius ?: number\n ) : void\n {\n this._log.debug(\n `Enabling physics with gravity (${ gravityVector.x }, ${ gravityVector.y }, ${ gravityVector.z })...`\n );\n\n const pluginOptions = floatingOriginWorldRadius !== undefined\n ? { floatingOriginWorldRadius }\n : undefined;\n\n // Each scene gets its own HavokPlugin so that scene.dispose() doesn't corrupt the shared WASM module.\n const plugin = new HavokPlugin(true, this._havok, pluginOptions);\n scene.enablePhysics(gravityVector, plugin);\n }\n\n // Camera Utilities\n /**\n * Creates a free camera at the specified position, targeting the origin. Attaches controls to the provided\n * canvas, or falls back to the engine's default canvas.\n * @param name\n * @param position\n * @param scene\n * @param canvas - Overrides the default canvas for input controls\n */\n public createFreeCamera(\n name : string,\n position : Vector3,\n scene : Scene,\n canvas ?: GameCanvas\n ) : FreeCamera\n {\n const camera = new FreeCamera(name, position, scene);\n camera.setTarget(Vector3.Zero());\n\n canvas = canvas ?? this._canvas;\n\n if(canvas)\n {\n camera.attachControl(canvas, true);\n }\n\n return camera;\n }\n\n // Lighting Utilities\n /**\n * Creates a hemispheric light (ambient lighting)\n * @param name\n * @param direction\n * @param scene\n * @param intensity - Default: 0.7\n */\n public createHemisphericLight(\n name : string,\n direction : Vector3,\n scene : Scene,\n intensity = 0.7\n ) : HemisphericLight\n {\n const light = new HemisphericLight(name, direction, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a directional light (sun-like lighting)\n * @param name\n * @param direction\n * @param scene\n * @param intensity - Default: 1.0\n */\n public createDirectionalLight(\n name : string,\n direction : Vector3,\n scene : Scene,\n intensity = 1.0\n ) : DirectionalLight\n {\n const light = new DirectionalLight(name, direction, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a point light (bulb-like lighting)\n * @param name\n * @param position\n * @param scene\n * @param intensity - Default: 1.0\n */\n public createPointLight(\n name : string,\n position : Vector3,\n scene : Scene,\n intensity = 1.0\n ) : PointLight\n {\n const light = new PointLight(name, position, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a spotlight (flashlight-like lighting)\n * @param name\n * @param position\n * @param direction\n * @param angle - Cone angle in radians\n * @param exponent - Controls light falloff from center to edge of cone\n * @param scene\n * @param intensity - Default: 1.0\n */\n public createSpotLight(\n name : string,\n position : Vector3,\n direction : Vector3,\n angle : number,\n exponent : number,\n scene : Scene,\n intensity = 1.0\n ) : SpotLight\n {\n const light = new SpotLight(name, position, direction, angle, exponent, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a rectangular area light (emits from a flat rectangle in the -Z direction)\n * @param name\n * @param position\n * @param width\n * @param height\n * @param scene\n * @param intensity - Default: 1.0\n */\n public createRectAreaLight(\n name : string,\n position : Vector3,\n width : number,\n height : number,\n scene : Scene,\n intensity = 1.0\n ) : RectAreaLight\n {\n const light = new RectAreaLight(name, position, width, height, scene);\n light.intensity = intensity;\n return light;\n }\n\n // Mesh Creation Utilities\n /**\n * Creates a sphere mesh. Defaults: diameter 1, 32 segments.\n * @param name\n * @param options\n * @param scene\n */\n public createSphere(\n name : string,\n options : { diameter ?: number; segments ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n const defaultOptions = { diameter: 1, segments: 32 };\n return MeshBuilder.CreateSphere(name, { ...defaultOptions, ...options }, scene);\n }\n\n /**\n * Creates a box mesh. Default size: 1.\n * @param name\n * @param options\n * @param scene\n */\n public createBox(\n name : string,\n options : { size ?: number; width ?: number; height ?: number; depth ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n const defaultOptions = { size: 1 };\n return MeshBuilder.CreateBox(name, { ...defaultOptions, ...options }, scene);\n }\n\n /**\n * Creates a ground mesh. Defaults: 6x6 with 2 subdivisions.\n * @param name\n * @param options\n * @param scene\n */\n public createGround(\n name : string,\n options : { width ?: number; height ?: number; subdivisions ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n const defaultOptions = { width: 6, height: 6, subdivisions: 2 };\n return MeshBuilder.CreateGround(name, { ...defaultOptions, ...options }, scene);\n }\n\n /**\n * Creates a cylinder mesh. Defaults: height 2, diameter 1 (top and bottom).\n * @param name\n * @param options\n * @param scene\n */\n public createCylinder(\n name : string,\n options : { height ?: number; diameterTop ?: number; diameterBottom ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n const defaultOptions = { height: 2, diameterTop: 1, diameterBottom: 1 };\n return MeshBuilder.CreateCylinder(name, { ...defaultOptions, ...options }, scene);\n }\n\n // Physics Utilities\n /**\n * Adds physics to a mesh. Defaults: mass 1, restitution 0.75, friction 0.5.\n * @param mesh\n * @param shapeType\n * @param physicsProps\n * @param scene\n */\n public addPhysics(\n mesh : Mesh,\n shapeType : PhysicsShapeType,\n physicsProps : { mass ?: number; restitution ?: number; friction ?: number } = {},\n scene : Scene\n ) : PhysicsAggregate\n {\n const defaultProps = { mass: 1, restitution: 0.75, friction: 0.5 };\n return new PhysicsAggregate(mesh, shapeType, { ...defaultProps, ...physicsProps }, scene);\n }\n\n // Model Loading Utilities\n /**\n * Loads a 3D model/asset into an AssetContainer without adding it to the scene automatically.\n * @param sceneFilename\n * @param scene\n */\n public async loadModel(\n sceneFilename : string,\n scene : Scene\n ) : Promise<AssetContainer>\n {\n this._log.debug(`Loading model: ${ sceneFilename }`);\n\n try\n {\n const result = await LoadAssetContainerAsync(`${ sceneFilename }`, scene);\n this._log.debug(`Model loaded successfully: ${ sceneFilename }`);\n return result;\n }\n catch (error)\n {\n this._log.error(`Failed to load model: ${ sceneFilename }`, error);\n throw error;\n }\n }\n\n /**\n * Imports meshes from a file directly into the scene.\n * @param meshNames - Specific meshes to import; empty array imports all\n * @param sceneFilename\n * @param scene\n */\n public async importMeshes(\n meshNames : string[],\n sceneFilename : string,\n scene : Scene\n ) : Promise<{\n meshes : AbstractMesh[];\n particleSystems : unknown[];\n skeletons : unknown[];\n animationGroups : unknown[];\n transformNodes : unknown[];\n geometries : unknown[];\n lights : Light[];\n }>\n {\n this._log.debug(`Importing meshes from: ${ sceneFilename }`);\n\n try\n {\n // Note: meshNames as empty array means \"import nothing\", use undefined for \"import all\"\n const options = meshNames.length > 0 ? { meshNames } : undefined;\n const result = await ImportMeshAsync(`${ sceneFilename }`, scene, options);\n this._log.debug(`Meshes imported successfully: ${ meshNames.length > 0 ? meshNames.join(', ') : 'all' }`);\n return result;\n }\n catch (error)\n {\n this._log.error(`Failed to import meshes from: ${ sceneFilename }`, error);\n throw error;\n }\n }\n\n /**\n * Tears down the scene engine and cleans up any resources\n */\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down SceneEngine');\n\n // Currently no specific resources to clean up\n // The scenes themselves are managed by the LevelManager\n // and physics engine is disposed at the application level\n\n this._log.info('SceneEngine torn down successfully');\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Asset Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { AbstractMesh, AssetContainer, InstancedMesh, Mesh } from '@babylonjs/core';\n\nimport type { GameEventBus } from '../classes/eventBus.ts';\nimport type { SceneEngine } from '../engines/scene.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport { type LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface CachedContainer\n{\n container : AssetContainer;\n refCount : number;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Central asset cache with preloading, GLB fragment extraction, mesh instancing, and reference counting.\n *\n * Supports fragment syntax (`models/props.glb#chest_lid`) to extract individual meshes from a shared container.\n * Containers are reference-counted and only disposed when all consumers release them.\n */\nexport class AssetManager implements Disposable\n{\n private _eventBus : GameEventBus;\n private _sceneEngine : SceneEngine;\n private _log : LoggerInterface;\n\n /** Full GLB/GLTF asset containers, keyed by base path (without fragment) */\n private _containers = new Map<string, CachedContainer>();\n\n /** Extracted fragment meshes (source meshes for instancing), keyed by full fragment path */\n private _sourceMeshes = new Map<string, AbstractMesh>();\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(eventBus : GameEventBus, sceneEngine : SceneEngine, logger ?: LoggingUtility)\n {\n this._eventBus = eventBus;\n this._sceneEngine = sceneEngine;\n this._log = logger?.getLogger('AssetManager') ?? new SAGELogger('AssetManager');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Helpers\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Split a path on `#` into base path and optional fragment name.\n */\n private _parsePath(path : string) : { basePath : string; fragment ?: string }\n {\n const hashIdx = path.indexOf('#');\n if(hashIdx === -1)\n {\n return { basePath: path };\n }\n\n return {\n basePath: path.substring(0, hashIdx),\n fragment: path.substring(hashIdx + 1),\n };\n }\n\n /**\n * Load (or retrieve from cache) the AssetContainer for a base path.\n * Increments refCount.\n */\n private async _loadContainer(basePath : string) : Promise<AssetContainer>\n {\n const cached = this._containers.get(basePath);\n if(cached)\n {\n cached.refCount++;\n return cached.container;\n }\n\n this._log.debug(`Loading asset container: ${ basePath }`);\n\n // LoadAssetContainerAsync needs a scene; create a temporary one\n const tempScene = this._sceneEngine.createScene();\n\n try\n {\n const container = await this._sceneEngine.loadModel(basePath, tempScene);\n\n this._containers.set(basePath, { container, refCount: 1 });\n this._log.debug(`Cached asset container: ${ basePath }`);\n\n return container;\n }\n finally\n {\n tempScene.dispose();\n }\n }\n\n /**\n * Find a mesh by name inside a container. Throws if not found.\n */\n private _findMeshInContainer(container : AssetContainer, meshName : string, fullPath : string) : AbstractMesh\n {\n const mesh = container.meshes.find((entry) => entry.name === meshName);\n if(!mesh)\n {\n const available = container.meshes.map((entry) => entry.name).join(', ');\n throw new Error(\n `Mesh '${ meshName }' not found in container for '${ fullPath }'. `\n + `Available meshes: ${ available }`\n );\n }\n\n return mesh;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Load an asset, caching the result. Supports fragment syntax (`path#meshName`).\n *\n * - Without fragment: loads and caches the full AssetContainer, returns it.\n * - With fragment: loads the container, finds the named mesh, caches it as a source mesh, returns the mesh.\n *\n * Increments refCount on the underlying container each time.\n */\n async load(path : string) : Promise<AssetContainer | AbstractMesh>\n {\n const { basePath, fragment } = this._parsePath(path);\n\n if(fragment)\n {\n // Check if we already cached this fragment mesh\n const cachedMesh = this._sourceMeshes.get(path);\n if(cachedMesh)\n {\n // Still increment refCount on the container\n const cached = this._containers.get(basePath);\n if(cached)\n {\n cached.refCount++;\n }\n\n return cachedMesh;\n }\n\n // Load the container (handles caching + refCount internally)\n const container = await this._loadContainer(basePath);\n\n // Find and cache the named mesh\n const mesh = this._findMeshInContainer(container, fragment, path);\n this._sourceMeshes.set(path, mesh);\n\n return mesh;\n }\n\n // No fragment — return the full container\n return this._loadContainer(basePath);\n }\n\n /**\n * Create an InstancedMesh from a cached source mesh. The path must have been loaded first.\n */\n instance(path : string) : InstancedMesh\n {\n const sourceMesh = this._sourceMeshes.get(path);\n if(!sourceMesh)\n {\n throw new Error(`Asset '${ path }' is not loaded. Call load() first.`);\n }\n\n return (sourceMesh as Mesh).createInstance(`${ sourceMesh.name }-instance`);\n }\n\n /**\n * Clone a cached source mesh into an independent copy. The path must have been loaded first.\n */\n clone(path : string) : Mesh\n {\n const sourceMesh = this._sourceMeshes.get(path);\n if(!sourceMesh)\n {\n throw new Error(`Asset '${ path }' is not loaded. Call load() first.`);\n }\n\n return (sourceMesh as Mesh).clone(`${ sourceMesh.name }-clone`);\n }\n\n /**\n * Preload a batch of asset paths. Emits `asset:progress` after each and `asset:complete` when finished.\n */\n async preload(paths : string[]) : Promise<void>\n {\n const total = paths.length;\n\n for(let i = 0; i < paths.length; i++)\n {\n // eslint-disable-next-line no-await-in-loop\n await this.load(paths[i]);\n\n this._eventBus.publish({\n type: 'asset:progress',\n payload: {\n path: paths[i],\n loaded: i + 1,\n total,\n },\n });\n }\n\n this._eventBus.publish({\n type: 'asset:complete',\n payload: { paths },\n });\n }\n\n /**\n * Decrement refCount for the container backing this path.\n * When refCount hits 0, dispose the container and clean up all fragment meshes from it.\n */\n dispose(path : string) : void\n {\n const { basePath } = this._parsePath(path);\n const cached = this._containers.get(basePath);\n if(!cached)\n {\n return;\n }\n\n cached.refCount--;\n\n if(cached.refCount <= 0)\n {\n this._log.debug(`Disposing asset container: ${ basePath } (refCount reached 0)`);\n\n // Clean up all fragment meshes that came from this container\n for(const [ fragmentPath, mesh ] of this._sourceMeshes)\n {\n if(fragmentPath.startsWith(`${ basePath }#`))\n {\n mesh.dispose();\n this._sourceMeshes.delete(fragmentPath);\n }\n }\n\n cached.container.dispose();\n this._containers.delete(basePath);\n }\n }\n\n /**\n * Dispose all cached containers and fragment meshes.\n */\n disposeAll() : void\n {\n for(const cached of this._containers.values())\n {\n cached.container.dispose();\n }\n\n for(const mesh of this._sourceMeshes.values())\n {\n mesh.dispose();\n }\n\n this._containers.clear();\n this._sourceMeshes.clear();\n }\n\n /**\n * Lifecycle teardown — disposes all assets.\n */\n async $teardown() : Promise<void>\n {\n this.disposeAll();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Utilities to test for capability of the current environment\n//----------------------------------------------------------------------------------------------------------------------\n\nexport function isBrowser() : boolean\n{\n return typeof window !== 'undefined' && typeof window.document !== 'undefined';\n}\n\nexport function hasWebGPU() : boolean\n{\n return isBrowser()\n && !!window.navigator.gpu;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Game Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractEngine, Scene } from '@babylonjs/core';\n\n// Interfaces\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Classes\nimport type { GameEventBus } from '../classes/eventBus.ts';\nimport type { GameEngine } from '../classes/gameEngine.ts';\n\n// Managers\nimport { LevelManager } from './level.ts';\n\n// Utils\nimport { isBrowser } from '../utils/capabilities.ts';\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Callback function for frame updates.\n * Called once per frame with the delta time in seconds.\n */\nexport type FrameCallback = (deltaTime : number) => void;\n\n/**\n * Internal representation of a registered frame callback.\n */\ninterface RegisteredFrameCallback\n{\n id : number;\n callback : FrameCallback;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class GameManager implements Disposable\n{\n private _engine : AbstractEngine;\n private _eventBus : GameEventBus;\n private _gameEngine : GameEngine | null = null;\n private _levelManager : LevelManager;\n private _log : LoggerInterface;\n private _boundRenderLoop : () => void;\n private _boundResizeHandler : () => void;\n\n /** Registered frame callbacks, executed in registration order */\n private _frameCallbacks : RegisteredFrameCallback[] = [];\n\n /** Counter for generating unique callback IDs */\n private _nextCallbackId = 0;\n\n /** Whether the game is currently paused */\n private _paused = false;\n\n public started = false;\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(\n engine : AbstractEngine,\n eventBus : GameEventBus,\n levelManager : LevelManager,\n logger ?: LoggingUtility\n )\n {\n this._engine = engine;\n this._eventBus = eventBus;\n this._levelManager = levelManager;\n this._log = logger?.getLogger('GameManager') || new SAGELogger('GameManager');\n\n // Store bound function references for later cleanup\n this._boundRenderLoop = this._renderLoop.bind(this);\n this._boundResizeHandler = this._resizeHandler.bind(this);\n\n if(isBrowser())\n {\n // Resize the engine on window resize\n window.addEventListener('resize', this._boundResizeHandler);\n }\n }\n\n get currentScene() : Scene | null\n {\n return this._levelManager.currentLevel?.scene ?? null;\n }\n\n /**\n * Late-bind the GameEngine after construction.\n * Called by createGameEngine() after the GameEngine instance is created.\n */\n $setGameEngine(gameEngine : GameEngine) : void\n {\n this._gameEngine = gameEngine;\n }\n\n /**\n * Whether the game is currently paused.\n * When paused, entity updates and frame callbacks are skipped, but rendering continues.\n */\n get isPaused() : boolean\n {\n return this._paused;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a callback to be called once per frame.\n *\n * **Advanced Usage**: This is intended for systems that need frame-by-frame updates\n * outside of the entity/behavior system. For game logic, prefer using entity behaviors\n * with their `update()` method instead.\n *\n * Callbacks are executed in registration order, after entity updates but before rendering.\n * Each callback receives the delta time in seconds (typically ~0.016 for 60fps).\n *\n * @param callback\n * @returns A function to unregister the callback\n *\n * @example\n * ```typescript\n * const unsubscribe = gameManager.registerFrameCallback((deltaTime) => {\n * // deltaTime is in seconds (e.g., 0.016 for 60fps)\n * console.log(`Frame delta: ${deltaTime}s`);\n * });\n *\n * // Later, to stop receiving callbacks:\n * unsubscribe();\n * ```\n */\n public registerFrameCallback(callback : FrameCallback) : () => void\n {\n const id = this._nextCallbackId++;\n this._frameCallbacks.push({ id, callback });\n\n this._log.debug(`Registered frame callback (id: ${ id })`);\n\n // Return unsubscribe function\n return () =>\n {\n const index = this._frameCallbacks.findIndex((cb) => cb.id === id);\n if(index !== -1)\n {\n this._frameCallbacks.splice(index, 1);\n this._log.debug(`Unregistered frame callback (id: ${ id })`);\n }\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n private _renderLoop() : void\n {\n const deltaTimeMs = this._engine.getDeltaTime();\n const deltaTime = deltaTimeMs / 1000; // Convert milliseconds to seconds\n\n // Skip game logic updates when paused\n if(!this._paused)\n {\n if(this._gameEngine === null)\n {\n throw new Error('GameManager: gameEngine not set. Call $setGameEngine() first.');\n }\n\n // No simulation exists until something hands the engine a scene to build one against\n // (GameEngine.rebuildSimulation) -- a game that never loads a level never gets one.\n if(this._gameEngine.simulation !== null)\n {\n // Not wrapped in a try/catch: a throw from an entity's update() is a defect in the behavior,\n // and it propagates out of the render loop rather than being carried through as a game event.\n this._gameEngine.simulation.runStepsForElapsedTime(deltaTime);\n }\n\n // Run registered frame callbacks\n for(const { callback } of this._frameCallbacks)\n {\n try\n {\n callback(deltaTime);\n }\n catch (err)\n {\n this._log.error('Error in frame callback:', err);\n }\n }\n }\n\n // Always render (shows frozen frame when paused)\n if(this.currentScene)\n {\n this.currentScene.render();\n }\n }\n\n private _resizeHandler() : void\n {\n if(this.started)\n {\n this._engine.resize();\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n async start() : Promise<void>\n {\n // Start the render loop\n this._engine.runRenderLoop(this._boundRenderLoop);\n\n this.started = true;\n\n this._log.info('SkewedAspect Game Engine started successfully');\n }\n\n async stop() : Promise<void>\n {\n this.started = false;\n this._engine.stopRenderLoop();\n\n this._log.info('SkewedAspect Game Engine stopped successfully');\n }\n\n /**\n * Pause the game. Entity updates and frame callbacks will be skipped,\n * but rendering continues (frozen frame). Input is still polled so\n * the game can be unpaused.\n *\n * Emits a 'game:paused' event.\n *\n * @param reason - Optional reason for pausing (included in event payload)\n */\n pause(reason ?: string) : void\n {\n if(this._paused)\n {\n this._log.debug('Game is already paused');\n return;\n }\n\n this._paused = true;\n this._log.info('Game paused', reason ? `(reason: ${ reason })` : '');\n\n this._eventBus.publish({\n type: 'game:paused',\n payload: { reason },\n });\n }\n\n /**\n * Resume the game after being paused. Entity updates and frame callbacks\n * will resume.\n *\n * Emits a 'game:resumed' event.\n *\n * @param reason - Optional reason for resuming (included in event payload)\n */\n resume(reason ?: string) : void\n {\n if(!this._paused)\n {\n this._log.debug('Game is not paused');\n return;\n }\n\n this._paused = false;\n this._log.info('Game resumed', reason ? `(reason: ${ reason })` : '');\n\n this._eventBus.publish({\n type: 'game:resumed',\n payload: { reason },\n });\n }\n\n /**\n * Tears down any resources held by the GameManager\n * This handles unregistering event listeners and any other cleanup\n */\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down GameManager');\n\n // If we're still running, stop first\n if(this.started)\n {\n await this.stop();\n }\n\n // Remove event listeners\n if(isBrowser())\n {\n window.removeEventListener('resize', this._boundResizeHandler);\n }\n\n // Clear frame callbacks\n this._frameCallbacks = [];\n\n this._log.info('GameManager torn down successfully');\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Post-Processing Pipeline Backend\n//\n// Applies post-processing effects via BabylonJS DefaultRenderingPipeline and SSAO2RenderingPipeline.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n Color4,\n DefaultRenderingPipeline,\n ImageProcessingConfiguration,\n SSAO2RenderingPipeline,\n type Scene,\n} from '@babylonjs/core';\n\nimport type { PostProcessingConfig } from '../interfaces/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Map from user-facing tonemap operator names to BabylonJS constants.\n * BabylonJS only has STANDARD (0) and ACES (1) — the other names all map to STANDARD.\n */\nconst TONEMAP_OPERATORS : Record<string, number> = {\n hable: ImageProcessingConfiguration.TONEMAPPING_STANDARD,\n reinhard: ImageProcessingConfiguration.TONEMAPPING_STANDARD,\n hejidawson: ImageProcessingConfiguration.TONEMAPPING_STANDARD,\n photographic: ImageProcessingConfiguration.TONEMAPPING_STANDARD,\n aces: ImageProcessingConfiguration.TONEMAPPING_ACES,\n};\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Apply post-processing effects to a scene based on a declarative config.\n * Creates BabylonJS rendering pipelines and configures them according to the provided settings.\n *\n * This is a level-config handler, not a node property handler.\n */\nexport function applyPipelinePostProcessing(scene : Scene, config : PostProcessingConfig) : void\n{\n // Can't set up post-processing without a camera\n if(!scene.activeCamera)\n {\n return;\n }\n\n // Warn about Frame Graph-only features\n if(config.volumetric)\n {\n console.warn('[SAGE] Volumetric lighting requires renderer: \"frameGraph\". Ignored in pipeline mode.');\n }\n\n // Create the default rendering pipeline\n const pipeline = new DefaultRenderingPipeline('sage-default', true, scene, scene.cameras);\n\n // Bloom\n if(config.bloom)\n {\n pipeline.bloomEnabled = true;\n\n if(config.bloom.weight !== undefined)\n {\n pipeline.bloomWeight = config.bloom.weight;\n }\n if(config.bloom.threshold !== undefined)\n {\n pipeline.bloomThreshold = config.bloom.threshold;\n }\n if(config.bloom.scale !== undefined)\n {\n pipeline.bloomScale = config.bloom.scale;\n }\n if(config.bloom.kernel !== undefined)\n {\n pipeline.bloomKernel = config.bloom.kernel;\n }\n }\n\n // Tone mapping\n if(config.tonemap)\n {\n pipeline.imageProcessingEnabled = true;\n pipeline.imageProcessing.toneMappingEnabled = true;\n\n if(config.tonemap.operator)\n {\n pipeline.imageProcessing.toneMappingType\n = TONEMAP_OPERATORS[config.tonemap.operator] ?? ImageProcessingConfiguration.TONEMAPPING_STANDARD;\n }\n }\n\n // Film grain\n if(config.grain)\n {\n pipeline.grainEnabled = true;\n\n if(config.grain.intensity !== undefined)\n {\n pipeline.grain.intensity = config.grain.intensity;\n }\n if(config.grain.animated !== undefined)\n {\n pipeline.grain.animated = config.grain.animated;\n }\n }\n\n // Vignette\n if(config.vignette)\n {\n pipeline.imageProcessingEnabled = true;\n pipeline.imageProcessing.vignetteEnabled = true;\n\n if(config.vignette.weight !== undefined)\n {\n pipeline.imageProcessing.vignetteWeight = config.vignette.weight;\n }\n if(config.vignette.stretch !== undefined)\n {\n pipeline.imageProcessing.vignetteStretch = config.vignette.stretch;\n }\n if(config.vignette.color)\n {\n pipeline.imageProcessing.vignetteColor = new Color4(\n config.vignette.color.r,\n config.vignette.color.g,\n config.vignette.color.b,\n 1\n );\n }\n }\n\n // Sharpen\n if(config.sharpen)\n {\n pipeline.sharpenEnabled = true;\n\n if(config.sharpen.edge !== undefined)\n {\n pipeline.sharpen.edgeAmount = config.sharpen.edge;\n }\n if(config.sharpen.color !== undefined)\n {\n pipeline.sharpen.colorAmount = config.sharpen.color;\n }\n }\n\n // Chromatic aberration\n if(config.chromaticAberration)\n {\n pipeline.chromaticAberrationEnabled = true;\n\n if(config.chromaticAberration.amount !== undefined)\n {\n pipeline.chromaticAberration.aberrationAmount = config.chromaticAberration.amount;\n }\n }\n\n // SSAO — uses its own separate pipeline\n if(config.ssao)\n {\n const ssao = new SSAO2RenderingPipeline('sage-ssao', scene, { ssaoRatio: 0.5, blurRatio: 1.0 });\n\n if(config.ssao.radius !== undefined)\n {\n ssao.radius = config.ssao.radius;\n }\n if(config.ssao.samples !== undefined)\n {\n ssao.samples = config.ssao.samples;\n }\n if(config.ssao.totalStrength !== undefined)\n {\n ssao.totalStrength = config.ssao.totalStrength;\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Post-Processing Dispatcher\n//\n// Routes to either the DefaultRenderingPipeline backend or the Frame Graph backend\n// based on config.renderer.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Scene } from '@babylonjs/core';\n\nimport type { PostProcessingConfig } from '../interfaces/level.ts';\n\nimport { applyPipelinePostProcessing } from './postProcessingPipeline.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport async function applyPostProcessing(scene : Scene, config : PostProcessingConfig) : Promise<void>\n{\n if(config.renderer === 'frameGraph')\n {\n const { applyFrameGraphPostProcessing } = await import('./postProcessingFrameGraph.ts');\n await applyFrameGraphPostProcessing(scene, config);\n }\n else\n {\n applyPipelinePostProcessing(scene, config);\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Vector / Quaternion Conversion Utilities\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Matrix, Quaternion, type TransformNode, Vector3 } from '@babylonjs/core';\n\nimport type { QuatConfig, Vec3Config } from '../interfaces/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Convert a BabylonJS Vector3 to a plain { x, y, z } object.\n */\nexport function toVec3Object(vector : Vector3) : Vec3Config\n{\n return { x: vector.x, y: vector.y, z: vector.z };\n}\n\n/**\n * Convert a plain { x, y, z } object to a BabylonJS Vector3.\n */\nexport function toVector3(vec : Vec3Config) : Vector3\n{\n return new Vector3(vec.x, vec.y, vec.z);\n}\n\n/**\n * Convert a BabylonJS Quaternion to a plain { x, y, z, w } object.\n */\nexport function toQuatObject(quat : Quaternion) : QuatConfig\n{\n return { x: quat.x, y: quat.y, z: quat.z, w: quat.w }; // eslint-disable-line id-length\n}\n\n/**\n * Convert a plain { x, y, z, w } object to a BabylonJS Quaternion.\n */\nexport function toQuaternion(quat : QuatConfig) : Quaternion\n{\n return new Quaternion(quat.x, quat.y, quat.z, quat.w);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// Transform Hierarchy Utilities\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Compute a node's transform in canonical Babylon space by walking the parent\n * chain and individually cancelling each import conversion ancestor.\n *\n * For each ancestor, computes its local contribution from BabylonJS's own world\n * matrices. If that local contribution has negative determinant (import conversion\n * node like glTF's __root__), it is individually cancelled from the running matrix\n * via left-multiplication by its inverse.\n *\n * Each conversion node is handled one at a time — no assumptions about hierarchy\n * depth or which node is \"the\" root. Handles arbitrarily nested import hierarchies.\n */\nexport function getCanonicalTransform(node : TransformNode) : {\n position : Vector3;\n rotation : Quaternion;\n scaling : Vector3;\n}\n{\n node.computeWorldMatrix(true);\n let canonical = node.getWorldMatrix().clone();\n\n // Walk up the parent chain. For each ancestor, compute its local contribution\n // (the difference between its world and its parent's world). If that local\n // has negative determinant, cancel it from the canonical matrix.\n let ancestor = node.parent as TransformNode | null;\n while(ancestor)\n {\n ancestor.computeWorldMatrix(true);\n const ancestorWorld = ancestor.getWorldMatrix();\n\n // Compute this ancestor's local contribution\n const parentOfAncestor = ancestor.parent as TransformNode | null;\n let ancestorLocal : Matrix;\n\n if(parentOfAncestor)\n {\n parentOfAncestor.computeWorldMatrix(true);\n ancestorLocal = ancestorWorld.multiply(Matrix.Invert(parentOfAncestor.getWorldMatrix()));\n }\n else\n {\n ancestorLocal = ancestorWorld;\n }\n\n if(ancestorLocal.determinant() < 0)\n {\n canonical = Matrix.Invert(ancestorLocal).multiply(canonical);\n }\n\n ancestor = parentOfAncestor;\n }\n\n const position = new Vector3();\n const rotation = new Quaternion();\n const scaling = new Vector3();\n canonical.decompose(scaling, rotation, position);\n\n return { position, rotation, scaling };\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Abstract Level Class\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Color3 } from '@babylonjs/core';\nimport { ClusteredLightContainer } from '@babylonjs/core/Lights/Clustered/clusteredLightContainer';\n\nimport type { Scene, TransformNode } from '@babylonjs/core';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type {\n ColorConfig,\n LevelConfig,\n LevelContext,\n LevelInstance,\n LevelLoadOptions,\n PropertyHandler,\n} from '../interfaces/level.ts';\n\nimport { SAGELogger } from '../utils/logger.ts';\nimport type { GameEngine } from './gameEngine.ts';\nimport { OutlineManager } from '../managers/outline.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract base class for game levels.\n *\n * Levels are responsible for creating and configuring their own scenes via the\n * `buildScene()` method. All runtime dependencies (eventBus, sceneEngine, simulation)\n * are injected via the constructor and are immediately available.\n *\n * Users should not instantiate Level subclasses directly. Instead, use LevelManager\n * which acts as a factory and injects the required dependencies:\n *\n * ```typescript\n * // Register a custom level class\n * levelManager.registerLevelClass('my-level', MyCustomLevel);\n *\n * // Load the level - manager creates instance with deps injected\n * const level = await levelManager.loadLevel({ name: 'Level1', class: 'my-level' });\n * ```\n */\nexport abstract class Level implements LevelInstance\n{\n public readonly name : string;\n protected readonly _log : LoggerInterface;\n protected readonly _context : LevelContext;\n protected _scene : Scene | null = null;\n protected _forRestore = false;\n public clusteredLights : ClusteredLightContainer | null = null;\n public outlines : OutlineManager | null = null;\n\n //------------------------------------------------------------------------------------------------------------------\n // Constructor\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Creates a new Level instance.\n *\n * @param config\n * @param context\n */\n constructor(config : LevelConfig, context : LevelContext)\n {\n this.name = config.name;\n this._context = context;\n this._log = context.logger?.getLogger(`Level:${ config.name }`)\n ?? new SAGELogger(`Level:${ config.name }`, 'info');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Accessors\n //------------------------------------------------------------------------------------------------------------------\n\n get scene() : Scene | null\n {\n return this._scene;\n }\n\n get isLoaded() : boolean\n {\n return this._scene !== null;\n }\n\n get gameEngine() : GameEngine\n {\n return this._context.gameEngine;\n }\n\n get propertyHandlers() : Map<string, PropertyHandler>\n {\n return this._context.propertyHandlers;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Protected Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Emit a progress event during loading\n *\n * @param progress - Percentage (0-100)\n * @param message\n */\n protected $emitProgress(progress : number, message : string) : void\n {\n this.gameEngine.eventBus.publish({\n type: 'level:progress',\n payload: {\n levelName: this.name,\n progress,\n message,\n },\n });\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n protected _toColor3(color : ColorConfig) : Color3\n {\n return new Color3(color.r, color.g, color.b);\n }\n\n /**\n * Get normalized metadata from a node, handling glTF extras.\n *\n * Babylon.js stores glTF custom properties in node.metadata.gltf.extras,\n * so we need to extract them for easier access.\n */\n protected _getNormalizedMetadata(node : TransformNode) : Record<string, unknown> | null\n {\n if(!node.metadata)\n {\n return null;\n }\n\n // Check for glTF extras (from imported GLB/GLTF files)\n const gltfExtras = node.metadata?.gltf?.extras as Record<string, unknown> | undefined;\n\n if(gltfExtras)\n {\n // Merge glTF extras with any existing top-level metadata\n return { ...gltfExtras };\n }\n\n // Return existing metadata if no glTF extras\n return node.metadata as Record<string, unknown>;\n }\n\n /**\n * Run registered property handlers on a node.\n */\n protected async _runPropertyHandlers(node : TransformNode) : Promise<void>\n {\n for(const [ property, handler ] of this.propertyHandlers)\n {\n if(property in node.metadata)\n {\n try\n {\n // eslint-disable-next-line no-await-in-loop\n await handler(node, node.metadata[property], this, this.gameEngine);\n }\n catch (error)\n {\n this._log.error(\n `Error in property handler '${ property }' for node '${ node.name }':`,\n error\n );\n }\n }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Load and create the scene for this level.\n * Progress events will be emitted during loading.\n */\n async load(options ?: LevelLoadOptions) : Promise<Scene>\n {\n if(this._scene)\n {\n this._log.warn(`Level ${ this.name } is already loaded`);\n return this._scene;\n }\n\n this._forRestore = options?.forRestore ?? false;\n\n this._log.info(`Loading level: ${ this.name }`);\n this._log.time(`level-${ this.name }-load`);\n\n try\n {\n this.$emitProgress(0, 'Starting level load...');\n\n // Let the concrete level build its content\n this._scene = await this.buildScene();\n\n this.$emitProgress(100, 'Level loaded successfully');\n this.gameEngine.eventBus.publish({\n type: 'level:complete',\n payload: {\n levelName: this.name,\n message: 'Level loaded successfully',\n level: this,\n },\n });\n\n this._log.timeEnd(`level-${ this.name }-load`);\n this._log.info(`Level ${ this.name } loaded successfully`);\n\n return this._scene;\n }\n catch (error)\n {\n this._log.error(`Failed to load level ${ this.name }:`, error);\n this.gameEngine.eventBus.publish({\n type: 'level:error',\n payload: {\n levelName: this.name,\n message: 'Failed to load level',\n error,\n },\n });\n throw error;\n }\n }\n\n /**\n * Abstract method for building the scene content.\n * Concrete levels must implement this to create their specific content.\n *\n * All dependencies are available via `this.gameEngine`.\n *\n */\n protected abstract buildScene() : Promise<Scene>;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Called when this level becomes the active level during a transition.\n * Override in subclasses to perform activation logic (e.g., start music, show UI).\n * Default implementation is a no-op.\n */\n onActivate?() : Promise<void> | void;\n\n /**\n * Called when this level is being replaced during a transition.\n * Override in subclasses to perform cleanup logic (e.g., fade out, save state).\n * Default implementation is a no-op.\n */\n onDeactivate?() : Promise<void> | void;\n\n /**\n * Dispose of this level's resources and scene.\n *\n * The `$` prefix indicates this is an internal lifecycle method, consistent with\n * other engine lifecycle methods like `$teardown()`.\n */\n async $dispose() : Promise<void>\n {\n if(this._scene)\n {\n this._log.info(`Disposing level: ${ this.name }`);\n\n if(this.outlines)\n {\n this.outlines.dispose();\n this.outlines = null;\n }\n\n if(this.clusteredLights)\n {\n this.clusteredLights.dispose();\n this.clusteredLights = null;\n }\n\n this._scene.dispose();\n this._scene = null;\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Outline Manager\n//\n// Manages named SelectionOutlineLayer instances for entity highlighting.\n// Each layer tracks a Set<NodeEntity> and rebuilds its selection on any change.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Color3, type Scene, SelectionOutlineLayer } from '@babylonjs/core';\n\nimport type { NodeEntity } from '../entities/nodeEntity.ts';\nimport type { OutlineLayerConfig } from '../interfaces/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface OutlineLayerEntry\n{\n layer : SelectionOutlineLayer;\n entities : Set<NodeEntity>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class OutlineManager\n{\n private _scene : Scene;\n private _layers = new Map<string, OutlineLayerEntry>();\n\n constructor(scene : Scene, configs ?: Record<string, OutlineLayerConfig>)\n {\n this._scene = scene;\n\n if(configs)\n {\n for(const [ name, config ] of Object.entries(configs))\n {\n this.create(name, config);\n }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n get(name : string) : SelectionOutlineLayer | undefined\n {\n return this._layers.get(name)?.layer;\n }\n\n create(name : string, config ?: OutlineLayerConfig) : SelectionOutlineLayer\n {\n const existing = this._layers.get(name);\n if(existing)\n {\n return existing.layer;\n }\n\n const layer = new SelectionOutlineLayer(`sage-outline-${ name }`, this._scene);\n\n if(config?.color)\n {\n layer.outlineColor = new Color3(config.color.r, config.color.g, config.color.b);\n }\n\n if(config?.thickness !== undefined) { layer.outlineThickness = config.thickness; }\n if(config?.occlusionStrength !== undefined) { layer.occlusionStrength = config.occlusionStrength; }\n\n this._layers.set(name, { layer, entities: new Set() });\n return layer;\n }\n\n highlightEntity(layerName : string, entity : NodeEntity) : void\n {\n const entry = this._layers.get(layerName);\n if(!entry)\n {\n return;\n }\n\n entry.entities.add(entity);\n this._rebuildSelection(entry);\n }\n\n unhighlightEntity(layerName : string, entity : NodeEntity) : void\n {\n const entry = this._layers.get(layerName);\n if(!entry)\n {\n return;\n }\n\n entry.entities.delete(entity);\n this._rebuildSelection(entry);\n }\n\n unhighlightEntityAll(entity : NodeEntity) : void\n {\n for(const entry of this._layers.values())\n {\n if(entry.entities.delete(entity))\n {\n this._rebuildSelection(entry);\n }\n }\n }\n\n clear(layerName : string) : void\n {\n const entry = this._layers.get(layerName);\n if(!entry)\n {\n return;\n }\n\n entry.entities.clear();\n entry.layer.clearSelection();\n }\n\n dispose() : void\n {\n for(const entry of this._layers.values())\n {\n entry.entities.clear();\n entry.layer.dispose();\n }\n\n this._layers.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private\n //------------------------------------------------------------------------------------------------------------------\n\n private _rebuildSelection(entry : OutlineLayerEntry) : void\n {\n entry.layer.clearSelection();\n\n for(const entity of entry.entities)\n {\n const meshes = entity.node.getChildMeshes();\n\n if(meshes.length > 0)\n {\n entry.layer.addSelection(meshes);\n }\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// GameLevel Class - Default Level Implementation\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n ArcRotateCamera,\n type Camera,\n type Color3,\n CubeTexture,\n DirectionalLight,\n FreeCamera,\n HDRCubeTexture,\n HemisphericLight,\n MeshBuilder,\n PBRMaterial,\n PointLight,\n Quaternion,\n RectAreaLight,\n Scene,\n SoundState,\n SpotLight,\n StandardMaterial,\n type StaticSound,\n Texture,\n TransformNode,\n UniversalCamera,\n Vector3,\n} from '@babylonjs/core';\nimport { GeospatialCamera } from '@babylonjs/core/Cameras/geospatialCamera';\nimport { ClusteredLightContainer } from '@babylonjs/core/Lights/Clustered/clusteredLightContainer';\n\nimport type {\n CameraDefinition,\n EntityDefinition,\n LevelConfig,\n LevelContext,\n LightDefinition,\n SpawnDefinition,\n} from '../interfaces/level.ts';\nimport { applyPostProcessing } from '../handlers/postProcessing.ts';\nimport { getCanonicalTransform, toQuatObject, toVec3Object, toVector3 } from '../utils/vectors.ts';\n\nimport type { StateBag } from '@skewedaspect/sage-core';\nimport type { NodeEntity } from '../entities/nodeEntity.ts';\nimport { Level } from './level.ts';\nimport { OutlineManager } from '../managers/outline.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Metadata collected from a spawn point node.\n * Transforms are normalized to canonical Babylon space — the source level's __root__\n * conversion is removed so these values are clean for any entity type.\n */\ninterface SpawnPointData\n{\n name : string;\n position : Vector3;\n rotation : Quaternion;\n scaling : Vector3;\n node : TransformNode;\n}\n\n/**\n * Metadata collected from an entity node\n */\ninterface EntityNodeData\n{\n type : string;\n node : TransformNode;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Default Level implementation that loads from YAML configuration.\n * Supports property handlers for processing scene node metadata.\n */\nexport class GameLevel extends Level\n{\n /** The level configuration */\n protected _config : LevelConfig;\n\n /** Collected spawn points from the scene */\n protected _spawnPoints : SpawnPointData[] = [];\n\n /** Collected entity nodes from the scene */\n protected _entityNodes : EntityNodeData[] = [];\n\n /** Entities spawned by this level */\n protected _spawnedEntities : NodeEntity[] = [];\n\n /** Level-scoped sounds created from config */\n protected _levelSounds = new Map<string, StaticSound>();\n\n /** Sounds that were playing before deactivation (for resume on activate) */\n private _playingSoundsBeforeDeactivate = new Set<string>();\n\n //------------------------------------------------------------------------------------------------------------------\n // Constructor\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a GameLevel from a configuration object\n *\n * @param config\n * @param context\n */\n constructor(config : LevelConfig, context : LevelContext)\n {\n super(config, context);\n this._config = config;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Accessors\n //------------------------------------------------------------------------------------------------------------------\n\n get config() : LevelConfig\n {\n return this._config;\n }\n\n get spawnedEntities() : readonly NodeEntity[]\n {\n return this._spawnedEntities;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Protected Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Build the scene by loading assets, processing node metadata, and spawning entities.\n */\n protected async buildScene() : Promise<Scene>\n {\n const { sceneEngine } = this.gameEngine.engines;\n const { assetManager } = this.gameEngine.managers;\n\n // Preload assets if configured\n if(this._config.preload && this._config.preload.length > 0)\n {\n this.$emitProgress(2, 'Preloading assets...');\n await assetManager.preload(this._config.preload);\n }\n\n // Create an empty scene and assign it early so property handlers can access it\n const scene = sceneEngine.createScene();\n this._scene = scene;\n\n // A NodeEntity always constructs its node in the simulation's own scene, so the simulation the level\n // spawns through has to be bound to this scene before anything below can spawn.\n this.gameEngine.rebuildSimulation(scene, this._config.meshSource);\n\n // Enable physics if configured (must be done before loading scene with colliders)\n if(this._config.physics)\n {\n this.$emitProgress(5, 'Initializing physics...');\n await this._enablePhysics(scene);\n }\n\n // Load the scene file if specified\n if(this._config.scene)\n {\n this.$emitProgress(10, 'Loading scene file...');\n await this._loadSceneFile(scene);\n }\n\n // Set up environment (skybox + IBL) — before cameras so the background is ready\n if(this._config.environment)\n {\n this.$emitProgress(15, 'Setting up environment...');\n this._processEnvironment(scene);\n }\n\n // Process cameras (must happen before post-processing, which requires activeCamera)\n this._processCameras(scene);\n\n // Process lights (override Blender-imported lights, create new ones)\n this._processLights(scene);\n\n // Set up clustered lighting if configured (must be after _processLights)\n this._setupClustering(scene);\n\n // Apply post-processing effects if configured\n if(this._config.postProcessing)\n {\n const renderer = this._config.postProcessing.renderer ?? 'pipeline';\n this._log.info(`Applying post-processing with ${ renderer } renderer`);\n await applyPostProcessing(scene, this._config.postProcessing);\n }\n\n // Create outline manager for entity highlighting\n this.outlines = new OutlineManager(scene, this._config.outlines);\n\n // Process all nodes for property handlers\n this.$emitProgress(50, 'Processing scene properties...');\n await this._processNodeProperties(scene);\n\n // A level loaded to restore a save spawns nothing of its own: the restored snapshot is the sole\n // source of entities, and adoption re-binds through the nodeName already in restored state\n // (design/simulation.md#saved-games).\n if(!this._forRestore)\n {\n // Process spawn points\n this.$emitProgress(70, 'Spawning entities...');\n await this._processSpawnPoints();\n\n // Process entity nodes\n this.$emitProgress(85, 'Configuring entities...');\n this._processEntityNodes();\n }\n\n // Load level-scoped sounds (last — everything else is ready)\n if(this._config.sounds)\n {\n this.$emitProgress(95, 'Loading sounds...');\n await this._processLevelSounds();\n }\n\n return scene;\n }\n\n /**\n * Import all meshes from the configured scene file (GLB/GLTF/Babylon) into the scene.\n */\n private async _loadSceneFile(scene : Scene) : Promise<void>\n {\n const scenePath = this._getScenePath();\n if(!scenePath)\n {\n return;\n }\n\n this._log.debug(`Loading scene file: ${ scenePath }`);\n\n // Set right-handed coordinate system before loading so loaders respect it\n if(this._isRightHanded())\n {\n scene.useRightHandedSystem = true;\n this._log.debug('Scene set to right-handed coordinate system');\n }\n\n try\n {\n await this.gameEngine.engines.sceneEngine.importMeshes([], scenePath, scene);\n this._log.debug('Scene file loaded successfully');\n }\n catch (error)\n {\n this._log.error(`Failed to load scene file: ${ scenePath }`, error);\n throw error;\n }\n }\n\n /**\n * Extract the scene file path from the config, which can be a string or SceneConfig object.\n */\n private _getScenePath() : string | undefined\n {\n if(!this._config.scene)\n {\n return undefined;\n }\n\n return typeof this._config.scene === 'string'\n ? this._config.scene\n : this._config.scene.path;\n }\n\n /**\n * Check if the scene config specifies a right-handed coordinate system.\n */\n private _isRightHanded() : boolean\n {\n if(!this._config.scene || typeof this._config.scene === 'string')\n {\n return false;\n }\n\n return this._config.scene.rightHanded === true;\n }\n\n /**\n * Set up the scene environment: IBL (image-based lighting) for PBR reflections and/or a visible skybox.\n */\n private _processEnvironment(scene : Scene) : void\n {\n const env = this._config.environment;\n if(!env) { return; }\n\n // Set up IBL for PBR reflections\n if(env.ibl)\n {\n scene.environmentTexture = this._createEnvironmentTexture(env.ibl, env.iblResolution ?? 256, scene);\n this._log.debug(`IBL set from: ${ env.ibl }`);\n }\n\n // Set up visible skybox\n if(env.skybox)\n {\n const ext = this._getFileExtension(env.skybox);\n\n if(ext === '.hdr' || ext === '.env')\n {\n // HDR/env skybox — use as IBL too if no separate IBL was specified\n if(!env.ibl)\n {\n const res = env.iblResolution ?? 256;\n scene.environmentTexture = this._createEnvironmentTexture(env.skybox, res, scene);\n }\n\n if(scene.environmentTexture)\n {\n scene.createDefaultSkybox(scene.environmentTexture, true, env.skyboxSize ?? 1000, 0, false);\n }\n }\n else\n {\n // JPG/PNG — create a textured sphere\n const size = env.skyboxSize ?? 1000;\n const skybox = MeshBuilder.CreateSphere('skybox', { diameter: size, segments: 32 }, scene);\n\n const mat = new StandardMaterial('skybox-mat', scene);\n mat.backFaceCulling = false;\n mat.disableLighting = true;\n mat.emissiveTexture = new Texture(env.skybox, scene);\n skybox.material = mat;\n skybox.infiniteDistance = true;\n }\n\n this._log.debug(`Skybox set from: ${ env.skybox }`);\n }\n\n // Apply rotation to environment texture\n if(env.rotation !== undefined && scene.environmentTexture)\n {\n (scene.environmentTexture as HDRCubeTexture).rotationY = env.rotation;\n }\n }\n\n /**\n * Create an environment texture from an HDR or env file.\n */\n private _createEnvironmentTexture(path : string, resolution : number, scene : Scene) : CubeTexture | HDRCubeTexture\n {\n const ext = this._getFileExtension(path);\n\n if(ext === '.env')\n {\n return CubeTexture.CreateFromPrefilteredData(path, scene);\n }\n\n return new HDRCubeTexture(path, scene, resolution);\n }\n\n private _getFileExtension(path : string) : string\n {\n const dotIndex = path.lastIndexOf('.');\n return dotIndex >= 0 ? path.slice(dotIndex).toLowerCase() : '';\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process cameras: apply YAML overrides to imported cameras, create new ones, and set the active camera.\n * If no YAML cameras config exists, the first imported camera (if any) is activated.\n */\n private _processCameras(scene : Scene) : void\n {\n const cameraConfigs = this._config.cameras;\n const canvas = this.gameEngine.canvas as HTMLCanvasElement | null;\n\n if(!cameraConfigs)\n {\n // No YAML config — if the scene has imported cameras, activate the first one\n if(scene.cameras.length > 0)\n {\n scene.activeCamera = scene.cameras[0];\n\n if(canvas)\n {\n scene.activeCamera.attachControl(canvas, true);\n }\n\n this._log.debug(`Activated imported camera: \"${ scene.activeCamera.name }\"`);\n }\n\n return;\n }\n\n let activeCamera : Camera | null = null;\n\n for(const [ name, config ] of Object.entries(cameraConfigs))\n {\n const existing = scene.getCameraByName(name);\n\n if(existing)\n {\n this._applyCameraConfig(existing, config);\n this._log.debug(`Applied overrides to camera: \"${ name }\"`);\n\n if(!activeCamera || config.active)\n {\n activeCamera = existing;\n }\n }\n else if(config.type)\n {\n const camera = this._createCamera(name, config, scene);\n this._log.debug(`Created ${ config.type } camera: \"${ name }\"`);\n\n if(!activeCamera || config.active)\n {\n activeCamera = camera;\n }\n }\n else\n {\n this._log.warn(\n `Camera \"${ name }\" not found in scene and no type specified — skipping`\n );\n }\n }\n\n if(activeCamera)\n {\n scene.activeCamera = activeCamera;\n\n const activeDef = cameraConfigs[activeCamera.name];\n if(canvas && activeDef?.attachControl !== false)\n {\n activeCamera.attachControl(canvas, true);\n }\n }\n }\n\n /**\n * Create a new camera from a YAML definition.\n */\n private _createCamera(name : string, config : CameraDefinition, scene : Scene) : Camera\n {\n const pos = config.position ? toVector3(config.position) : Vector3.Zero();\n\n let camera : Camera;\n\n switch (config.type)\n {\n case 'arcRotate':\n {\n const target = config.target ? toVector3(config.target) : Vector3.Zero();\n\n camera = new ArcRotateCamera(\n name,\n config.alpha ?? Math.PI / 2,\n config.beta ?? Math.PI / 3,\n config.radius ?? 10,\n target,\n scene\n );\n break;\n }\n\n case 'universal':\n {\n camera = new UniversalCamera(name, pos, scene);\n break;\n }\n\n case 'geospatial':\n {\n if(!config.planetRadius)\n {\n this._log.warn(`Geospatial camera \"${ name }\" requires planetRadius -- skipping`);\n return new FreeCamera(name, Vector3.Zero(), scene);\n }\n\n const cam = new GeospatialCamera(name, scene, {\n planetRadius: config.planetRadius,\n });\n\n if(config.center) { cam.center = toVector3(config.center); }\n if(config.yaw !== undefined) { cam.yaw = config.yaw; }\n if(config.pitch !== undefined) { cam.pitch = config.pitch; }\n if(config.radius !== undefined) { cam.radius = config.radius; }\n if(config.checkCollisions !== undefined) { cam.checkCollisions = config.checkCollisions; }\n\n if(config.radiusMin !== undefined) { cam.limits.radiusMin = config.radiusMin; }\n if(config.radiusMax !== undefined) { cam.limits.radiusMax = config.radiusMax; }\n if(config.pitchMin !== undefined) { cam.limits.pitchMin = config.pitchMin; }\n if(config.pitchMax !== undefined) { cam.limits.pitchMax = config.pitchMax; }\n if(config.yawMin !== undefined) { cam.limits.yawMin = config.yawMin; }\n if(config.yawMax !== undefined) { cam.limits.yawMax = config.yawMax; }\n\n camera = cam;\n break;\n }\n\n case 'free':\n default:\n {\n camera = new FreeCamera(name, pos, scene);\n break;\n }\n }\n\n this._applyCameraConfig(camera, config);\n return camera;\n }\n\n /**\n * Apply shared camera properties from a config to an existing camera.\n */\n private _applyCameraConfig(camera : Camera, config : CameraDefinition) : void\n {\n if(config.fov !== undefined) { camera.fov = config.fov; }\n if(config.position) { camera.position = toVector3(config.position); }\n if(config.minZ !== undefined) { camera.minZ = config.minZ; }\n if(config.maxZ !== undefined) { camera.maxZ = config.maxZ; }\n\n if(camera instanceof FreeCamera)\n {\n if(config.speed !== undefined) { camera.speed = config.speed; }\n if(config.rotation) { camera.rotation = toVector3(config.rotation); }\n }\n\n if(camera instanceof ArcRotateCamera)\n {\n if(config.target) { camera.setTarget(toVector3(config.target)); }\n if(config.lowerRadiusLimit !== undefined) { camera.lowerRadiusLimit = config.lowerRadiusLimit; }\n if(config.upperRadiusLimit !== undefined) { camera.upperRadiusLimit = config.upperRadiusLimit; }\n if(config.lowerBetaLimit !== undefined) { camera.lowerBetaLimit = config.lowerBetaLimit; }\n if(config.upperBetaLimit !== undefined) { camera.upperBetaLimit = config.upperBetaLimit; }\n if(config.wheelPrecision !== undefined) { camera.wheelPrecision = config.wheelPrecision; }\n }\n\n if(camera instanceof GeospatialCamera)\n {\n if(config.yaw !== undefined) { camera.yaw = config.yaw; }\n if(config.pitch !== undefined) { camera.pitch = config.pitch; }\n if(config.radius !== undefined) { camera.radius = config.radius; }\n if(config.center) { camera.center = toVector3(config.center); }\n if(config.checkCollisions !== undefined) { camera.checkCollisions = config.checkCollisions; }\n if(config.radiusMin !== undefined) { camera.limits.radiusMin = config.radiusMin; }\n if(config.radiusMax !== undefined) { camera.limits.radiusMax = config.radiusMax; }\n if(config.pitchMin !== undefined) { camera.limits.pitchMin = config.pitchMin; }\n if(config.pitchMax !== undefined) { camera.limits.pitchMax = config.pitchMax; }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process lights: apply YAML overrides to imported lights and create new ones.\n * If no YAML lights config exists, imported lights are left unchanged.\n */\n private _processLights(scene : Scene) : void\n {\n const lightConfigs = this._config.lights;\n if(!lightConfigs) { return; }\n\n for(const [ name, config ] of Object.entries(lightConfigs))\n {\n const existing = scene.getLightByName(name);\n\n if(existing)\n {\n this._applyLightConfig(existing, config);\n this._log.debug(`Applied overrides to light: \"${ name }\"`);\n }\n else if(config.type)\n {\n this._createLight(name, config, scene);\n this._log.debug(`Created ${ config.type } light: \"${ name }\"`);\n }\n else\n {\n this._log.warn(\n `Light \"${ name }\" not found in scene and no type specified — skipping`\n );\n }\n }\n }\n\n /**\n * Create a new light from a YAML definition.\n */\n private _createLight(name : string, config : LightDefinition, scene : Scene) : void\n {\n const dir = config.direction ? toVector3(config.direction) : new Vector3(0, -1, 0);\n const pos = config.position ? toVector3(config.position) : Vector3.Zero();\n\n switch (config.type)\n {\n case 'hemispheric':\n {\n const light = new HemisphericLight(name, dir, scene);\n\n if(config.groundColor)\n {\n light.groundColor = this._toColor3(config.groundColor);\n }\n\n this._applyLightConfig(light, config);\n break;\n }\n\n case 'directional':\n {\n const light = new DirectionalLight(name, dir, scene);\n this._applyLightConfig(light, config);\n break;\n }\n\n case 'point':\n {\n const light = new PointLight(name, pos, scene);\n this._applyLightConfig(light, config);\n break;\n }\n\n case 'spot':\n {\n const light = new SpotLight(\n name,\n pos,\n dir,\n config.angle ?? Math.PI / 3,\n config.exponent ?? 2,\n scene\n );\n this._applyLightConfig(light, config);\n break;\n }\n\n case 'rectarea':\n {\n const light = new RectAreaLight(\n name,\n pos,\n config.width ?? 1,\n config.height ?? 1,\n scene\n );\n this._applyLightConfig(light, config);\n\n // RectAreaLight emits along local -Z and has no direction property. If a direction\n // is specified, parent the light to a rotated pivot node that orients -Z toward it.\n if(config.direction)\n {\n const pivot = new TransformNode(`${ name }_pivot`, scene);\n pivot.position = light.position.clone();\n light.position = Vector3.Zero();\n light.parent = pivot;\n\n // lookAt aligns +Z toward the target; we want -Z toward dir, so look opposite\n pivot.lookAt(pivot.position.subtract(dir));\n }\n\n break;\n }\n }\n }\n\n /**\n * Apply shared light properties from a config to an existing light.\n */\n private _applyLightConfig(\n light : { intensity : number; diffuse : Color3; specular : Color3 },\n config : LightDefinition\n ) : void\n {\n if(config.intensity !== undefined) { light.intensity = config.intensity; }\n if(config.diffuse) { light.diffuse = this._toColor3(config.diffuse); }\n if(config.specular) { light.specular = this._toColor3(config.specular); }\n }\n\n /**\n * Set up clustered lighting: create a container, move eligible lights into it,\n * and fix PBR material falloff for compatibility.\n */\n private _setupClustering(scene : Scene) : void\n {\n const clusterConfig = this._config.clustering;\n if(!clusterConfig?.enabled)\n {\n return;\n }\n\n // Collect eligible lights from the scene\n const eligibleLights = scene.lights.filter(\n (light) => ClusteredLightContainer.IsLightSupported(light)\n );\n\n if(eligibleLights.length === 0)\n {\n this._log.debug('Clustering enabled but no eligible lights found');\n return;\n }\n\n // Remove eligible lights from the scene before adding to container\n for(const light of eligibleLights)\n {\n scene.removeLight(light);\n }\n\n // Create the container with collected lights\n const container = new ClusteredLightContainer('sage-clustered', eligibleLights, scene);\n\n // Apply tuning parameters\n if(clusterConfig.horizontalTiles !== undefined) { container.horizontalTiles = clusterConfig.horizontalTiles; }\n if(clusterConfig.verticalTiles !== undefined) { container.verticalTiles = clusterConfig.verticalTiles; }\n if(clusterConfig.depthSlices !== undefined) { container.depthSlices = clusterConfig.depthSlices; }\n if(clusterConfig.maxRange !== undefined) { container.maxRange = clusterConfig.maxRange; }\n\n this.clusteredLights = container;\n\n this._log.debug(\n `Clustered lighting enabled: ${ eligibleLights.length } lights, `\n + `${ container.horizontalTiles }x${ container.verticalTiles } tiles, `\n + `${ container.depthSlices } depth slices`\n );\n\n // Fix PBR material falloff — physical falloff is incompatible with clustering\n for(const material of scene.materials)\n {\n if(material instanceof PBRMaterial)\n {\n material.useGLTFLightFalloff = true;\n }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Configure and enable the Havok physics plugin with the level's gravity settings.\n */\n private async _enablePhysics(scene : Scene) : Promise<void>\n {\n const physicsConfig = this._config.physics;\n\n // Determine gravity\n let gravity = new Vector3(0, -9.81, 0); // Default gravity\n\n if(typeof physicsConfig === 'object' && physicsConfig.gravity)\n {\n gravity = new Vector3(\n physicsConfig.gravity.x,\n physicsConfig.gravity.y,\n physicsConfig.gravity.z\n );\n }\n\n // When large world rendering is active, enable Havok multi-region physics\n const floatingOriginRadius = this.gameEngine.largeWorldRendering\n ? 100000\n : undefined;\n\n this._log.debug(`Enabling physics with gravity: ${ gravity.toString() }`);\n await this.gameEngine.engines.sceneEngine.enablePhysics(scene, gravity, floatingOriginRadius);\n }\n\n /**\n * Walk all transform nodes and meshes, normalizing glTF metadata and dispatching to property handlers.\n */\n private async _processNodeProperties(scene : Scene) : Promise<void>\n {\n const nodes = scene.transformNodes.concat(scene.meshes as TransformNode[]);\n\n for(const node of nodes)\n {\n // Normalize metadata - glTF extras are in node.metadata.gltf.extras\n const metadata = this._getNormalizedMetadata(node);\n\n if(metadata && Object.keys(metadata).length > 0)\n {\n // Store normalized metadata back on node for property handlers\n node.metadata = { ...node.metadata, ...metadata };\n\n // eslint-disable-next-line no-await-in-loop\n await this._processNodeMetadata(node);\n }\n }\n\n this._log.debug(\n `Processed ${ nodes.length } nodes, found ${ this._spawnPoints.length } spawn points `\n + `and ${ this._entityNodes.length } entity nodes`\n );\n }\n\n /**\n * Check a node's metadata for spawn/entity markers and run any registered property handlers.\n */\n private async _processNodeMetadata(node : TransformNode) : Promise<void>\n {\n // Normalize the spawn marker into canonical Babylon space. glTF imports\n // wrap content under a conversion root (RH→LH), and decomposing a\n // mirrored matrix produces poisoned scaling/rotation. getCanonicalTransform\n // cancels the import root before decomposing, giving clean values that\n // work uniformly for primitives and GLB entities.\n if('spawn' in node.metadata)\n {\n const { position, rotation, scaling } = getCanonicalTransform(node);\n\n this._spawnPoints.push({\n name: node.metadata.spawn as string,\n position,\n rotation,\n scaling,\n node,\n });\n }\n\n // Check for entity property\n if('entity' in node.metadata)\n {\n this._entityNodes.push({\n type: node.metadata.entity as string,\n node,\n });\n }\n\n // Run registered property handlers\n await this._runPropertyHandlers(node);\n }\n\n /**\n * Match collected spawn points against the level config's spawn definitions and instantiate entities.\n */\n private async _processSpawnPoints() : Promise<void>\n {\n for(const spawnPoint of this._spawnPoints)\n {\n const spawnDef = this._config.spawns?.[spawnPoint.name];\n\n if(!spawnDef)\n {\n this._log.warn(`No spawn definition found for '${ spawnPoint.name }', skipping`);\n }\n else\n {\n // eslint-disable-next-line no-await-in-loop\n await this._processSpawnPoint(spawnPoint, spawnDef);\n }\n }\n }\n\n /**\n * Spawn an entity at the given spawn point and dispose of the placeholder node.\n */\n private async _processSpawnPoint(spawnPoint : SpawnPointData, spawnDef : SpawnDefinition) : Promise<void>\n {\n try\n {\n const entity = await this._spawnEntity(spawnDef, spawnPoint);\n this._spawnedEntities.push(entity);\n\n // Remove the placeholder node from the scene\n spawnPoint.node.dispose();\n\n this._log.debug(`Spawned entity '${ spawnDef.entity }' at spawn point '${ spawnPoint.name }'`);\n }\n catch (error)\n {\n this._log.error(`Failed to spawn entity at spawn point '${ spawnPoint.name }':`, error);\n }\n }\n\n /**\n * Spawn an entity through the simulation, inheriting position and rotation from the spawn point. The node\n * and its mesh are the simulation's own business, built by NodeEntity's constructor from the class name and\n * state -- the level does no mesh building of its own.\n *\n * A spawn point's scaling has nowhere to go: NodeEntityState declares position and rotation, not scaling,\n * and inventing a state field for it is not this level's call to make. It is dropped.\n */\n private async _spawnEntity(spawnDef : SpawnDefinition, spawnPoint : SpawnPointData) : Promise<NodeEntity>\n {\n const initialState = {\n ...spawnDef.config,\n ...(spawnDef.name === undefined ? {} : { displayName: spawnDef.name }),\n tags: spawnDef.tags ?? [],\n position: toVec3Object(spawnPoint.position),\n rotation: toQuatObject(spawnPoint.rotation),\n };\n\n const state = initialState as unknown as StateBag;\n const { simulation } = this.gameEngine;\n\n if(simulation === null)\n {\n throw new Error('GameLevel: rebuildSimulation has not run against this level\\'s scene yet.');\n }\n\n return simulation.spawn(spawnDef.entity, undefined, state) as NodeEntity;\n }\n\n /**\n * Match collected entity nodes against the level config's entity definitions and spawn entities adopting\n * their marked node.\n */\n private _processEntityNodes() : void\n {\n for(const entityNode of this._entityNodes)\n {\n const entityDef = this._config.entities?.[entityNode.type];\n\n if(!entityDef)\n {\n this._log.warn(`No entity definition found for '${ entityNode.type }', skipping`);\n }\n else\n {\n this._spawnEntityNode(entityDef, entityNode);\n }\n }\n }\n\n /**\n * Spawn an entity adopting an entity marker's node. `nodeName` is the marked node's own name, written by\n * the level rather than by configuration, since it is the binding key adoption resolves through\n * (design/authoring.md#the-binding-key).\n */\n private _spawnEntityNode(entityDef : EntityDefinition, entityNode : EntityNodeData) : void\n {\n try\n {\n const initialState = {\n ...entityDef.config,\n ...(entityDef.name === undefined ? {} : { displayName: entityDef.name }),\n tags: entityDef.tags ?? [],\n nodeName: entityNode.node.name,\n };\n\n const state = initialState as unknown as StateBag;\n const { simulation } = this.gameEngine;\n\n if(simulation === null)\n {\n throw new Error('GameLevel: rebuildSimulation has not run against this level\\'s scene yet.');\n }\n\n const entity = simulation.spawn(entityDef.entity, undefined, state) as NodeEntity;\n this._spawnedEntities.push(entity);\n\n this._log.debug(`Spawned entity '${ entityDef.entity }' adopting node '${ entityNode.node.name }'`);\n }\n catch (error)\n {\n this._log.error(`Failed to spawn entity adopting node '${ entityNode.node.name }':`, error);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Level Sounds\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create level-scoped sounds from YAML config. Sounds are tracked for lifecycle management\n * (pause on deactivate, resume on activate, dispose on unload).\n */\n private async _processLevelSounds() : Promise<void>\n {\n const soundConfigs = this._config.sounds;\n if(!soundConfigs) { return; }\n\n const { audioManager } = this.gameEngine.managers;\n if(!audioManager)\n {\n this._log.warn('No AudioManager configured. Level sounds will not be created.');\n return;\n }\n\n for(const [ name, config ] of Object.entries(soundConfigs))\n {\n try\n {\n // eslint-disable-next-line no-await-in-loop\n const sound = await audioManager.createSound(name, config.url, config.channel, {\n loop: config.loop ?? false,\n volume: config.volume ?? 1,\n });\n\n this._levelSounds.set(name, sound);\n\n if(config.autoplay)\n {\n sound.play();\n }\n\n this._log.debug(`Created level sound: \"${ name }\"`);\n }\n catch (error)\n {\n this._log.error(`Failed to create level sound \"${ name }\":`, error);\n }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Pause all playing level sounds when the level is deactivated.\n * Subclasses that override this must call `super.onDeactivate()`.\n */\n async onDeactivate() : Promise<void>\n {\n for(const [ name, sound ] of this._levelSounds)\n {\n if(sound.state === SoundState.Started)\n {\n this._playingSoundsBeforeDeactivate.add(name);\n sound.pause();\n }\n }\n }\n\n /**\n * Resume previously playing level sounds when the level is reactivated.\n * Subclasses that override this must call `super.onActivate()`.\n */\n async onActivate() : Promise<void>\n {\n for(const name of this._playingSoundsBeforeDeactivate)\n {\n const sound = this._levelSounds.get(name);\n if(sound)\n {\n sound.play();\n }\n }\n\n this._playingSoundsBeforeDeactivate.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Dispose of this level's resources\n */\n override async $dispose() : Promise<void>\n {\n // Despawn all spawned entities\n const { simulation } = this.gameEngine;\n\n if(simulation !== null)\n {\n for(const entity of this._spawnedEntities)\n {\n simulation.despawn(entity.id);\n }\n }\n\n this._spawnedEntities = [];\n this._spawnPoints = [];\n this._entityNodes = [];\n\n // Dispose level sounds\n for(const sound of this._levelSounds.values())\n {\n sound.dispose();\n }\n this._levelSounds.clear();\n this._playingSoundsBeforeDeactivate.clear();\n\n await super.$dispose();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Level Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type {\n LevelConfig,\n LevelConstructor,\n LevelContext,\n LevelLoadOptions,\n PropertyHandler,\n} from '../interfaces/level.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type {\n LevelCompletePayload,\n LevelErrorPayload,\n LevelProgressPayload,\n} from '../events/payloads.ts';\n\nimport { Level } from '../classes/level.ts';\nimport { GameLevel } from '../classes/gameLevel.ts';\nimport { GameEventBus } from '../classes/eventBus.ts';\nimport type { GameEngine } from '../classes/gameEngine.ts';\n\n// Utils\nimport { type LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Options for level transitions.\n */\nexport interface TransitionOptions extends LevelLoadOptions\n{\n /** If true, the old level is kept loaded (not disposed) after transitioning away */\n keepAlive ?: boolean;\n\n /** If true, the new level is loaded but not activated — currentLevel remains unchanged */\n preloadOnly ?: boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Manages game levels, acting as a factory for level instantiation.\n *\n * The LevelManager is responsible for:\n * - Registering level configurations and custom level classes\n * - Creating level instances with properly injected dependencies\n * - Managing the lifecycle of loaded levels (loading, activation, disposal)\n *\n * Levels are not instantiated directly. Instead, use the manager's factory methods:\n *\n * ```typescript\n * // Register a config\n * levelManager.registerLevelConfig({ name: 'Level1', scene: 'level1.glb' });\n *\n * // Register a custom level class\n * levelManager.registerLevelClass('custom', MyCustomLevel);\n *\n * // Load creates the instance with deps injected\n * await levelManager.loadLevel('Level1');\n * ```\n */\nexport class LevelManager implements Disposable\n{\n private _eventBus : GameEventBus;\n private _gameEngine : GameEngine | null = null;\n private _log : LoggerInterface;\n private _logger ?: LoggingUtility;\n\n /** Map of registered level configurations by name */\n private _levelConfigs = new Map<string, LevelConfig>();\n\n /** Map of registered custom level classes by name */\n private _levelClasses = new Map<string, LevelConstructor>();\n\n /** Map of loaded level instances by name */\n private _loadedLevels = new Map<string, Level>();\n\n /** Map of registered property handlers for scene node metadata */\n private _propertyHandlers = new Map<string, PropertyHandler>();\n\n /** The currently active level */\n private _currentLevel : Level | null = null;\n\n /** The level currently inside load(), if any */\n private _loadingLevel : Level | null = null;\n\n /** Unsubscribe functions for event subscriptions */\n private _eventUnsubscribers : (() => void)[] = [];\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Gets the currently active level\n */\n public get currentLevel() : Level | null\n {\n return this._currentLevel;\n }\n\n /**\n * Gets the level currently being loaded, if any.\n *\n * During a transition, `currentLevel` still points at the outgoing level while the incoming one builds its\n * scene — anything that needs \"the level work is happening in right now\" (dynamic entity mesh creation during\n * spawn processing, for instance) must prefer this over `currentLevel`.\n */\n public get loadingLevel() : Level | null\n {\n return this._loadingLevel;\n }\n\n /**\n * Gets the registered property handlers\n */\n public get propertyHandlers() : Map<string, PropertyHandler>\n {\n return this._propertyHandlers;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(\n eventBus : GameEventBus,\n logger ?: LoggingUtility\n )\n {\n this._eventBus = eventBus;\n this._logger = logger;\n this._log = logger?.getLogger('LevelManager') || new SAGELogger('LevelManager');\n\n // Subscribe to level events and store unsubscribe handles\n this._eventUnsubscribers.push(\n this._eventBus.subscribe('level:progress', (event) =>\n {\n this._handleProgress(event.payload);\n })\n );\n\n this._eventUnsubscribers.push(\n this._eventBus.subscribe('level:complete', (event) =>\n {\n this._handleComplete(event.payload);\n })\n );\n\n this._eventUnsubscribers.push(\n this._eventBus.subscribe('level:error', (event) =>\n {\n this._handleError(event.payload);\n })\n );\n\n this._log.info('LevelManager initialized');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Late-bind the GameEngine after construction.\n * Called by createGameEngine() after the GameEngine instance is created.\n */\n $setGameEngine(gameEngine : GameEngine) : void\n {\n this._gameEngine = gameEngine;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n private _handleProgress(payload : LevelProgressPayload) : void\n {\n this._log.debug(`Level ${ payload.levelName } progress: ${ payload.progress }%`);\n }\n\n private _handleComplete(payload : LevelCompletePayload) : void\n {\n this._log.info(`Level ${ payload.levelName } loaded successfully`);\n }\n\n private _handleError(payload : LevelErrorPayload) : void\n {\n this._log.error(`Level ${ payload.levelName } error: ${ payload.message }`);\n }\n\n /**\n * Creates the LevelContext with all dependencies\n */\n private _createContext() : LevelContext\n {\n if(!this._gameEngine)\n {\n throw new Error('LevelManager: gameEngine not set. Call $setGameEngine() first.');\n }\n\n return {\n gameEngine: this._gameEngine,\n propertyHandlers: this._propertyHandlers,\n logger: this._logger,\n };\n }\n\n /**\n * Creates a Level instance from a config using the appropriate class\n */\n private _createLevelInstance(config : LevelConfig) : Level\n {\n const context = this._createContext();\n\n if(config.class)\n {\n // Look up the registered class\n const LevelClass = this._levelClasses.get(config.class);\n if(!LevelClass)\n {\n throw new Error(`Level class '${ config.class }' is not registered.`);\n }\n\n this._log.debug(`Creating level '${ config.name }' using class '${ config.class }'`);\n return new LevelClass(config, context) as Level;\n }\n\n // Use GameLevel as default\n this._log.debug(`Creating level '${ config.name }' using GameLevel`);\n return new GameLevel(config, context);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Configuration Registration API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Registers a level configuration.\n * The level will be instantiated when loadLevel() is called.\n *\n * @param config\n */\n public registerLevelConfig(config : LevelConfig) : void\n {\n if(this._levelConfigs.has(config.name))\n {\n this._log.warn(`Level config '${ config.name }' is already registered. Overwriting.`);\n }\n\n this._levelConfigs.set(config.name, config);\n this._log.info(`Registered level config: ${ config.name }`);\n }\n\n /**\n * Registers a custom Level class that can be referenced in configs.\n * When a config specifies `class: \"my-level\"`, this class will be instantiated.\n *\n * @param name - The key used in LevelConfig.class to reference this constructor\n * @param levelClass\n */\n public registerLevelClass(name : string, levelClass : LevelConstructor) : void\n {\n if(this._levelClasses.has(name))\n {\n this._log.warn(`Level class '${ name }' is already registered. Overwriting.`);\n }\n\n this._levelClasses.set(name, levelClass);\n this._log.info(`Registered level class: ${ name }`);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Property Handler API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a property handler for processing custom properties on scene nodes.\n * Handlers are called for each node that has the specified property in its metadata.\n *\n * @param property - The metadata property name to match (e.g., 'sound', 'collider')\n * @param handler\n */\n public registerPropertyHandler(property : string, handler : PropertyHandler) : void\n {\n if(this._propertyHandlers.has(property))\n {\n this._log.warn(`Property handler '${ property }' is already registered. Overwriting.`);\n }\n\n this._propertyHandlers.set(property, handler);\n this._log.debug(`Registered property handler: ${ property }`);\n }\n\n /**\n * Unregister a property handler.\n *\n * @param property\n */\n public unregisterPropertyHandler(property : string) : void\n {\n this._propertyHandlers.delete(property);\n }\n\n /**\n * Clear all registered property handlers.\n * Primarily useful for testing to ensure a clean state between tests.\n */\n public clearAllPropertyHandlers() : void\n {\n this._propertyHandlers.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Level Loading API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Gets a loaded level by name.\n * Returns null if the level is not currently loaded.\n *\n * @param name\n */\n public getLevel(name : string) : Level | null\n {\n return this._loadedLevels.get(name) || null;\n }\n\n /**\n * Gets a registered level config by name.\n *\n * @param name\n */\n public getLevelConfig(name : string) : LevelConfig | null\n {\n return this._levelConfigs.get(name) || null;\n }\n\n /**\n * Loads a level by name. The level config must be registered first.\n *\n * @param name\n * @param options\n */\n public async loadLevel(name : string, options ?: LevelLoadOptions) : Promise<Level>;\n\n /**\n * Loads a level from a config. The config will be registered automatically.\n *\n * @param config\n * @param options\n */\n public async loadLevel(config : LevelConfig, options ?: LevelLoadOptions) : Promise<Level>;\n\n /**\n * Loads a level. If a name is passed, the config must be registered.\n * If a config is passed, it will be registered automatically.\n * Returns the existing instance if already loaded.\n *\n * @param configOrName\n * @param options\n */\n public async loadLevel(configOrName : string | LevelConfig, options ?: LevelLoadOptions) : Promise<Level>\n {\n // Resolve the config\n let config : LevelConfig;\n if(typeof configOrName === 'string')\n {\n const registered = this._levelConfigs.get(configOrName);\n if(!registered)\n {\n throw new Error(`Level config '${ configOrName }' is not registered.`);\n }\n config = registered;\n }\n else\n {\n config = configOrName;\n // Auto-register the config\n this.registerLevelConfig(config);\n }\n\n // Check if already loaded\n const existing = this._loadedLevels.get(config.name);\n if(existing)\n {\n this._log.warn(`Level '${ config.name }' is already loaded`);\n return existing;\n }\n\n // Create and load the level\n this._log.debug(`Loading level: ${ config.name }`);\n const level = this._createLevelInstance(config);\n\n this._loadingLevel = level;\n try\n {\n await level.load(options);\n }\n finally\n {\n this._loadingLevel = null;\n }\n\n // Store the loaded level\n this._loadedLevels.set(config.name, level);\n\n return level;\n }\n\n /**\n * Activates a level by name, loading it if necessary.\n *\n * @param name\n */\n public async activateLevel(name : string) : Promise<Level>;\n\n /**\n * Activates a level from a config, loading it if necessary.\n *\n * @param config\n */\n public async activateLevel(config : LevelConfig) : Promise<Level>;\n\n /**\n * Activates a level, loading it if necessary.\n * Sets it as the currentLevel used by the render loop.\n *\n * @param configOrName\n */\n public async activateLevel(configOrName : string | LevelConfig) : Promise<Level>\n {\n const level = typeof configOrName === 'string'\n ? await this.loadLevel(configOrName)\n : await this.loadLevel(configOrName);\n this._currentLevel = level;\n this._log.info(`Activated level: ${ level.name }`);\n return level;\n }\n\n /**\n * Unloads a level and disposes its resources.\n * Clears currentLevel if this was the active level.\n *\n * @param name\n */\n public async unloadLevel(name : string) : Promise<void>\n {\n const level = this._loadedLevels.get(name);\n if(level)\n {\n // If this is the current level, clear it\n if(this._currentLevel === level)\n {\n this._currentLevel = null;\n }\n\n // Dispose the level\n await level.$dispose();\n\n // Remove from loaded levels\n this._loadedLevels.delete(name);\n\n this._log.info(`Unloaded level: ${ name }`);\n }\n }\n\n /**\n * Unloads the current level and disposes its resources\n */\n public async unloadCurrentLevel() : Promise<void>\n {\n if(this._currentLevel)\n {\n await this.unloadLevel(this._currentLevel.name);\n }\n else\n {\n this._log.warn('No current level to unload');\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Transitions\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Orchestrates a level-to-level transition with lifecycle hooks and events.\n *\n * Flow: deactivate old -> load new -> dispose old (unless keepAlive) -> activate new.\n * If `preloadOnly` is true, the new level is loaded but not activated.\n *\n * @param levelName - The name of the level to transition to (must be registered)\n * @param options\n */\n public async transition(levelName : string, options ?: TransitionOptions) : Promise<void>\n {\n const oldLevel = this._currentLevel;\n\n try\n {\n // 1. Emit start\n this._eventBus.publish({\n type: 'level:transition:start',\n payload: {\n from: oldLevel?.name,\n to: levelName,\n },\n });\n\n // 2. Deactivate the old level\n if(oldLevel?.onDeactivate)\n {\n await oldLevel.onDeactivate();\n }\n\n // 3. Emit loading progress\n this._eventBus.publish({\n type: 'level:transition:progress',\n payload: {\n stage: 'loading',\n levelName,\n },\n });\n\n // 4. Load the new level\n const newLevel = await this.loadLevel(levelName, options);\n\n // 5. If preloadOnly, stop here\n if(options?.preloadOnly)\n {\n return;\n }\n\n // 6. Dispose old level unless keepAlive\n if(oldLevel && !options?.keepAlive)\n {\n await this.unloadLevel(oldLevel.name);\n }\n\n // 7. Set new level as current\n this._currentLevel = newLevel;\n\n // 8. Activate the new level\n if(newLevel.onActivate)\n {\n await newLevel.onActivate();\n }\n\n // 9. Emit complete\n this._eventBus.publish({\n type: 'level:transition:complete',\n payload: { levelName },\n });\n }\n catch (error)\n {\n // 10. Emit error event and re-throw\n this._eventBus.publish({\n type: 'level:transition:error',\n payload: {\n from: oldLevel?.name,\n to: levelName,\n error,\n },\n });\n\n throw error;\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Tears down the level manager and cleans up any resources\n */\n async $teardown() : Promise<void>\n {\n // Unsubscribe from all events\n for(const unsubscribe of this._eventUnsubscribers)\n {\n unsubscribe();\n }\n this._eventUnsubscribers = [];\n\n // Dispose of all loaded levels\n for(const level of this._loadedLevels.values())\n {\n // eslint-disable-next-line no-await-in-loop\n await level.$dispose();\n }\n\n this._loadedLevels.clear();\n this._levelConfigs.clear();\n this._currentLevel = null;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Audio Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { AudioBus, IStaticSoundOptions, StaticSound } from '@babylonjs/core';\n\n// Engines\nimport type { AudioEngine } from '../engines/audio.ts';\n\n// Interfaces\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Utils\nimport type { LoggingUtility } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Public channel info exposed to consumers (no BabylonJS internals).\n */\nexport interface ChannelInfo\n{\n volume : number;\n muted : boolean;\n}\n\n/**\n * Internal channel state including the BabylonJS bus reference.\n */\ninterface ChannelState extends ChannelInfo\n{\n bus : AudioBus;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class AudioManager implements Disposable\n{\n private _audioEngine : AudioEngine;\n private _channels = new Map<string, ChannelState>();\n private _masterMuted = false;\n private _masterVolumeBeforeMute = 1;\n private _log : LoggerInterface;\n\n constructor(audioEngine : AudioEngine, logger : LoggingUtility)\n {\n this._audioEngine = audioEngine;\n this._log = logger.getLogger('AudioManager');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n async initialize(channels : string[]) : Promise<void>\n {\n this._log.info(`Initializing audio channels: ${ channels.join(', ') }`);\n\n for(const name of channels)\n {\n // eslint-disable-next-line no-await-in-loop\n const bus = await this._audioEngine.createBus(name);\n this._channels.set(name, { bus, volume: 1, muted: false });\n }\n\n this._log.info('Audio channels initialized.');\n }\n\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down audio manager...');\n\n for(const state of this._channels.values())\n {\n state.bus.dispose();\n }\n\n this._channels.clear();\n this._log.info('Audio manager torn down.');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Sound Creation\n //------------------------------------------------------------------------------------------------------------------\n\n async createSound(\n name : string,\n url : string,\n channel ?: string,\n options ?: Partial<IStaticSoundOptions>\n ) : Promise<StaticSound>\n {\n let bus : AudioBus | undefined;\n\n if(channel)\n {\n const state = this._channels.get(channel);\n if(!state)\n {\n this._log.warn(`Unknown audio channel \"${ channel }\", sound \"${ name }\" will use default bus.`);\n }\n else\n {\n bus = state.bus;\n }\n }\n\n return this._audioEngine.createSound(name, url, bus, options);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Master Volume\n //------------------------------------------------------------------------------------------------------------------\n\n setMasterVolume(volume : number) : void\n {\n if(!this._masterMuted)\n {\n this._audioEngine.setMasterVolume(volume);\n }\n else\n {\n // Store for when we unmute\n this._masterVolumeBeforeMute = volume;\n }\n }\n\n getMasterVolume() : number\n {\n if(this._masterMuted)\n {\n return this._masterVolumeBeforeMute;\n }\n\n return this._audioEngine.getMasterVolume();\n }\n\n setMasterMuted(muted : boolean) : void\n {\n if(muted === this._masterMuted) { return; }\n\n this._masterMuted = muted;\n\n if(muted)\n {\n this._masterVolumeBeforeMute = this._audioEngine.getMasterVolume();\n this._audioEngine.setMasterVolume(0);\n }\n else\n {\n this._audioEngine.setMasterVolume(this._masterVolumeBeforeMute);\n }\n }\n\n isMasterMuted() : boolean\n {\n return this._masterMuted;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Channel Volume\n //------------------------------------------------------------------------------------------------------------------\n\n setChannelVolume(channel : string, volume : number) : void\n {\n const state = this._channels.get(channel);\n if(!state)\n {\n this._log.warn(`Unknown audio channel: ${ channel }`);\n return;\n }\n\n state.volume = volume;\n\n if(!state.muted)\n {\n this._audioEngine.setBusVolume(state.bus, volume);\n }\n }\n\n getChannelVolume(channel : string) : number\n {\n const state = this._channels.get(channel);\n return state?.volume ?? 1;\n }\n\n setChannelMuted(channel : string, muted : boolean) : void\n {\n const state = this._channels.get(channel);\n if(!state)\n {\n this._log.warn(`Unknown audio channel: ${ channel }`);\n return;\n }\n\n if(muted === state.muted) { return; }\n\n state.muted = muted;\n this._audioEngine.setBusVolume(state.bus, muted ? 0 : state.volume);\n }\n\n isChannelMuted(channel : string) : boolean\n {\n const state = this._channels.get(channel);\n return state?.muted ?? false;\n }\n\n getChannels() : string[]\n {\n return Array.from(this._channels.keys());\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Simulation Save Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameSimulationManager, GameSimulationSnapshot } from '@skewedaspect/sage-core';\n\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LevelLoadOptions } from '../interfaces/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nconst saveFormatVersion = 1;\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface LevelLoader\n{\n readonly currentLevel : { name : string } | null;\n transition(levelName : string, options ?: LevelLoadOptions) : Promise<void>;\n}\n\nexport interface SaveEnvelope\n{\n saveFormatVersion : number;\n engineVersion : string;\n level : string;\n timestamp : number;\n custom : Record<string, unknown>;\n snapshot : GameSimulationSnapshot;\n}\n\nexport type BeforeSaveHook = () => Record<string, unknown>;\n\nexport interface LoadResult\n{\n simulation : GameSimulationManager;\n envelope : SaveEnvelope;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class SimulationSaveManager implements Disposable\n{\n readonly #levelLoader : LevelLoader;\n readonly #engineVersion : string;\n readonly #now : () => number;\n\n readonly #beforeSaveHooks : BeforeSaveHook[] = [];\n\n constructor(levelLoader : LevelLoader, engineVersion : string, now : () => number = Date.now)\n {\n this.#levelLoader = levelLoader;\n this.#engineVersion = engineVersion;\n this.#now = now;\n }\n\n onBeforeSave(hook : BeforeSaveHook) : void\n {\n this.#beforeSaveHooks.push(hook);\n }\n\n save(simulation : GameSimulationManager) : SaveEnvelope\n {\n let custom : Record<string, unknown> = {};\n\n for(const hook of this.#beforeSaveHooks)\n {\n custom = { ...custom, ...hook() };\n }\n\n return {\n saveFormatVersion,\n engineVersion: this.#engineVersion,\n level: this.#levelLoader.currentLevel?.name ?? '',\n timestamp: this.#now(),\n custom,\n snapshot: simulation.snapshot(),\n };\n }\n\n async load(envelope : SaveEnvelope, simulation : GameSimulationManager) : Promise<LoadResult>\n {\n if(envelope.saveFormatVersion !== saveFormatVersion)\n {\n throw new Error(\n `The save format version \"${ envelope.saveFormatVersion }\" is not one this engine knows how to load.`\n );\n }\n\n await this.#levelLoader.transition(envelope.level, { forRestore: true });\n\n return { simulation: simulation.fromSnapshot(envelope.snapshot), envelope };\n }\n\n async $teardown() : Promise<void>\n {\n this.#beforeSaveHooks.length = 0;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Collider Debug Manager\n//\n// Wraps BabylonJS PhysicsViewer to provide per-node collider debug visualization with color support.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { type AbstractMesh, Color3, StandardMaterial, type TransformNode } from '@babylonjs/core';\nimport { PhysicsViewer } from '@babylonjs/core/Debug/physicsViewer';\n\nimport type { Disposable } from '../interfaces/lifecycle.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type ColliderDebugConfig = boolean | string | Record<string, string | false>;\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface ShownEntry\n{\n debugMesh : AbstractMesh;\n colorMaterial ?: StandardMaterial;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class ColliderDebugManager implements Disposable\n{\n private _viewer : PhysicsViewer | null = null;\n private _shown = new Map<TransformNode, ShownEntry>();\n private _materials = new Map<string, StandardMaterial>();\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n showColliderForNode(node : TransformNode, color ?: string) : void\n {\n if(!node.physicsBody || this._shown.has(node))\n {\n return;\n }\n\n const viewer = this._getViewer(node);\n const debugMesh = viewer.showBody(node.physicsBody);\n\n if(!debugMesh)\n {\n return;\n }\n\n const entry : ShownEntry = { debugMesh };\n\n if(color)\n {\n const material = this._getColorMaterial(color, node);\n debugMesh.material = material;\n entry.colorMaterial = material;\n }\n\n this._shown.set(node, entry);\n }\n\n hideColliderForNode(node : TransformNode) : void\n {\n const entry = this._shown.get(node);\n if(!entry || !this._viewer)\n {\n return;\n }\n\n if(node.physicsBody)\n {\n this._viewer.hideBody(node.physicsBody);\n }\n\n this._shown.delete(node);\n }\n\n hideAll() : void\n {\n for(const [ node ] of this._shown)\n {\n this.hideColliderForNode(node);\n }\n }\n\n isShown(node : TransformNode) : boolean\n {\n return this._shown.has(node);\n }\n\n async $teardown() : Promise<void>\n {\n this.dispose();\n }\n\n dispose() : void\n {\n if(this._viewer)\n {\n this.hideAll();\n this._viewer.dispose();\n this._viewer = null;\n }\n\n this._shown.clear();\n\n for(const mat of this._materials.values())\n {\n mat.dispose();\n }\n\n this._materials.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private\n //------------------------------------------------------------------------------------------------------------------\n\n private _getViewer(node : TransformNode) : PhysicsViewer\n {\n if(!this._viewer)\n {\n this._viewer = new PhysicsViewer(node.getScene());\n }\n\n return this._viewer;\n }\n\n private _getColorMaterial(color : string, node : TransformNode) : StandardMaterial\n {\n let material = this._materials.get(color);\n if(material)\n {\n return material;\n }\n\n material = new StandardMaterial(`sage-collider-debug-${ color }`, node.getScene());\n material.emissiveColor = Color3.FromHexString(color);\n material.disableLighting = true;\n material.wireframe = true;\n this._materials.set(color, material);\n\n return material;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Graphics Utility\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n AbstractEngine,\n Engine,\n type EngineOptions,\n NullEngine,\n type NullEngineOptions,\n WebGPUEngine,\n type WebGPUEngineOptions,\n} from '@babylonjs/core';\n\n// Interfaces\nimport type { BabylonEngineOptions, GameCanvas, RenderEngineOptions } from '../interfaces/game.ts';\n\n// Utilities\nimport { hasWebGPU } from './capabilities.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Builds and initializes a WebGPU engine.\n *\n * @param canvas\n * @param options\n */\nasync function _buildWebGPUEngine(\n canvas : HTMLCanvasElement | OffscreenCanvas,\n options : WebGPUEngineOptions\n) : Promise<WebGPUEngine>\n{\n const engine = new WebGPUEngine(canvas, options);\n await engine.initAsync();\n return engine;\n}\n\n/**\n * Builds a WebGL engine.\n *\n * @param canvas\n * @param options\n */\nfunction _buildWebGLEngine(canvas : GameCanvas, options : BabylonEngineOptions) : Engine\n{\n return new Engine(canvas, options.antialias, options.options, options.adaptToDeviceRatio);\n}\n\n/**\n * Builds a Null engine, which is used when no rendering is required.\n *\n * @param options\n */\nfunction _buildNullEngine(options : NullEngineOptions) : NullEngine\n{\n return new NullEngine(options as NullEngineOptions);\n}\n\n//------------------------------------------------------------------------------------------------------------------\n\n/**\n * Creates an appropriate engine based on the provided canvas and options.\n * Passing null for canvas creates a NullEngine. Otherwise auto-detects WebGPU with WebGL fallback,\n * unless a specific engine type is forced via `options.engine`.\n *\n * @param canvas - Pass null to get a NullEngine (headless/testing)\n * @param options\n */\nexport async function createEngine(\n canvas : GameCanvas | null,\n options : RenderEngineOptions,\n largeWorldRendering = false\n) : Promise<AbstractEngine>\n{\n // Check if we should use a null engine (no canvas provided)\n if(canvas === null)\n {\n console.debug('Using Null Engine');\n return _buildNullEngine(options as NullEngineOptions);\n }\n\n // Inject large world rendering into engine options if requested. We have to build new objects\n // here because BabylonJS declares useLargeWorldRendering as readonly on its option types.\n if(largeWorldRendering)\n {\n if('options' in options)\n {\n const babylonOpts = options as BabylonEngineOptions;\n options = {\n ...babylonOpts,\n options: { ...babylonOpts.options, useLargeWorldRendering: true },\n } as RenderEngineOptions;\n }\n else\n {\n // WebGPUEngineOptions -- useLargeWorldRendering lives at the top level\n options = { ...options, useLargeWorldRendering: true } as RenderEngineOptions;\n }\n }\n\n // Extract the forceEngine option if available\n const forceEngine = options.engine || 'auto';\n\n // Force WebGPU engine if specified and available\n if(forceEngine === 'webgpu')\n {\n if(hasWebGPU())\n {\n try\n {\n console.debug('Using forced WebGPU engine');\n return await _buildWebGPUEngine(canvas, options);\n }\n catch (error)\n {\n console.error('Forced WebGPU initialization failed:', error);\n throw new Error(\n 'Forced WebGPU failed to initialize. If WebGPU is required, check browser compatibility.',\n { cause: error }\n );\n }\n }\n else\n {\n throw new Error('WebGPU was forced but is not available in this browser.');\n }\n }\n\n // Force WebGL engine if specified\n if(forceEngine === 'webgl')\n {\n console.debug('Using forced WebGL engine');\n return _buildWebGLEngine(canvas, options as BabylonEngineOptions);\n }\n\n // Auto detection (default behavior)\n if(hasWebGPU())\n {\n try\n {\n console.debug('Using WebGPU');\n\n return _buildWebGPUEngine(canvas, options)\n .catch((error) =>\n {\n console.warn('WebGPU initialization failed, falling back to WebGL:', error);\n return _buildWebGLEngine(canvas, options as EngineOptions);\n });\n }\n catch (error)\n {\n console.warn('WebGPU initialization failed, falling back to WebGL:', error);\n }\n }\n else\n {\n console.warn('WebGPU not supported, falling back to WebGL.');\n }\n\n console.debug('Using WebGL');\n\n return _buildWebGLEngine(canvas, options as EngineOptions);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n","//----------------------------------------------------------------------------------------------------------------------\n// Physics Utility\n//----------------------------------------------------------------------------------------------------------------------\n\nimport HavokPhysics, { type HavokPhysicsWithBindings } from '@babylonjs/havok';\nimport { HavokPlugin } from '@babylonjs/core';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Initializes the Havok WASM module and returns the raw bindings.\n *\n * Each BabylonJS scene needs its own HavokPlugin instance (because scene.dispose() disposes the\n * plugin attached to it). The raw WASM module can be shared across plugins safely.\n *\n * @param wasmBinary - Pre-loaded Havok WASM. Left unset, Havok fetches its own over HTTP, which\n * only works in a browser.\n */\nexport async function initHavok(wasmBinary ?: BufferSource) : Promise<HavokPhysicsWithBindings>\n{\n // Emscripten declares the slot as ArrayBuffer, but hands it straight to WebAssembly, which\n // takes any BufferSource. A node Buffer is the common case and is not an ArrayBuffer.\n return await HavokPhysics(wasmBinary ? { wasmBinary: wasmBinary as ArrayBuffer } : undefined);\n}\n\n/**\n * Creates a new HavokPlugin from an existing WASM instance.\n */\nexport function createHavokPlugin(havok : HavokPhysicsWithBindings) : HavokPlugin\n{\n return new HavokPlugin(true, havok);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// GameTimer - Pause-aware timer utilities\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A pollable cooldown handle.\n */\nexport interface CooldownHandle\n{\n readonly ready : boolean;\n reset() : void;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface DelayEntry\n{\n id : number;\n remaining : number;\n callback : () => void;\n}\n\ninterface IntervalEntry\n{\n id : number;\n periodMs : number;\n elapsed : number;\n callback : () => void;\n}\n\ninterface CooldownEntry\n{\n id : number;\n periodMs : number;\n remaining : number;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Pause-aware game timer providing delay, interval, and cooldown primitives.\n *\n * All times are in game-time milliseconds. When the game is paused, `tick()` is simply not called, so all timers\n * freeze automatically.\n */\nexport class GameTimer\n{\n private _nextId = 0;\n private _delays : DelayEntry[] = [];\n private _intervals : IntervalEntry[] = [];\n private _cooldowns : CooldownEntry[] = [];\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * One-shot timer. Fires the callback after `ms` milliseconds of game time.\n *\n * @returns A cancel function that prevents the callback from firing.\n */\n delay(ms : number, callback : () => void) : () => void\n {\n if(ms < 0)\n {\n throw new RangeError(`delay() requires ms >= 0, got ${ ms }`);\n }\n\n const id = this._nextId++;\n this._delays.push({ id, remaining: ms, callback });\n\n return () =>\n {\n this._delays = this._delays.filter((entry) => entry.id !== id);\n };\n }\n\n /**\n * Repeating timer. Fires the callback every `ms` milliseconds of game time.\n *\n * @returns A cancel function that stops the interval.\n */\n interval(ms : number, callback : () => void) : () => void\n {\n if(ms <= 0)\n {\n throw new RangeError(`interval() requires ms > 0, got ${ ms }`);\n }\n\n const id = this._nextId++;\n this._intervals.push({ id, periodMs: ms, elapsed: 0, callback });\n\n return () =>\n {\n this._intervals = this._intervals.filter((entry) => entry.id !== id);\n };\n }\n\n /**\n * Pollable cooldown. Starts ready; after calling `reset()`, becomes not-ready until `ms` elapses.\n */\n cooldown(ms : number) : CooldownHandle\n {\n const id = this._nextId++;\n const entry : CooldownEntry = { id, periodMs: ms, remaining: 0 };\n this._cooldowns.push(entry);\n\n return {\n get ready() : boolean\n {\n return entry.remaining <= 0;\n },\n reset() : void\n {\n entry.remaining = entry.periodMs;\n },\n };\n }\n\n /**\n * Cancel all active delays, intervals, and cooldowns.\n */\n cancelAll() : void\n {\n this._delays = [];\n this._intervals = [];\n\n // Set all cooldowns to ready before clearing, so existing CooldownHandle references aren't stuck\n for(const entry of this._cooldowns)\n {\n entry.remaining = 0;\n }\n\n this._cooldowns = [];\n }\n\n /**\n * Advance all timers by `dtMs` milliseconds. Called once per frame by the game loop.\n */\n tick(dtMs : number) : void\n {\n // Process delays\n const firedDelays : number[] = [];\n for(const entry of this._delays)\n {\n entry.remaining -= dtMs;\n if(entry.remaining <= 0)\n {\n entry.callback();\n firedDelays.push(entry.id);\n }\n }\n\n if(firedDelays.length > 0)\n {\n this._delays = this._delays.filter((entry) => !firedDelays.includes(entry.id));\n }\n\n // Process intervals\n for(const entry of this._intervals)\n {\n entry.elapsed += dtMs;\n while(entry.elapsed >= entry.periodMs)\n {\n entry.callback();\n entry.elapsed -= entry.periodMs;\n }\n }\n\n // Process cooldowns\n for(const entry of this._cooldowns)\n {\n if(entry.remaining > 0)\n {\n entry.remaining = Math.max(0, entry.remaining - dtMs);\n }\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Built-in Debug Console Commands\n//\n// Registers default references and commands onto the debug console namespace, providing convenient\n// access to game engine internals from the browser dev console.\n//----------------------------------------------------------------------------------------------------------------------\n\n/* eslint-disable no-console */\n\nimport type { GameEntity, StateBag } from '@skewedaspect/sage-core';\n\nimport { DebugConsole } from '../classes/debugConsole.ts';\nimport { NodeEntity } from '../entities/nodeEntity.ts';\n\nimport type { NodeSimulation } from '../entities/nodeSimulation.ts';\n\n// Types\nimport type { GameEngine } from '../classes/gameEngine.ts';\nimport type { Unsubscribe } from '../classes/eventBus.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * The current simulation, or a warning: nothing has called rebuildSimulation yet (no level has loaded, and\n * the game has not built one directly).\n */\nfunction requireSimulation(ge : GameEngine) : NodeSimulation | undefined\n{\n if(ge.simulation === null)\n {\n console.warn('[sage] No simulation yet -- nothing has given the engine a scene to build one against.');\n return undefined;\n }\n\n return ge.simulation;\n}\n\n/**\n * Resolves an entity from an ID string or returns the entity if already a GameEntity instance.\n */\nfunction resolveEntity(\n idOrEntity : string | GameEntity,\n ge : GameEngine\n) : GameEntity | undefined\n{\n if(typeof idOrEntity === 'string')\n {\n return requireSimulation(ge)?.getEntity(idOrEntity);\n }\n\n return idOrEntity;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Registers all default references and commands onto the debug console namespace.\n *\n * The entity system's class table carries no behavior list on the entity itself (entities.md#the-entity-\n * class-table), and a NodeEntity has no collider-debug method of its own -- so, unlike the 0.9 console, this\n * has no `list(type)`, no `entities` proxy, and no `showColliders`/`hideColliders`.\n */\nexport function registerBuiltins(dc : DebugConsole, ge : GameEngine) : void\n{\n const gm = ge.managers.gameManager;\n const lm = ge.managers.levelManager;\n\n // Track active event loggers in a closure\n const activeLoggers : Unsubscribe[] = [];\n\n //------------------------------------------------------------------------------------------------------------------\n // Exposed references\n //------------------------------------------------------------------------------------------------------------------\n\n dc.expose('engine', ge);\n dc.expose('events', ge.eventBus);\n dc.expose('physics', ge.physics);\n dc.expose('colliders', ge.debug.colliders);\n\n // Dynamic getters\n dc.expose('level', { get: () => lm.currentLevel });\n dc.expose('scene', { get: () => lm.currentLevel?.scene ?? null });\n\n //------------------------------------------------------------------------------------------------------------------\n // Commands\n //------------------------------------------------------------------------------------------------------------------\n\n dc.expose('tp', (idOrEntity : string | GameEntity, x : number, y : number, z : number) =>\n {\n const entity = resolveEntity(idOrEntity, ge);\n if(!entity)\n {\n console.warn(`[sage] Entity not found: ${ idOrEntity }`);\n return;\n }\n\n if(!(entity instanceof NodeEntity))\n {\n console.warn(`[sage] Entity \"${ entity.id }\" has no node`);\n return;\n }\n\n entity.node.position.x = x;\n entity.node.position.y = y;\n entity.node.position.z = z;\n console.log(`[sage] Teleported \"${ entity.id }\" to (${ x }, ${ y }, ${ z })`);\n });\n\n dc.expose('spawn', (name : string, x : number, y : number, z : number) =>\n {\n const simulation = requireSimulation(ge);\n if(!simulation)\n {\n return undefined;\n }\n\n const entity = simulation.spawn(name, undefined, { position: { x, y, z } } as StateBag);\n console.log(`[sage] Spawned \"${ name }\" (${ entity.id }) at (${ x }, ${ y }, ${ z })`);\n return entity;\n });\n\n dc.expose('list', () =>\n {\n const simulation = requireSimulation(ge);\n if(!simulation)\n {\n return [];\n }\n\n const entities = simulation.entities;\n\n const rows = entities.map((ent) => ({\n id: ent.id,\n node: ent instanceof NodeEntity ? ent.node.name : '(no node)',\n }));\n\n console.table(rows);\n return entities;\n });\n\n dc.expose('inspect', (idOrEntity : string | GameEntity) =>\n {\n const entity = resolveEntity(idOrEntity, ge);\n if(!entity)\n {\n console.warn(`[sage] Entity not found: ${ idOrEntity }`);\n return undefined;\n }\n\n const info = {\n id: entity.id,\n state: entity.state,\n node: entity instanceof NodeEntity ? entity.node.name : null,\n position: entity instanceof NodeEntity ? entity.node.position : null,\n };\n\n console.log('[sage] Entity info:', info);\n return info;\n });\n\n dc.expose('logEvents', (pattern ?: string | RegExp) =>\n {\n const resolvedPattern = pattern ?? '*';\n interface EventSub\n {\n subscribe : (\n pat : string | RegExp,\n cb : (event : { type : string; payload ?: unknown }) => void\n ) => Unsubscribe;\n }\n\n const unsub = (ge.eventBus as unknown as EventSub).subscribe(resolvedPattern, (event) =>\n {\n console.log(`[sage] Event: ${ event.type }`, event.payload);\n });\n\n activeLoggers.push(unsub);\n console.log(`[sage] Logging events matching \"${ resolvedPattern }\"`);\n return unsub;\n });\n\n dc.expose('stopLogging', () =>\n {\n for(const unsub of activeLoggers)\n {\n unsub();\n }\n\n console.log(`[sage] Stopped ${ activeLoggers.length } event logger(s)`);\n activeLoggers.length = 0;\n });\n\n dc.expose('pause', () =>\n {\n gm.pause('debug console');\n console.log('[sage] Game paused');\n });\n\n dc.expose('resume', () =>\n {\n gm.resume('debug console');\n console.log('[sage] Game resumed');\n });\n\n dc.expose('slow', (factor : number) =>\n {\n (ge.renderEngine as unknown as Record<string, unknown>).timeStep = (1 / 60) / factor;\n console.log(`[sage] Time scale set to ${ factor }x (timeStep=${ (1 / 60) / factor })`);\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Logger Interfaces\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Defines the available logging levels as string literals.\n */\nexport const LogLevels = [\n 'trace',\n 'debug',\n 'info',\n 'warn',\n 'error',\n 'none',\n] as const;\n\n/**\n * Type for LogLevel values\n */\nexport type LogLevel = typeof LogLevels[number];\n\n/**\n * Interface for a logging backend implementation.\n * Backends handle the actual output of log messages.\n */\nexport interface LoggingBackend\n{\n /**\n * Log a message at TRACE level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n trace(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Log a message at DEBUG level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n debug(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Log a message at INFO level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n info(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Log a message at WARN level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n warn(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Log a message at ERROR level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n error(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Start a timer with the specified label.\n *\n * @param category - Typically the class or module name\n * @param label - Must be unique; used to match with timeEnd()\n */\n time(category : string, label : string) : void;\n\n /**\n * End a timer with the specified label and log the time elapsed.\n *\n * @param category - Typically the class or module name\n * @param label - Must match a previous time() call\n */\n timeEnd(category : string, label : string) : void;\n}\n\n/**\n * Interface for a logger instance.\n * Each instance is typically associated with a specific category/component.\n */\nexport interface LoggerInterface\n{\n /** Log a message at TRACE level */\n trace(message : string, ...args : unknown[]) : void;\n\n /** Log a message at DEBUG level */\n debug(message : string, ...args : unknown[]) : void;\n\n /** Log a message at INFO level */\n info(message : string, ...args : unknown[]) : void;\n\n /** Log a message at WARN level */\n warn(message : string, ...args : unknown[]) : void;\n\n /** Log a message at ERROR level */\n error(message : string, ...args : unknown[]) : void;\n\n /** Start a timer with the given label */\n time(label : string) : void;\n\n /** End a timer and log the elapsed time */\n timeEnd(label : string) : void;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Collider Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n AbstractMesh,\n Mesh,\n PhysicsAggregate,\n type PhysicsAggregateParameters,\n PhysicsShapeType,\n Quaternion,\n type TransformNode,\n Vector3,\n} from '@babylonjs/core';\n\nimport type { LevelManager } from '../managers/level.ts';\nimport { SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nconst logger = new SAGELogger('ColliderHandler');\n\n/**\n * Computes collider bounds from a node's descendant hierarchy.\n *\n * This is needed when a parent TransformNode has no geometry of its own -- for example, when Blender's\n * GLB exporter splits a multi-material mesh into primitive children, giving us a bare TransformNode\n * parent with Mesh children. Without this, `PhysicsAggregate` would read the parent's raw bounding info\n * (a default unit cube) and produce a garbage collider.\n *\n * Returns `extents` (full box size) and `center` (offset from the node's origin in its local frame),\n * which is the format `PhysicsAggregate` expects in its options.\n */\nfunction computeHierarchyBounds(node : TransformNode) : { extents : Vector3; center : Vector3 }\n{\n const { min, max } = node.getHierarchyBoundingVectors(true);\n const extents = max.subtract(min);\n const worldCenter = min.add(max).scale(0.5);\n\n // Convert world center to an offset in the node's local frame.\n // For the common case (identity rotation) this is just worldCenter - absolutePosition.\n // If the parent has rotation, undo it so the center lives in the node's local space.\n const worldOffset = worldCenter.subtract(node.absolutePosition);\n const rotation = node.absoluteRotationQuaternion;\n const inverseRotation = Quaternion.Inverse(rotation);\n const center = new Vector3();\n worldOffset.rotateByQuaternionToRef(inverseRotation, center);\n\n return { extents, center };\n}\n\n/**\n * Creates a physics aggregate for a node with the given shape type.\n *\n * Works with both Mesh and TransformNode. For non-Mesh TransformNodes with child geometry\n * (multi-material mesh case), computes explicit extents/center from the hierarchy bounding box\n * to avoid `PhysicsAggregate` falling back to a default unit cube.\n */\nfunction createPhysicsAggregate(node : TransformNode, shapeType : PhysicsShapeType, mass : number) : void\n{\n const scene = node.getScene();\n const options : PhysicsAggregateParameters = { mass };\n\n // Non-Mesh TransformNodes have no geometry of their own; compute bounds from descendants.\n if(!(node instanceof Mesh) && (shapeType === PhysicsShapeType.BOX || shapeType === PhysicsShapeType.SPHERE))\n {\n const { extents, center } = computeHierarchyBounds(node);\n options.center = center;\n\n if(shapeType === PhysicsShapeType.BOX)\n {\n options.extents = extents;\n }\n else\n {\n // Sphere radius: use the largest half-extent so the sphere fully contains the geometry.\n options.radius = Math.max(extents.x, extents.y, extents.z) / 2;\n }\n }\n\n new PhysicsAggregate(node, shapeType, options, scene);\n}\n\n/**\n * Handles mesh collider setup, using a child mesh if marked with collider_mesh.\n */\nfunction handleMeshCollider(mesh : Mesh, mass : number) : void\n{\n const colliderChild = mesh.getChildMeshes().find(\n (child) => child.metadata?.collider_mesh === true\n );\n\n if(colliderChild instanceof Mesh)\n {\n colliderChild.isVisible = false;\n createPhysicsAggregate(colliderChild, PhysicsShapeType.MESH, mass);\n }\n else\n {\n createPhysicsAggregate(mesh, PhysicsShapeType.MESH, mass);\n }\n}\n\n/**\n * Register the collider property handler.\n *\n * Supports the following properties on scene nodes:\n * - `collider` (string): Collider type - \"box\", \"sphere\", \"mesh\", or \"none\"\n * - `collider_mesh` (boolean): On a child node, marks it as the collision source for parent's \"mesh\" collider\n * - `collider_mass` (number): Mass for the physics body. Defaults to 0 (static). Set > 0 for dynamic bodies.\n * - `collider_kinematic` (boolean): When true, sets `disablePreStep = false` so the physics body\n * tracks mesh transform each frame. Required for animated colliders (doors, elevators, platforms).\n *\n * For \"mesh\" colliders, if a child node has `collider_mesh: true`, that child's geometry\n * is used for collision and the child is made invisible.\n *\n * @param levelManager\n */\nexport function registerColliderHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('collider', (node, value, level, _gameEngine) =>\n {\n const colliderType = value as string;\n logger.debug(`Processing collider: ${ node.name } -> ${ colliderType } (isMesh: ${ node instanceof Mesh })`);\n\n if(!level.scene)\n {\n throw new Error('Scene not available for collider handler');\n }\n\n if(!(node instanceof Mesh) && colliderType === 'mesh')\n {\n logger.warn(`Skipping mesh collider for ${ node.name }: not a Mesh instance`);\n return;\n }\n\n const mass = (node.metadata?.collider_mass as number | undefined) ?? 0;\n\n switch (colliderType)\n {\n case 'box':\n createPhysicsAggregate(node, PhysicsShapeType.BOX, mass);\n break;\n\n case 'sphere':\n createPhysicsAggregate(node, PhysicsShapeType.SPHERE, mass);\n break;\n\n case 'mesh':\n handleMeshCollider(node as Mesh, mass);\n break;\n\n case 'none':\n if(node instanceof AbstractMesh)\n {\n node.checkCollisions = false;\n }\n break;\n\n default:\n logger.warn(`Unknown collider type: ${ colliderType }`);\n }\n\n // Enable kinematic mode -- physics body tracks mesh transform each frame\n if(node.metadata?.collider_kinematic && node instanceof AbstractMesh)\n {\n if(node.physicsBody)\n {\n node.physicsBody.disablePreStep = false;\n }\n }\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// LOD (Level of Detail) Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Mesh } from '@babylonjs/core';\n\nimport type { LevelManager } from '../managers/level.ts';\nimport { SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nconst logger = new SAGELogger('LodHandler');\n\n/**\n * Parses a comma-separated string of distances into an array of numbers.\n * Filters out invalid (NaN) values.\n */\nfunction parseDistances(distancesStr : string) : number[]\n{\n return distancesStr\n .split(',')\n .map((str) => parseFloat(str.trim()))\n .filter((num) => !isNaN(num));\n}\n\n/**\n * Register the LOD (Level of Detail) property handler.\n *\n * Supports the following property on scene nodes:\n * - `lod_distances` (string): Comma-separated distances for LOD levels\n *\n * The mesh's children are used as LOD levels in order.\n * Example: \"10,25,50\" means:\n * - 0-10 units: use first child mesh\n * - 10-25 units: use second child mesh\n * - 25-50 units: use third child mesh\n * - 50+ units: use null (not visible)\n *\n * @param levelManager\n */\nexport function registerLodHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('lod_distances', (node, value, _level, _gameEngine) =>\n {\n const distancesStr = value as string;\n\n if(!(node instanceof Mesh))\n {\n return;\n }\n\n const distances = parseDistances(distancesStr);\n\n if(distances.length === 0)\n {\n logger.warn(`Invalid lod_distances value: ${ distancesStr }`);\n return;\n }\n\n const children = node.getChildMeshes(true) as Mesh[];\n const levelCount = Math.min(distances.length, children.length);\n\n // Add LOD levels for each distance/child pair\n for(let i = 0; i < levelCount; i++)\n {\n node.addLODLevel(distances[i], children[i]);\n }\n\n // Add null LOD for beyond the last distance (not visible)\n const lastDistance = distances[distances.length - 1];\n node.addLODLevel(lastDistance, null);\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Occluder Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Mesh } from '@babylonjs/core';\n\nimport type { LevelManager } from '../managers/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Register the occluder property handler.\n *\n * Supports the following property on scene nodes:\n * - `occluder` (boolean): If true, marks the mesh as an occluder for occlusion culling\n *\n * Occluder meshes are used by the engine to hide objects that are behind them,\n * improving rendering performance. The occluder mesh itself is made invisible.\n *\n * @param levelManager\n */\nexport function registerOccluderHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('occluder', (node, value, _level, _gameEngine) =>\n {\n if(!value)\n {\n return;\n }\n\n // Only meshes can be occluders\n if(!(node instanceof Mesh))\n {\n return;\n }\n\n // Mark as occluder and hide\n // Note: isOccluder exists at runtime but is missing from @babylonjs/core type definitions as of 9.0\n (node as Mesh & { isOccluder : boolean }).isOccluder = true;\n node.isVisible = false;\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Metadata Utilities\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Collect properties with a common prefix into a single object.\n * Useful for gathering related properties from node metadata.\n *\n * For example, given metadata with:\n * - sound_volume: 0.5\n * - sound_loop: true\n * - sound_spatial: false\n *\n * Calling collectPrefixedProperties(metadata, 'sound') returns:\n * { volume: 0.5, loop: true, spatial: false }\n *\n * @param metadata - The node metadata to search\n * @param prefix - The prefix to match (without trailing underscore)\n * @returns Object with sub-properties (prefix and underscore removed from keys)\n */\nexport function collectPrefixedProperties(\n metadata : Record<string, unknown>,\n prefix : string\n) : Record<string, unknown>\n{\n const result : Record<string, unknown> = {};\n const prefixWithUnderscore = `${ prefix }_`;\n\n for(const [ key, value ] of Object.entries(metadata))\n {\n if(key.startsWith(prefixWithUnderscore))\n {\n const subKey = key.slice(prefixWithUnderscore.length);\n result[subKey] = value;\n }\n }\n\n return result;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Sound Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { collectPrefixedProperties } from '../utils/metadata.ts';\nimport { SAGELogger } from '../utils/logger.ts';\nimport type { LevelManager } from '../managers/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nconst logger = new SAGELogger('SoundHandler');\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Register the sound property handler.\n *\n * Supports the following properties on scene nodes:\n * - `sound` (string): Path to the sound file\n * - `sound_volume` (number): Volume 0-1, default 1\n * - `sound_loop` (boolean): Whether to loop, default true\n * - `sound_spatial` (boolean): Whether to use 3D spatial audio, default true\n * - `sound_distance` (number): Maximum distance for spatial audio, default 100\n * - `sound_autoplay` (boolean): Whether to autoplay, default true\n * - `sound_channel` (string): Audio channel name, default 'ambient'\n *\n * @param levelManager\n */\nexport function registerSoundHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('sound', (node, value, level, gameEngine) =>\n {\n const soundPath = value as string;\n const scene = level.scene;\n\n if(!scene)\n {\n throw new Error('Scene not available for sound handler');\n }\n\n // Collect prefixed properties\n const extras = collectPrefixedProperties(\n node.metadata as Record<string, unknown>,\n 'sound'\n );\n\n const volume = (extras.volume as number) ?? 1;\n const loop = (extras.loop as boolean) ?? true;\n const spatial = (extras.spatial as boolean) ?? true;\n const maxDistance = (extras.distance as number) ?? 100;\n const autoplay = (extras.autoplay as boolean) ?? true;\n const channel = (extras.channel as string) ?? 'ambient';\n\n const audioManager = gameEngine.managers.audioManager;\n\n if(!audioManager)\n {\n logger.warn(`No AudioManager available. Sound for \"${ node.name }\" skipped.`);\n return;\n }\n\n audioManager.createSound(\n `${ node.name }_sound`,\n soundPath,\n channel,\n {\n loop,\n autoplay,\n volume,\n spatialEnabled: spatial,\n spatialMaxDistance: maxDistance,\n spatialDistanceModel: 'linear',\n }\n )\n .then((sound) =>\n {\n if(spatial)\n {\n sound.spatial.attach(node);\n }\n })\n .catch((err) =>\n {\n logger.error(`Failed to create sound for node \"${ node.name }\": ${ err }`);\n });\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Trigger Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractMesh, ActionManager, ExecuteCodeAction } from '@babylonjs/core';\n\nimport type { GameEventBus } from '../classes/eventBus.ts';\nimport type { LevelManager } from '../managers/level.ts';\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Registers an intersection action (enter or exit) for a trigger mesh against a target mesh.\n */\nfunction registerIntersectionAction(\n actionManager : ActionManager,\n triggerName : string,\n eventBus : GameEventBus,\n targetMesh : AbstractMesh,\n actionType : 'enter' | 'exit'\n) : void\n{\n const triggerType = actionType === 'enter'\n ? ActionManager.OnIntersectionEnterTrigger\n : ActionManager.OnIntersectionExitTrigger;\n\n const eventType = `trigger:${ actionType }` as const;\n\n actionManager.registerAction(\n new ExecuteCodeAction(\n {\n trigger: triggerType,\n parameter: { mesh: targetMesh },\n },\n (evt) =>\n {\n eventBus.publish({\n type: eventType,\n payload: {\n trigger: triggerName,\n other: evt.source as AbstractMesh,\n },\n });\n }\n )\n );\n}\n\n/**\n * Sets up intersection triggers between a trigger mesh and a target mesh.\n */\nfunction registerTriggersForMesh(\n actionManager : ActionManager,\n triggerName : string,\n eventBus : GameEventBus,\n targetMesh : AbstractMesh\n) : void\n{\n registerIntersectionAction(actionManager, triggerName, eventBus, targetMesh, 'enter');\n registerIntersectionAction(actionManager, triggerName, eventBus, targetMesh, 'exit');\n}\n\n/**\n * Register the trigger property handler.\n *\n * Supports the following property on scene nodes:\n * - `trigger` (string): The trigger zone name\n *\n * When an object enters or exits the trigger zone, events are published:\n * - `trigger:enter` with payload { trigger: string, other: AbstractMesh }\n * - `trigger:exit` with payload { trigger: string, other: AbstractMesh }\n *\n * The trigger mesh is made invisible but retains collision detection.\n * Intersection checks are registered against all non-trigger meshes in the scene,\n * including meshes added after the handler runs (e.g. dynamically spawned entities).\n *\n * @param levelManager\n */\nexport function registerTriggerHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('trigger', (node, value, level, gameEngine) =>\n {\n const triggerName = value as string;\n const scene = level.scene;\n\n if(!scene)\n {\n throw new Error('Scene not available for trigger handler');\n }\n\n if(!(node instanceof AbstractMesh))\n {\n return;\n }\n\n const eventBus = gameEngine.eventBus;\n\n // Make the trigger invisible but keep collision\n node.isVisible = false;\n node.checkCollisions = true;\n\n // Create action manager if needed\n const actionManager = (node.actionManager as ActionManager) ?? new ActionManager(scene);\n node.actionManager = actionManager;\n\n // Register intersection triggers against all existing non-trigger meshes\n for(const mesh of scene.meshes)\n {\n if(mesh !== node && !mesh.metadata?.trigger)\n {\n registerTriggersForMesh(actionManager, triggerName, eventBus, mesh);\n }\n }\n\n // Register triggers for meshes added later (e.g. dynamically spawned entities)\n const observer = scene.onNewMeshAddedObservable.add((mesh) =>\n {\n if(mesh !== node && !mesh.metadata?.trigger)\n {\n registerTriggersForMesh(actionManager, triggerName, eventBus, mesh);\n }\n });\n\n // Clean up observer when the trigger mesh is disposed\n node.onDisposeObservable.add(() =>\n {\n scene.onNewMeshAddedObservable.remove(observer);\n });\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Visible Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractMesh, type TransformNode } from '@babylonjs/core';\nimport type { LevelManager } from '../managers/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Register the visible property handler.\n *\n * Supports the following property on scene nodes:\n * - `visible` (boolean | string | number): Controls mesh visibility.\n * Accepts true/false, 'true'/'false', or 1/0.\n *\n * @param levelManager\n */\nexport function registerVisibleHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('visible', (node : TransformNode, value : unknown, _level, _gameEngine) =>\n {\n // Only meshes have isVisible property\n if(!(node instanceof AbstractMesh))\n {\n return;\n }\n\n // Parse value as boolean\n // Supports: true/false (boolean), 'true'/'false' (string), 1/0 (number)\n let visible = true;\n\n if(typeof value === 'boolean')\n {\n visible = value;\n }\n else if(typeof value === 'string')\n {\n visible = value.toLowerCase() === 'true' || value === '1';\n }\n else if(typeof value === 'number')\n {\n visible = value !== 0;\n }\n\n node.isVisible = visible;\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Property Handlers Index\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LevelManager } from '../managers/level.ts';\n\nimport { registerColliderHandler } from './collider.ts';\nimport { registerLodHandler } from './lod.ts';\nimport { registerOccluderHandler } from './occluder.ts';\nimport { applyPostProcessing } from './postProcessing.ts';\nimport { registerSoundHandler } from './sound.ts';\nimport { registerTriggerHandler } from './trigger.ts';\nimport { registerVisibleHandler } from './visible.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport {\n applyPostProcessing,\n registerColliderHandler,\n registerLodHandler,\n registerOccluderHandler,\n registerSoundHandler,\n registerTriggerHandler,\n registerVisibleHandler,\n};\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Register all built-in property handlers with the given LevelManager.\n * Call this during game initialization to enable default property handling.\n *\n * @param levelManager\n */\nexport function registerAllPropertyHandlers(levelManager : LevelManager) : void\n{\n registerColliderHandler(levelManager);\n registerLodHandler(levelManager);\n registerOccluderHandler(levelManager);\n registerSoundHandler(levelManager);\n registerTriggerHandler(levelManager);\n registerVisibleHandler(levelManager);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// defineEntity\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type {\n Action, BehaviorClass, ComposedOf, GameEntityType, GameSimulationContext, StateBag, StateFor, ValidTuple,\n} from '@skewedaspect/sage-core';\n\nimport { NodeEntity } from './nodeEntity.ts';\n\nimport type { MeshConfig } from './meshConfig.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface GameEntityDefinition<Behaviors extends readonly BehaviorClass[]>\n{\n type : string;\n tags ?: string[];\n defaultState ?: Partial<StateFor<Behaviors>>;\n behaviors : Behaviors;\n mesh ?: MeshConfig;\n actions ?: readonly Action[];\n}\n\nexport type GameEntityDefinitionClass<Behaviors extends readonly BehaviorClass[]> = typeof NodeEntity & {\n readonly behaviors : Behaviors;\n};\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// A supplied field replaces the definition's own default whole (design/entities.md#state); tags merge as a\n// deduplicated union instead, since a spawn's own extra tags are additions to a definition's baseline, not a\n// replacement for it (design/authoring.md#composition-and-the-class-table).\nfunction foldCreateState<Behaviors extends readonly BehaviorClass[]>(\n definition : GameEntityDefinition<Behaviors>,\n supplied : Partial<StateFor<Behaviors>> | undefined\n) : Partial<StateFor<Behaviors>>\n{\n const suppliedState = (supplied ?? {}) as StateBag;\n const definitionTags = definition.tags ?? [];\n const suppliedTags = (suppliedState['tags'] as string[] | undefined) ?? [];\n\n return {\n ...definition.defaultState,\n ...suppliedState,\n tags: [ ...new Set([ ...definitionTags, ...suppliedTags ]) ],\n } as Partial<StateFor<Behaviors>>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport function defineEntity<Behaviors extends readonly BehaviorClass[]>(\n definition : GameEntityDefinition<Behaviors> & { behaviors : ValidTuple<Behaviors> }\n) : GameEntityDefinitionClass<Behaviors>\n{\n class DefinedEntity extends NodeEntity\n {\n static override readonly behaviors = definition.behaviors;\n static override readonly meshConfig = definition.mesh;\n static override readonly actions = definition.actions;\n\n static override create<Type extends GameEntityType>(\n this : Type & { behaviors : ValidTuple<Type['behaviors']> },\n id : string,\n context : GameSimulationContext,\n state ?: Partial<StateFor<Type['behaviors']>>,\n name ?: string\n ) : ComposedOf<Type>\n {\n const folded = foldCreateState(definition, state as Partial<StateFor<Behaviors>> | undefined);\n\n return super.create(id, context, folded as Partial<StateFor<Type['behaviors']>>, name);\n }\n }\n\n return DefinedEntity as unknown as GameEntityDefinitionClass<Behaviors>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Standard Mesh Source\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Color3, MeshBuilder, PBRMaterial, StandardMaterial } from '@babylonjs/core';\n\nimport type { AbstractMesh, AssetContainer, Mesh, Scene } from '@babylonjs/core';\nimport type { GameEntityType } from '@skewedaspect/sage-core';\n\nimport type { ColorConfig } from '../interfaces/level.ts';\nimport type { EntityMeshLoader } from '../interfaces/game.ts';\n\nimport { NodeEntity } from './nodeEntity.ts';\nimport type { MeshConfig, MeshMaterialConfig } from './meshConfig.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// What the standard mesh source needs from the asset manager: loading a glb (or a fragment naming one of its\n// meshes), and cloning a fragment's cached source mesh into a fresh, independent one.\nexport interface MeshAssetSource\n{\n load(path : string) : Promise<AssetContainer | AbstractMesh>;\n clone(path : string) : Mesh;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nfunction toColor3(color : ColorConfig) : Color3\n{\n return new Color3(color.r, color.g, color.b);\n}\n\nfunction buildPrimitiveMesh(scene : Scene, source : string, params : Record<string, unknown> | undefined) : Mesh\n{\n const builderName = `Create${ source.charAt(0).toUpperCase() }${ source.slice(1) }`;\n const builders = MeshBuilder as unknown as Record<string, (name : string, options : object, scene : Scene) => Mesh>;\n const builder = builders[builderName];\n\n if(typeof builder !== 'function')\n {\n throw new Error(`No Babylon mesh builder provides the primitive shape \"${ source }\".`);\n }\n\n return builder(source, params ?? {}, scene);\n}\n\nfunction applyMaterial(scene : Scene, mesh : Mesh, config : MeshMaterialConfig) : void\n{\n if(config.type === 'pbr')\n {\n const material = new PBRMaterial(`${ mesh.name }-material`, scene);\n\n if(config.color !== undefined)\n {\n material.albedoColor = toColor3(config.color);\n }\n\n if(config.emissive !== undefined)\n {\n material.emissiveColor = toColor3(config.emissive);\n }\n\n if(config.metallic !== undefined)\n {\n material.metallic = config.metallic;\n }\n\n if(config.roughness !== undefined)\n {\n material.roughness = config.roughness;\n }\n\n mesh.material = material;\n }\n else\n {\n const material = new StandardMaterial(`${ mesh.name }-material`, scene);\n\n if(config.color !== undefined)\n {\n material.diffuseColor = toColor3(config.color);\n }\n\n if(config.emissive !== undefined)\n {\n material.emissiveColor = toColor3(config.emissive);\n }\n\n mesh.material = material;\n }\n}\n\nasync function buildMesh(scene : Scene, config : MeshConfig, assets : MeshAssetSource) : Promise<Mesh>\n{\n let mesh : Mesh;\n\n if(config.source.includes('#'))\n {\n await assets.load(config.source);\n mesh = assets.clone(config.source);\n }\n else\n {\n mesh = buildPrimitiveMesh(scene, config.source, config.params);\n }\n\n mesh.isVisible = config.visible ?? true;\n\n if(config.material !== undefined)\n {\n applyMaterial(scene, mesh, config.material);\n }\n\n return mesh;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport function buildStandardMeshSource(\n entityClasses : Readonly<Record<string, GameEntityType>>,\n assets : MeshAssetSource\n) : EntityMeshLoader\n{\n return async (scene, name) : Promise<AbstractMesh | undefined> =>\n {\n const entityClass = entityClasses[name] as typeof NodeEntity | undefined;\n const config = entityClass?.meshConfig;\n\n if(config === undefined)\n {\n return undefined;\n }\n\n return buildMesh(scene, config, assets);\n };\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Action Registry\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// An action is registered by name before anything binds to it (input.md#actions).\nexport class ActionRegistry\n{\n readonly #actions = new Map<string, Action>();\n\n register(action : Action) : void\n {\n if(this.#actions.has(action.name))\n {\n throw new Error(`An action is already registered under the name \"${ action.name }\".`);\n }\n\n if(action.type === 'analog' && action.minValue !== undefined && action.maxValue !== undefined)\n {\n if(action.minValue > action.maxValue)\n {\n throw new Error(\n `Action \"${ action.name }\": minValue (${ action.minValue }) is above `\n + `maxValue (${ action.maxValue }).`\n );\n }\n }\n\n this.#actions.set(action.name, action);\n }\n\n get(name : string) : Action | undefined\n {\n return this.#actions.get(name);\n }\n\n list() : readonly Action[]\n {\n return [ ...this.#actions.values() ];\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Action Context\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Binding } from './binding.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface ContextEntry\n{\n readonly exclusive : boolean;\n active : boolean;\n}\n\n// A named group of bindings that activate and deactivate together (input.md#action-contexts).\nexport class ActionContextRegistry\n{\n readonly #contexts = new Map<string, ContextEntry>();\n readonly #bindings : Binding<unknown>[] = [];\n\n // Exclusive by default (input.md#action-contexts).\n registerContext(name : string, exclusive = true) : void\n {\n this.#contexts.set(name, { exclusive, active: false });\n }\n\n // Activating an exclusive context deactivates every other exclusive one. Primes every member binding\n // against the current state the caller reports for it, so an input already held does not read as a\n // fresh press.\n activateContext(name : string, currentState : (binding : Binding<unknown>) => unknown) : void\n {\n const context = this.#requireContext(name);\n\n if(context.exclusive)\n {\n for(const [ otherName, other ] of this.#contexts)\n {\n if(otherName !== name && other.exclusive)\n {\n other.active = false;\n }\n }\n }\n\n context.active = true;\n\n for(const binding of this.#bindings)\n {\n if(binding.context === name)\n {\n binding.prime(currentState(binding));\n }\n }\n }\n\n deactivateContext(name : string) : void\n {\n this.#requireContext(name).active = false;\n }\n\n isActive(name : string) : boolean\n {\n return this.#contexts.get(name)?.active === true;\n }\n\n isRegistered(name : string) : boolean\n {\n return this.#contexts.has(name);\n }\n\n // Registers a binding under its own named context, or under none.\n addBinding(binding : Binding<unknown>) : void\n {\n this.#bindings.push(binding);\n }\n\n // True when the binding names no context, or names one that is active.\n isLive(binding : Binding<unknown>) : boolean\n {\n if(binding.context === undefined)\n {\n return true;\n }\n\n return this.isActive(binding.context);\n }\n\n #requireContext(name : string) : ContextEntry\n {\n const context = this.#contexts.get(name);\n\n if(context === undefined)\n {\n throw new Error(`No context is registered under the name \"${ name }\".`);\n }\n\n return context;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Keyboard Device Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { KeyboardState } from '../deviceState.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// Reads one key by its DOM code (input.md#device-readers).\nexport class KeyboardReader implements DeviceReader<KeyboardState>\n{\n readonly sourceType = 'key';\n readonly sourceKey : string;\n\n constructor(sourceKey : string)\n {\n this.sourceKey = sourceKey;\n }\n\n getValue(state : KeyboardState) : boolean | undefined\n {\n return state.keys[this.sourceKey];\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Mouse Device Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { MouseState } from '../deviceState.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type MouseSourceType = 'button' | 'position' | 'wheel';\n\n// Reads a mouse button, the cursor position, or a wheel axis (input.md#device-readers).\nexport class MouseReader implements DeviceReader<MouseState>\n{\n readonly sourceType : MouseSourceType;\n readonly sourceKey : string;\n\n constructor(sourceType : MouseSourceType, sourceKey : string)\n {\n this.sourceType = sourceType;\n this.sourceKey = sourceKey;\n }\n\n getValue(state : MouseState) : boolean | number | undefined\n {\n if(this.sourceType === 'button')\n {\n return state.buttons[this.sourceKey];\n }\n\n if(this.sourceType === 'position')\n {\n if(this.sourceKey === 'x')\n {\n return state.position.x;\n }\n\n if(this.sourceKey === 'y')\n {\n return state.position.y;\n }\n\n return undefined;\n }\n\n if(this.sourceType === 'wheel')\n {\n if(this.sourceKey === 'deltaX')\n {\n return state.wheel.deltaX;\n }\n\n if(this.sourceKey === 'deltaY')\n {\n return state.wheel.deltaY;\n }\n\n if(this.sourceKey === 'deltaZ')\n {\n return state.wheel.deltaZ;\n }\n\n return undefined;\n }\n\n return undefined;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Gamepad Device Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GamepadState } from '../deviceState.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type GamepadSourceType = 'button' | 'axis';\n\n// Reads a gamepad button or stick axis (input.md#device-readers).\nexport class GamepadReader implements DeviceReader<GamepadState>\n{\n readonly sourceType : GamepadSourceType;\n readonly sourceKey : string;\n\n constructor(sourceType : GamepadSourceType, sourceKey : string)\n {\n this.sourceType = sourceType;\n this.sourceKey = sourceKey;\n }\n\n getValue(state : GamepadState) : boolean | number | undefined\n {\n if(this.sourceType === 'button')\n {\n return state.buttons[this.sourceKey];\n }\n\n if(this.sourceType === 'axis')\n {\n return state.axes[this.sourceKey];\n }\n\n return undefined;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Trigger Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition, BindingFire, DeviceID } from '../binding.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type EdgeMode = 'rising' | 'falling' | 'both';\n\nexport interface TriggerOptions\n{\n edge ?: EdgeMode;\n threshold ?: number;\n}\n\n// Fires once on the rising edge, the falling edge, or both. An analog input crossing the threshold counts\n// as the change (input.md#bindings).\nexport class TriggerBinding<State> implements Binding<State>\n{\n readonly kind = 'trigger' as const;\n readonly action : Action;\n readonly device : DeviceID;\n readonly context ?: string;\n\n readonly #reader : DeviceReader<State>;\n readonly #edge : EdgeMode;\n readonly #threshold ?: number;\n\n // undefined until the first process()/prime() call establishes a baseline, so that call never fires.\n #pressed : boolean | undefined;\n\n constructor(\n action : Action,\n device : DeviceID,\n reader : DeviceReader<State>,\n options : TriggerOptions = {},\n context ?: string\n )\n {\n this.action = action;\n this.device = device;\n this.context = context;\n\n this.#reader = reader;\n this.#edge = options.edge ?? 'rising';\n this.#threshold = options.threshold;\n }\n\n process(state : State) : BindingFire | undefined\n {\n const pressed = this.#isPressed(this.#reader.getValue(state));\n const previous = this.#pressed;\n\n this.#pressed = pressed;\n\n if(previous === undefined)\n {\n return undefined;\n }\n\n if(!this.#crossesEdge(previous, pressed))\n {\n return undefined;\n }\n\n if(this.action.type === 'analog')\n {\n return { value: this.#numericValue(this.#reader.getValue(state)) };\n }\n\n return { value: pressed };\n }\n\n prime(state : State) : void\n {\n this.#pressed = this.#isPressed(this.#reader.getValue(state));\n }\n\n toDefinition() : BindingDefinition\n {\n const options : Record<string, unknown> = { edge: this.#edge };\n\n if(this.#threshold !== undefined)\n {\n options.threshold = this.#threshold;\n }\n\n return {\n kind: 'trigger',\n action: this.action.name,\n input: { device: this.device, sourceType: this.#reader.sourceType, sourceKey: this.#reader.sourceKey },\n context: this.context,\n options,\n };\n }\n\n #crossesEdge(previous : boolean, pressed : boolean) : boolean\n {\n if(this.#edge === 'rising')\n {\n return !previous && pressed;\n }\n\n if(this.#edge === 'falling')\n {\n return previous && !pressed;\n }\n\n return previous !== pressed;\n }\n\n #isPressed(raw : boolean | number | undefined) : boolean\n {\n if(typeof raw === 'boolean')\n {\n return raw;\n }\n\n if(typeof raw === 'number')\n {\n if(this.#threshold !== undefined)\n {\n return raw >= this.#threshold;\n }\n\n return raw !== 0;\n }\n\n return false;\n }\n\n #numericValue(raw : boolean | number | undefined) : number\n {\n if(typeof raw === 'number')\n {\n return raw;\n }\n\n if(raw === true)\n {\n return 1;\n }\n\n return 0;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Toggle Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition, BindingFire, DeviceID } from '../binding.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface ToggleOptions\n{\n onValue ?: boolean | number;\n offValue ?: boolean | number;\n initialState ?: boolean;\n threshold ?: number;\n invert ?: boolean;\n}\n\n// Alternates each time its input is pressed and holds its state between presses (input.md#bindings).\nexport class ToggleBinding<State> implements Binding<State>\n{\n readonly kind = 'toggle' as const;\n readonly action : Action;\n readonly device : DeviceID;\n readonly context ?: string;\n\n readonly #reader : DeviceReader<State>;\n readonly #onValue : boolean | number;\n readonly #offValue : boolean | number;\n readonly #threshold ?: number;\n readonly #invert : boolean;\n\n // No declared default (input.md#bindings): left undeclared, the on/off bit that alternation flips starts\n // undefined, and the first flip lands on true the same way any boolean negation of undefined does.\n #isOn : boolean | undefined;\n\n // undefined until the first process()/prime() call establishes a baseline, so that call never fires.\n #previousEffective : boolean | undefined;\n\n constructor(\n action : Action,\n device : DeviceID,\n reader : DeviceReader<State>,\n options : ToggleOptions = {},\n context ?: string\n )\n {\n this.action = action;\n this.device = device;\n this.context = context;\n\n this.#reader = reader;\n this.#onValue = options.onValue ?? true;\n this.#offValue = options.offValue ?? false;\n this.#threshold = options.threshold;\n this.#invert = options.invert ?? false;\n this.#isOn = options.initialState;\n }\n\n process(state : State) : BindingFire | undefined\n {\n const effective = this.#effective(this.#reader.getValue(state));\n const previous = this.#previousEffective;\n\n this.#previousEffective = effective;\n\n if(previous === undefined)\n {\n return undefined;\n }\n\n if(previous || !effective)\n {\n return undefined;\n }\n\n this.#isOn = !this.#isOn;\n\n if(this.#isOn)\n {\n return { value: this.#onValue };\n }\n\n return { value: this.#offValue };\n }\n\n prime(state : State) : void\n {\n this.#previousEffective = this.#effective(this.#reader.getValue(state));\n }\n\n toDefinition() : BindingDefinition\n {\n const options : Record<string, unknown> = {\n onValue: this.#onValue,\n offValue: this.#offValue,\n invert: this.#invert,\n };\n\n if(this.#threshold !== undefined)\n {\n options.threshold = this.#threshold;\n }\n\n if(this.#isOn !== undefined)\n {\n options.initialState = this.#isOn;\n }\n\n return {\n kind: 'toggle',\n action: this.action.name,\n input: { device: this.device, sourceType: this.#reader.sourceType, sourceKey: this.#reader.sourceKey },\n context: this.context,\n options,\n };\n }\n\n // The pressed state, inverted when the binding alternates on a release instead of a press.\n #effective(raw : boolean | number | undefined) : boolean\n {\n const pressed = this.#isPressed(raw);\n\n if(this.#invert)\n {\n return !pressed;\n }\n\n return pressed;\n }\n\n #isPressed(raw : boolean | number | undefined) : boolean\n {\n if(typeof raw === 'boolean')\n {\n return raw;\n }\n\n if(typeof raw === 'number')\n {\n if(this.#threshold !== undefined)\n {\n return raw >= this.#threshold;\n }\n\n return raw !== 0;\n }\n\n return false;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Value Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition, BindingFire, DeviceID } from '../binding.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface ValueOptions\n{\n scale ?: number;\n offset ?: number;\n invert ?: boolean;\n deadzone ?: number;\n min ?: number;\n max ?: number;\n emitOnChange ?: boolean;\n}\n\n// Passes its input through continuously: scales, offsets, clamps, inverts, and ignores magnitudes inside\n// a deadzone. By default sends nothing while the value it would send is unchanged (input.md#bindings).\nexport class ValueBinding<State> implements Binding<State>\n{\n readonly kind = 'value' as const;\n readonly action : Action;\n readonly device : DeviceID;\n readonly context ?: string;\n\n readonly #reader : DeviceReader<State>;\n readonly #scale : number;\n readonly #offset : number;\n readonly #invert : boolean;\n readonly #deadzone : number;\n readonly #min ?: number;\n readonly #max ?: number;\n readonly #emitOnChange : boolean;\n\n #lastSent : number | undefined;\n\n constructor(\n action : Action,\n device : DeviceID,\n reader : DeviceReader<State>,\n options : ValueOptions = {},\n context ?: string\n )\n {\n this.action = action;\n this.device = device;\n this.context = context;\n\n this.#reader = reader;\n this.#scale = options.scale ?? 1;\n this.#offset = options.offset ?? 0;\n this.#invert = options.invert ?? false;\n this.#deadzone = options.deadzone ?? 0;\n this.#min = options.min;\n this.#max = options.max;\n this.#emitOnChange = options.emitOnChange ?? true;\n }\n\n process(state : State) : BindingFire | undefined\n {\n const value = this.#compute(state);\n const previous = this.#lastSent;\n\n this.#lastSent = value;\n\n if(this.#emitOnChange && value === previous)\n {\n return undefined;\n }\n\n return { value };\n }\n\n prime(state : State) : void\n {\n this.#lastSent = this.#compute(state);\n }\n\n toDefinition() : BindingDefinition\n {\n const options : Record<string, unknown> = {\n scale: this.#scale,\n offset: this.#offset,\n invert: this.#invert,\n deadzone: this.#deadzone,\n emitOnChange: this.#emitOnChange,\n };\n\n if(this.#min !== undefined)\n {\n options.min = this.#min;\n }\n\n if(this.#max !== undefined)\n {\n options.max = this.#max;\n }\n\n return {\n kind: 'value',\n action: this.action.name,\n input: { device: this.device, sourceType: this.#reader.sourceType, sourceKey: this.#reader.sourceKey },\n context: this.context,\n options,\n };\n }\n\n #compute(state : State) : number\n {\n let value = this.#toNumber(this.#reader.getValue(state));\n\n value = value * this.#scale;\n value = value + this.#offset;\n\n if(this.#min !== undefined && value < this.#min)\n {\n value = this.#min;\n }\n\n if(this.#max !== undefined && value > this.#max)\n {\n value = this.#max;\n }\n\n if(this.#invert)\n {\n value = -value;\n }\n\n if(Math.abs(value) < this.#deadzone)\n {\n value = 0;\n }\n\n return value;\n }\n\n #toNumber(raw : boolean | number | undefined) : number\n {\n if(typeof raw === 'number')\n {\n return raw;\n }\n\n if(raw === true)\n {\n return 1;\n }\n\n return 0;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Binding Resolution\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition } from './binding.ts';\nimport type { DeviceReader } from './deviceReader.ts';\n\nimport { TriggerBinding, type TriggerOptions } from './bindings/trigger.ts';\nimport { ToggleBinding, type ToggleOptions } from './bindings/toggle.ts';\nimport { ValueBinding, type ValueOptions } from './bindings/value.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// Builds the live binding a definition describes: the kind picks trigger, toggle, or value; the action and\n// the reader are already resolved by the caller (input.md#bindings).\nexport function resolveBinding<State>(\n definition : BindingDefinition,\n action : Action,\n reader : DeviceReader<State>\n) : Binding<State>\n{\n const device = definition.input.device;\n const options = definition.options ?? {};\n\n if(definition.kind === 'trigger')\n {\n return new TriggerBinding(action, device, reader, options as TriggerOptions, definition.context);\n }\n\n if(definition.kind === 'toggle')\n {\n return new ToggleBinding(action, device, reader, options as ToggleOptions, definition.context);\n }\n\n return new ValueBinding(action, device, reader, options as ValueOptions, definition.context);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Delivering an Action\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEvent, GameSimulationContext } from '@skewedaspect/sage-core';\n\nimport type { Binding } from './binding.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// A fired binding reaches its entity as an `action:<name>` event, delivered targeted, carrying the boolean\n// or number the binding computed (events.md#delivery, input.md#actions).\nexport function deliverAction<State>(\n binding : Binding<State>,\n state : State,\n context : GameSimulationContext,\n senderID : string,\n targetID : string\n) : void\n{\n const fire = binding.process(state);\n\n if(fire === undefined)\n {\n return;\n }\n\n // The action name is only known at runtime, so the event map can never narrow `type` to a literal here --\n // the same gap sage-core's own GameEntity.emit() bridges by widening payload to `unknown` before the cast.\n const event : { type : string; senderID : string; targetID ?: string; payload : unknown } = {\n type: `action:${ binding.action.name }`,\n senderID,\n targetID,\n payload: fire.value,\n };\n\n context.bus.emit(event as GameEvent);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Input Capture\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { BindingInput, DeviceType } from './binding.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface CaptureOptions\n{\n deviceTypes ?: readonly DeviceType[];\n sourceTypes ?: readonly string[];\n signal ?: AbortSignal;\n}\n\nexport interface CandidateInput\n{\n device : { type : DeviceType; index : number };\n sourceType : string;\n sourceKey : string;\n value : boolean | number;\n}\n\n// Waits for the next deliberate input and answers with the definition that would bind it (input.md#input-capture).\nexport class InputCapture\n{\n readonly #deviceTypes ?: readonly DeviceType[];\n readonly #sourceTypes ?: readonly string[];\n\n #resolveWait : ((input : BindingInput) => void) | undefined;\n #rejectWait : ((reason : unknown) => void) | undefined;\n\n constructor(options : CaptureOptions = {})\n {\n this.#deviceTypes = options.deviceTypes;\n this.#sourceTypes = options.sourceTypes;\n\n options.signal?.addEventListener('abort', () =>\n {\n this.#rejectWait?.(options.signal?.reason ?? new Error('The capture was aborted.'));\n this.#resolveWait = undefined;\n this.#rejectWait = undefined;\n });\n }\n\n // A rebinding screen feeds every candidate input it sees here as it arrives.\n feed(candidate : CandidateInput) : void\n {\n if(this.#resolveWait === undefined)\n {\n return;\n }\n\n if(!this.#isDeliberate(candidate.value))\n {\n return;\n }\n\n if(this.#deviceTypes !== undefined && !this.#deviceTypes.includes(candidate.device.type))\n {\n return;\n }\n\n if(this.#sourceTypes !== undefined && !this.#sourceTypes.includes(candidate.sourceType))\n {\n return;\n }\n\n this.#resolveWait({\n device: candidate.device,\n sourceType: candidate.sourceType,\n sourceKey: candidate.sourceKey,\n });\n\n this.#resolveWait = undefined;\n this.#rejectWait = undefined;\n }\n\n wait() : Promise<BindingInput>\n {\n return new Promise((resolve, reject) =>\n {\n this.#resolveWait = resolve;\n this.#rejectWait = reject;\n });\n }\n\n #isDeliberate(value : boolean | number) : boolean\n {\n if(value === true)\n {\n return true;\n }\n\n if(typeof value === 'number' && value !== 0)\n {\n return true;\n }\n\n return false;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Configuration\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { ActionContextRegistry } from './actionContext.ts';\nimport type { ActionRegistry } from './actionRegistry.ts';\nimport type { Binding, BindingDefinition } from './binding.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface ContextDefinition\n{\n name : string;\n exclusive : boolean;\n}\n\n// The whole set of actions, bindings, and contexts, JSON-compatible (input.md#bindings).\nexport interface Configuration\n{\n actions : readonly Action[];\n bindings : readonly BindingDefinition[];\n contexts : readonly ContextDefinition[];\n}\n\nexport function exportConfiguration(\n actions : readonly Action[],\n bindings : readonly Binding<unknown>[],\n contexts : readonly ContextDefinition[]\n) : Configuration\n{\n return {\n actions: [ ...actions ],\n bindings: bindings.map((binding) => binding.toDefinition()),\n contexts: [ ...contexts ],\n };\n}\n\n// Registers every action and every context a configuration names. Binding definitions are handed back\n// for the caller to resolve, since resolution needs a device reader the configuration itself does not carry.\nexport function importConfiguration(\n config : Configuration,\n actions : ActionRegistry,\n contexts : ActionContextRegistry\n) : readonly BindingDefinition[]\n{\n for(const action of config.actions)\n {\n actions.register(action);\n }\n\n for(const context of config.contexts)\n {\n contexts.registerContext(context.name, context.exclusive);\n }\n\n return [ ...config.bindings ];\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","for(var r=256,n=[];r--;)n[r]=(r+256).toString(16).substring(1);export function hexoid(r){r=r||16;var t=\"\",o=0;return function(){if(!t||256===o){for(t=\"\",o=(1+r)/2|0;o--;)t+=n[256*Math.random()|0];t=t.substring(o=0,r-2)}return t+n[o++]}}","//----------------------------------------------------------------------------------------------------------------------\n// ID Generation Utility\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { hexoid } from 'hexoid';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Fast, unique ID generator for entities.\n * Uses hexoid for high-performance ID generation (~200M IDs/sec).\n * Generates 16-character hexadecimal strings.\n *\n * @example\n * ```typescript\n * const id = generateId(); // \"8cd88e1a9b13aac0\"\n * ```\n */\nexport const generateId = hexoid(16);\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// State Machine\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../classes/eventBus.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface TransitionDef\n{\n guard ?: () => boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A generic finite state machine with optional guard functions, lifecycle callbacks, and event bus integration.\n *\n * @template States - A string union of valid state names.\n */\nexport class StateMachine<States extends string>\n{\n private _currentState : States;\n private readonly transitions = new Map<string, TransitionDef>();\n private readonly wildcardTransitions = new Map<States, TransitionDef>();\n private readonly enterCallbacks = new Map<States, (() => void)[]>();\n private readonly exitCallbacks = new Map<States, (() => void)[]>();\n private readonly eventBus ?: GameEventBus<Record<string, unknown>>;\n\n constructor(initialState : States, eventBus ?: GameEventBus<Record<string, unknown>>)\n {\n this._currentState = initialState;\n this.eventBus = eventBus;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Properties\n //------------------------------------------------------------------------------------------------------------------\n\n get currentState() : States\n {\n return this._currentState;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Configuration\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a valid transition from one state to another, with an optional guard function.\n */\n addTransition(from : States, to : States, guard ?: () => boolean) : void\n {\n const key = `${ from }->${ to }`;\n this.transitions.set(key, { guard });\n }\n\n /**\n * Register a wildcard transition: allows transitioning to the given state from any state.\n */\n addTransitionFromAny(to : States, guard ?: () => boolean) : void\n {\n this.wildcardTransitions.set(to, { guard });\n }\n\n /**\n * Register a callback to run when entering a state.\n */\n onEnter(state : States, callback : () => void) : void\n {\n const callbacks = this.enterCallbacks.get(state) ?? [];\n callbacks.push(callback);\n this.enterCallbacks.set(state, callbacks);\n }\n\n /**\n * Register a callback to run when exiting a state.\n */\n onExit(state : States, callback : () => void) : void\n {\n const callbacks = this.exitCallbacks.get(state) ?? [];\n callbacks.push(callback);\n this.exitCallbacks.set(state, callbacks);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Queries\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Check if a transition to the given state is defined (ignoring guards).\n */\n canTransition(to : States) : boolean\n {\n const key = `${ this._currentState }->${ to }`;\n return this.transitions.has(key) || this.wildcardTransitions.has(to);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Transitions\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Execute a transition to the target state.\n * Flow: validate -> run guard -> call exit callbacks -> change state -> call enter callbacks.\n *\n * @throws If no transition is defined from the current state to the target, or if a guard rejects.\n */\n transition(to : States) : void\n {\n const key = `${ this._currentState }->${ to }`;\n let transitionDef = this.transitions.get(key);\n\n // Fall back to wildcard\n if(!transitionDef)\n {\n transitionDef = this.wildcardTransitions.get(to);\n }\n\n if(!transitionDef)\n {\n throw new Error(\n `No transition defined from '${ this._currentState }' to '${ to }'.`\n );\n }\n\n // Run guard\n if(transitionDef.guard && !transitionDef.guard())\n {\n throw new Error(\n `Guard rejected transition from '${ this._currentState }' to '${ to }'.`\n );\n }\n\n const previousState = this._currentState;\n\n // Exit callbacks\n const exitCbs = this.exitCallbacks.get(previousState);\n if(exitCbs)\n {\n for(const cb of exitCbs)\n {\n cb();\n }\n }\n\n // Emit exit event -- cast needed because template literal event types can't resolve conditionally\n if(this.eventBus)\n {\n this.eventBus.publish({\n type: `state:exit:${ previousState }`,\n payload: { from: previousState, to },\n } as { type : string; payload : unknown });\n }\n\n // Change state\n this._currentState = to;\n\n // Enter callbacks\n const enterCbs = this.enterCallbacks.get(to);\n if(enterCbs)\n {\n for(const cb of enterCbs)\n {\n cb();\n }\n }\n\n // Emit enter event -- cast needed because template literal event types can't resolve conditionally\n if(this.eventBus)\n {\n this.eventBus.publish({\n type: `state:enter:${ to }`,\n payload: { from: previousState, to },\n } as { type : string; payload : unknown });\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// SkewedAspect Game Engine\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { EntityMeshLoader, GameCanvas, RenderEngineOptions, SageOptions } from './interfaces/game.ts';\n\n// Base Classes\nimport { DebugConsole } from './classes/debugConsole.ts';\nimport { GameEngine, type SimulationFactory } from './classes/gameEngine.ts';\nimport { GameEventBus } from './classes/eventBus.ts';\n\n// Entities\nimport { NodeSimulation } from './entities/nodeSimulation.ts';\nimport { NodePool } from './entities/nodePool.ts';\n\nimport type { GameEntityType } from '@skewedaspect/sage-core';\nimport type { Scene } from '@babylonjs/core';\n\n// Engines\nimport { AudioEngine } from './engines/audio.ts';\nimport { SceneEngine } from './engines/scene.ts';\n\n// Managers\nimport { AssetManager } from './managers/asset.ts';\nimport { GameManager } from './managers/game.ts';\nimport { LevelManager } from './managers/level.ts';\nimport { AudioManager } from './managers/audio.ts';\nimport { SimulationSaveManager } from './managers/simulationSave.ts';\nimport { ColliderDebugManager } from './managers/colliderDebug.ts';\n\n// Utils\nimport { createEngine } from './utils/graphics.ts';\nimport { createHavokPlugin, initHavok } from './utils/physics.ts';\nimport { LoggingUtility } from './utils/logger.ts';\nimport { GameTimer } from './utils/timer.ts';\nimport { VERSION } from './utils/version.ts';\n\n// Debug\nimport { registerBuiltins } from './debug/builtins.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Creates and initializes a fully-wired GameEngine with rendering, physics, and entity management. A\n * game builds its own action registry, contexts, and bindings from the exported input module -- the engine\n * supplies the pieces input.md and events.md#delivery describe, not a device-to-binding routing loop.\n *\n * @param canvas\n * @param entityClasses - The entity class table, keyed by class name, that the simulation spawns from\n * @param options\n */\nexport async function createGameEngine(\n canvas : GameCanvas,\n entityClasses : Readonly<Record<string, GameEntityType>>,\n options : SageOptions = {}\n) : Promise<GameEngine>\n{\n // Initialize logging\n const logger = new LoggingUtility(options.logLevel || 'debug');\n const engineLogger = logger.getLogger('SAGE');\n\n engineLogger.info(`Initializing SAGE Game Engine v${ VERSION }...`);\n\n // Initialize BabylonJS stuff\n engineLogger.debug('Creating rendering engine...');\n\n const renderOptions : RenderEngineOptions = {\n antialias: true,\n adaptToDeviceRatio: true,\n ...options.renderOptions,\n };\n\n const engine = await createEngine(canvas, renderOptions, options.largeWorldRendering ?? false);\n\n engineLogger.debug('Creating physics engine...');\n const havok = await initHavok(options.havokBinary);\n const physics = createHavokPlugin(havok);\n\n // Initialize internals\n engineLogger.debug('Creating event bus...');\n const eventBus = new GameEventBus(logger);\n\n // Initialize SAGE Engines\n engineLogger.debug('Creating scene engine...');\n const sceneEngine = new SceneEngine(canvas, engine, havok, logger);\n\n // Initialize SAGE Managers\n engineLogger.debug('Creating managers...');\n const assetManager = new AssetManager(eventBus, sceneEngine, logger);\n const levelManager = new LevelManager(eventBus, logger);\n const saveManager = new SimulationSaveManager(levelManager, VERSION);\n const gameManager = new GameManager(engine, eventBus, levelManager, logger);\n\n // A factory rather than an instance: no scene exists yet at boot, so there is nothing to build a\n // NodeSimulation against until a level (GameLevel.buildScene) or the game itself calls rebuildSimulation.\n const fixedStepDuration = options.fixedStepDuration ?? 1 / 60;\n const noMesh : EntityMeshLoader = async () => undefined;\n\n const buildSimulation : SimulationFactory = (scene : Scene, meshSource ?: EntityMeshLoader) => new NodeSimulation({\n fixedStepDuration,\n entityClasses,\n scene,\n pool: new NodePool(),\n\n // NodeSimulationContext's MeshLoader carries no scene of its own -- bind the scene this simulation\n // was just built for, so the mesh source does not have to track which scene is current.\n loadMesh: (name, state) => (meshSource ?? noMesh)(scene, name, state),\n });\n\n // Initialize debug tools\n let debugConsole : DebugConsole | null = null;\n\n if(options.debug !== false)\n {\n engineLogger.debug('Creating debug console...');\n const namespaceName = (typeof options.debug === 'object' ? options.debug.namespace : undefined) ?? 'sage';\n debugConsole = new DebugConsole(namespaceName);\n }\n\n engineLogger.debug('Creating collider debug manager...');\n const colliderDebugManager = new ColliderDebugManager();\n\n // Initialize timer\n engineLogger.debug('Creating game timer...');\n const timer = new GameTimer();\n gameManager.registerFrameCallback((dt) => timer.tick(dt * 1000));\n\n // Initialize audio system (opt-in)\n let audioEngine : AudioEngine | undefined;\n let audioManager : AudioManager | undefined;\n\n if(options.audioChannels && options.audioChannels.length > 0)\n {\n engineLogger.debug('Creating audio engine...');\n audioEngine = new AudioEngine(logger);\n await audioEngine.initialize();\n\n engineLogger.debug('Creating audio manager...');\n audioManager = new AudioManager(audioEngine, logger);\n await audioManager.initialize(options.audioChannels);\n }\n\n engineLogger.info(`SAGE Game Engine v${ VERSION } initialized successfully.`);\n\n const ge = new GameEngine(\n canvas,\n engine,\n physics,\n eventBus,\n logger,\n timer,\n buildSimulation,\n\n // Engines\n {\n sceneEngine,\n audioEngine,\n },\n\n // Managers\n {\n assetManager,\n gameManager,\n levelManager,\n saveManager,\n audioManager,\n },\n\n options.largeWorldRendering ?? false,\n\n // Debug tools\n {\n colliders: colliderDebugManager,\n console: debugConsole,\n expose: (name : string, value : unknown) =>\n {\n debugConsole?.expose(name, value);\n },\n }\n );\n\n // Late-bind: these managers are constructed before GameEngine (because GameEngine takes them as\n // constructor args), but they need a back-reference at runtime. This must happen before any level\n // loads or entity attaches, so do it immediately after construction. Do not reorder.\n levelManager.$setGameEngine(ge);\n gameManager.$setGameEngine(ge);\n\n // Register debug console built-ins\n if(debugConsole)\n {\n registerBuiltins(debugConsole, ge);\n }\n\n return ge;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// Exports\n//----------------------------------------------------------------------------------------------------------------------\n\nexport * from './interfaces/game.ts';\nexport * from './interfaces/logger.ts';\n\n// Export base classes\nexport { AssetManager, AudioEngine, GameEngine, GameEventBus, SceneEngine };\nexport type { SimulationFactory } from './classes/gameEngine.ts';\n\nexport * from './classes/eventBus.ts';\nexport * from './classes/level.ts';\nexport * from './classes/gameLevel.ts';\nexport * from './events/index.ts';\nexport * from './handlers/index.ts';\nexport * from './interfaces/level.ts';\nexport * from './interfaces/lifecycle.ts';\nexport * from './utils/logger.ts';\nexport * from './utils/metadata.ts';\nexport * from './utils/timer.ts';\nexport * from './utils/version.ts';\n\n// Export the entity layer -- sage-core's own base classes, so a game can author entities and behaviors\n// without a direct sage-core dependency. EventMap augmentation still has to target sage-core directly (a\n// `declare module` only merges into the module that declares the interface), so this re-export is for\n// reading the type, not for augmenting it.\n//\n// sage-core's `GameEvent` is renamed to `GameEntityEvent` here: `./classes/eventBus.ts` below already\n// exports its own, unrelated `GameEvent` -- 0.9's library event bus shape -- under that name, and this\n// package holds both systems at once.\nexport { Behavior, GameEntity, GameSimulationManager } from '@skewedaspect/sage-core';\nexport type {\n BehaviorClass, ComposedOf, EventMap, EventSubscription, GameEntityState, GameEntityType,\n GameEvent as GameEntityEvent, GameSimulationContext, GameSimulationOptions, GameSimulationSnapshot,\n NarrowedTo, OpsFor, StateBag, StateFor,\n} from '@skewedaspect/sage-core';\nexport { NodeEntity } from './entities/nodeEntity.ts';\nexport type { NodeEntityState, QuaternionState, Vector3State } from './entities/nodeEntity.ts';\nexport { NodePool } from './entities/nodePool.ts';\nexport { NodeSimulation } from './entities/nodeSimulation.ts';\nexport type { NodeSimulationOptions } from './entities/nodeSimulation.ts';\nexport type { MeshLoader, NodeSet, NodeSimulationContext } from './entities/nodeSimulationContext.ts';\nexport type { EntityPickFilter, EntityPickResult } from './entities/entityPicking.ts';\n\n// Export the authoring surface: entity definitions compiling onto the class table, and the standard mesh\n// source built from it (docs/design/authoring.md).\nexport { defineEntity } from './entities/defineEntity.ts';\nexport type { GameEntityDefinition, GameEntityDefinitionClass } from './entities/defineEntity.ts';\nexport { buildStandardMeshSource } from './entities/standardMeshSource.ts';\nexport type { MeshAssetSource } from './entities/standardMeshSource.ts';\nexport type { MeshConfig, MeshMaterialConfig } from './entities/meshConfig.ts';\n\n// Export the input system: the action registry, action contexts, the three binding kinds, device readers,\n// binding resolution and delivery, capture, and configuration import/export (docs/design/input.md,\n// docs/design/events.md#delivery). A game builds its own registry, context, and binding instances from\n// these -- the engine wires none of it together, and routes no device state to any binding on its own.\nexport { ActionRegistry } from './input/actionRegistry.ts';\nexport { ActionContextRegistry } from './input/actionContext.ts';\nexport type {\n Binding, BindingDefinition, BindingFire, BindingInput, BindingKind, DeviceID, DeviceType,\n} from './input/binding.ts';\nexport type { DeviceReader } from './input/deviceReader.ts';\nexport type { GamepadState, KeyboardState, MouseState } from './input/deviceState.ts';\nexport { KeyboardReader } from './input/deviceReaders/keyboard.ts';\nexport { MouseReader } from './input/deviceReaders/mouse.ts';\nexport type { MouseSourceType } from './input/deviceReaders/mouse.ts';\nexport { GamepadReader } from './input/deviceReaders/gamepad.ts';\nexport type { GamepadSourceType } from './input/deviceReaders/gamepad.ts';\nexport { TriggerBinding } from './input/bindings/trigger.ts';\nexport type { EdgeMode, TriggerOptions } from './input/bindings/trigger.ts';\nexport { ToggleBinding } from './input/bindings/toggle.ts';\nexport type { ToggleOptions } from './input/bindings/toggle.ts';\nexport { ValueBinding } from './input/bindings/value.ts';\nexport type { ValueOptions } from './input/bindings/value.ts';\nexport { resolveBinding } from './input/resolveBinding.ts';\nexport { deliverAction } from './input/deliverAction.ts';\nexport { InputCapture } from './input/inputCapture.ts';\nexport type { CandidateInput, CaptureOptions } from './input/inputCapture.ts';\nexport { exportConfiguration, importConfiguration } from './input/configuration.ts';\nexport type { Configuration, ContextDefinition } from './input/configuration.ts';\nexport type { Action, AnalogAction, DigitalAction } from '@skewedaspect/sage-core';\n\n// Export manager types\nexport type { FrameCallback } from './managers/game.ts';\nexport type { TransitionOptions } from './managers/level.ts';\nexport { AudioManager } from './managers/audio.ts';\nexport type { ChannelInfo } from './managers/audio.ts';\nexport { ColliderDebugManager } from './managers/colliderDebug.ts';\nexport type { ColliderDebugConfig } from './managers/colliderDebug.ts';\nexport { OutlineManager } from './managers/outline.ts';\nexport { SimulationSaveManager } from './managers/simulationSave.ts';\nexport type { BeforeSaveHook, LevelLoader, LoadResult, SaveEnvelope } from './managers/simulationSave.ts';\n\n// Export debug tools\nexport { DebugConsole } from './classes/debugConsole.ts';\n\n// Export utilities\nexport { generateId } from './utils/id.ts';\nexport { StateMachine } from './utils/stateMachine.ts';\nexport {\n getCanonicalTransform,\n toQuatObject, toQuaternion, toVec3Object, toVector3,\n} from './utils/vectors.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n"],"x_google_ignoreList":[54],"mappings":"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"}
1
+ {"version":3,"file":"sage.es.js","names":["r","n"],"sources":["../src/classes/debugConsole.ts","../src/utils/version.ts","../src/classes/gameEngine.ts","../src/classes/loggers/consoleBackend.ts","../src/classes/loggers/nullBackend.ts","../src/utils/logger.ts","../src/classes/eventBus.ts","../../sage-core/dist/sage-core.js","../src/entities/nodeEntity.ts","../src/entities/entityPicking.ts","../src/entities/nodeSimulation.ts","../src/entities/nodePool.ts","../src/engines/audio.ts","../src/engines/scene.ts","../src/managers/asset.ts","../src/utils/capabilities.ts","../src/managers/game.ts","../src/input/actionContext.ts","../src/input/actionRegistry.ts","../src/input/deliverAction.ts","../src/managers/input.ts","../src/handlers/postProcessingPipeline.ts","../src/handlers/postProcessing.ts","../src/utils/vectors.ts","../src/classes/level.ts","../src/managers/outline.ts","../src/classes/gameLevel.ts","../src/managers/level.ts","../src/managers/audio.ts","../src/managers/simulationSave.ts","../src/managers/colliderDebug.ts","../src/utils/graphics.ts","../src/utils/physics.ts","../src/utils/timer.ts","../src/debug/builtins.ts","../src/interfaces/logger.ts","../src/utils/bounds.ts","../src/handlers/collider.ts","../src/handlers/lod.ts","../src/handlers/occluder.ts","../src/utils/metadata.ts","../src/handlers/sound.ts","../src/handlers/trigger.ts","../src/handlers/visible.ts","../src/handlers/index.ts","../src/entities/defineEntity.ts","../src/entities/standardMeshSource.ts","../src/behaviors/sound.ts","../src/input/deviceReaders/keyboard.ts","../src/input/deviceReaders/mouse.ts","../src/input/deviceReaders/gamepad.ts","../src/input/bindings/trigger.ts","../src/input/bindings/toggle.ts","../src/input/bindings/value.ts","../src/input/bindings/delta.ts","../src/input/resolveBinding.ts","../src/input/inputCapture.ts","../src/input/configuration.ts","../../../node_modules/hexoid/dist/index.mjs","../src/utils/id.ts","../src/utils/stateMachine.ts","../src/sage.ts"],"sourcesContent":["//----------------------------------------------------------------------------------------------------------------------\n// Debug Console\n//\n// Manages a namespace object on `window` that exposes game engine internals to the browser dev console.\n// Supports direct values, callable commands, and dynamic getters via Object.defineProperty.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Disposable } from '../interfaces/lifecycle.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface DynamicGetter\n{\n get : () => unknown;\n}\n\ninterface ExposedEntry\n{\n getter ?: () => unknown;\n value ?: unknown;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class DebugConsole implements Disposable\n{\n private _namespaceName : string;\n private _entries = new Map<string, ExposedEntry>();\n private _disposed = false;\n\n constructor(namespaceName = 'sage')\n {\n this._namespaceName = namespaceName;\n this._installNamespace();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n expose(name : string, value : unknown | DynamicGetter) : void\n {\n if(this._disposed)\n {\n return;\n }\n\n if(this._isDynamicGetter(value))\n {\n this._entries.set(name, { getter: value.get });\n this._defineGetter(name, value.get);\n }\n else\n {\n this._entries.set(name, { value });\n this._defineValue(name, value);\n }\n }\n\n setNamespace(name : string) : void\n {\n if(this._disposed || name === this._namespaceName)\n {\n return;\n }\n\n // Checked before the old namespace comes down, so a collision leaves the console where it was\n this._assertNamespaceFree(name);\n\n this._removeNamespace();\n this._namespaceName = name;\n this._installNamespace();\n\n for(const [ entryName, entry ] of this._entries)\n {\n if(entry.getter)\n {\n this._defineGetter(entryName, entry.getter);\n }\n else\n {\n this._defineValue(entryName, entry.value);\n }\n }\n }\n\n get namespaceName() : string\n {\n return this._namespaceName;\n }\n\n dispose() : void\n {\n if(this._disposed)\n {\n return;\n }\n\n this._removeNamespace();\n this._entries.clear();\n this._disposed = true;\n }\n\n async $teardown() : Promise<void>\n {\n this.dispose();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private\n //------------------------------------------------------------------------------------------------------------------\n\n private _isDynamicGetter(value : unknown) : value is DynamicGetter\n {\n return typeof value === 'object'\n && value !== null\n && 'get' in value\n && typeof (value as DynamicGetter).get === 'function';\n }\n\n private _getNamespace() : Record<string, unknown> | undefined\n {\n return Reflect.get(window, this._namespaceName) as Record<string, unknown> | undefined;\n }\n\n private _assertNamespaceFree(name : string) : void\n {\n if(typeof window === 'undefined')\n {\n return;\n }\n\n if(Reflect.has(window, name))\n {\n throw new Error(\n `window.${ name } is already occupied, and the debug console will not overwrite it. `\n + 'Pick a different namespace with the debug.namespace option.'\n );\n }\n }\n\n private _installNamespace() : void\n {\n if(typeof window === 'undefined')\n {\n return;\n }\n\n this._assertNamespaceFree(this._namespaceName);\n\n Reflect.set(window, this._namespaceName, {});\n }\n\n private _removeNamespace() : void\n {\n if(typeof window === 'undefined')\n {\n return;\n }\n\n Reflect.deleteProperty(window, this._namespaceName);\n }\n\n private _defineGetter(name : string, getter : () => unknown) : void\n {\n const ns = this._getNamespace();\n if(!ns)\n {\n return;\n }\n\n Object.defineProperty(ns, name, {\n get: getter,\n enumerable: true,\n configurable: true,\n });\n }\n\n private _defineValue(name : string, value : unknown) : void\n {\n const ns = this._getNamespace();\n if(!ns)\n {\n return;\n }\n\n Object.defineProperty(ns, name, {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Version utility for SAGE\n//----------------------------------------------------------------------------------------------------------------------\n\ndeclare const __APP_VERSION__ : string;\n\nexport const VERSION = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0-dev';\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Game Engine\n//\n// WARNING: This module must be imported as `import type` everywhere except sage.ts. A regular import\n// would create a circular runtime dependency (managers import GameEngine, GameEngine imports managers).\n// The `import type` is erased at compile time, keeping the runtime graph acyclic.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractEngine, HavokPlugin } from '@babylonjs/core';\n\n// Interfaces\nimport type { Scene } from '@babylonjs/core';\nimport type { EntityMeshLoader, GameCanvas } from '../interfaces/game.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Entities\nimport type { NodeSimulation } from '../entities/nodeSimulation.ts';\n\n// Engines\nimport { SceneEngine } from '../engines/scene.ts';\nimport type { AudioEngine } from '../engines/audio.ts';\n\n// Managers\nimport { AssetManager } from '../managers/asset.ts';\nimport type { ColliderDebugManager } from '../managers/colliderDebug.ts';\nimport type { DebugConsole } from './debugConsole.ts';\nimport { GameManager } from '../managers/game.ts';\nimport type { InputManager } from '../managers/input.ts';\nimport { LevelManager } from '../managers/level.ts';\nimport type { SimulationSaveManager } from '../managers/simulationSave.ts';\nimport type { AudioManager } from '../managers/audio.ts';\n\n// Utils\nimport { GameEventBus, type GameEventCallback, type Unsubscribe } from './eventBus.ts';\nimport type { LibraryEventPayloadMap } from '../events/payloads.ts';\nimport type { WildcardPattern } from '../events/types.ts';\nimport { LoggingUtility } from '../utils/logger.ts';\nimport { GameTimer } from '../utils/timer.ts';\nimport { VERSION } from '../utils/version.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type definition for game lifecycle hook functions\n */\n\nexport type GameHook = (gameEngine : GameEngine) => Promise<void>;\n\n/** Builds a NodeSimulation bound to a scene and a mesh source -- a fresh one is built for every level that loads. */\nexport type SimulationFactory = (scene : Scene, meshSource ?: EntityMeshLoader) => NodeSimulation;\n\n/**\n * Interface representing the engines used in the game.\n * All engines must implement Disposable for proper cleanup.\n */\ninterface Engines extends Record<string, Disposable | undefined>\n{\n sceneEngine : SceneEngine;\n audioEngine ?: AudioEngine;\n}\n\n/**\n * Interface representing the managers used in the game.\n * All managers must implement Disposable for proper cleanup.\n */\ninterface Managers extends Record<string, Disposable | undefined>\n{\n assetManager : AssetManager;\n gameManager : GameManager;\n inputManager : InputManager;\n levelManager : LevelManager;\n saveManager : SimulationSaveManager;\n audioManager ?: AudioManager;\n}\n\n/**\n * Interface representing the debug tools available on the engine.\n */\ninterface DebugTools\n{\n colliders : ColliderDebugManager;\n console : DebugConsole | null;\n expose(name : string, value : unknown) : void;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Central hub that owns the render loop, physics, event bus, and all managers.\n * Created via `createGameEngine()` in sage.ts.\n */\nexport class GameEngine\n{\n public canvas : GameCanvas;\n public renderEngine : AbstractEngine;\n public physics : HavokPlugin;\n public managers : Managers;\n public engines : Engines;\n public eventBus : GameEventBus;\n public logger : LoggingUtility;\n // Null until something gives the engine a scene to build one against: rebuildSimulation, called by a level\n // once its own scene exists, or by the game directly for a game that never loads one.\n public simulation : NodeSimulation | null = null;\n public timer : GameTimer;\n public debug : DebugTools;\n public largeWorldRendering : boolean;\n public started = false;\n\n private _stopped = false;\n\n private _log : LoggerInterface;\n private _buildSimulation : SimulationFactory;\n\n // Lifecycle hooks\n private _beforeStartHook : GameHook | null = null;\n private _onStartHook : GameHook | null = null;\n private _onTeardownHook : GameHook | null = null;\n\n /**\n * Creates an instance of GameEngine.\n * @param canvas\n * @param renderEngine\n * @param physics\n * @param eventBus\n * @param logger\n * @param timer\n * @param buildSimulation - Builds a fresh simulation for a scene, once rebuildSimulation is called with one.\n * @param engines\n * @param managers\n */\n constructor(\n canvas : GameCanvas,\n renderEngine : AbstractEngine,\n physics : HavokPlugin,\n eventBus : GameEventBus,\n logger : LoggingUtility,\n timer : GameTimer,\n buildSimulation : SimulationFactory,\n engines : Engines,\n managers : Managers,\n largeWorldRendering = false,\n debug ?: DebugTools\n )\n {\n this.canvas = canvas;\n this.renderEngine = renderEngine;\n this.physics = physics;\n this.eventBus = eventBus;\n this.logger = logger;\n this.timer = timer;\n this._buildSimulation = buildSimulation;\n this.engines = engines;\n this.managers = managers;\n this.debug = debug ?? {\n colliders: undefined as unknown as ColliderDebugManager,\n console: null,\n expose: () => { /* no-op */ },\n };\n this.largeWorldRendering = largeWorldRendering;\n\n this._log = logger.getLogger('GameEngine');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Builds a fresh NodeSimulation bound to the given scene and mesh source, and swaps it in. A level calls\n * this once its own scene is ready, so every NodeEntity it spawns gets a node in that scene and loads its\n * mesh through that source. Left unset, a spawned entity gets a node with no mesh.\n */\n rebuildSimulation(scene : Scene, meshSource ?: EntityMeshLoader) : NodeSimulation\n {\n this.simulation = this._buildSimulation(scene, meshSource);\n\n return this.simulation;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a function to be called before the game engine starts\n * @param hook\n * @throws Error if a hook is already registered\n */\n onBeforeStart(hook : GameHook) : void\n {\n if(this._beforeStartHook !== null)\n {\n throw new Error('A beforeStart hook is already registered. Only one hook is allowed per lifecycle event.');\n }\n this._beforeStartHook = hook;\n }\n\n /**\n * Register a function to be called after the game engine starts\n * @param hook\n * @throws Error if a hook is already registered\n */\n onStart(hook : GameHook) : void\n {\n if(this._onStartHook !== null)\n {\n throw new Error('An onStart hook is already registered. Only one hook is allowed per lifecycle event.');\n }\n this._onStartHook = hook;\n }\n\n /**\n * Register a function to be called when the game engine stops\n * @param hook\n * @throws Error if a hook is already registered\n */\n onTeardown(hook : GameHook) : void\n {\n if(this._onTeardownHook !== null)\n {\n throw new Error('An onTeardown hook is already registered. Only one hook is allowed per lifecycle event.');\n }\n this._onTeardownHook = hook;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Event Subscriptions\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Subscribe to an event on the event bus.\n *\n * This is a convenience method that forwards to `eventBus.subscribe()`.\n *\n * @param eventType - Exact event type string, wildcard pattern (e.g. 'level:*'), or RegExp\n * @param callback\n * @returns A function that removes this subscription when called\n *\n * @example\n * // Subscribe to a specific event\n * const unsub = engine.subscribe('level:complete', (event) => {\n * console.log('Level complete:', event.payload.levelName);\n * });\n *\n * // Subscribe to every level event\n * engine.subscribe('level:*', (event) => {\n * console.log('Level event:', event.type);\n * });\n */\n subscribe<T extends keyof LibraryEventPayloadMap & string>(\n eventType : T | WildcardPattern | RegExp,\n callback : GameEventCallback<T, LibraryEventPayloadMap>\n ) : Unsubscribe\n {\n return this.eventBus.subscribe(eventType, callback);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Start the engine: runs beforeStart hook, starts the game manager, then runs onStart hook.\n */\n async start() : Promise<void>\n {\n if(!this.started)\n {\n this._log.info(`Starting SkewedAspect Game Engine (Version: ${ VERSION })...`);\n\n // Execute beforeStart hook if registered\n if(this._beforeStartHook)\n {\n this._log.debug('Executing beforeStart hook...');\n await this._beforeStartHook(this);\n }\n\n // Start the game manager\n await this.managers.gameManager.start();\n\n this._log.info('Game engine started successfully');\n\n // Execute onStart hook if registered\n if(this._onStartHook)\n {\n this._log.debug('Executing onStart hook...');\n await this._onStartHook(this);\n }\n\n // Mark game as started\n this.started = true;\n }\n else\n {\n this._log.warn('Game engine is already started. Skipping start.');\n }\n }\n\n /**\n * Stop the engine: runs the teardown hook, then tears down all managers and engines in order.\n *\n * Managers claim what they need when they are built, not when the engine starts -- the input manager's\n * window listeners, the debug console's namespace -- so an engine that never started still has\n * everything to release. Tearing down twice is the no-op.\n */\n async stop() : Promise<void>\n {\n if(!this._stopped)\n {\n this._stopped = true;\n\n this._log.info(`Stopping SkewedAspect Game Engine (Version: ${ VERSION })...`);\n\n let teardownError : Error | null = null;\n\n // Execute onTeardown hook if registered\n if(this._onTeardownHook)\n {\n try\n {\n this._log.debug('Executing onTeardown hook...');\n await this._onTeardownHook(this);\n }\n catch (err)\n {\n this._log.error(`Error in onTeardown hook: ${ err }`);\n teardownError = teardownError || (err as Error);\n }\n }\n\n // Dispose debug console before tearing down managers\n if(this.debug.console)\n {\n this.debug.console.dispose();\n }\n\n // Teardown all managers\n for(const managerKey of Object.keys(this.managers) as (keyof Managers)[])\n {\n const manager = this.managers[managerKey];\n if(manager)\n {\n try\n {\n this._log.debug(`Tearing down manager: ${ managerKey }`);\n // eslint-disable-next-line no-await-in-loop\n await manager.$teardown();\n }\n catch (err)\n {\n this._log.error(`Error tearing down manager ${ managerKey }: ${ err }`);\n teardownError = teardownError || (err as Error);\n }\n }\n }\n\n // Teardown all engines\n for(const engineKey of Object.keys(this.engines) as (keyof Engines)[])\n {\n const engine = this.engines[engineKey];\n if(engine)\n {\n try\n {\n this._log.debug(`Tearing down engine: ${ engineKey }`);\n // eslint-disable-next-line no-await-in-loop\n await engine.$teardown();\n }\n catch (err)\n {\n this._log.error(`Error tearing down engine ${ engineKey }: ${ err }`);\n teardownError = teardownError || (err as Error);\n }\n }\n }\n\n this.started = false;\n this._log.info('Game engine stopped successfully');\n\n // If we encountered any errors during teardown, throw the first one\n if(teardownError)\n {\n throw teardownError;\n }\n }\n else\n {\n this._log.warn('Game engine is already stopped. Skipping stop.');\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Console Logger Backend\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LogLevel, LoggingBackend } from '../../interfaces/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ntype ColorLevel = LogLevel | 'timer';\n\n/**\n * A logging backend that logs to the console.\n */\nexport class ConsoleBackend implements LoggingBackend\n{\n private timers : Map<string, number>;\n\n constructor()\n {\n this.timers = new Map();\n }\n\n /**\n * Get the CSS style for a specific log level\n */\n private getStyleForLevel(level : ColorLevel) : string\n {\n // CSS styles for browser console\n const styles : Record<ColorLevel, string> = {\n trace: 'color: #999999', // Dim gray\n debug: 'color: #00AAAA', // Cyan\n info: 'color: #00AA00', // Green\n warn: 'color: #AAAA00', // Yellow\n error: 'color: #AA0000', // Red\n timer: 'color: #AA00AA', // Magenta\n none: 'color: inherit', // Default\n };\n\n return styles[level] || styles.none;\n }\n\n /**\n * Format a log message with category, timestamp and log level\n */\n private formatMessage(category : string, level : ColorLevel) : [string, string, string, string]\n {\n // Format time as HH:MM:SS AM/PM\n const now = new Date();\n const hours = now.getHours() % 12 || 12; // Convert 0 to 12 for 12 AM\n const minutes = now.getMinutes().toString()\n .padStart(2, '0');\n const seconds = now.getSeconds().toString()\n .padStart(2, '0');\n const ampm = now.getHours() >= 12 ? 'PM' : 'AM';\n const timestamp = `[${ hours }:${ minutes }:${ seconds } ${ ampm }]`;\n\n const upperLevel = level.toUpperCase();\n\n // Return format strings and styling that will be used with console methods\n return [\n `${ timestamp } %c${ upperLevel }%c (${ category }): `, // Format string with placeholders\n this.getStyleForLevel(level), // Level style\n '', // Default style\n '', // Reset style\n ];\n }\n\n trace(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'trace');\n console.debug(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n debug(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'debug');\n console.debug(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n info(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'info');\n console.info(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n warn(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'warn');\n console.warn(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n error(category : string, message : string, ...args : unknown[]) : void\n {\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'error');\n console.error(format, defaultStyle, levelStyle, resetStyle, message, ...args);\n }\n\n /**\n * Start a timer with the specified label.\n */\n time(category : string, label : string) : void\n {\n const timerKey = `${ category }:${ label }`;\n this.timers.set(timerKey, performance.now());\n }\n\n /**\n * End a timer and log the elapsed time.\n */\n timeEnd(category : string, label : string) : void\n {\n const timerKey = `${ category }:${ label }`;\n const startTime = this.timers.get(timerKey);\n\n if(startTime !== undefined)\n {\n const duration = performance.now() - startTime;\n this.timers.delete(timerKey);\n\n const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'timer');\n console.info(\n `${ format } Timer '${ label }' completed in ${ duration.toFixed(2) }ms`,\n defaultStyle,\n levelStyle,\n resetStyle\n );\n }\n else\n {\n console.warn(`[${ category }]: Timer '${ label }' does not exist`);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Null Logger Backend\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LoggingBackend } from '../../interfaces/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A logging backend that does nothing (discards all logs).\n * Useful for production environments or when you want to completely disable logging.\n */\nexport class NullBackend implements LoggingBackend\n{\n trace(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n debug(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n info(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n warn(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n error(_category : string, _message : string, ..._args : unknown[]) : void\n {\n // Do nothing\n }\n\n time(_category : string, _label : string) : void\n {\n // Do nothing\n }\n\n timeEnd(_category : string, _label : string) : void\n {\n // Do nothing\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Logger Utility\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type {\n LogLevel,\n LoggerInterface,\n LoggingBackend,\n} from '../interfaces/logger.ts';\n\nimport { ConsoleBackend } from '../classes/loggers/consoleBackend.ts';\nimport { NullBackend } from '../classes/loggers/nullBackend.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Implementation of Logger interface.\n * Each instance is bound to a specific category and backend.\n */\nexport class SAGELogger implements LoggerInterface\n{\n private category : string;\n private backend : LoggingBackend;\n private minLevel : LogLevel;\n\n constructor(category : string, minLevel : LogLevel = 'none', backend : LoggingBackend = new NullBackend())\n {\n this.category = category;\n this.backend = backend;\n this.minLevel = minLevel;\n }\n\n /**\n * Update the logger's backend and minimum level\n */\n public updateSettings(backend : LoggingBackend, minLevel : LogLevel) : void\n {\n this.backend = backend;\n this.minLevel = minLevel;\n }\n\n trace(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('trace'))\n {\n this.backend.trace(this.category, message, ...args);\n }\n }\n\n debug(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('debug'))\n {\n this.backend.debug(this.category, message, ...args);\n }\n }\n\n info(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('info'))\n {\n this.backend.info(this.category, message, ...args);\n }\n }\n\n warn(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('warn'))\n {\n this.backend.warn(this.category, message, ...args);\n }\n }\n\n error(message : string, ...args : unknown[]) : void\n {\n if(this.shouldLog('error'))\n {\n this.backend.error(this.category, message, ...args);\n }\n }\n\n time(label : string) : void\n {\n if(this.minLevel !== 'none')\n {\n this.backend.time(this.category, label);\n }\n }\n\n timeEnd(label : string) : void\n {\n if(this.minLevel !== 'none')\n {\n this.backend.timeEnd(this.category, label);\n }\n }\n\n /**\n * Determine if a message at the given level should be logged based on the current minimum level.\n */\n private shouldLog(level : LogLevel) : boolean\n {\n if(this.minLevel === 'none')\n {\n return false;\n }\n\n switch (this.minLevel)\n {\n case 'trace':\n return true;\n case 'debug':\n return level !== 'trace';\n case 'info':\n return level === 'info' || level === 'warn' || level === 'error';\n case 'warn':\n return level === 'warn' || level === 'error';\n case 'error':\n return level === 'error';\n default:\n return false;\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A utility for creating loggers and managing logging configuration.\n */\nexport class LoggingUtility\n{\n private backend : LoggingBackend;\n private level : LogLevel;\n private loggers : Map<string, LoggerInterface>;\n\n /**\n * Create a new LoggingUtility.\n *\n * @param level - The minimum logging level (defaults to INFO)\n * @param backend - The logging backend to use (defaults to ConsoleBackend)\n */\n constructor(level : LogLevel = 'debug', backend : LoggingBackend = new ConsoleBackend())\n {\n this.backend = backend;\n this.level = level;\n this.loggers = new Map();\n }\n\n /**\n * Set the logging backend. Updates all existing loggers to use the new backend.\n *\n * @param backend\n */\n public setBackend(backend : LoggingBackend) : void\n {\n this.backend = backend;\n\n // Update all existing loggers to use the new backend\n this.loggers.forEach((logger) =>\n {\n if(logger instanceof SAGELogger)\n {\n logger.updateSettings(this.backend, this.level);\n }\n });\n }\n\n /**\n * Set the minimum logging level. Updates all existing loggers.\n *\n * @param level\n */\n public setLevel(level : LogLevel) : void\n {\n this.level = level;\n\n // Update all existing loggers to use the new level\n this.loggers.forEach((logger) =>\n {\n if(logger instanceof SAGELogger)\n {\n logger.updateSettings(this.backend, this.level);\n }\n });\n }\n\n /**\n * Get the current minimum logging level.\n */\n public getLevel() : LogLevel\n {\n return this.level;\n }\n\n /**\n * Get or create a logger for the specified category. Returns an existing logger if one was already created.\n *\n * @param category - Typically the class or module name\n */\n public getLogger(category : string) : LoggerInterface\n {\n let logger = this.loggers.get(category);\n if(!logger)\n {\n logger = new SAGELogger(category, this.level, this.backend);\n this.loggers.set(category, logger);\n }\n\n return logger;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport { ConsoleBackend } from '../classes/loggers/consoleBackend.ts';\nexport { NullBackend } from '../classes/loggers/nullBackend.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Event Bus\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LibraryEventPayloadMap } from '../events/payloads.ts';\nimport type { WildcardPattern } from '../events/types.ts';\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A typed game event with compile-time payload checking.\n *\n * @template T - The event type string (e.g., 'input:changed')\n * @template TPayloadMap - The payload map to resolve types from\n */\nexport interface GameEvent<\n T extends string = string,\n TPayloadMap extends Record<string, unknown> = Record<string, unknown>,\n>\n{\n /** The string identifier/category of the event (e.g. \"input:keydown\"). */\n type : T;\n /** The optional ID of whoever sent this event. */\n senderID ?: string;\n /** The optional ID of who should receive this event. */\n targetID ?: string;\n /** The payload, typed based on the payload map. */\n payload : T extends keyof TPayloadMap ? TPayloadMap[T] : unknown;\n}\n\n/**\n * A callback function that receives a {@link GameEvent} with a given payload type.\n *\n * @template T - The event type string\n * @template TPayloadMap - The payload map to resolve types from\n */\nexport type GameEventCallback<\n T extends string = string,\n TPayloadMap extends Record<string, unknown> = Record<string, unknown>,\n> = (event : GameEvent<T, TPayloadMap>) => void | Promise<void>;\n\n/**\n * Represents a subscription record internally within the EventBus.\n *\n * We store the callback as if it handles `GameEvent<any>`\n * because we can't unify multiple different payload types in the same structure.\n */\ninterface Subscription\n{\n /** Precompiled RegExp if this subscription is pattern-based. */\n pattern ?: RegExp;\n /** The callback, but stored to accept any payload type (due to the union nature). */\n callback : (event : GameEvent<any, any>) => void | Promise<void>;\n}\n\n/**\n * An unsubscribe function, returned by any subscribe method.\n */\nexport type Unsubscribe = () => void;\n\n/**\n * Callback for wildcard/pattern subscriptions.\n * Payload is unknown since multiple event types may match.\n */\nexport type WildcardEventCallback\n = (event : GameEvent<string, Record<string, unknown>>) => void | Promise<void>;\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A high-performance, type-safe event bus that supports exact or pattern-based subscriptions.\n *\n * The generic parameter `TPayloadMap` maps event type strings to their payload types,\n * enabling compile-time checking of event/payload combinations.\n *\n * @template TPayloadMap - Map of event type strings to their payload types.\n * Defaults to LibraryEventPayloadMap for internal library usage.\n *\n * @example\n * // Using library events (default)\n * const bus = new GameEventBus();\n * bus.publish({ type: 'level:progress', payload: { levelName: 'Level1', progress: 50 } });\n *\n * @example\n * // User-defined events with full type safety\n * interface MyEvents {\n * 'game:score-updated': { score: number; player: string };\n * 'game:paused': { reason: string };\n * [key: string]: unknown; // Allow other events\n * }\n *\n * const bus = new GameEventBus<MyEvents>();\n * bus.publish({ type: 'game:score-updated', payload: { score: 100, player: 'alice' } });\n *\n * @example\n * // Merging user events with library events\n * const bus = new GameEventBus<MyEvents & LibraryEventPayloadMap>();\n */\nexport class GameEventBus<TPayloadMap extends Record<string, unknown> = LibraryEventPayloadMap> implements Disposable\n{\n /**\n * For exact event-type matches:\n * - Key = event type string (e.g. \"input:keydown\")\n * - Value = set of subscriptions for that exact type\n */\n private directMap = new Map<string, Set<Subscription>>();\n\n /**\n * For pattern-based (wildcard or RegExp) subscriptions.\n */\n private patternSubs = new Set<Subscription>();\n\n /**\n * Logger instance\n */\n private _log : LoggerInterface;\n\n /**\n * Creates a new GameEventBus instance\n *\n * @param logger\n */\n constructor(logger ?: LoggingUtility)\n {\n this._log = logger?.getLogger('EventBus') || new SAGELogger('EventBus');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Subscribe\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Subscribe with automatic detection:\n * - If `eventTypeOrPattern` is a `RegExp` or a string containing `*`, treat it as a pattern subscription.\n * - Otherwise, treat it as an exact subscription.\n *\n * @template T - The event type string (must be a key in TPayloadMap for type safety)\n * @param eventTypeOrPattern - Exact event type string, wildcard pattern, or RegExp\n * @param callback\n * @returns A function that removes this subscription when called\n */\n public subscribe<T extends keyof TPayloadMap & string>(\n eventTypeOrPattern : T | WildcardPattern | RegExp,\n callback : GameEventCallback<T, TPayloadMap>\n ) : Unsubscribe\n {\n this._log.trace(`Subscribe request for: ${ eventTypeOrPattern.toString() }`);\n\n // If it's explicitly a RegExp or includes '*', treat as pattern:\n if(\n eventTypeOrPattern instanceof RegExp\n || (typeof eventTypeOrPattern === 'string' && eventTypeOrPattern.includes('*'))\n )\n {\n return this.subscribePattern(\n eventTypeOrPattern as WildcardPattern | RegExp,\n callback as WildcardEventCallback\n );\n }\n else\n {\n return this.subscribeExact(eventTypeOrPattern as T, callback);\n }\n }\n\n /**\n * Subscribes to an exact event type (e.g. \"input:keydown\").\n *\n * @template T - The event type string (must be a key in TPayloadMap for type safety)\n * @param eventType\n * @param callback\n * @returns A function that removes this subscription when called\n */\n public subscribeExact<T extends keyof TPayloadMap & string>(\n eventType : T,\n callback : GameEventCallback<T, TPayloadMap>\n ) : Unsubscribe\n {\n this._log.debug(`Adding exact subscription for: ${ eventType }`);\n\n let subs = this.directMap.get(eventType);\n if(!subs)\n {\n subs = new Set();\n this.directMap.set(eventType, subs);\n }\n\n // Store as a Subscription that accepts `GameEvent<any>`\n const subscription : Subscription = {\n callback: (event) => callback(event as GameEvent<T, TPayloadMap>),\n };\n subs.add(subscription);\n\n return () =>\n {\n this._log.debug(`Removing exact subscription for: ${ eventType }`);\n subs.delete(subscription);\n if(subs.size === 0)\n {\n this.directMap.delete(eventType);\n }\n };\n }\n\n /**\n * Subscribes with either:\n * - a wildcard string (contains `*`) that gets converted to a RegExp\n * - a RegExp directly\n *\n * Note: Payload typing is not available for wildcards since multiple event types may match.\n *\n * @param patternOrRegExp - Wildcard string (e.g. `\"input:*\"`) or a RegExp\n * @param callback\n * @returns A function that removes this subscription when called\n */\n public subscribePattern(\n patternOrRegExp : WildcardPattern | RegExp,\n callback : WildcardEventCallback\n ) : Unsubscribe\n {\n let regex : RegExp;\n\n if(typeof patternOrRegExp === 'string')\n {\n // We replace '*' with '.*' for wildcard matching\n const escaped = patternOrRegExp\n .replace(/[-/\\\\^$+?.()|[\\]{}]/g, '\\\\$&') // escape special chars\n .replace(/\\*/g, '.*'); // replace '*' with '.*'\n\n // e.g. \"input:*\" => \"input:.*\" => new RegExp(\"^input:.*$\")\n regex = new RegExp(`^${ escaped }$`);\n this._log.debug(\n `Adding pattern subscription for string: ${ patternOrRegExp }, regex: ${ regex.toString() }`\n );\n }\n else\n {\n // If already a RegExp, use as-is\n regex = patternOrRegExp;\n this._log.debug(`Adding pattern subscription for regex: ${ regex.toString() }`);\n }\n\n const subscription : Subscription = {\n pattern: regex,\n callback: (event) => callback(event as GameEvent<string, Record<string, unknown>>),\n };\n this.patternSubs.add(subscription);\n\n return () =>\n {\n this._log.debug(`Removing pattern subscription: ${ regex.toString() }`);\n this.patternSubs.delete(subscription);\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Publish\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Publishes an event, invoking all matching callbacks.\n *\n * **Async Execution Model:**\n * Callbacks are invoked asynchronously via the microtask queue using `Promise.resolve().then()`.\n * This means `publish()` returns immediately, before any callbacks execute. This design:\n * - Prevents stack overflow from recursive event publishing\n * - Allows multiple events to be queued before processing\n * - Is ideal for fire-and-forget events (notifications, state changes)\n *\n * **Not suitable for request-response patterns.** If you need to wait for a callback's\n * result, consider using a different communication mechanism.\n *\n * @template T - The event type string (must be a key in TPayloadMap for type safety)\n * @param event\n */\n public publish<T extends keyof TPayloadMap & string>(\n event : GameEvent<T, TPayloadMap>\n ) : void\n {\n this._log.trace(`Publishing event: ${ event.type }`, event);\n\n let callbackCount = 0;\n\n // 1) Exact match\n const directSubs = this.directMap.get(event.type);\n if(directSubs)\n {\n callbackCount += directSubs.size;\n for(const sub of directSubs)\n {\n Promise.resolve()\n .then(() => sub.callback(event))\n .catch((err) =>\n {\n this._log.error(`Error in event handler for '${ event.type }':`, err);\n });\n }\n }\n\n // 2) Pattern subscriptions\n for(const sub of this.patternSubs)\n {\n if(sub.pattern && sub.pattern.test(event.type))\n {\n callbackCount++;\n Promise.resolve()\n .then(() => sub.callback(event))\n .catch((err) =>\n {\n this._log.error(`Error in pattern event handler for '${ event.type }':`, err);\n });\n }\n }\n\n if(callbackCount === 0)\n {\n this._log.debug(`No subscribers found for event: ${ event.type }`);\n }\n else\n {\n this._log.trace(`Event ${ event.type } dispatched to ${ callbackCount } subscribers`);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Tears down the event bus and clears all subscriptions.\n * After calling this method, no events will be delivered.\n */\n public async $teardown() : Promise<void>\n {\n this._log.debug('Tearing down EventBus');\n this.directMap.clear();\n this.patternSubs.clear();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//#region src/behavior.ts\nvar e = class {}, t = class extends e {\n\tdefaults = { displayName: \"\" };\n};\n//#endregion\n//#region src/stateMachineBehavior.ts\nfunction n(e, t) {\n\treturn t;\n}\nvar r = class extends e {\n\tstatic ops = [\"transitionTo\"];\n\tonUpdate(e) {\n\t\tlet t = this.state.currentState;\n\t\tthis.states[t].update?.call(this, e);\n\t}\n\ttransitionTo(e) {\n\t\tif (!(e in this.states)) throw Error(`StateMachineBehavior: no state named \"${e}\" is declared.`);\n\t\tif (e === this.state.currentState) return;\n\t\tlet t = this.state.currentState;\n\t\tthis.states[t].exit?.call(this, e), this.state.currentState = e, this.states[e].enter?.call(this, t);\n\t}\n}, i = 32, a = 64, o = 5;\n//#endregion\n//#region src/events.ts\nfunction s(e) {\n\tlet t = e.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\\\\\*/g, \".*\");\n\treturn RegExp(`^${t}$`);\n}\n//#endregion\n//#region src/eventBus.ts\nvar c = class e {\n\t#e = [];\n\t#t = [];\n\t#n = /* @__PURE__ */ new Map();\n\t#r = /* @__PURE__ */ new Map();\n\tregister(e, t) {\n\t\tif (this.#n.has(e.id)) throw Error(`A subscriber is already registered under the ID \"${e.id}\".`);\n\t\tthis.#n.set(e.id, e);\n\t\tlet n = [], r = [];\n\t\tfor (let i of t) {\n\t\t\tlet t = i;\n\t\t\tif (t.endsWith(\":*\")) {\n\t\t\t\tlet n = {\n\t\t\t\t\tpattern: s(t),\n\t\t\t\t\tsubscriber: e\n\t\t\t\t};\n\t\t\t\tr.push(n), this.#t.push(n);\n\t\t\t} else n.includes(t) || (n.push(t), this.#i(t, e));\n\t\t}\n\t\treturn this.#o(e, n, r);\n\t}\n\temit(e) {\n\t\tthis.#e.push(e);\n\t}\n\tdrain() {\n\t\tlet e = [], t = 0;\n\t\tfor (; this.#e.length > 0;) {\n\t\t\tif (t += 1, t > 64) {\n\t\t\t\tthis.#e = [], e.push(/* @__PURE__ */ Error(\"The drain exceeded 64 passes.\"));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlet n = this.#e;\n\t\t\tthis.#e = [];\n\t\t\tfor (let t of n) this.#s(t, e);\n\t\t}\n\t\tif (e.length > 0) throw AggregateError(e, \"The drain raised.\");\n\t}\n\t#i(e, t) {\n\t\tlet n = this.#r.get(e) ?? [];\n\t\tn.push(t), this.#r.set(e, n);\n\t}\n\t#a(e, t) {\n\t\tlet n = this.#r.get(e);\n\t\tif (n === void 0) return;\n\t\tlet r = n.indexOf(t);\n\t\tr >= 0 && n.splice(r, 1), n.length === 0 && this.#r.delete(e);\n\t}\n\t#o(e, t, n) {\n\t\tlet r = !0;\n\t\treturn () => {\n\t\t\tif (!r) return;\n\t\t\tr = !1, this.#n.get(e.id) === e && this.#n.delete(e.id);\n\t\t\tfor (let n of t) this.#a(n, e);\n\t\t\tlet i = [];\n\t\t\tfor (let e of this.#t) n.includes(e) || i.push(e);\n\t\t\tthis.#t = i;\n\t\t};\n\t}\n\t#s(t, n) {\n\t\tif (t.targetID !== void 0) {\n\t\t\tlet r = this.#n.get(t.targetID);\n\t\t\tr !== void 0 && e.#l(r, t, n);\n\t\t\treturn;\n\t\t}\n\t\tlet r = /* @__PURE__ */ new Set();\n\t\tfor (let i of [...this.#r.get(t.type) ?? []]) e.#c(i, t, r, n);\n\t\tfor (let i of [...this.#t]) i.pattern.test(t.type) && e.#c(i.subscriber, t, r, n);\n\t}\n\tstatic #c(t, n, r, i) {\n\t\tr.has(t) || (r.add(t), e.#l(t, n, i));\n\t}\n\tstatic #l(e, t, n) {\n\t\ttry {\n\t\t\te.receive(t);\n\t\t} catch (e) {\n\t\t\tn.push(e);\n\t\t}\n\t}\n};\n//#endregion\n//#region src/gameEntity.ts\nfunction l(e, t) {\n\tthrow TypeError(`Entity \"${e}\" state holds a value that is not finite JSON-compatible data at \"${t}\".`);\n}\nfunction u(e, t, n, r) {\n\tif (e === null || typeof e == \"string\" || typeof e == \"boolean\") return e;\n\tif (typeof e == \"number\") return Number.isFinite(e) || l(t, n), e;\n\tif (Array.isArray(e)) {\n\t\tr.has(e) && l(t, n), r.add(e);\n\t\tlet i = [];\n\t\tfor (let [a, o] of e.entries()) i.push(u(o, t, `${n}[${a}]`, r));\n\t\treturn r.delete(e), i;\n\t}\n\tif (typeof e == \"object\") {\n\t\tlet i = Object.getPrototypeOf(e);\n\t\t(i !== Object.prototype && i !== null || r.has(e)) && l(t, n), r.add(e);\n\t\tlet a = Object.create(null);\n\t\tfor (let [i, o] of Object.entries(e)) a[i] = u(o, t, n === \"\" ? i : `${n}.${i}`, r);\n\t\treturn r.delete(e), a;\n\t}\n\treturn l(t, n);\n}\nfunction d(e, t) {\n\treturn u(e, t, \"\", /* @__PURE__ */ new Set());\n}\nvar f = class e {\n\tid;\n\tname;\n\tcontext;\n\tstate;\n\tops;\n\teventSubscriptions;\n\tstatic behaviors = [];\n\t#e = [];\n\t#t = [];\n\t#n;\n\t#r = 0;\n\t#i;\n\tstatic create(t, n, r, i) {\n\t\treturn e.#a(this, t, n, r ?? {}, !0, i);\n\t}\n\tstatic restore(t, n, r, i) {\n\t\treturn e.#a(this, t, n, r, !1, i);\n\t}\n\tstatic #a(e, t, n, r, i, a) {\n\t\treturn new e(t, n, r, i, a);\n\t}\n\tconstructor(t, n, r, i, a) {\n\t\tthis.id = t, this.name = a ?? new.target.name, this.context = n;\n\t\tlet o = new.target.behaviors;\n\t\tfor (let e of o) this.#e.push(new e());\n\t\tthis.state = e.#u(this.#e, r, t);\n\t\tlet s = Object.create(null);\n\t\tthis.ops = s;\n\t\tlet c = [];\n\t\tfor (let [e, t] of o.entries()) {\n\t\t\tlet n = this.#e[e];\n\t\t\tn !== void 0 && (Object.assign(n, {\n\t\t\t\tstate: this.state,\n\t\t\t\tops: s,\n\t\t\t\tentity: this\n\t\t\t}), this.#o(n, t, s), this.#s(n, t, c));\n\t\t}\n\t\tthis.eventSubscriptions = c, this.#n = n.bus.register(this, c), i && this.#l();\n\t}\n\thasBehavior(e) {\n\t\tfor (let t of this.#e) if (t instanceof e) return !0;\n\t\treturn !1;\n\t}\n\tgetEntity(e) {\n\t\treturn this.context.getEntity(e);\n\t}\n\temit(e, t, n) {\n\t\tlet r = {\n\t\t\ttype: e,\n\t\t\tsenderID: this.id,\n\t\t\ttargetID: n,\n\t\t\tpayload: t\n\t\t};\n\t\tif (this.#i !== void 0) {\n\t\t\tthis.#i.push(r);\n\t\t\treturn;\n\t\t}\n\t\tthis.context.bus.emit(r);\n\t}\n\tupdate(e) {\n\t\tfor (let t of this.#e) t.onUpdate?.(e);\n\t}\n\treceive(t) {\n\t\tlet n = t;\n\t\tfor (let r of this.#t) if (e.#f(r, n.type) && r.instance.onEvent?.(t) === !0) return !0;\n\t\treturn !1;\n\t}\n\tdestroy() {\n\t\tthis.#n();\n\t}\n\tattachedBehaviors() {\n\t\treturn [...this.#e];\n\t}\n\t#o(e, t, n) {\n\t\tfor (let r of t.ops ?? []) {\n\t\t\tif (Object.hasOwn(n, r)) throw Error(`Two behaviors on entity \"${this.id}\" expose the operation \"${String(r)}\".`);\n\t\t\tlet i = e[r];\n\t\t\tif (typeof i != \"function\") throw Error(`Behavior \"${t.name}\" declares the operation \"${String(r)}\" with no method behind it.`);\n\t\t\tn[r] = this.#c(r, i, e);\n\t\t}\n\t}\n\t#s(e, t, n) {\n\t\tlet r = [], i = [];\n\t\tfor (let e of t.events ?? []) {\n\t\t\tlet t = e;\n\t\t\tt.endsWith(\":*\") ? i.push(s(t)) : r.push(t), n.includes(e) || n.push(e);\n\t\t}\n\t\t(r.length > 0 || i.length > 0) && this.#t.push({\n\t\t\tinstance: e,\n\t\t\texact: r,\n\t\t\tpatterns: i\n\t\t});\n\t}\n\t#c(e, t, n) {\n\t\treturn (...r) => {\n\t\t\tthis.#r += 1;\n\t\t\ttry {\n\t\t\t\tif (this.#r > 32) throw RangeError(`Operation \"${String(e)}\" on entity \"${this.id}\" ran deeper than the operation call depth limit of 32.`);\n\t\t\t\treturn t.apply(n, r);\n\t\t\t} finally {\n\t\t\t\t--this.#r;\n\t\t\t}\n\t\t};\n\t}\n\t#l() {\n\t\tthis.#i = [];\n\t\ttry {\n\t\t\tfor (let e of this.#e) e.onCreate?.();\n\t\t} catch (e) {\n\t\t\tthrow this.#i = void 0, this.#n(), e;\n\t\t}\n\t\tlet e = this.#i;\n\t\tthis.#i = void 0;\n\t\tfor (let t of e) this.context.bus.emit(t);\n\t}\n\tstatic #u(t, n, r) {\n\t\tlet i = Object.create(null);\n\t\ti.tags = [];\n\t\tfor (let e of t) Object.assign(i, d(e.defaults, r));\n\t\treturn Object.assign(i, d(n, r)), e.#d(i, r), i;\n\t}\n\tstatic #d(e, t) {\n\t\tlet n = e.tags;\n\t\tif (!Array.isArray(n)) throw TypeError(`Entity \"${t}\" tags must be a list of strings.`);\n\t\tlet r = [];\n\t\tfor (let e of n) {\n\t\t\tif (typeof e != \"string\") throw TypeError(`Entity \"${t}\" tags must be a list of strings.`);\n\t\t\tif (r.includes(e)) throw Error(`Entity \"${t}\" carries the tag \"${e}\" more than once.`);\n\t\t\tr.push(e);\n\t\t}\n\t}\n\tstatic #f(e, t) {\n\t\tif (e.exact.includes(t)) return !0;\n\t\tfor (let n of e.patterns) if (n.test(t)) return !0;\n\t\treturn !1;\n\t}\n}, p = class e {\n\t#e = new c();\n\t#t;\n\t#n;\n\t#r;\n\t#i = 0;\n\t#a = 0;\n\t#o = 0;\n\t#s = [];\n\tconstructor(e) {\n\t\tif (this.#t = e.fixedStepDuration, this.#n = e.entityClasses, this.#r = e.allocator, !Number.isFinite(this.#t) || this.#t <= 0) throw RangeError(\"A fixed step duration must be a finite number greater than zero.\");\n\t}\n\tget bus() {\n\t\treturn this.#e;\n\t}\n\tget tick() {\n\t\treturn this.#i;\n\t}\n\tget entities() {\n\t\treturn [...this.#s];\n\t}\n\tspawn(e, t, n) {\n\t\tlet r = this.#f(e), i = t;\n\t\tif (i === void 0 && (i = this.#d()), this.getEntity(i) !== void 0) throw Error(`The simulation already holds an entity under the ID \"${i}\".`);\n\t\tlet a = r.create(i, this, n ?? {}, e);\n\t\treturn this.#s.push(a), a;\n\t}\n\tdespawn(e) {\n\t\tlet t = this.#s.findIndex((t) => t.id === e);\n\t\tif (t < 0) return;\n\t\tlet n = this.#s[t];\n\t\tn !== void 0 && (n.destroy(), this.#s.splice(t, 1));\n\t}\n\tgetEntity(e) {\n\t\treturn this.#s.find((t) => t.id === e);\n\t}\n\tgetEntitiesByTag(t) {\n\t\tlet n = [];\n\t\tfor (let r of this.#s) e.#p(r).tags.includes(t) && n.push(r);\n\t\treturn n;\n\t}\n\trunStepsForElapsedTime(e) {\n\t\tif (!Number.isFinite(e) || e < 0) throw RangeError(\"Elapsed time must be a finite number of zero or more.\");\n\t\tthis.#o += e;\n\t\tlet t = 0;\n\t\tfor (; this.#l() && t < 5;) this.#u(), t += 1;\n\t\tt === 5 && this.#l() && (this.#o %= this.#t);\n\t}\n\tsnapshot() {\n\t\tlet e = [];\n\t\tfor (let t of this.#s) e.push({\n\t\t\tid: t.id,\n\t\t\tname: t.name,\n\t\t\tstate: d(t.state, t.id)\n\t\t});\n\t\treturn {\n\t\t\ttick: this.#i,\n\t\t\tidCounter: this.#a,\n\t\t\tentities: e\n\t\t};\n\t}\n\tfromSnapshot(e) {\n\t\tlet t = this.buildReplacementSimulation({\n\t\t\tfixedStepDuration: this.#t,\n\t\t\tentityClasses: this.#n,\n\t\t\tallocator: this.#r\n\t\t});\n\t\treturn t.#c(e), t;\n\t}\n\tbuildReplacementSimulation(t) {\n\t\treturn new e(t);\n\t}\n\t#c(e) {\n\t\tlet t = [];\n\t\ttry {\n\t\t\tfor (let n of e.entities) {\n\t\t\t\tlet e = this.#f(n.name).restore(n.id, this, n.state, n.name);\n\t\t\t\tt.push(e);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tfor (let e of t) e.destroy();\n\t\t\tthrow e;\n\t\t}\n\t\tthis.#s = t, this.#i = e.tick, this.#a = e.idCounter;\n\t}\n\t#l() {\n\t\treturn this.#o + 2 ** -52 >= this.#t;\n\t}\n\t#u() {\n\t\tfor (let e of [...this.#s]) e.update(this.#t);\n\t\ttry {\n\t\t\tthis.#e.drain();\n\t\t} finally {\n\t\t\tthis.#o -= this.#t, Math.abs(this.#o) <= 2 ** -52 && (this.#o = 0), this.#i += 1;\n\t\t}\n\t}\n\t#d() {\n\t\treturn this.#r === void 0 ? (this.#a += 1, `entity-${this.#a}`) : this.#r();\n\t}\n\t#f(e) {\n\t\tlet t = this.#n[e];\n\t\tif (t === void 0) throw Error(`The entity class table holds no class named \"${e}\".`);\n\t\treturn t;\n\t}\n\tstatic #p(e) {\n\t\treturn e.state;\n\t}\n};\n//#endregion\nexport { e as Behavior, c as EventBus, f as GameEntity, p as GameSimulationManager, t as NamedBehavior, r as StateMachineBehavior, n as defineStates, o as dilationLimit, a as drainPassLimit, i as operationCallDepthLimit };\n\n//# sourceMappingURL=sage-core.js.map","//----------------------------------------------------------------------------------------------------------------------\n// NodeEntity\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Quaternion, TransformNode, Vector3 } from '@babylonjs/core';\nimport { GameEntity } from '@skewedaspect/sage-core';\n\nimport type { AbstractMesh } from '@babylonjs/core';\nimport type { Action, GameEntityState, GameSimulationContext, StateBag } from '@skewedaspect/sage-core';\n\nimport type { AudioManager } from '../managers/audio.ts';\nimport type { ColliderDebugConfig } from '../managers/colliderDebug.ts';\nimport type { NodeSet, NodeSimulationContext } from './nodeSimulationContext.ts';\nimport type { MeshConfig } from './meshConfig.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface Vector3State\n{\n x : number;\n y : number;\n z : number;\n}\n\nexport interface QuaternionState\n{\n x : number;\n y : number;\n z : number;\n w : number;\n}\n\nexport interface NodeEntityState extends GameEntityState\n{\n parentID ?: string;\n nodeName ?: string;\n position : Vector3State;\n rotation : QuaternionState;\n debugCollider ?: ColliderDebugConfig;\n}\n\n// Declared here, not on sage-core's Behavior, because a GameEntity never has a scene node\n// (design/behaviors.md#hooks). A behavior opts in by implementing either method; NodeEntity recognizes it\n// structurally, the same way it recognizes onCreate/onUpdate/onEvent.\nexport interface NodeBehaviorHooks\n{\n onMeshLoaded ?() : void;\n onDestroy ?() : void;\n}\n\n// A NodeSet a load is still in flight for maps here to whoever wants to hear when it settles. A set with no\n// entry has already settled, mesh or not -- prewarm and an already-finished load both leave nothing pending\n// (design/entities.md#pooling). Keyed by object identity rather than a field on NodeSet itself, so a set an\n// older part of the codebase or a test builds by hand, with no notion of this, is still exactly \"settled.\"\nconst pendingMeshLoads = new WeakMap<NodeSet, () => void>();\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport abstract class NodeEntity extends GameEntity\n{\n static readonly poolable : boolean = false;\n static readonly poolSize ?: number;\n static readonly meshConfig ?: MeshConfig;\n static readonly actions ?: readonly Action[];\n\n // A poolable class's node and mesh outlive this entity: `destroy()` hands this exact object to the pool, so a\n // mesh that finishes loading after that -- from this entity's own load, still in flight -- attaches to\n // whichever entity is holding it by then, the same object either way (design/entities.md#pooling).\n readonly #set : NodeSet;\n\n get node() : TransformNode\n {\n return this.#set.node;\n }\n\n get mesh() : AbstractMesh | undefined\n {\n return this.#set.mesh;\n }\n\n set mesh(mesh : AbstractMesh | undefined)\n {\n this.#set.mesh = mesh;\n }\n\n get audio() : AudioManager | undefined\n {\n return (this.context as NodeSimulationContext).audio;\n }\n\n showCollider(config ?: ColliderDebugConfig, color ?: string) : void\n {\n const colliders = (this.context as NodeSimulationContext).colliders;\n const nodes = this.#physicsNodes();\n\n if(config === undefined || config === true)\n {\n for(const node of nodes)\n {\n colliders.showColliderForNode(node);\n }\n\n return;\n }\n\n if(config === false)\n {\n return;\n }\n\n if(typeof config === 'string')\n {\n if(config.startsWith('#'))\n {\n for(const node of nodes)\n {\n colliders.showColliderForNode(node, config);\n }\n\n return;\n }\n\n const target = nodes.find((node) => node.name === config);\n\n if(target)\n {\n colliders.showColliderForNode(target, color);\n }\n\n return;\n }\n\n for(const node of nodes)\n {\n const entry = config[node.name];\n\n if(entry)\n {\n colliders.showColliderForNode(node, entry);\n }\n }\n }\n\n hideCollider(name ?: string) : void\n {\n const colliders = (this.context as NodeSimulationContext).colliders;\n const nodes = this.#physicsNodes();\n\n if(name === undefined)\n {\n for(const node of nodes)\n {\n colliders.hideColliderForNode(node);\n }\n\n return;\n }\n\n const target = nodes.find((node) => node.name === name);\n\n if(target)\n {\n colliders.hideColliderForNode(target);\n }\n }\n\n // this.node itself counts, alongside its descendants (design/debugging.md#per-entity-collider-visualization).\n #physicsNodes() : TransformNode[]\n {\n const nodes : TransformNode[] = [];\n\n if(this.node.physicsBody)\n {\n nodes.push(this.node);\n }\n\n for(const child of this.node.getChildTransformNodes(false))\n {\n if(child.physicsBody)\n {\n nodes.push(child);\n }\n }\n\n return nodes;\n }\n\n protected constructor(\n id : string,\n context : GameSimulationContext,\n providedState : object,\n runCreateHooks : boolean,\n name ?: string\n )\n {\n super(id, context, providedState, runCreateHooks, name);\n\n const nodeContext = context as NodeSimulationContext;\n const entityClass = new.target as typeof NodeEntity;\n\n const spatial = this.state as NodeEntityState;\n\n if(spatial.position === undefined)\n {\n spatial.position = { x: 0, y: 0, z: 0 };\n }\n\n if(spatial.rotation === undefined)\n {\n spatial.rotation = { x: 0, y: 0, z: 0, w: 1 }; // eslint-disable-line id-length\n }\n\n if(spatial.nodeName !== undefined)\n {\n this.#set = { node: NodeEntity.#findAuthoredNode(nodeContext, spatial.nodeName), mesh: undefined };\n this.#settleMeshLoadedNow();\n }\n else\n {\n const pooled = entityClass.poolable ? nodeContext.pool.take(this.name) : undefined;\n\n if(pooled !== undefined)\n {\n this.#set = pooled;\n this.#set.node.setEnabled(true);\n this.#placeNodeFromState();\n\n if(pendingMeshLoads.has(pooled))\n {\n pendingMeshLoads.set(pooled, () => { this.#runMeshLoadedHooks(); });\n }\n else\n {\n this.#settleMeshLoadedNow();\n }\n }\n else\n {\n this.#set = { node: new TransformNode(id, nodeContext.scene), mesh: undefined };\n this.#placeNodeFromState();\n\n const set = this.#set;\n\n pendingMeshLoads.set(set, () => { this.#runMeshLoadedHooks(); });\n\n nodeContext.loadMesh(this.name, this.state as StateBag).then((mesh) =>\n {\n if(mesh !== undefined)\n {\n mesh.parent = set.node;\n }\n\n set.mesh = mesh;\n\n const notify = pendingMeshLoads.get(set);\n pendingMeshLoads.delete(set);\n notify?.();\n });\n }\n }\n\n if(spatial.parentID !== undefined)\n {\n const parentEntity = nodeContext.getEntity(spatial.parentID);\n\n if(parentEntity instanceof NodeEntity)\n {\n this.node.parent = parentEntity.node;\n }\n }\n }\n\n // Runs onMeshLoaded synchronously, inside construction -- adoption or a pooled claim already settled.\n // A throwing hook here fails the construction it runs inside, the same as a throwing onCreate does\n // (design/behaviors.md#onmeshloaded): super.destroy() only unsubscribes, bypassing this class's own\n // destroy(), which assumes a fully-built entity.\n #settleMeshLoadedNow() : void\n {\n try\n {\n this.#runMeshLoadedHooks();\n }\n catch(error)\n {\n super.destroy();\n throw error;\n }\n }\n\n #runMeshLoadedHooks() : void\n {\n const spatial = this.state as NodeEntityState;\n\n if(spatial.debugCollider !== undefined)\n {\n this.showCollider(spatial.debugCollider);\n }\n\n for(const instance of this.attachedBehaviors())\n {\n (instance as NodeBehaviorHooks).onMeshLoaded?.();\n }\n }\n\n #runDestroyHooks() : void\n {\n for(const instance of this.attachedBehaviors())\n {\n (instance as NodeBehaviorHooks).onDestroy?.();\n }\n }\n\n override update(dt : number) : void\n {\n super.update(dt);\n\n const spatial = this.state as NodeEntityState;\n\n // An adopted node's transform came from the level, in the level's own space, under whatever parent\n // authoring gave it; writing state's unparented position and rotation over that would move it wrong\n // (design/authoring.md#position-rotation-and-scale).\n if(spatial.nodeName !== undefined)\n {\n return;\n }\n\n this.#placeNodeFromState();\n }\n\n #placeNodeFromState() : void\n {\n const spatial = this.state as NodeEntityState;\n\n this.node.position = new Vector3(spatial.position.x, spatial.position.y, spatial.position.z);\n this.node.rotationQuaternion = new Quaternion(\n spatial.rotation.x,\n spatial.rotation.y,\n spatial.rotation.z,\n spatial.rotation.w\n );\n }\n\n override destroy() : void\n {\n this.hideCollider();\n\n this.#runDestroyHooks();\n\n super.destroy();\n\n // A set this entity was still waiting to hear from stays pending for whoever claims it next\n // (design/entities.md#pooling); this entity itself just stops being who that is.\n if(pendingMeshLoads.has(this.#set))\n {\n pendingMeshLoads.set(this.#set, () => { /* this entity is gone; nothing left to notify */ });\n }\n\n const spatial = this.state as NodeEntityState;\n\n // The node is the level's content, not a resource this entity borrowed -- destroying the entity never\n // disposes, hides, or pools it (design/authoring.md#pooling-and-destruction).\n if(spatial.nodeName !== undefined)\n {\n return;\n }\n\n const entityClass = this.constructor as typeof NodeEntity;\n\n if(entityClass.poolable)\n {\n const nodeContext = this.context as NodeSimulationContext;\n nodeContext.pool.give(this.name, this.#set, entityClass.poolSize);\n }\n else\n {\n this.node.dispose();\n }\n }\n\n static #findAuthoredNode(nodeContext : NodeSimulationContext, nodeName : string) : TransformNode\n {\n const found = nodeContext.scene.getNodeByName(nodeName);\n\n if(!(found instanceof TransformNode))\n {\n throw new Error(`NodeEntity: no authored node named \"${ nodeName }\" exists in the scene.`);\n }\n\n return found;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Entity Picking\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Vector3 } from '@babylonjs/core';\n\nimport type { AbstractMesh, Node, PickingInfo } from '@babylonjs/core';\nimport type { GameEntity, GameEntityState } from '@skewedaspect/sage-core';\n\nimport { NodeEntity } from './nodeEntity.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface EntityPickFilter\n{\n tags ?: readonly string[];\n}\n\nexport interface EntityPickResult\n{\n entity : NodeEntity;\n mesh : AbstractMesh;\n point : Vector3;\n distance : number;\n normal : Vector3;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// Climbs the mesh's own ancestor chain -- itself, then its parent, and so on -- against the simulation's\n// current entity list, rather than a maintained node-to-entity lookup: nothing here needs invalidating on\n// spawn, despawn, or a pooled node changing hands.\nexport function findEntityForMesh(entities : readonly GameEntity[], mesh : AbstractMesh) : NodeEntity | undefined\n{\n const nodeEntities : NodeEntity[] = [];\n\n for(const entity of entities)\n {\n if(entity instanceof NodeEntity)\n {\n nodeEntities.push(entity);\n }\n }\n\n let current : Node | null = mesh;\n\n while(current !== null)\n {\n for(const entity of nodeEntities)\n {\n if(entity.node === current)\n {\n return entity;\n }\n }\n\n current = current.parent;\n }\n\n return undefined;\n}\n\nfunction passesFilter(entity : NodeEntity, filter : EntityPickFilter | undefined) : boolean\n{\n if(filter?.tags === undefined)\n {\n return true;\n }\n\n const tags = (entity.state as GameEntityState).tags;\n\n for(const tag of filter.tags)\n {\n if(!tags.includes(tag))\n {\n return false;\n }\n }\n\n return true;\n}\n\nexport function toEntityPickResult(\n entities : readonly GameEntity[],\n info : PickingInfo | null,\n filter ?: EntityPickFilter\n) : EntityPickResult | undefined\n{\n if(info === null || !info.hit || info.pickedMesh === null || info.pickedPoint === null)\n {\n return undefined;\n }\n\n const entity = findEntityForMesh(entities, info.pickedMesh);\n\n if(entity === undefined || !passesFilter(entity, filter))\n {\n return undefined;\n }\n\n const normal = info.getNormal(true, true);\n\n return {\n entity,\n mesh: info.pickedMesh,\n point: info.pickedPoint,\n distance: info.distance,\n normal: normal ?? Vector3.Zero(),\n };\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// NodeSimulation\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { TransformNode } from '@babylonjs/core';\nimport { GameSimulationManager } from '@skewedaspect/sage-core';\n\nimport type { GameSimulationOptions, StateBag } from '@skewedaspect/sage-core';\nimport type { AbstractMesh, Camera, Ray, Scene } from '@babylonjs/core';\n\nimport { findEntityForMesh, toEntityPickResult } from './entityPicking.ts';\nimport type { EntityPickFilter, EntityPickResult } from './entityPicking.ts';\nimport type { NodeEntity } from './nodeEntity.ts';\nimport type { NodePool } from './nodePool.ts';\nimport type { MeshLoader, NodeSimulationContext } from './nodeSimulationContext.ts';\nimport type { AudioManager } from '../managers/audio.ts';\nimport type { ColliderDebugManager } from '../managers/colliderDebug.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface NodeSimulationOptions extends GameSimulationOptions\n{\n scene : Scene;\n loadMesh : MeshLoader;\n pool : NodePool;\n audio ?: AudioManager;\n colliders : ColliderDebugManager;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class NodeSimulation extends GameSimulationManager implements NodeSimulationContext\n{\n readonly scene : Scene;\n readonly pool : NodePool;\n readonly loadMesh : MeshLoader;\n readonly audio : AudioManager | undefined;\n readonly colliders : ColliderDebugManager;\n\n readonly #entityClasses : GameSimulationOptions['entityClasses'];\n\n constructor(options : NodeSimulationOptions)\n {\n super(options);\n\n this.scene = options.scene;\n this.pool = options.pool;\n this.loadMesh = options.loadMesh;\n this.audio = options.audio;\n this.colliders = options.colliders;\n this.#entityClasses = options.entityClasses;\n }\n\n async prewarm(name : string, count : number, state : StateBag = {}) : Promise<void>\n {\n const entityClass = this.#entityClasses[name] as typeof NodeEntity | undefined;\n\n const meshes = await Promise.all(\n Array.from({ length: count }, () => this.loadMesh(name, state))\n );\n\n for(const mesh of meshes)\n {\n const node = new TransformNode(name, this.scene);\n\n if(mesh !== undefined)\n {\n mesh.parent = node;\n }\n\n this.pool.give(name, { node, mesh }, entityClass?.poolSize);\n }\n }\n\n pickEntityWithRay(ray : Ray, filter ?: EntityPickFilter) : EntityPickResult | undefined\n {\n return toEntityPickResult(this.entities, this.scene.pickWithRay(ray), filter);\n }\n\n pickEntity(x : number, y : number, filter ?: EntityPickFilter) : EntityPickResult | undefined\n {\n return toEntityPickResult(this.entities, this.scene.pick(x, y), filter);\n }\n\n pickEntityForward(camera : Camera, range : number, filter ?: EntityPickFilter) : EntityPickResult | undefined\n {\n return this.pickEntityWithRay(camera.getForwardRay(range), filter);\n }\n\n getEntityForMesh(mesh : AbstractMesh) : NodeEntity | undefined\n {\n return findEntityForMesh(this.entities, mesh);\n }\n\n protected override buildReplacementSimulation(options : GameSimulationOptions) : NodeSimulation\n {\n return new NodeSimulation({\n ...options,\n scene: this.scene,\n pool: this.pool,\n loadMesh: this.loadMesh,\n audio: this.audio,\n colliders: this.colliders,\n });\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// NodePool\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { poolSizeLimit } from './constants.ts';\n\nimport type { NodeSet } from './nodeSimulationContext.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// Node and mesh sets, keyed by entity name, capped per class at `poolSizeLimit`.\nexport class NodePool\n{\n readonly #sets = new Map<string, NodeSet[]>();\n\n take(name : string) : NodeSet | undefined\n {\n const sets = this.#sets.get(name);\n\n if(sets === undefined)\n {\n return undefined;\n }\n\n return sets.pop();\n }\n\n give(name : string, set : NodeSet, limit : number = poolSizeLimit) : void\n {\n set.node.parent = null;\n set.node.setEnabled(false);\n\n let sets = this.#sets.get(name);\n\n if(sets === undefined)\n {\n sets = [];\n this.#sets.set(name, sets);\n }\n\n if(sets.length >= limit)\n {\n set.node.dispose();\n return;\n }\n\n sets.push(set);\n }\n\n clear(name : string) : void\n {\n const sets = this.#sets.get(name);\n\n if(sets === undefined)\n {\n return;\n }\n\n for(const set of sets)\n {\n set.node.dispose();\n }\n\n this.#sets.delete(name);\n }\n\n clearAll() : void\n {\n for(const name of [ ...this.#sets.keys() ])\n {\n this.clear(name);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Audio Engine\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n type AudioBus,\n type AudioEngineV2,\n CreateAudioEngineAsync,\n type IStaticSoundOptions,\n type MainAudioBus,\n type PrimaryAudioBus,\n type StaticSound,\n} from '@babylonjs/core';\n\n// Interfaces\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Utils\nimport type { LoggingUtility } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class AudioEngine implements Disposable\n{\n private _engine : AudioEngineV2 | null = null;\n private _mainBus : MainAudioBus | null = null;\n private _buses = new Map<string, AudioBus>();\n private _log : LoggerInterface;\n\n constructor(logger : LoggingUtility)\n {\n this._log = logger.getLogger('AudioEngine');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n async initialize() : Promise<void>\n {\n this._log.info('Initializing audio engine...');\n\n this._engine = await CreateAudioEngineAsync();\n this._mainBus = await this._engine.createMainBusAsync('main');\n\n this._log.info('Audio engine initialized.');\n }\n\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down audio engine...');\n\n this._buses.clear();\n this._mainBus = null;\n\n if(this._engine)\n {\n this._engine.dispose();\n this._engine = null;\n }\n\n this._log.info('Audio engine torn down.');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Bus Management\n //------------------------------------------------------------------------------------------------------------------\n\n async createBus(name : string) : Promise<AudioBus>\n {\n if(!this._engine || !this._mainBus)\n {\n throw new Error('AudioEngine not initialized');\n }\n\n this._log.debug(`Creating audio bus: ${ name }`);\n const bus = await this._engine.createBusAsync(name, { outBus: this._mainBus });\n this._buses.set(name, bus);\n return bus;\n }\n\n getBus(name : string) : AudioBus | undefined\n {\n return this._buses.get(name);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Sound Creation\n //------------------------------------------------------------------------------------------------------------------\n\n async createSound(\n name : string,\n url : string,\n bus ?: AudioBus,\n options ?: Partial<IStaticSoundOptions>\n ) : Promise<StaticSound>\n {\n if(!this._engine)\n {\n throw new Error('AudioEngine not initialized');\n }\n\n const outBus : PrimaryAudioBus | undefined = bus ?? this._mainBus ?? undefined;\n\n // Spread user options and inject outBus; the cast is needed because\n // IStaticSoundOptions.outBus accepts Nullable<PrimaryAudioBus>\n const soundOptions = {\n ...options,\n ...(outBus ? { outBus } : {}),\n } as Partial<IStaticSoundOptions>;\n\n return this._engine.createSoundAsync(name, url, soundOptions);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Volume\n //------------------------------------------------------------------------------------------------------------------\n\n setMasterVolume(volume : number) : void\n {\n if(!this._engine)\n {\n throw new Error('AudioEngine not initialized');\n }\n\n this._engine.volume = volume;\n }\n\n getMasterVolume() : number\n {\n if(!this._engine)\n {\n return 1;\n }\n\n return this._engine.volume;\n }\n\n setBusVolume(bus : AudioBus, volume : number) : void\n {\n bus.volume = volume;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Scene Engine\n//\n// Wraps BabylonJS scene lifecycle, mesh loading, physics, cameras, and lights.\n// All built-in loaders (glTF, OBJ, STL, etc.) are registered via registerBuiltInLoaders()\n// and loaded on-demand when first used.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n AbstractEngine,\n AbstractMesh,\n AssetContainer,\n DirectionalLight,\n FreeCamera,\n HavokPlugin,\n HemisphericLight,\n ImportMeshAsync,\n Light,\n LoadAssetContainerAsync,\n Mesh,\n MeshBuilder,\n PhysicsAggregate,\n PhysicsShapeType,\n PointLight,\n RectAreaLight,\n Scene,\n SpotLight,\n Vector3,\n} from '@babylonjs/core';\n\n// Register all built-in loaders (glTF, OBJ, STL, etc.) -- loaded on-demand when first used\nimport { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic';\nregisterBuiltInLoaders();\nimport type { HavokPhysicsWithBindings } from '@babylonjs/havok';\n\n// Interfaces\nimport type { GameCanvas } from '../interfaces/game.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Utils\nimport { type LoggingUtility } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class SceneEngine implements Disposable\n{\n private _canvas : GameCanvas;\n private _engine : AbstractEngine;\n private _havok : HavokPhysicsWithBindings;\n private _log : LoggerInterface;\n\n constructor(canvas : GameCanvas, engine : AbstractEngine, havok : HavokPhysicsWithBindings, logger : LoggingUtility)\n {\n this._canvas = canvas;\n this._engine = engine;\n this._havok = havok;\n this._log = logger.getLogger('SceneEngine');\n }\n\n /**\n * Creates a new scene with physics enabled\n */\n public createScene() : Scene\n {\n return new Scene(this._engine);\n }\n\n public enablePhysics(\n scene : Scene,\n gravityVector : Vector3 = new Vector3(0, -9.8, 0),\n floatingOriginWorldRadius ?: number\n ) : void\n {\n this._log.debug(\n `Enabling physics with gravity (${ gravityVector.x }, ${ gravityVector.y }, ${ gravityVector.z })...`\n );\n\n const pluginOptions = floatingOriginWorldRadius !== undefined\n ? { floatingOriginWorldRadius }\n : undefined;\n\n // Each scene gets its own HavokPlugin so that scene.dispose() doesn't corrupt the shared WASM module.\n const plugin = new HavokPlugin(true, this._havok, pluginOptions);\n scene.enablePhysics(gravityVector, plugin);\n }\n\n // Camera Utilities\n /**\n * Creates a free camera at the specified position, targeting the origin. Attaches controls to the provided\n * canvas, or falls back to the engine's default canvas.\n * @param name\n * @param position\n * @param scene\n * @param canvas - Overrides the default canvas for input controls\n */\n public createFreeCamera(\n name : string,\n position : Vector3,\n scene : Scene,\n canvas ?: GameCanvas\n ) : FreeCamera\n {\n const camera = new FreeCamera(name, position, scene);\n camera.setTarget(Vector3.Zero());\n\n canvas = canvas ?? this._canvas;\n\n if(canvas)\n {\n camera.attachControl(canvas, true);\n }\n\n return camera;\n }\n\n // Lighting Utilities\n /**\n * Creates a hemispheric light (ambient lighting)\n * @param name\n * @param direction\n * @param scene\n * @param intensity - Default: 0.7\n */\n public createHemisphericLight(\n name : string,\n direction : Vector3,\n scene : Scene,\n intensity = 0.7\n ) : HemisphericLight\n {\n const light = new HemisphericLight(name, direction, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a directional light (sun-like lighting)\n * @param name\n * @param direction\n * @param scene\n * @param intensity - Default: 1.0\n */\n public createDirectionalLight(\n name : string,\n direction : Vector3,\n scene : Scene,\n intensity = 1.0\n ) : DirectionalLight\n {\n const light = new DirectionalLight(name, direction, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a point light (bulb-like lighting)\n * @param name\n * @param position\n * @param scene\n * @param intensity - Default: 1.0\n */\n public createPointLight(\n name : string,\n position : Vector3,\n scene : Scene,\n intensity = 1.0\n ) : PointLight\n {\n const light = new PointLight(name, position, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a spotlight (flashlight-like lighting)\n * @param name\n * @param position\n * @param direction\n * @param angle - Cone angle in radians\n * @param exponent - Controls light falloff from center to edge of cone\n * @param scene\n * @param intensity - Default: 1.0\n */\n public createSpotLight(\n name : string,\n position : Vector3,\n direction : Vector3,\n angle : number,\n exponent : number,\n scene : Scene,\n intensity = 1.0\n ) : SpotLight\n {\n const light = new SpotLight(name, position, direction, angle, exponent, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a rectangular area light (emits from a flat rectangle in the -Z direction)\n * @param name\n * @param position\n * @param width\n * @param height\n * @param scene\n * @param intensity - Default: 1.0\n */\n public createRectAreaLight(\n name : string,\n position : Vector3,\n width : number,\n height : number,\n scene : Scene,\n intensity = 1.0\n ) : RectAreaLight\n {\n const light = new RectAreaLight(name, position, width, height, scene);\n light.intensity = intensity;\n return light;\n }\n\n // Mesh Creation Utilities\n /**\n * Creates a sphere mesh. Defaults: diameter 1, 32 segments.\n * @param name\n * @param options\n * @param scene\n */\n public createSphere(\n name : string,\n options : { diameter ?: number; segments ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n const defaultOptions = { diameter: 1, segments: 32 };\n return MeshBuilder.CreateSphere(name, { ...defaultOptions, ...options }, scene);\n }\n\n /**\n * Creates a box mesh. Default size: 1.\n * @param name\n * @param options\n * @param scene\n */\n public createBox(\n name : string,\n options : { size ?: number; width ?: number; height ?: number; depth ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n const defaultOptions = { size: 1 };\n return MeshBuilder.CreateBox(name, { ...defaultOptions, ...options }, scene);\n }\n\n /**\n * Creates a ground mesh. Defaults: 6x6 with 2 subdivisions.\n * @param name\n * @param options\n * @param scene\n */\n public createGround(\n name : string,\n options : { width ?: number; height ?: number; subdivisions ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n const defaultOptions = { width: 6, height: 6, subdivisions: 2 };\n return MeshBuilder.CreateGround(name, { ...defaultOptions, ...options }, scene);\n }\n\n /**\n * Creates a cylinder mesh. Defaults: height 2, diameter 1 (top and bottom).\n * @param name\n * @param options\n * @param scene\n */\n public createCylinder(\n name : string,\n options : { height ?: number; diameterTop ?: number; diameterBottom ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n const defaultOptions = { height: 2, diameterTop: 1, diameterBottom: 1 };\n return MeshBuilder.CreateCylinder(name, { ...defaultOptions, ...options }, scene);\n }\n\n // Physics Utilities\n /**\n * Adds physics to a mesh. Defaults: mass 1, restitution 0.75, friction 0.5.\n * @param mesh\n * @param shapeType\n * @param physicsProps\n * @param scene\n */\n public addPhysics(\n mesh : Mesh,\n shapeType : PhysicsShapeType,\n physicsProps : { mass ?: number; restitution ?: number; friction ?: number } = {},\n scene : Scene\n ) : PhysicsAggregate\n {\n const defaultProps = { mass: 1, restitution: 0.75, friction: 0.5 };\n return new PhysicsAggregate(mesh, shapeType, { ...defaultProps, ...physicsProps }, scene);\n }\n\n // Model Loading Utilities\n /**\n * Loads a 3D model/asset into an AssetContainer without adding it to the scene automatically.\n * @param sceneFilename\n * @param scene\n */\n public async loadModel(\n sceneFilename : string,\n scene : Scene\n ) : Promise<AssetContainer>\n {\n this._log.debug(`Loading model: ${ sceneFilename }`);\n\n try\n {\n const result = await LoadAssetContainerAsync(`${ sceneFilename }`, scene);\n this._log.debug(`Model loaded successfully: ${ sceneFilename }`);\n return result;\n }\n catch (error)\n {\n this._log.error(`Failed to load model: ${ sceneFilename }`, error);\n throw error;\n }\n }\n\n /**\n * Imports meshes from a file directly into the scene.\n * @param meshNames - Specific meshes to import; empty array imports all\n * @param sceneFilename\n * @param scene\n */\n public async importMeshes(\n meshNames : string[],\n sceneFilename : string,\n scene : Scene\n ) : Promise<{\n meshes : AbstractMesh[];\n particleSystems : unknown[];\n skeletons : unknown[];\n animationGroups : unknown[];\n transformNodes : unknown[];\n geometries : unknown[];\n lights : Light[];\n }>\n {\n this._log.debug(`Importing meshes from: ${ sceneFilename }`);\n\n try\n {\n // Note: meshNames as empty array means \"import nothing\", use undefined for \"import all\"\n const options = meshNames.length > 0 ? { meshNames } : undefined;\n const result = await ImportMeshAsync(`${ sceneFilename }`, scene, options);\n this._log.debug(`Meshes imported successfully: ${ meshNames.length > 0 ? meshNames.join(', ') : 'all' }`);\n return result;\n }\n catch (error)\n {\n this._log.error(`Failed to import meshes from: ${ sceneFilename }`, error);\n throw error;\n }\n }\n\n /**\n * Tears down the scene engine and cleans up any resources\n */\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down SceneEngine');\n\n // Currently no specific resources to clean up\n // The scenes themselves are managed by the LevelManager\n // and physics engine is disposed at the application level\n\n this._log.info('SceneEngine torn down successfully');\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Asset Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { AbstractMesh, AssetContainer, InstancedMesh, Mesh } from '@babylonjs/core';\n\nimport type { GameEventBus } from '../classes/eventBus.ts';\nimport type { SceneEngine } from '../engines/scene.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport { type LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface CachedContainer\n{\n container : AssetContainer;\n refCount : number;\n}\n\n/**\n * How many assets a preload loads at once. Six is the per-origin connection limit browsers have long held\n * for HTTP/1.1: below it a batch leaves the connection pool idle, and above it the extra loads only queue\n * somewhere else while splitting the same bandwidth and competing for the same main thread to parse on.\n */\nconst MAX_CONCURRENT_LOADS = 6;\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Central asset cache with preloading, GLB fragment extraction, mesh instancing, and reference counting.\n *\n * Supports fragment syntax (`models/props.glb#chest_lid`) to extract individual meshes from a shared container.\n * Containers are reference-counted and only disposed when all consumers release them.\n */\nexport class AssetManager implements Disposable\n{\n private _eventBus : GameEventBus;\n private _sceneEngine : SceneEngine;\n private _log : LoggerInterface;\n\n /** Full GLB/GLTF asset containers, keyed by base path (without fragment) */\n private _containers = new Map<string, CachedContainer>();\n\n /** Extracted fragment meshes (source meshes for instancing), keyed by full fragment path */\n private _sourceMeshes = new Map<string, AbstractMesh>();\n\n /** Container loads still in flight, keyed by base path, so concurrent callers share one load */\n private _pendingContainers = new Map<string, Promise<AssetContainer>>();\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(eventBus : GameEventBus, sceneEngine : SceneEngine, logger ?: LoggingUtility)\n {\n this._eventBus = eventBus;\n this._sceneEngine = sceneEngine;\n this._log = logger?.getLogger('AssetManager') ?? new SAGELogger('AssetManager');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Helpers\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Split a path on `#` into base path and optional fragment name.\n */\n private _parsePath(path : string) : { basePath : string; fragment ?: string }\n {\n const hashIdx = path.indexOf('#');\n if(hashIdx === -1)\n {\n return { basePath: path };\n }\n\n return {\n basePath: path.substring(0, hashIdx),\n fragment: path.substring(hashIdx + 1),\n };\n }\n\n /**\n * Load (or retrieve from cache) the AssetContainer for a base path, counting one reference for this\n * caller. A caller arriving while the same base path is already loading shares that load instead of\n * starting a second one, which would leave the first container cached over and uncounted.\n */\n private async _loadContainer(basePath : string) : Promise<AssetContainer>\n {\n const cached = this._containers.get(basePath);\n if(cached)\n {\n cached.refCount++;\n return cached.container;\n }\n\n let inFlight = this._pendingContainers.get(basePath);\n if(!inFlight)\n {\n inFlight = this._fetchContainer(basePath);\n this._pendingContainers.set(basePath, inFlight);\n }\n\n const container = await inFlight;\n\n // The fetch caches with no references counted; each caller that awaited it counts its own here.\n // A disposeAll() during the fetch leaves nothing to count against, so check before counting.\n const entry = this._containers.get(basePath);\n if(entry)\n {\n entry.refCount++;\n }\n\n return container;\n }\n\n /**\n * Read one container off disk and cache it, counting no references; `_loadContainer` does the counting.\n */\n private async _fetchContainer(basePath : string) : Promise<AssetContainer>\n {\n this._log.debug(`Loading asset container: ${ basePath }`);\n\n // LoadAssetContainerAsync needs a scene; create a temporary one\n const tempScene = this._sceneEngine.createScene();\n\n try\n {\n const container = await this._sceneEngine.loadModel(basePath, tempScene);\n\n this._containers.set(basePath, { container, refCount: 0 });\n this._log.debug(`Cached asset container: ${ basePath }`);\n\n return container;\n }\n finally\n {\n tempScene.dispose();\n this._pendingContainers.delete(basePath);\n }\n }\n\n /**\n * Find a mesh by name inside a container. Throws if not found.\n */\n private _findMeshInContainer(container : AssetContainer, meshName : string, fullPath : string) : AbstractMesh\n {\n const mesh = container.meshes.find((entry) => entry.name === meshName);\n if(!mesh)\n {\n const available = container.meshes.map((entry) => entry.name).join(', ');\n throw new Error(\n `Mesh '${ meshName }' not found in container for '${ fullPath }'. `\n + `Available meshes: ${ available }`\n );\n }\n\n return mesh;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Load an asset, caching the result. Supports fragment syntax (`path#meshName`).\n *\n * - Without fragment: loads and caches the full AssetContainer, returns it.\n * - With fragment: loads the container, finds the named mesh, caches it as a source mesh, returns the mesh.\n *\n * Increments refCount on the underlying container each time.\n */\n async load(path : string) : Promise<AssetContainer | AbstractMesh>\n {\n const { basePath, fragment } = this._parsePath(path);\n\n if(fragment)\n {\n // Check if we already cached this fragment mesh\n const cachedMesh = this._sourceMeshes.get(path);\n if(cachedMesh)\n {\n // Still increment refCount on the container\n const cached = this._containers.get(basePath);\n if(cached)\n {\n cached.refCount++;\n }\n\n return cachedMesh;\n }\n\n // Load the container (handles caching + refCount internally)\n const container = await this._loadContainer(basePath);\n\n // Find and cache the named mesh\n const mesh = this._findMeshInContainer(container, fragment, path);\n this._sourceMeshes.set(path, mesh);\n\n return mesh;\n }\n\n // No fragment — return the full container\n return this._loadContainer(basePath);\n }\n\n /**\n * Create an InstancedMesh from a cached source mesh. The path must have been loaded first.\n */\n instance(path : string) : InstancedMesh\n {\n const sourceMesh = this._sourceMeshes.get(path);\n if(!sourceMesh)\n {\n throw new Error(`Asset '${ path }' is not loaded. Call load() first.`);\n }\n\n return (sourceMesh as Mesh).createInstance(`${ sourceMesh.name }-instance`);\n }\n\n /**\n * Clone a cached source mesh into an independent copy. The path must have been loaded first.\n */\n clone(path : string) : Mesh\n {\n const sourceMesh = this._sourceMeshes.get(path);\n if(!sourceMesh)\n {\n throw new Error(`Asset '${ path }' is not loaded. Call load() first.`);\n }\n\n return (sourceMesh as Mesh).clone(`${ sourceMesh.name }-clone`);\n }\n\n /**\n * Preload a batch of asset paths, up to `MAX_CONCURRENT_LOADS` at a time, so a batch costs about as\n * long as its slowest asset rather than the sum of them all.\n *\n * Emits `asset:progress` as each asset finishes loading, naming the asset that finished and counting\n * one to one up to the total. Loads finish out of request order, so the paths do too.\n *\n * Every asset is attempted, and every load has finished before this returns. If any failed, it throws\n * an `AggregateError` naming all of them and emits no `asset:complete`.\n */\n async preload(paths : string[]) : Promise<void>\n {\n const total = paths.length;\n const failures : { path : string; error : unknown }[] = [];\n let nextIndex = 0;\n let loaded = 0;\n\n // A worker takes the next unclaimed path the moment its own load finishes, so a slow asset holds\n // up only itself. Nothing here yields between reading nextIndex and advancing it, so no two\n // workers can claim the same path.\n const runWorker = async () : Promise<void> =>\n {\n while(nextIndex < paths.length)\n {\n const path = paths[nextIndex];\n nextIndex++;\n\n let succeeded = true;\n\n try\n {\n // eslint-disable-next-line no-await-in-loop\n await this.load(path);\n }\n catch (error)\n {\n succeeded = false;\n failures.push({ path, error });\n }\n\n if(succeeded)\n {\n loaded++;\n\n this._eventBus.publish({\n type: 'asset:progress',\n payload: {\n path,\n loaded,\n total,\n },\n });\n }\n }\n };\n\n const workers : Promise<void>[] = [];\n const workerCount = Math.min(MAX_CONCURRENT_LOADS, total);\n for(let i = 0; i < workerCount; i++)\n {\n workers.push(runWorker());\n }\n\n await Promise.all(workers);\n\n if(failures.length > 0)\n {\n const failedPaths = failures.map((failure) => failure.path).join(', ');\n\n throw new AggregateError(\n failures.map((failure) => failure.error),\n `Preload failed for ${ failures.length } of ${ total } assets: ${ failedPaths }`\n );\n }\n\n this._eventBus.publish({\n type: 'asset:complete',\n payload: { paths },\n });\n }\n\n /**\n * Decrement refCount for the container backing this path.\n * When refCount hits 0, dispose the container and clean up all fragment meshes from it.\n */\n dispose(path : string) : void\n {\n const { basePath } = this._parsePath(path);\n const cached = this._containers.get(basePath);\n if(!cached)\n {\n return;\n }\n\n cached.refCount--;\n\n if(cached.refCount <= 0)\n {\n this._log.debug(`Disposing asset container: ${ basePath } (refCount reached 0)`);\n\n // Clean up all fragment meshes that came from this container\n for(const [ fragmentPath, mesh ] of this._sourceMeshes)\n {\n if(fragmentPath.startsWith(`${ basePath }#`))\n {\n mesh.dispose();\n this._sourceMeshes.delete(fragmentPath);\n }\n }\n\n cached.container.dispose();\n this._containers.delete(basePath);\n }\n }\n\n /**\n * Dispose all cached containers and fragment meshes.\n */\n disposeAll() : void\n {\n for(const cached of this._containers.values())\n {\n cached.container.dispose();\n }\n\n for(const mesh of this._sourceMeshes.values())\n {\n mesh.dispose();\n }\n\n this._containers.clear();\n this._sourceMeshes.clear();\n }\n\n /**\n * Lifecycle teardown — disposes all assets.\n */\n async $teardown() : Promise<void>\n {\n this.disposeAll();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Utilities to test for capability of the current environment\n//----------------------------------------------------------------------------------------------------------------------\n\nexport function isBrowser() : boolean\n{\n return typeof window !== 'undefined' && typeof window.document !== 'undefined';\n}\n\nexport function hasWebGPU() : boolean\n{\n return isBrowser()\n && !!window.navigator.gpu;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Game Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractEngine, Scene } from '@babylonjs/core';\n\n// Interfaces\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Classes\nimport type { GameEventBus } from '../classes/eventBus.ts';\nimport type { GameEngine } from '../classes/gameEngine.ts';\n\n// Managers\nimport type { InputManager } from './input.ts';\nimport { LevelManager } from './level.ts';\n\n// Utils\nimport { isBrowser } from '../utils/capabilities.ts';\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Callback function for frame updates.\n * Called once per frame with the delta time in seconds.\n */\nexport type FrameCallback = (deltaTime : number) => void;\n\n/** How a frame callback answers to pause (design/ui.md#when-a-view-observes). */\nexport interface FrameCallbackOptions\n{\n suspendedByPause ?: boolean;\n}\n\n/**\n * Internal representation of a registered frame callback.\n */\ninterface RegisteredFrameCallback\n{\n id : number;\n callback : FrameCallback;\n suspendedByPause : boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class GameManager implements Disposable\n{\n private _engine : AbstractEngine;\n private _eventBus : GameEventBus;\n private _gameEngine : GameEngine | null = null;\n private _inputManager : InputManager | null = null;\n private _levelManager : LevelManager;\n private _log : LoggerInterface;\n private _boundRenderLoop : () => void;\n private _boundResizeHandler : () => void;\n\n /** Registered frame callbacks, executed in registration order */\n private _frameCallbacks : RegisteredFrameCallback[] = [];\n\n /** Counter for generating unique callback IDs */\n private _nextCallbackId = 0;\n\n /** Whether the game is currently paused */\n private _paused = false;\n\n public started = false;\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(\n engine : AbstractEngine,\n eventBus : GameEventBus,\n levelManager : LevelManager,\n logger ?: LoggingUtility\n )\n {\n this._engine = engine;\n this._eventBus = eventBus;\n this._levelManager = levelManager;\n this._log = logger?.getLogger('GameManager') || new SAGELogger('GameManager');\n\n // Store bound function references for later cleanup\n this._boundRenderLoop = this._renderLoop.bind(this);\n this._boundResizeHandler = this._resizeHandler.bind(this);\n\n if(isBrowser())\n {\n // Resize the engine on window resize\n window.addEventListener('resize', this._boundResizeHandler);\n }\n }\n\n get currentScene() : Scene | null\n {\n return this._levelManager.currentLevel?.scene ?? null;\n }\n\n /**\n * Late-bind the GameEngine after construction.\n * Called by createGameEngine() after the GameEngine instance is created.\n */\n $setGameEngine(gameEngine : GameEngine) : void\n {\n this._gameEngine = gameEngine;\n }\n\n // The routing loop's caller-side half (input.md#the-routing-loop): the frame loop calls\n // `routeCapturedInput` on whatever is registered here immediately before `runStepsForElapsedTime`,\n // once a simulation exists.\n setInputManager(inputManager : InputManager) : void\n {\n this._inputManager = inputManager;\n }\n\n /**\n * Whether the game is currently paused.\n * When paused, entity updates and frame callbacks are skipped, but rendering continues.\n */\n get isPaused() : boolean\n {\n return this._paused;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a callback to be called once per frame.\n *\n * **Advanced Usage**: This is intended for systems that need frame-by-frame updates\n * outside of the entity/behavior system. For game logic, prefer using entity behaviors\n * with their `update()` method instead.\n *\n * Callbacks are executed in registration order, after entity updates but before rendering.\n * Each callback receives the delta time in seconds (typically ~0.016 for 60fps).\n *\n * @param callback\n * @returns A function to unregister the callback\n *\n * @example\n * ```typescript\n * const unsubscribe = gameManager.registerFrameCallback((deltaTime) => {\n * // deltaTime is in seconds (e.g., 0.016 for 60fps)\n * console.log(`Frame delta: ${deltaTime}s`);\n * });\n *\n * // Later, to stop receiving callbacks:\n * unsubscribe();\n * ```\n */\n public registerFrameCallback(callback : FrameCallback, options ?: FrameCallbackOptions) : () => void\n {\n const id = this._nextCallbackId++;\n this._frameCallbacks.push({ id, callback, suspendedByPause: options?.suspendedByPause ?? true });\n\n this._log.debug(`Registered frame callback (id: ${ id })`);\n\n // Return unsubscribe function\n return () =>\n {\n const index = this._frameCallbacks.findIndex((cb) => cb.id === id);\n if(index !== -1)\n {\n this._frameCallbacks.splice(index, 1);\n this._log.debug(`Unregistered frame callback (id: ${ id })`);\n }\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n private _renderLoop() : void\n {\n const deltaTimeMs = this._engine.getDeltaTime();\n const deltaTime = deltaTimeMs / 1000; // Convert milliseconds to seconds\n\n // Skip game logic updates when paused\n if(!this._paused)\n {\n if(this._gameEngine === null)\n {\n throw new Error('GameManager: gameEngine not set. Call $setGameEngine() first.');\n }\n\n // No simulation exists until something hands the engine a scene to build one against\n // (GameEngine.rebuildSimulation) -- a game that never loads a level never gets one.\n if(this._gameEngine.simulation !== null)\n {\n // The routing loop needs a simulation to deliver into; with none built yet, it does not run\n // (input.md#the-routing-loop).\n if(this._inputManager !== null)\n {\n this._inputManager.routeCapturedInput(this._gameEngine.simulation);\n }\n\n // Not wrapped in a try/catch: a throw from an entity's update() is a defect in the behavior,\n // and it propagates out of the render loop rather than being carried through as a game event.\n this._gameEngine.simulation.runStepsForElapsedTime(deltaTime);\n }\n }\n\n // A frame callback answers to pause per registration: presentation work keeps running against the\n // frozen frame, and a game's own update does not (ui.md#when-a-view-observes).\n for(const { callback, suspendedByPause } of this._frameCallbacks)\n {\n const suspended = this._paused && suspendedByPause;\n\n if(!suspended)\n {\n try\n {\n callback(deltaTime);\n }\n catch (err)\n {\n this._log.error('Error in frame callback:', err);\n }\n }\n }\n\n // Always render (shows frozen frame when paused)\n if(this.currentScene)\n {\n this.currentScene.render();\n }\n }\n\n private _resizeHandler() : void\n {\n if(this.started)\n {\n this._engine.resize();\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n async start() : Promise<void>\n {\n // Start the render loop\n this._engine.runRenderLoop(this._boundRenderLoop);\n\n this.started = true;\n\n this._log.info('SkewedAspect Game Engine started successfully');\n }\n\n async stop() : Promise<void>\n {\n this.started = false;\n this._engine.stopRenderLoop();\n\n this._log.info('SkewedAspect Game Engine stopped successfully');\n }\n\n /**\n * Pause the game. Entity updates and frame callbacks will be skipped,\n * but rendering continues (frozen frame). Input is still polled so\n * the game can be unpaused.\n *\n * Emits a 'game:paused' event.\n *\n * @param reason - Optional reason for pausing (included in event payload)\n */\n pause(reason ?: string) : void\n {\n if(this._paused)\n {\n this._log.debug('Game is already paused');\n return;\n }\n\n this._paused = true;\n this._log.info('Game paused', reason ? `(reason: ${ reason })` : '');\n\n this._eventBus.publish({\n type: 'game:paused',\n payload: { reason },\n });\n }\n\n /**\n * Resume the game after being paused. Entity updates and frame callbacks\n * will resume.\n *\n * Emits a 'game:resumed' event.\n *\n * @param reason - Optional reason for resuming (included in event payload)\n */\n resume(reason ?: string) : void\n {\n if(!this._paused)\n {\n this._log.debug('Game is not paused');\n return;\n }\n\n this._paused = false;\n this._log.info('Game resumed', reason ? `(reason: ${ reason })` : '');\n\n this._eventBus.publish({\n type: 'game:resumed',\n payload: { reason },\n });\n }\n\n /**\n * Tears down any resources held by the GameManager\n * This handles unregistering event listeners and any other cleanup\n */\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down GameManager');\n\n // If we're still running, stop first\n if(this.started)\n {\n await this.stop();\n }\n\n // Remove event listeners\n if(isBrowser())\n {\n window.removeEventListener('resize', this._boundResizeHandler);\n }\n\n // Clear frame callbacks\n this._frameCallbacks = [];\n\n this._log.info('GameManager torn down successfully');\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Action Context\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Binding } from './binding.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface ContextEntry\n{\n readonly exclusive : boolean;\n active : boolean;\n}\n\n// A named group of bindings that activate and deactivate together (input.md#action-contexts).\nexport class ActionContextRegistry\n{\n readonly #contexts = new Map<string, ContextEntry>();\n readonly #bindings : Binding<unknown>[] = [];\n\n // Exclusive by default (input.md#action-contexts).\n registerContext(name : string, exclusive = true) : void\n {\n this.#contexts.set(name, { exclusive, active: false });\n }\n\n // Activating an exclusive context deactivates every other exclusive one. Primes every member binding\n // against the current state the caller reports for it, so an input already held does not read as a\n // fresh press.\n activateContext(name : string, currentState : (binding : Binding<unknown>) => unknown) : void\n {\n const context = this.#requireContext(name);\n\n if(context.exclusive)\n {\n for(const [ otherName, other ] of this.#contexts)\n {\n if(otherName !== name && other.exclusive)\n {\n other.active = false;\n }\n }\n }\n\n context.active = true;\n\n for(const binding of this.#bindings)\n {\n if(binding.context === name)\n {\n binding.prime(currentState(binding));\n }\n }\n }\n\n deactivateContext(name : string) : void\n {\n this.#requireContext(name).active = false;\n }\n\n isActive(name : string) : boolean\n {\n return this.#contexts.get(name)?.active === true;\n }\n\n isRegistered(name : string) : boolean\n {\n return this.#contexts.has(name);\n }\n\n // Registers a binding under its own named context, or under none.\n addBinding(binding : Binding<unknown>) : void\n {\n this.#bindings.push(binding);\n }\n\n // Retires a binding entirely: the routing loop stops delivering through it and the next context\n // activation stops priming it. Idempotent -- removing a binding not held, or removing it twice, changes\n // nothing (input.md#action-contexts).\n removeBinding(binding : Binding<unknown>) : void\n {\n const index = this.#bindings.indexOf(binding);\n\n if(index !== -1)\n {\n this.#bindings.splice(index, 1);\n }\n }\n\n // Every binding currently registered, in the order it was added -- the same list the routing loop walks\n // and a context activation primes (input.md#action-contexts).\n list() : readonly Binding<unknown>[]\n {\n return [ ...this.#bindings ];\n }\n\n // True when the binding names no context, or names one that is active.\n isLive(binding : Binding<unknown>) : boolean\n {\n if(binding.context === undefined)\n {\n return true;\n }\n\n return this.isActive(binding.context);\n }\n\n #requireContext(name : string) : ContextEntry\n {\n const context = this.#contexts.get(name);\n\n if(context === undefined)\n {\n throw new Error(`No context is registered under the name \"${ name }\".`);\n }\n\n return context;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Action Registry\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// An action is registered by name before anything binds to it (input.md#actions).\nexport class ActionRegistry\n{\n readonly #actions = new Map<string, Action>();\n\n register(action : Action) : void\n {\n if(this.#actions.has(action.name))\n {\n throw new Error(`An action is already registered under the name \"${ action.name }\".`);\n }\n\n if(action.type === 'analog' && action.minValue !== undefined && action.maxValue !== undefined)\n {\n if(action.minValue > action.maxValue)\n {\n throw new Error(\n `Action \"${ action.name }\": minValue (${ action.minValue }) is above `\n + `maxValue (${ action.maxValue }).`\n );\n }\n }\n\n this.#actions.set(action.name, action);\n }\n\n get(name : string) : Action | undefined\n {\n return this.#actions.get(name);\n }\n\n list() : readonly Action[]\n {\n return [ ...this.#actions.values() ];\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Delivering an Action\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action, GameEvent, GameSimulationContext } from '@skewedaspect/sage-core';\n\nimport type { Binding } from './binding.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// A fired binding reaches every subscribed entity as a broadcast `action:<name>` event, carrying the\n// boolean or number the binding computed -- deciding which entities answer is game logic, not something\n// this delivers (events.md#delivery, input.md#actions).\nexport function deliverAction<State>(\n binding : Binding<State>,\n state : State,\n context : GameSimulationContext,\n senderID : string\n) : void\n{\n const fire = binding.process(state);\n\n if(fire === undefined)\n {\n return;\n }\n\n // The action name is only known at runtime, so the event map can never narrow `type` to a literal here --\n // the same gap sage-core's own GameEntity.emit() bridges by widening payload to `unknown` before the cast.\n const event : { type : string; senderID : string; payload : unknown } = {\n type: `action:${ binding.action.name }`,\n senderID,\n payload: fire.value,\n };\n\n context.bus.emit(event as GameEvent);\n}\n\n// A value action reaches every subscribed entity the same way, as a broadcast carrying the value the\n// routing loop computed for it. No one binding decides that value, so this takes the action and the value\n// rather than a binding (input.md#value-actions, input.md#the-routing-loop).\nexport function deliverValueAction(\n action : Action,\n value : number,\n context : GameSimulationContext,\n senderID : string\n) : void\n{\n const event : { type : string; senderID : string; payload : unknown } = {\n type: `action:${ action.name }`,\n senderID,\n payload: value,\n };\n\n context.bus.emit(event as GameEvent);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Input Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action, GameSimulationContext, Unsubscribe } from '@skewedaspect/sage-core';\n\n// Interfaces\nimport type { Disposable } from '../interfaces/lifecycle.ts';\n\n// Input\nimport type { Binding, DeviceType } from '../input/binding.ts';\nimport type { CandidateInput, InputCapture } from '../input/inputCapture.ts';\nimport type { GamepadState, KeyboardState, MouseState } from '../input/deviceState.ts';\n\nimport { ActionContextRegistry } from '../input/actionContext.ts';\nimport { ActionRegistry } from '../input/actionRegistry.ts';\nimport { deliverAction, deliverValueAction } from '../input/deliverAction.ts';\n\n// Utils\nimport { isBrowser } from '../utils/capabilities.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// `input:deviceChanged` is the library's own event, so `sage` augments the map core declares; a\n// `declare module` merges only into the module holding the interface (events.md#the-event-map).\ndeclare module '@skewedaspect/sage-core'\n{\n interface EventMap\n {\n 'input:deviceChanged' : DeviceChangedPayload;\n }\n}\n\nexport interface DeviceChangedPayload\n{\n deviceType : DeviceType;\n\n /** Undefined for the first deliberate input of a session, which replaces no device type. */\n previousDeviceType : DeviceType | undefined;\n}\n\n// The routing loop's own emissions carry this fixed sender, naming the routing loop itself and no entity,\n// so a test or a behavior can compare against it (input.md#the-routing-loop).\nexport const ROUTING_LOOP_SENDER_ID = 'sage-input';\n\n// An analog input is deliberate once its distance from rest passes this; a stick at rest drifts below it\n// for as long as its gamepad is connected (input.md#device-state-capture).\nexport const DELIBERATE_THRESHOLD = 0.2;\n\n// At rest. A gamepad button or axis at exactly this value is not deliberate, the same threshold InputCapture\n// applies to a candidate, and it is where a value action rests before the action's own bounds clamp it\n// (input.md#device-state-capture, input.md#value-actions).\nconst REST_VALUE = 0;\n\n// Raw mouse movement is measured in pixels, where the deliberate threshold would be no threshold at all\n// (input.md#device-state-capture).\nexport const MOVEMENT_THRESHOLD = 2;\n\n// A gamepad reports its buttons as numbers, so a worn trigger is held to the threshold the same as a stick.\nfunction isDeliberate(value : number) : boolean\n{\n return Math.abs(value - REST_VALUE) > DELIBERATE_THRESHOLD;\n}\n\nfunction isDeliberateMovement(x : number, y : number) : boolean\n{\n return Math.abs(x - REST_VALUE) > MOVEMENT_THRESHOLD || Math.abs(y - REST_VALUE) > MOVEMENT_THRESHOLD;\n}\n\ninterface MutableGamepadState\n{\n buttons : Record<string, number>;\n axes : Record<string, number>;\n}\n\n// One action's running total over the live value bindings driving it, for the pass being routed\n// (input.md#value-actions).\ninterface ValueActionSum\n{\n action : Action;\n value : number;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// `sage`'s owner of input: one action registry, one action context registry, device state capture, and the\n// routing loop that runs before each frame's fixed steps (input.md).\nexport class InputManager implements Disposable\n{\n readonly actions = new ActionRegistry();\n readonly contexts = new ActionContextRegistry();\n\n readonly #keys : Record<string, boolean> = {};\n readonly #mouseButtons : Record<string, boolean> = {};\n readonly #mousePosition = { x: 0, y: 0 };\n readonly #mouseWheel = { deltaX: 0, deltaY: 0, deltaZ: 0 };\n readonly #mouseMovement = { x: 0, y: 0 };\n readonly #gamepadStates = new Map<number, MutableGamepadState>();\n\n readonly #captures = new Set<InputCapture>();\n\n // Every value action the last pass delivered away from rest, by name. A pass that finds one back at\n // rest delivers that rest once and drops it from here (input.md#value-actions).\n readonly #valueActionsAwayFromRest = new Map<string, Action>();\n\n #lastActiveDeviceType : DeviceType | undefined;\n\n // What the previous routing pass left in #lastActiveDeviceType (input.md#the-routing-loop).\n #reportedDeviceType : DeviceType | undefined;\n\n readonly #handleKeyDown : (event : KeyboardEvent) => void;\n readonly #handleKeyUp : (event : KeyboardEvent) => void;\n readonly #handlePointerDown : (event : PointerEvent) => void;\n readonly #handlePointerUp : (event : PointerEvent) => void;\n readonly #handlePointerMove : (event : PointerEvent) => void;\n readonly #handleWheel : (event : WheelEvent) => void;\n\n constructor()\n {\n this.#handleKeyDown = (event) =>\n {\n if(this.#claims('keyboard', 'key', event.code))\n {\n event.preventDefault();\n }\n\n this.#keys[event.code] = true;\n this.#lastActiveDeviceType = 'keyboard';\n this.#feedCandidate({\n device: { type: 'keyboard', index: 0 }, sourceType: 'key', sourceKey: event.code, value: true,\n });\n };\n\n this.#handleKeyUp = (event) =>\n {\n this.#keys[event.code] = false;\n };\n\n this.#handlePointerDown = (event) =>\n {\n if(event.pointerType !== 'mouse') { return; }\n\n const sourceKey = String(event.button);\n\n if(this.#claims('mouse', 'button', sourceKey))\n {\n event.preventDefault();\n }\n\n this.#mouseButtons[sourceKey] = true;\n this.#lastActiveDeviceType = 'mouse';\n this.#feedCandidate({ device: { type: 'mouse', index: 0 }, sourceType: 'button', sourceKey, value: true });\n };\n\n this.#handlePointerUp = (event) =>\n {\n if(event.pointerType !== 'mouse') { return; }\n\n this.#mouseButtons[String(event.button)] = false;\n };\n\n this.#handlePointerMove = (event) =>\n {\n if(event.pointerType !== 'mouse') { return; }\n\n this.#mousePosition.x = event.clientX;\n this.#mousePosition.y = event.clientY;\n\n this.#mouseMovement.x += event.movementX;\n this.#mouseMovement.y += event.movementY;\n\n if(isDeliberateMovement(event.movementX, event.movementY))\n {\n this.#lastActiveDeviceType = 'mouse';\n }\n };\n\n this.#handleWheel = (event) =>\n {\n if(this.#claims('mouse', 'wheel'))\n {\n event.preventDefault();\n }\n\n this.#mouseWheel.deltaX = event.deltaX;\n this.#mouseWheel.deltaY = event.deltaY;\n this.#mouseWheel.deltaZ = event.deltaZ;\n\n if(event.deltaX !== REST_VALUE || event.deltaY !== REST_VALUE || event.deltaZ !== REST_VALUE)\n {\n this.#lastActiveDeviceType = 'mouse';\n this.#feedWheelCandidates(event);\n }\n };\n\n if(isBrowser())\n {\n window.addEventListener('keydown', this.#handleKeyDown);\n window.addEventListener('keyup', this.#handleKeyUp);\n window.addEventListener('pointerdown', this.#handlePointerDown);\n window.addEventListener('pointerup', this.#handlePointerUp);\n window.addEventListener('pointermove', this.#handlePointerMove);\n window.addEventListener('wheel', this.#handleWheel, { passive: false });\n }\n }\n\n // The device type behind the most recent deliberate input -- a button held true, or an analog value\n // away from zero -- undefined before any device has reported one (input.md#device-state-capture).\n get lastActiveDeviceType() : DeviceType | undefined\n {\n return this.#lastActiveDeviceType;\n }\n\n // The state currently captured for one device, the same shape every device reader and a context\n // activation's priming read (input.md#device-state-capture).\n deviceState(type : 'keyboard', index : number) : KeyboardState;\n deviceState(type : 'mouse', index : number) : MouseState;\n deviceState(type : 'gamepad', index : number) : GamepadState;\n deviceState(type : DeviceType, index : number) : KeyboardState | MouseState | GamepadState\n {\n return this.#captureFor(type, index);\n }\n\n // Feeds every deliberate candidate this manager's device state capture observes to the given capture,\n // while it holds a pending wait. Answers with a function that unregisters it (input.md#input-capture).\n registerCapture(capture : InputCapture) : Unsubscribe\n {\n this.#captures.add(capture);\n\n return () =>\n {\n this.#captures.delete(capture);\n };\n }\n\n // Every binding registered for the named action, filtered to a device type or a context when the\n // caller gives one (input.md#looking-up-bindings).\n getBindingsForAction(\n action : string,\n deviceType ?: DeviceType,\n context ?: string\n ) : readonly Binding<unknown>[]\n {\n const matches : Binding<unknown>[] = [];\n\n for(const binding of this.contexts.list())\n {\n const matchesAction = binding.action.name === action;\n const matchesDeviceType = deviceType === undefined || binding.device.type === deviceType;\n const matchesContext = context === undefined || binding.context === context;\n\n if(matchesAction && matchesDeviceType && matchesContext)\n {\n matches.push(binding);\n }\n }\n\n return matches;\n }\n\n // Captures device state, updates lastActiveDeviceType, and routes: every live trigger, toggle, and delta\n // binding hands its device's captured state to `deliverAction`, while the live value bindings are summed\n // by action and each action that has one is delivered once with the value that sum computes. Both\n // broadcast with `ROUTING_LOOP_SENDER_ID` (input.md#the-routing-loop, input.md#value-actions).\n routeCapturedInput(context : GameSimulationContext) : void\n {\n this.#pollGamepads();\n\n const sums = new Map<string, ValueActionSum>();\n\n for(const binding of this.contexts.list())\n {\n if(this.contexts.isLive(binding))\n {\n const state = this.#captureFor(binding.device.type, binding.device.index);\n\n if(binding.kind === 'value')\n {\n this.#addContribution(sums, binding, state);\n }\n else\n {\n deliverAction(binding, state, context, ROUTING_LOOP_SENDER_ID);\n }\n }\n }\n\n this.#deliverValueActions(sums, context);\n this.#reportDeviceChange(context);\n\n // A one-shot sample -- the mouse wheel -- resets once this pass has read it, so a still wheel\n // reports nothing on the next frame. Held state is never reset this way (input.md#device-state-capture).\n this.#mouseMovement.x = REST_VALUE;\n this.#mouseMovement.y = REST_VALUE;\n\n this.#mouseWheel.deltaX = REST_VALUE;\n this.#mouseWheel.deltaY = REST_VALUE;\n this.#mouseWheel.deltaZ = REST_VALUE;\n }\n\n // True while a live binding claims the captured input named. An undefined sourceKey matches any key of\n // that source type, which is what a wheel event needs: it carries every axis at once\n // (input.md#claiming-an-input-from-the-browser).\n #claims(deviceType : DeviceType, sourceType : string, sourceKey ?: string) : boolean\n {\n for(const binding of this.contexts.list())\n {\n if(binding.preventDefault && this.contexts.isLive(binding))\n {\n const { device, sourceType: boundType, sourceKey: boundKey } = binding.input;\n\n if(device.type === deviceType && boundType === sourceType\n && (sourceKey === undefined || boundKey === sourceKey))\n {\n return true;\n }\n }\n }\n\n return false;\n }\n\n async $teardown() : Promise<void>\n {\n if(isBrowser())\n {\n window.removeEventListener('keydown', this.#handleKeyDown);\n window.removeEventListener('keyup', this.#handleKeyUp);\n window.removeEventListener('pointerdown', this.#handlePointerDown);\n window.removeEventListener('pointerup', this.#handlePointerUp);\n window.removeEventListener('pointermove', this.#handlePointerMove);\n window.removeEventListener('wheel', this.#handleWheel);\n }\n }\n\n // Adds one value binding's contribution to its action's running total, starting that total when this is\n // the first binding the pass has seen for the action (input.md#value-actions).\n #addContribution(sums : Map<string, ValueActionSum>, binding : Binding<unknown>, state : unknown) : void\n {\n const fire = binding.process(state);\n\n if(fire === undefined)\n {\n return;\n }\n\n // `BindingFire` carries whatever any of the four kinds sends; a value binding's is always a number.\n const contribution = Number(fire.value);\n const running = sums.get(binding.action.name);\n\n if(running === undefined)\n {\n sums.set(binding.action.name, { action: binding.action, value: contribution });\n }\n else\n {\n running.value = running.value + contribution;\n }\n }\n\n // Delivers each value action on every pass it is away from rest, and once more on the pass it returns\n // to rest. An action left with no live value binding to contribute still settles: an empty total is\n // zero, which is rest (input.md#value-actions).\n #deliverValueActions(sums : Map<string, ValueActionSum>, context : GameSimulationContext) : void\n {\n for(const [ name, action ] of this.#valueActionsAwayFromRest)\n {\n if(!sums.has(name))\n {\n sums.set(name, { action, value: 0 });\n }\n }\n\n for(const [ name, sum ] of sums)\n {\n const value = this.#clampToAction(sum.action, sum.value);\n const awayFromRest = value !== this.#clampToAction(sum.action, REST_VALUE);\n const wasAwayFromRest = this.#valueActionsAwayFromRest.has(name);\n\n if(awayFromRest)\n {\n this.#valueActionsAwayFromRest.set(name, sum.action);\n }\n else\n {\n this.#valueActionsAwayFromRest.delete(name);\n }\n\n if(awayFromRest || wasAwayFromRest)\n {\n deliverValueAction(sum.action, value, context, ROUTING_LOOP_SENDER_ID);\n }\n }\n }\n\n // A value action's value stays inside the bounds its declaration set, its rest included\n // (input.md#value-actions).\n #clampToAction(action : Action, value : number) : number\n {\n if(action.type !== 'analog')\n {\n return value;\n }\n\n if(action.minValue !== undefined && value < action.minValue)\n {\n return action.minValue;\n }\n\n if(action.maxValue !== undefined && value > action.maxValue)\n {\n return action.maxValue;\n }\n\n return value;\n }\n\n #captureFor(type : DeviceType, index : number) : KeyboardState | MouseState | GamepadState\n {\n if(type === 'keyboard')\n {\n return { keys: this.#keys };\n }\n\n if(type === 'mouse')\n {\n return {\n buttons: this.#mouseButtons,\n position: this.#mousePosition,\n wheel: this.#mouseWheel,\n movement: this.#mouseMovement,\n };\n }\n\n const gamepad = this.#gamepadStates.get(index);\n\n if(gamepad === undefined)\n {\n return { buttons: {}, axes: {} };\n }\n\n return gamepad;\n }\n\n // Polls every connected gamepad (input.md#device-state-capture). Unlike the DOM listeners above, this\n // needs no window: `navigator.getGamepads` is a plain function, present or absent on its own account.\n #pollGamepads() : void\n {\n if(typeof navigator === 'undefined' || typeof navigator.getGamepads !== 'function')\n {\n return;\n }\n\n const gamepads = navigator.getGamepads();\n\n for(let index = 0; index < gamepads.length; index++)\n {\n const gamepad = gamepads[index];\n\n if(gamepad !== null)\n {\n this.#pollOneGamepad(index, gamepad);\n }\n }\n }\n\n #pollOneGamepad(index : number, gamepad : Gamepad) : void\n {\n const buttons : Record<string, number> = {};\n let deliberate = false;\n\n for(let buttonIndex = 0; buttonIndex < gamepad.buttons.length; buttonIndex++)\n {\n const value = gamepad.buttons[buttonIndex].value;\n const sourceKey = String(buttonIndex);\n\n buttons[sourceKey] = value;\n\n if(isDeliberate(value))\n {\n deliberate = true;\n this.#feedCandidate({ device: { type: 'gamepad', index }, sourceType: 'button', sourceKey, value });\n }\n }\n\n const axes : Record<string, number> = {};\n\n for(let axisIndex = 0; axisIndex < gamepad.axes.length; axisIndex++)\n {\n const value = gamepad.axes[axisIndex];\n const sourceKey = String(axisIndex);\n\n axes[sourceKey] = value;\n\n if(isDeliberate(value))\n {\n deliberate = true;\n this.#feedCandidate({ device: { type: 'gamepad', index }, sourceType: 'axis', sourceKey, value });\n }\n }\n\n this.#gamepadStates.set(index, { buttons, axes });\n\n if(deliberate)\n {\n this.#lastActiveDeviceType = 'gamepad';\n }\n }\n\n // Emitted when this pass leaves a different device type than the previous pass did: a broadcast under\n // the routing loop's own sender, alongside the actions it just delivered (input.md#the-routing-loop).\n #reportDeviceChange(context : GameSimulationContext) : void\n {\n if(this.#lastActiveDeviceType === this.#reportedDeviceType)\n {\n return;\n }\n\n const previousDeviceType = this.#reportedDeviceType;\n this.#reportedDeviceType = this.#lastActiveDeviceType;\n\n if(this.#lastActiveDeviceType === undefined)\n {\n return;\n }\n\n context.bus.emit({\n type: 'input:deviceChanged',\n senderID: ROUTING_LOOP_SENDER_ID,\n payload: { deviceType: this.#lastActiveDeviceType, previousDeviceType },\n });\n }\n\n // One candidate per axis the notch actually turned, so a rebind screen binds the axis the player\n // scrolled rather than whichever this reads first (input.md#input-capture).\n #feedWheelCandidates(event : WheelEvent) : void\n {\n const axes = [\n { sourceKey: 'deltaY', value: event.deltaY },\n { sourceKey: 'deltaX', value: event.deltaX },\n { sourceKey: 'deltaZ', value: event.deltaZ },\n ];\n\n for(const axis of axes)\n {\n if(axis.value !== REST_VALUE)\n {\n this.#feedCandidate({\n device: { type: 'mouse', index: 0 },\n sourceType: 'wheel',\n sourceKey: axis.sourceKey,\n value: axis.value,\n });\n }\n }\n }\n\n #feedCandidate(candidate : CandidateInput) : void\n {\n for(const capture of this.#captures)\n {\n capture.feed(candidate);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Post-Processing Pipeline Backend\n//\n// Applies post-processing effects via BabylonJS DefaultRenderingPipeline and SSAO2RenderingPipeline.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n Color4,\n DefaultRenderingPipeline,\n ImageProcessingConfiguration,\n SSAO2RenderingPipeline,\n type Scene,\n} from '@babylonjs/core';\n\nimport type { PostProcessingConfig } from '../interfaces/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Map from user-facing tonemap operator names to BabylonJS constants.\n * BabylonJS only has STANDARD (0) and ACES (1) — the other names all map to STANDARD.\n */\nconst TONEMAP_OPERATORS : Record<string, number> = {\n hable: ImageProcessingConfiguration.TONEMAPPING_STANDARD,\n reinhard: ImageProcessingConfiguration.TONEMAPPING_STANDARD,\n hejidawson: ImageProcessingConfiguration.TONEMAPPING_STANDARD,\n photographic: ImageProcessingConfiguration.TONEMAPPING_STANDARD,\n aces: ImageProcessingConfiguration.TONEMAPPING_ACES,\n};\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Apply post-processing effects to a scene based on a declarative config.\n * Creates BabylonJS rendering pipelines and configures them according to the provided settings.\n *\n * This is a level-config handler, not a node property handler.\n */\nexport function applyPipelinePostProcessing(scene : Scene, config : PostProcessingConfig) : void\n{\n // Can't set up post-processing without a camera\n if(!scene.activeCamera)\n {\n return;\n }\n\n // Warn about Frame Graph-only features\n if(config.volumetric)\n {\n console.warn('[SAGE] Volumetric lighting requires renderer: \"frameGraph\". Ignored in pipeline mode.');\n }\n\n // Create the default rendering pipeline\n const pipeline = new DefaultRenderingPipeline('sage-default', true, scene, scene.cameras);\n\n // Bloom\n if(config.bloom)\n {\n pipeline.bloomEnabled = true;\n\n if(config.bloom.weight !== undefined)\n {\n pipeline.bloomWeight = config.bloom.weight;\n }\n if(config.bloom.threshold !== undefined)\n {\n pipeline.bloomThreshold = config.bloom.threshold;\n }\n if(config.bloom.scale !== undefined)\n {\n pipeline.bloomScale = config.bloom.scale;\n }\n if(config.bloom.kernel !== undefined)\n {\n pipeline.bloomKernel = config.bloom.kernel;\n }\n }\n\n // Tone mapping\n if(config.tonemap)\n {\n pipeline.imageProcessingEnabled = true;\n pipeline.imageProcessing.toneMappingEnabled = true;\n\n if(config.tonemap.operator)\n {\n pipeline.imageProcessing.toneMappingType\n = TONEMAP_OPERATORS[config.tonemap.operator] ?? ImageProcessingConfiguration.TONEMAPPING_STANDARD;\n }\n }\n\n // Film grain\n if(config.grain)\n {\n pipeline.grainEnabled = true;\n\n if(config.grain.intensity !== undefined)\n {\n pipeline.grain.intensity = config.grain.intensity;\n }\n if(config.grain.animated !== undefined)\n {\n pipeline.grain.animated = config.grain.animated;\n }\n }\n\n // Vignette\n if(config.vignette)\n {\n pipeline.imageProcessingEnabled = true;\n pipeline.imageProcessing.vignetteEnabled = true;\n\n if(config.vignette.weight !== undefined)\n {\n pipeline.imageProcessing.vignetteWeight = config.vignette.weight;\n }\n if(config.vignette.stretch !== undefined)\n {\n pipeline.imageProcessing.vignetteStretch = config.vignette.stretch;\n }\n if(config.vignette.color)\n {\n pipeline.imageProcessing.vignetteColor = new Color4(\n config.vignette.color.r,\n config.vignette.color.g,\n config.vignette.color.b,\n 1\n );\n }\n }\n\n // Sharpen\n if(config.sharpen)\n {\n pipeline.sharpenEnabled = true;\n\n if(config.sharpen.edge !== undefined)\n {\n pipeline.sharpen.edgeAmount = config.sharpen.edge;\n }\n if(config.sharpen.color !== undefined)\n {\n pipeline.sharpen.colorAmount = config.sharpen.color;\n }\n }\n\n // Chromatic aberration\n if(config.chromaticAberration)\n {\n pipeline.chromaticAberrationEnabled = true;\n\n if(config.chromaticAberration.amount !== undefined)\n {\n pipeline.chromaticAberration.aberrationAmount = config.chromaticAberration.amount;\n }\n }\n\n // SSAO — uses its own separate pipeline\n if(config.ssao)\n {\n const ssao = new SSAO2RenderingPipeline('sage-ssao', scene, { ssaoRatio: 0.5, blurRatio: 1.0 });\n\n if(config.ssao.radius !== undefined)\n {\n ssao.radius = config.ssao.radius;\n }\n if(config.ssao.samples !== undefined)\n {\n ssao.samples = config.ssao.samples;\n }\n if(config.ssao.totalStrength !== undefined)\n {\n ssao.totalStrength = config.ssao.totalStrength;\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Post-Processing Dispatcher\n//\n// Routes to either the DefaultRenderingPipeline backend or the Frame Graph backend\n// based on config.renderer.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Scene } from '@babylonjs/core';\n\nimport type { PostProcessingConfig } from '../interfaces/level.ts';\n\nimport { applyPipelinePostProcessing } from './postProcessingPipeline.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport async function applyPostProcessing(scene : Scene, config : PostProcessingConfig) : Promise<void>\n{\n if(config.renderer === 'frameGraph')\n {\n const { applyFrameGraphPostProcessing } = await import('./postProcessingFrameGraph.ts');\n await applyFrameGraphPostProcessing(scene, config);\n }\n else\n {\n applyPipelinePostProcessing(scene, config);\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Vector / Quaternion Conversion Utilities\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Matrix, Quaternion, type TransformNode, Vector3 } from '@babylonjs/core';\n\nimport type { QuatConfig, Vec3Config } from '../interfaces/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Convert a BabylonJS Vector3 to a plain { x, y, z } object.\n */\nexport function toVec3Object(vector : Vector3) : Vec3Config\n{\n return { x: vector.x, y: vector.y, z: vector.z };\n}\n\n/**\n * Convert a plain { x, y, z } object to a BabylonJS Vector3.\n */\nexport function toVector3(vec : Vec3Config) : Vector3\n{\n return new Vector3(vec.x, vec.y, vec.z);\n}\n\n/**\n * Convert a BabylonJS Quaternion to a plain { x, y, z, w } object.\n */\nexport function toQuatObject(quat : Quaternion) : QuatConfig\n{\n return { x: quat.x, y: quat.y, z: quat.z, w: quat.w }; // eslint-disable-line id-length\n}\n\n/**\n * Convert a plain { x, y, z, w } object to a BabylonJS Quaternion.\n */\nexport function toQuaternion(quat : QuatConfig) : Quaternion\n{\n return new Quaternion(quat.x, quat.y, quat.z, quat.w);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// Transform Hierarchy Utilities\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Compute a node's transform in canonical Babylon space by walking the parent\n * chain and individually cancelling each import conversion ancestor.\n *\n * For each ancestor, computes its local contribution from BabylonJS's own world\n * matrices. If that local contribution has negative determinant (import conversion\n * node like glTF's __root__), it is individually cancelled from the running matrix\n * via left-multiplication by its inverse.\n *\n * Each conversion node is handled one at a time — no assumptions about hierarchy\n * depth or which node is \"the\" root. Handles arbitrarily nested import hierarchies.\n */\nexport function getCanonicalTransform(node : TransformNode) : {\n position : Vector3;\n rotation : Quaternion;\n scaling : Vector3;\n}\n{\n node.computeWorldMatrix(true);\n let canonical = node.getWorldMatrix().clone();\n\n // Walk up the parent chain. For each ancestor, compute its local contribution\n // (the difference between its world and its parent's world). If that local\n // has negative determinant, cancel it from the canonical matrix.\n let ancestor = node.parent as TransformNode | null;\n while(ancestor)\n {\n ancestor.computeWorldMatrix(true);\n const ancestorWorld = ancestor.getWorldMatrix();\n\n // Compute this ancestor's local contribution\n const parentOfAncestor = ancestor.parent as TransformNode | null;\n let ancestorLocal : Matrix;\n\n if(parentOfAncestor)\n {\n parentOfAncestor.computeWorldMatrix(true);\n ancestorLocal = ancestorWorld.multiply(Matrix.Invert(parentOfAncestor.getWorldMatrix()));\n }\n else\n {\n ancestorLocal = ancestorWorld;\n }\n\n if(ancestorLocal.determinant() < 0)\n {\n canonical = Matrix.Invert(ancestorLocal).multiply(canonical);\n }\n\n ancestor = parentOfAncestor;\n }\n\n const position = new Vector3();\n const rotation = new Quaternion();\n const scaling = new Vector3();\n canonical.decompose(scaling, rotation, position);\n\n return { position, rotation, scaling };\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Abstract Level Class\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Color3 } from '@babylonjs/core';\nimport { ClusteredLightContainer } from '@babylonjs/core/Lights/Clustered/clusteredLightContainer';\n\nimport type { Scene, TransformNode } from '@babylonjs/core';\nimport type { NodeSimulation } from '../entities/nodeSimulation.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type {\n ColorConfig,\n LevelConfig,\n LevelContext,\n LevelInstance,\n LevelLoadOptions,\n PropertyHandler,\n} from '../interfaces/level.ts';\n\nimport { SAGELogger } from '../utils/logger.ts';\nimport type { GameEngine } from './gameEngine.ts';\nimport { OutlineManager } from '../managers/outline.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract base class for game levels.\n *\n * Levels are responsible for creating and configuring their own scenes via the\n * `buildScene()` method. All runtime dependencies (eventBus, sceneEngine, simulation)\n * are injected via the constructor and are immediately available.\n *\n * Users should not instantiate Level subclasses directly. Instead, use LevelManager\n * which acts as a factory and injects the required dependencies:\n *\n * ```typescript\n * // Register a custom level class\n * levelManager.registerLevelClass('my-level', MyCustomLevel);\n *\n * // Load the level - manager creates instance with deps injected\n * const level = await levelManager.loadLevel({ name: 'Level1', class: 'my-level' });\n * ```\n */\nexport abstract class Level implements LevelInstance\n{\n public readonly name : string;\n protected readonly _log : LoggerInterface;\n protected readonly _context : LevelContext;\n protected _scene : Scene | null = null;\n protected _forRestore = false;\n\n /**\n * The simulation this level's scene was built against. A transition loads the next level before\n * disposing this one, so by the time this level despawns what it spawned, the engine already holds the\n * next level's simulation, minting the same IDs.\n */\n protected _simulation : NodeSimulation | null = null;\n public clusteredLights : ClusteredLightContainer | null = null;\n public outlines : OutlineManager | null = null;\n\n //------------------------------------------------------------------------------------------------------------------\n // Constructor\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Creates a new Level instance.\n *\n * @param config\n * @param context\n */\n constructor(config : LevelConfig, context : LevelContext)\n {\n this.name = config.name;\n this._context = context;\n this._log = context.logger?.getLogger(`Level:${ config.name }`)\n ?? new SAGELogger(`Level:${ config.name }`, 'info');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Accessors\n //------------------------------------------------------------------------------------------------------------------\n\n get scene() : Scene | null\n {\n return this._scene;\n }\n\n get isLoaded() : boolean\n {\n return this._scene !== null;\n }\n\n get gameEngine() : GameEngine\n {\n return this._context.gameEngine;\n }\n\n get propertyHandlers() : Map<string, PropertyHandler>\n {\n return this._context.propertyHandlers;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Protected Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Emit a progress event during loading\n *\n * @param progress - Percentage (0-100)\n * @param message\n */\n /**\n * The simulation this level owns, or nothing for a level that never built one. The level manager binds\n * the engine to the current level's simulation (design/simulation.md#which-simulation-the-engine-holds).\n */\n get $simulation() : NodeSimulation | null\n {\n return this._simulation;\n }\n\n /**\n * Take over a simulation built to replace this level's own, as a save load does once it has restored\n * into a fresh one.\n *\n * @param simulation\n */\n $adoptSimulation(simulation : NodeSimulation) : void\n {\n this._simulation = simulation;\n }\n\n protected $emitProgress(progress : number, message : string) : void\n {\n this.gameEngine.eventBus.publish({\n type: 'level:progress',\n payload: {\n levelName: this.name,\n progress,\n message,\n },\n });\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n protected _toColor3(color : ColorConfig) : Color3\n {\n return new Color3(color.r, color.g, color.b);\n }\n\n /**\n * Get normalized metadata from a node, handling glTF extras.\n *\n * Babylon.js stores glTF custom properties in node.metadata.gltf.extras,\n * so we need to extract them for easier access.\n */\n protected _getNormalizedMetadata(node : TransformNode) : Record<string, unknown> | null\n {\n if(!node.metadata)\n {\n return null;\n }\n\n // Check for glTF extras (from imported GLB/GLTF files)\n const gltfExtras = node.metadata?.gltf?.extras as Record<string, unknown> | undefined;\n\n if(gltfExtras)\n {\n // Merge glTF extras with any existing top-level metadata\n return { ...gltfExtras };\n }\n\n // Return existing metadata if no glTF extras\n return node.metadata as Record<string, unknown>;\n }\n\n /**\n * Run registered property handlers on a node.\n */\n protected async _runPropertyHandlers(node : TransformNode) : Promise<void>\n {\n for(const [ property, handler ] of this.propertyHandlers)\n {\n if(property in node.metadata)\n {\n try\n {\n // eslint-disable-next-line no-await-in-loop\n await handler(node, node.metadata[property], this, this.gameEngine);\n }\n catch (error)\n {\n this._log.error(\n `Error in property handler '${ property }' for node '${ node.name }':`,\n error\n );\n }\n }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Load and create the scene for this level.\n * Progress events will be emitted during loading.\n */\n async load(options ?: LevelLoadOptions) : Promise<Scene>\n {\n if(this._scene)\n {\n this._log.warn(`Level ${ this.name } is already loaded`);\n return this._scene;\n }\n\n this._forRestore = options?.forRestore ?? false;\n\n this._log.info(`Loading level: ${ this.name }`);\n this._log.time(`level-${ this.name }-load`);\n\n try\n {\n this.$emitProgress(0, 'Starting level load...');\n\n // Let the concrete level build its content\n this._scene = await this.buildScene();\n\n this.$emitProgress(100, 'Level loaded successfully');\n this.gameEngine.eventBus.publish({\n type: 'level:complete',\n payload: {\n levelName: this.name,\n message: 'Level loaded successfully',\n level: this,\n },\n });\n\n this._log.timeEnd(`level-${ this.name }-load`);\n this._log.info(`Level ${ this.name } loaded successfully`);\n\n return this._scene;\n }\n catch (error)\n {\n this._log.error(`Failed to load level ${ this.name }:`, error);\n this.gameEngine.eventBus.publish({\n type: 'level:error',\n payload: {\n levelName: this.name,\n message: 'Failed to load level',\n error,\n },\n });\n throw error;\n }\n }\n\n /**\n * Abstract method for building the scene content.\n * Concrete levels must implement this to create their specific content.\n *\n * All dependencies are available via `this.gameEngine`.\n *\n */\n protected abstract buildScene() : Promise<Scene>;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Called when this level becomes the active level during a transition.\n * Override in subclasses to perform activation logic (e.g., start music, show UI).\n * Default implementation is a no-op.\n */\n onActivate?() : Promise<void> | void;\n\n /**\n * Called when this level is being replaced during a transition.\n * Override in subclasses to perform cleanup logic (e.g., fade out, save state).\n * Default implementation is a no-op.\n */\n onDeactivate?() : Promise<void> | void;\n\n /**\n * Dispose of this level's resources and scene.\n *\n * The `$` prefix indicates this is an internal lifecycle method, consistent with\n * other engine lifecycle methods like `$teardown()`.\n */\n async $dispose() : Promise<void>\n {\n if(this._scene)\n {\n this._log.info(`Disposing level: ${ this.name }`);\n\n if(this.outlines)\n {\n this.outlines.dispose();\n this.outlines = null;\n }\n\n if(this.clusteredLights)\n {\n this.clusteredLights.dispose();\n this.clusteredLights = null;\n }\n\n this._scene.dispose();\n this._scene = null;\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Outline Manager\n//\n// Manages named SelectionOutlineLayer instances for entity highlighting.\n// Each layer tracks a Set<NodeEntity> and rebuilds its selection on any change.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Color3, type Scene, SelectionOutlineLayer } from '@babylonjs/core';\n\nimport type { NodeEntity } from '../entities/nodeEntity.ts';\nimport type { OutlineLayerConfig } from '../interfaces/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface OutlineLayerEntry\n{\n layer : SelectionOutlineLayer;\n entities : Set<NodeEntity>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class OutlineManager\n{\n private _scene : Scene;\n private _layers = new Map<string, OutlineLayerEntry>();\n\n constructor(scene : Scene, configs ?: Record<string, OutlineLayerConfig>)\n {\n this._scene = scene;\n\n if(configs)\n {\n for(const [ name, config ] of Object.entries(configs))\n {\n this.create(name, config);\n }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n get(name : string) : SelectionOutlineLayer | undefined\n {\n return this._layers.get(name)?.layer;\n }\n\n create(name : string, config ?: OutlineLayerConfig) : SelectionOutlineLayer\n {\n const existing = this._layers.get(name);\n if(existing)\n {\n return existing.layer;\n }\n\n const layer = new SelectionOutlineLayer(`sage-outline-${ name }`, this._scene);\n\n if(config?.color)\n {\n layer.outlineColor = new Color3(config.color.r, config.color.g, config.color.b);\n }\n\n if(config?.thickness !== undefined) { layer.outlineThickness = config.thickness; }\n if(config?.occlusionStrength !== undefined) { layer.occlusionStrength = config.occlusionStrength; }\n\n this._layers.set(name, { layer, entities: new Set() });\n return layer;\n }\n\n highlightEntity(layerName : string, entity : NodeEntity) : void\n {\n const entry = this._layers.get(layerName);\n if(!entry)\n {\n return;\n }\n\n entry.entities.add(entity);\n this._rebuildSelection(entry);\n }\n\n unhighlightEntity(layerName : string, entity : NodeEntity) : void\n {\n const entry = this._layers.get(layerName);\n if(!entry)\n {\n return;\n }\n\n entry.entities.delete(entity);\n this._rebuildSelection(entry);\n }\n\n unhighlightEntityAll(entity : NodeEntity) : void\n {\n for(const entry of this._layers.values())\n {\n if(entry.entities.delete(entity))\n {\n this._rebuildSelection(entry);\n }\n }\n }\n\n clear(layerName : string) : void\n {\n const entry = this._layers.get(layerName);\n if(!entry)\n {\n return;\n }\n\n entry.entities.clear();\n entry.layer.clearSelection();\n }\n\n dispose() : void\n {\n for(const entry of this._layers.values())\n {\n entry.entities.clear();\n entry.layer.dispose();\n }\n\n this._layers.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private\n //------------------------------------------------------------------------------------------------------------------\n\n private _rebuildSelection(entry : OutlineLayerEntry) : void\n {\n entry.layer.clearSelection();\n\n for(const entity of entry.entities)\n {\n const meshes = entity.node.getChildMeshes();\n\n if(meshes.length > 0)\n {\n entry.layer.addSelection(meshes);\n }\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// GameLevel Class - Default Level Implementation\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n ArcRotateCamera,\n type Camera,\n type Color3,\n CubeTexture,\n DirectionalLight,\n FreeCamera,\n HDRCubeTexture,\n HemisphericLight,\n MeshBuilder,\n PBRMaterial,\n PointLight,\n Quaternion,\n RectAreaLight,\n Scene,\n SoundState,\n SpotLight,\n StandardMaterial,\n type StaticSound,\n Texture,\n TransformNode,\n UniversalCamera,\n Vector3,\n} from '@babylonjs/core';\nimport { GeospatialCamera } from '@babylonjs/core/Cameras/geospatialCamera';\nimport { ClusteredLightContainer } from '@babylonjs/core/Lights/Clustered/clusteredLightContainer';\n\nimport type {\n CameraDefinition,\n EntityDefinition,\n LevelConfig,\n LevelContext,\n LightDefinition,\n SpawnDefinition,\n} from '../interfaces/level.ts';\nimport { applyPostProcessing } from '../handlers/postProcessing.ts';\nimport { getCanonicalTransform, toQuatObject, toVec3Object, toVector3 } from '../utils/vectors.ts';\n\nimport type { StateBag } from '@skewedaspect/sage-core';\nimport type { NodeEntity } from '../entities/nodeEntity.ts';\nimport type { NodeSimulation } from '../entities/nodeSimulation.ts';\nimport { Level } from './level.ts';\nimport { OutlineManager } from '../managers/outline.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Metadata collected from a spawn point node.\n * Transforms are normalized to canonical Babylon space — the source level's __root__\n * conversion is removed so these values are clean for any entity type.\n */\ninterface SpawnPointData\n{\n name : string;\n position : Vector3;\n rotation : Quaternion;\n scaling : Vector3;\n node : TransformNode;\n}\n\n/**\n * Metadata collected from an entity node\n */\ninterface EntityNodeData\n{\n type : string;\n node : TransformNode;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Default Level implementation that loads from YAML configuration.\n * Supports property handlers for processing scene node metadata.\n */\nexport class GameLevel extends Level\n{\n /** The level configuration */\n protected _config : LevelConfig;\n\n /** Collected spawn points from the scene */\n protected _spawnPoints : SpawnPointData[] = [];\n\n /** Collected entity nodes from the scene */\n protected _entityNodes : EntityNodeData[] = [];\n\n /** Entities spawned by this level */\n protected _spawnedEntities : NodeEntity[] = [];\n\n /** Level-scoped sounds created from config */\n protected _levelSounds = new Map<string, StaticSound>();\n\n /** Sounds that were playing before deactivation (for resume on activate) */\n private _playingSoundsBeforeDeactivate = new Set<string>();\n\n //------------------------------------------------------------------------------------------------------------------\n // Constructor\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a GameLevel from a configuration object\n *\n * @param config\n * @param context\n */\n constructor(config : LevelConfig, context : LevelContext)\n {\n super(config, context);\n this._config = config;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Accessors\n //------------------------------------------------------------------------------------------------------------------\n\n get config() : LevelConfig\n {\n return this._config;\n }\n\n get spawnedEntities() : readonly NodeEntity[]\n {\n return this._spawnedEntities;\n }\n\n /**\n * The simulation this level built its scene against: the one its own spawns and despawns go through,\n * whichever simulation the engine holds at the time. Throws before `buildScene` has bound one.\n */\n protected get simulation() : NodeSimulation\n {\n if(this._simulation === null)\n {\n throw new Error(`Level ${ this.name }: rebuildSimulation has not run against this level's scene yet.`);\n }\n\n return this._simulation;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Protected Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Build the scene by loading assets, processing node metadata, and spawning entities.\n */\n protected async buildScene() : Promise<Scene>\n {\n const { sceneEngine } = this.gameEngine.engines;\n const { assetManager } = this.gameEngine.managers;\n\n // Preload assets if configured\n if(this._config.preload && this._config.preload.length > 0)\n {\n this.$emitProgress(2, 'Preloading assets...');\n await assetManager.preload(this._config.preload);\n }\n\n // Create an empty scene and assign it early so property handlers can access it\n const scene = sceneEngine.createScene();\n this._scene = scene;\n\n // A NodeEntity always constructs its node in the simulation's own scene, so the simulation the level\n // spawns through has to be bound to this scene before anything below can spawn.\n this._simulation = this.gameEngine.rebuildSimulation(scene, this._config.meshSource);\n\n // Enable physics if configured (must be done before loading scene with colliders)\n if(this._config.physics)\n {\n this.$emitProgress(5, 'Initializing physics...');\n await this._enablePhysics(scene);\n }\n\n // Load the scene file if specified\n if(this._config.scene)\n {\n this.$emitProgress(10, 'Loading scene file...');\n await this._loadSceneFile(scene);\n }\n\n // Set up environment (skybox + IBL) — before cameras so the background is ready\n if(this._config.environment)\n {\n this.$emitProgress(15, 'Setting up environment...');\n this._processEnvironment(scene);\n }\n\n // Process cameras (must happen before post-processing, which requires activeCamera)\n this._processCameras(scene);\n\n // Process lights (override Blender-imported lights, create new ones)\n this._processLights(scene);\n\n // Set up clustered lighting if configured (must be after _processLights)\n this._setupClustering(scene);\n\n // Apply post-processing effects if configured\n if(this._config.postProcessing)\n {\n const renderer = this._config.postProcessing.renderer ?? 'pipeline';\n this._log.info(`Applying post-processing with ${ renderer } renderer`);\n await applyPostProcessing(scene, this._config.postProcessing);\n }\n\n // Create outline manager for entity highlighting\n this.outlines = new OutlineManager(scene, this._config.outlines);\n\n // Process all nodes for property handlers\n this.$emitProgress(50, 'Processing scene properties...');\n await this._processNodeProperties(scene);\n\n // A level loaded to restore a save spawns nothing of its own: the restored snapshot is the sole\n // source of entities, and adoption re-binds through the nodeName already in restored state\n // (design/simulation.md#saved-games).\n if(!this._forRestore)\n {\n // Process spawn points\n this.$emitProgress(70, 'Spawning entities...');\n await this._processSpawnPoints();\n\n // Process entity nodes\n this.$emitProgress(85, 'Configuring entities...');\n this._processEntityNodes();\n }\n\n // Load level-scoped sounds (last — everything else is ready)\n if(this._config.sounds)\n {\n this.$emitProgress(95, 'Loading sounds...');\n await this._processLevelSounds();\n }\n\n return scene;\n }\n\n /**\n * Import all meshes from the configured scene file (GLB/GLTF/Babylon) into the scene.\n */\n private async _loadSceneFile(scene : Scene) : Promise<void>\n {\n const scenePath = this._getScenePath();\n if(!scenePath)\n {\n return;\n }\n\n this._log.debug(`Loading scene file: ${ scenePath }`);\n\n // Set right-handed coordinate system before loading so loaders respect it\n if(this._isRightHanded())\n {\n scene.useRightHandedSystem = true;\n this._log.debug('Scene set to right-handed coordinate system');\n }\n\n try\n {\n await this.gameEngine.engines.sceneEngine.importMeshes([], scenePath, scene);\n this._log.debug('Scene file loaded successfully');\n }\n catch (error)\n {\n this._log.error(`Failed to load scene file: ${ scenePath }`, error);\n throw error;\n }\n }\n\n /**\n * Extract the scene file path from the config, which can be a string or SceneConfig object.\n */\n private _getScenePath() : string | undefined\n {\n if(!this._config.scene)\n {\n return undefined;\n }\n\n return typeof this._config.scene === 'string'\n ? this._config.scene\n : this._config.scene.path;\n }\n\n /**\n * Check if the scene config specifies a right-handed coordinate system.\n */\n private _isRightHanded() : boolean\n {\n if(!this._config.scene || typeof this._config.scene === 'string')\n {\n return false;\n }\n\n return this._config.scene.rightHanded === true;\n }\n\n /**\n * Set up the scene environment: IBL (image-based lighting) for PBR reflections and/or a visible skybox.\n */\n private _processEnvironment(scene : Scene) : void\n {\n const env = this._config.environment;\n if(!env) { return; }\n\n // Set up IBL for PBR reflections\n if(env.ibl)\n {\n scene.environmentTexture = this._createEnvironmentTexture(env.ibl, env.iblResolution ?? 256, scene);\n this._log.debug(`IBL set from: ${ env.ibl }`);\n }\n\n // Set up visible skybox\n if(env.skybox)\n {\n const ext = this._getFileExtension(env.skybox);\n\n if(ext === '.hdr' || ext === '.env')\n {\n // HDR/env skybox — use as IBL too if no separate IBL was specified\n if(!env.ibl)\n {\n const res = env.iblResolution ?? 256;\n scene.environmentTexture = this._createEnvironmentTexture(env.skybox, res, scene);\n }\n\n if(scene.environmentTexture)\n {\n scene.createDefaultSkybox(scene.environmentTexture, true, env.skyboxSize ?? 1000, 0, false);\n }\n }\n else\n {\n // JPG/PNG — create a textured sphere\n const size = env.skyboxSize ?? 1000;\n const skybox = MeshBuilder.CreateSphere('skybox', { diameter: size, segments: 32 }, scene);\n\n const mat = new StandardMaterial('skybox-mat', scene);\n mat.backFaceCulling = false;\n mat.disableLighting = true;\n mat.emissiveTexture = new Texture(env.skybox, scene);\n skybox.material = mat;\n skybox.infiniteDistance = true;\n }\n\n this._log.debug(`Skybox set from: ${ env.skybox }`);\n }\n\n // Apply rotation to environment texture\n if(env.rotation !== undefined && scene.environmentTexture)\n {\n (scene.environmentTexture as HDRCubeTexture).rotationY = env.rotation;\n }\n }\n\n /**\n * Create an environment texture from an HDR or env file.\n */\n private _createEnvironmentTexture(path : string, resolution : number, scene : Scene) : CubeTexture | HDRCubeTexture\n {\n const ext = this._getFileExtension(path);\n\n if(ext === '.env')\n {\n return CubeTexture.CreateFromPrefilteredData(path, scene);\n }\n\n return new HDRCubeTexture(path, scene, resolution);\n }\n\n private _getFileExtension(path : string) : string\n {\n const dotIndex = path.lastIndexOf('.');\n return dotIndex >= 0 ? path.slice(dotIndex).toLowerCase() : '';\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process cameras: apply YAML overrides to imported cameras, create new ones, and set the active camera.\n * If no YAML cameras config exists, the first imported camera (if any) is activated.\n */\n private _processCameras(scene : Scene) : void\n {\n const cameraConfigs = this._config.cameras;\n const canvas = this.gameEngine.canvas as HTMLCanvasElement | null;\n\n if(!cameraConfigs)\n {\n // No YAML config — if the scene has imported cameras, activate the first one\n if(scene.cameras.length > 0)\n {\n scene.activeCamera = scene.cameras[0];\n\n if(canvas)\n {\n scene.activeCamera.attachControl(canvas, true);\n }\n\n this._log.debug(`Activated imported camera: \"${ scene.activeCamera.name }\"`);\n }\n\n return;\n }\n\n let activeCamera : Camera | null = null;\n\n for(const [ name, config ] of Object.entries(cameraConfigs))\n {\n const existing = scene.getCameraByName(name);\n\n if(existing)\n {\n this._applyCameraConfig(existing, config);\n this._log.debug(`Applied overrides to camera: \"${ name }\"`);\n\n if(!activeCamera || config.active)\n {\n activeCamera = existing;\n }\n }\n else if(config.type)\n {\n const camera = this._createCamera(name, config, scene);\n this._log.debug(`Created ${ config.type } camera: \"${ name }\"`);\n\n if(!activeCamera || config.active)\n {\n activeCamera = camera;\n }\n }\n else\n {\n this._log.warn(\n `Camera \"${ name }\" not found in scene and no type specified — skipping`\n );\n }\n }\n\n if(activeCamera)\n {\n scene.activeCamera = activeCamera;\n\n const activeDef = cameraConfigs[activeCamera.name];\n if(canvas && activeDef?.attachControl !== false)\n {\n activeCamera.attachControl(canvas, true);\n }\n }\n }\n\n /**\n * Create a new camera from a YAML definition.\n */\n private _createCamera(name : string, config : CameraDefinition, scene : Scene) : Camera\n {\n const pos = config.position ? toVector3(config.position) : Vector3.Zero();\n\n let camera : Camera;\n\n switch (config.type)\n {\n case 'arcRotate':\n {\n const target = config.target ? toVector3(config.target) : Vector3.Zero();\n\n camera = new ArcRotateCamera(\n name,\n config.alpha ?? Math.PI / 2,\n config.beta ?? Math.PI / 3,\n config.radius ?? 10,\n target,\n scene\n );\n break;\n }\n\n case 'universal':\n {\n camera = new UniversalCamera(name, pos, scene);\n break;\n }\n\n case 'geospatial':\n {\n if(!config.planetRadius)\n {\n this._log.warn(`Geospatial camera \"${ name }\" requires planetRadius -- skipping`);\n return new FreeCamera(name, Vector3.Zero(), scene);\n }\n\n const cam = new GeospatialCamera(name, scene, {\n planetRadius: config.planetRadius,\n });\n\n if(config.center) { cam.center = toVector3(config.center); }\n if(config.yaw !== undefined) { cam.yaw = config.yaw; }\n if(config.pitch !== undefined) { cam.pitch = config.pitch; }\n if(config.radius !== undefined) { cam.radius = config.radius; }\n if(config.checkCollisions !== undefined) { cam.checkCollisions = config.checkCollisions; }\n\n if(config.radiusMin !== undefined) { cam.limits.radiusMin = config.radiusMin; }\n if(config.radiusMax !== undefined) { cam.limits.radiusMax = config.radiusMax; }\n if(config.pitchMin !== undefined) { cam.limits.pitchMin = config.pitchMin; }\n if(config.pitchMax !== undefined) { cam.limits.pitchMax = config.pitchMax; }\n if(config.yawMin !== undefined) { cam.limits.yawMin = config.yawMin; }\n if(config.yawMax !== undefined) { cam.limits.yawMax = config.yawMax; }\n\n camera = cam;\n break;\n }\n\n case 'free':\n default:\n {\n camera = new FreeCamera(name, pos, scene);\n break;\n }\n }\n\n this._applyCameraConfig(camera, config);\n return camera;\n }\n\n /**\n * Apply shared camera properties from a config to an existing camera.\n */\n private _applyCameraConfig(camera : Camera, config : CameraDefinition) : void\n {\n if(config.fov !== undefined) { camera.fov = config.fov; }\n if(config.position) { camera.position = toVector3(config.position); }\n if(config.minZ !== undefined) { camera.minZ = config.minZ; }\n if(config.maxZ !== undefined) { camera.maxZ = config.maxZ; }\n\n if(camera instanceof FreeCamera)\n {\n if(config.speed !== undefined) { camera.speed = config.speed; }\n if(config.rotation) { camera.rotation = toVector3(config.rotation); }\n }\n\n if(camera instanceof ArcRotateCamera)\n {\n if(config.target) { camera.setTarget(toVector3(config.target)); }\n if(config.lowerRadiusLimit !== undefined) { camera.lowerRadiusLimit = config.lowerRadiusLimit; }\n if(config.upperRadiusLimit !== undefined) { camera.upperRadiusLimit = config.upperRadiusLimit; }\n if(config.lowerBetaLimit !== undefined) { camera.lowerBetaLimit = config.lowerBetaLimit; }\n if(config.upperBetaLimit !== undefined) { camera.upperBetaLimit = config.upperBetaLimit; }\n if(config.wheelPrecision !== undefined) { camera.wheelPrecision = config.wheelPrecision; }\n }\n\n if(camera instanceof GeospatialCamera)\n {\n if(config.yaw !== undefined) { camera.yaw = config.yaw; }\n if(config.pitch !== undefined) { camera.pitch = config.pitch; }\n if(config.radius !== undefined) { camera.radius = config.radius; }\n if(config.center) { camera.center = toVector3(config.center); }\n if(config.checkCollisions !== undefined) { camera.checkCollisions = config.checkCollisions; }\n if(config.radiusMin !== undefined) { camera.limits.radiusMin = config.radiusMin; }\n if(config.radiusMax !== undefined) { camera.limits.radiusMax = config.radiusMax; }\n if(config.pitchMin !== undefined) { camera.limits.pitchMin = config.pitchMin; }\n if(config.pitchMax !== undefined) { camera.limits.pitchMax = config.pitchMax; }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process lights: apply YAML overrides to imported lights and create new ones.\n * If no YAML lights config exists, imported lights are left unchanged.\n */\n private _processLights(scene : Scene) : void\n {\n const lightConfigs = this._config.lights;\n if(!lightConfigs) { return; }\n\n for(const [ name, config ] of Object.entries(lightConfigs))\n {\n const existing = scene.getLightByName(name);\n\n if(existing)\n {\n this._applyLightConfig(existing, config);\n this._log.debug(`Applied overrides to light: \"${ name }\"`);\n }\n else if(config.type)\n {\n this._createLight(name, config, scene);\n this._log.debug(`Created ${ config.type } light: \"${ name }\"`);\n }\n else\n {\n this._log.warn(\n `Light \"${ name }\" not found in scene and no type specified — skipping`\n );\n }\n }\n }\n\n /**\n * Create a new light from a YAML definition.\n */\n private _createLight(name : string, config : LightDefinition, scene : Scene) : void\n {\n const dir = config.direction ? toVector3(config.direction) : new Vector3(0, -1, 0);\n const pos = config.position ? toVector3(config.position) : Vector3.Zero();\n\n switch (config.type)\n {\n case 'hemispheric':\n {\n const light = new HemisphericLight(name, dir, scene);\n\n if(config.groundColor)\n {\n light.groundColor = this._toColor3(config.groundColor);\n }\n\n this._applyLightConfig(light, config);\n break;\n }\n\n case 'directional':\n {\n const light = new DirectionalLight(name, dir, scene);\n this._applyLightConfig(light, config);\n break;\n }\n\n case 'point':\n {\n const light = new PointLight(name, pos, scene);\n this._applyLightConfig(light, config);\n break;\n }\n\n case 'spot':\n {\n const light = new SpotLight(\n name,\n pos,\n dir,\n config.angle ?? Math.PI / 3,\n config.exponent ?? 2,\n scene\n );\n this._applyLightConfig(light, config);\n break;\n }\n\n case 'rectarea':\n {\n const light = new RectAreaLight(\n name,\n pos,\n config.width ?? 1,\n config.height ?? 1,\n scene\n );\n this._applyLightConfig(light, config);\n\n // RectAreaLight emits along local -Z and has no direction property. If a direction\n // is specified, parent the light to a rotated pivot node that orients -Z toward it.\n if(config.direction)\n {\n const pivot = new TransformNode(`${ name }_pivot`, scene);\n pivot.position = light.position.clone();\n light.position = Vector3.Zero();\n light.parent = pivot;\n\n // lookAt aligns +Z toward the target; we want -Z toward dir, so look opposite\n pivot.lookAt(pivot.position.subtract(dir));\n }\n\n break;\n }\n }\n }\n\n /**\n * Apply shared light properties from a config to an existing light.\n */\n private _applyLightConfig(\n light : { intensity : number; diffuse : Color3; specular : Color3 },\n config : LightDefinition\n ) : void\n {\n if(config.intensity !== undefined) { light.intensity = config.intensity; }\n if(config.diffuse) { light.diffuse = this._toColor3(config.diffuse); }\n if(config.specular) { light.specular = this._toColor3(config.specular); }\n }\n\n /**\n * Set up clustered lighting: create a container, move eligible lights into it,\n * and fix PBR material falloff for compatibility.\n */\n private _setupClustering(scene : Scene) : void\n {\n const clusterConfig = this._config.clustering;\n if(!clusterConfig?.enabled)\n {\n return;\n }\n\n // Collect eligible lights from the scene\n const eligibleLights = scene.lights.filter(\n (light) => ClusteredLightContainer.IsLightSupported(light)\n );\n\n if(eligibleLights.length === 0)\n {\n this._log.debug('Clustering enabled but no eligible lights found');\n return;\n }\n\n // Remove eligible lights from the scene before adding to container\n for(const light of eligibleLights)\n {\n scene.removeLight(light);\n }\n\n // Create the container with collected lights\n const container = new ClusteredLightContainer('sage-clustered', eligibleLights, scene);\n\n // Apply tuning parameters\n if(clusterConfig.horizontalTiles !== undefined) { container.horizontalTiles = clusterConfig.horizontalTiles; }\n if(clusterConfig.verticalTiles !== undefined) { container.verticalTiles = clusterConfig.verticalTiles; }\n if(clusterConfig.depthSlices !== undefined) { container.depthSlices = clusterConfig.depthSlices; }\n if(clusterConfig.maxRange !== undefined) { container.maxRange = clusterConfig.maxRange; }\n\n this.clusteredLights = container;\n\n this._log.debug(\n `Clustered lighting enabled: ${ eligibleLights.length } lights, `\n + `${ container.horizontalTiles }x${ container.verticalTiles } tiles, `\n + `${ container.depthSlices } depth slices`\n );\n\n // Fix PBR material falloff — physical falloff is incompatible with clustering\n for(const material of scene.materials)\n {\n if(material instanceof PBRMaterial)\n {\n material.useGLTFLightFalloff = true;\n }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Configure and enable the Havok physics plugin with the level's gravity settings.\n */\n private async _enablePhysics(scene : Scene) : Promise<void>\n {\n const physicsConfig = this._config.physics;\n\n // Determine gravity\n let gravity = new Vector3(0, -9.81, 0); // Default gravity\n\n if(typeof physicsConfig === 'object' && physicsConfig.gravity)\n {\n gravity = new Vector3(\n physicsConfig.gravity.x,\n physicsConfig.gravity.y,\n physicsConfig.gravity.z\n );\n }\n\n // When large world rendering is active, enable Havok multi-region physics\n const floatingOriginRadius = this.gameEngine.largeWorldRendering\n ? 100000\n : undefined;\n\n this._log.debug(`Enabling physics with gravity: ${ gravity.toString() }`);\n await this.gameEngine.engines.sceneEngine.enablePhysics(scene, gravity, floatingOriginRadius);\n }\n\n /**\n * Walk all transform nodes and meshes, normalizing glTF metadata and dispatching to property handlers.\n */\n private async _processNodeProperties(scene : Scene) : Promise<void>\n {\n const nodes = scene.transformNodes.concat(scene.meshes as TransformNode[]);\n\n for(const node of nodes)\n {\n // Normalize metadata - glTF extras are in node.metadata.gltf.extras\n const metadata = this._getNormalizedMetadata(node);\n\n if(metadata && Object.keys(metadata).length > 0)\n {\n // Store normalized metadata back on node for property handlers\n node.metadata = { ...node.metadata, ...metadata };\n\n // eslint-disable-next-line no-await-in-loop\n await this._processNodeMetadata(node);\n }\n }\n\n this._log.debug(\n `Processed ${ nodes.length } nodes, found ${ this._spawnPoints.length } spawn points `\n + `and ${ this._entityNodes.length } entity nodes`\n );\n }\n\n /**\n * Check a node's metadata for spawn/entity markers and run any registered property handlers.\n */\n private async _processNodeMetadata(node : TransformNode) : Promise<void>\n {\n // Normalize the spawn marker into canonical Babylon space. glTF imports\n // wrap content under a conversion root (RH→LH), and decomposing a\n // mirrored matrix produces poisoned scaling/rotation. getCanonicalTransform\n // cancels the import root before decomposing, giving clean values that\n // work uniformly for primitives and GLB entities.\n if('spawn' in node.metadata)\n {\n const { position, rotation, scaling } = getCanonicalTransform(node);\n\n this._spawnPoints.push({\n name: node.metadata.spawn as string,\n position,\n rotation,\n scaling,\n node,\n });\n }\n\n // Check for entity property\n if('entity' in node.metadata)\n {\n this._entityNodes.push({\n type: node.metadata.entity as string,\n node,\n });\n }\n\n // Run registered property handlers\n await this._runPropertyHandlers(node);\n }\n\n /**\n * Match collected spawn points against the level config's spawn definitions and instantiate entities.\n */\n private async _processSpawnPoints() : Promise<void>\n {\n for(const spawnPoint of this._spawnPoints)\n {\n const spawnDef = this._config.spawns?.[spawnPoint.name];\n\n if(!spawnDef)\n {\n this._log.warn(`No spawn definition found for '${ spawnPoint.name }', skipping`);\n }\n else\n {\n // eslint-disable-next-line no-await-in-loop\n await this._processSpawnPoint(spawnPoint, spawnDef);\n }\n }\n }\n\n /**\n * Spawn an entity at the given spawn point and dispose of the placeholder node.\n */\n private async _processSpawnPoint(spawnPoint : SpawnPointData, spawnDef : SpawnDefinition) : Promise<void>\n {\n try\n {\n const entity = await this._spawnEntity(spawnDef, spawnPoint);\n this._spawnedEntities.push(entity);\n\n // Remove the placeholder node from the scene\n spawnPoint.node.dispose();\n\n this._log.debug(`Spawned entity '${ spawnDef.entity }' at spawn point '${ spawnPoint.name }'`);\n }\n catch (error)\n {\n this._log.error(`Failed to spawn entity at spawn point '${ spawnPoint.name }':`, error);\n }\n }\n\n /**\n * Spawn an entity through the simulation, inheriting position and rotation from the spawn point. The node\n * and its mesh are the simulation's own business, built by NodeEntity's constructor from the class name and\n * state -- the level does no mesh building of its own.\n *\n * A spawn point's scaling has nowhere to go: NodeEntityState declares position and rotation, not scaling,\n * and inventing a state field for it is not this level's call to make. It is dropped.\n */\n private async _spawnEntity(spawnDef : SpawnDefinition, spawnPoint : SpawnPointData) : Promise<NodeEntity>\n {\n const initialState = {\n ...spawnDef.config,\n ...(spawnDef.name === undefined ? {} : { displayName: spawnDef.name }),\n ...(spawnDef.debugCollider === undefined ? {} : { debugCollider: spawnDef.debugCollider }),\n tags: spawnDef.tags ?? [],\n position: toVec3Object(spawnPoint.position),\n rotation: toQuatObject(spawnPoint.rotation),\n };\n\n const state = initialState as unknown as StateBag;\n\n return this.simulation.spawn(spawnDef.entity, undefined, state) as NodeEntity;\n }\n\n /**\n * Match collected entity nodes against the level config's entity definitions and spawn entities adopting\n * their marked node.\n */\n private _processEntityNodes() : void\n {\n for(const entityNode of this._entityNodes)\n {\n const entityDef = this._config.entities?.[entityNode.type];\n\n if(!entityDef)\n {\n this._log.warn(`No entity definition found for '${ entityNode.type }', skipping`);\n }\n else\n {\n this._spawnEntityNode(entityDef, entityNode);\n }\n }\n }\n\n /**\n * Spawn an entity adopting an entity marker's node. `nodeName` is the marked node's own name, written by\n * the level rather than by configuration, since it is the binding key adoption resolves through\n * (design/authoring.md#the-binding-key).\n */\n private _spawnEntityNode(entityDef : EntityDefinition, entityNode : EntityNodeData) : void\n {\n try\n {\n const initialState = {\n ...entityDef.config,\n ...(entityDef.name === undefined ? {} : { displayName: entityDef.name }),\n ...(entityDef.debugCollider === undefined ? {} : { debugCollider: entityDef.debugCollider }),\n tags: entityDef.tags ?? [],\n nodeName: entityNode.node.name,\n };\n\n const state = initialState as unknown as StateBag;\n const entity = this.simulation.spawn(entityDef.entity, undefined, state) as NodeEntity;\n this._spawnedEntities.push(entity);\n\n this._log.debug(`Spawned entity '${ entityDef.entity }' adopting node '${ entityNode.node.name }'`);\n }\n catch (error)\n {\n this._log.error(`Failed to spawn entity adopting node '${ entityNode.node.name }':`, error);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Level Sounds\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create level-scoped sounds from YAML config. Sounds are tracked for lifecycle management\n * (pause on deactivate, resume on activate, dispose on unload).\n */\n private async _processLevelSounds() : Promise<void>\n {\n const soundConfigs = this._config.sounds;\n if(!soundConfigs) { return; }\n\n const { audioManager } = this.gameEngine.managers;\n if(!audioManager)\n {\n this._log.warn('No AudioManager configured. Level sounds will not be created.');\n return;\n }\n\n for(const [ name, config ] of Object.entries(soundConfigs))\n {\n try\n {\n // eslint-disable-next-line no-await-in-loop\n const sound = await audioManager.createSound(name, config.url, config.channel, {\n loop: config.loop ?? false,\n volume: config.volume ?? 1,\n });\n\n this._levelSounds.set(name, sound);\n\n if(config.autoplay)\n {\n sound.play();\n }\n\n this._log.debug(`Created level sound: \"${ name }\"`);\n }\n catch (error)\n {\n this._log.error(`Failed to create level sound \"${ name }\":`, error);\n }\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Pause all playing level sounds when the level is deactivated.\n * Subclasses that override this must call `super.onDeactivate()`.\n */\n async onDeactivate() : Promise<void>\n {\n for(const [ name, sound ] of this._levelSounds)\n {\n if(sound.state === SoundState.Started)\n {\n this._playingSoundsBeforeDeactivate.add(name);\n sound.pause();\n }\n }\n }\n\n /**\n * Resume previously playing level sounds when the level is reactivated.\n * Subclasses that override this must call `super.onActivate()`.\n */\n async onActivate() : Promise<void>\n {\n for(const name of this._playingSoundsBeforeDeactivate)\n {\n const sound = this._levelSounds.get(name);\n if(sound)\n {\n sound.play();\n }\n }\n\n this._playingSoundsBeforeDeactivate.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Dispose of this level's resources\n */\n override async $dispose() : Promise<void>\n {\n // Despawn all spawned entities, from the simulation this level built them in -- the engine may\n // already hold the next level's.\n if(this._simulation !== null)\n {\n for(const entity of this._spawnedEntities)\n {\n this._simulation.despawn(entity.id);\n }\n }\n\n this._simulation = null;\n this._spawnedEntities = [];\n this._spawnPoints = [];\n this._entityNodes = [];\n\n // Dispose level sounds\n for(const sound of this._levelSounds.values())\n {\n sound.dispose();\n }\n this._levelSounds.clear();\n this._playingSoundsBeforeDeactivate.clear();\n\n await super.$dispose();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Level Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type {\n LevelConfig,\n LevelConstructor,\n LevelContext,\n LevelLoadOptions,\n PropertyHandler,\n} from '../interfaces/level.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type {\n LevelCompletePayload,\n LevelErrorPayload,\n LevelProgressPayload,\n} from '../events/payloads.ts';\n\nimport { Level } from '../classes/level.ts';\nimport { GameLevel } from '../classes/gameLevel.ts';\nimport { GameEventBus } from '../classes/eventBus.ts';\nimport type { GameEngine } from '../classes/gameEngine.ts';\nimport type { NodeSimulation } from '../entities/nodeSimulation.ts';\n\n// Utils\nimport { type LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Options for level transitions.\n */\nexport interface TransitionOptions extends LevelLoadOptions\n{\n /** If true, the old level is kept loaded (not disposed) after transitioning away */\n keepAlive ?: boolean;\n\n /** If true, the new level is loaded but not activated — currentLevel remains unchanged */\n preloadOnly ?: boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Manages game levels, acting as a factory for level instantiation.\n *\n * The LevelManager is responsible for:\n * - Registering level configurations and custom level classes\n * - Creating level instances with properly injected dependencies\n * - Managing the lifecycle of loaded levels (loading, activation, disposal)\n *\n * Levels are not instantiated directly. Instead, use the manager's factory methods:\n *\n * ```typescript\n * // Register a config\n * levelManager.registerLevelConfig({ name: 'Level1', scene: 'level1.glb' });\n *\n * // Register a custom level class\n * levelManager.registerLevelClass('custom', MyCustomLevel);\n *\n * // Load creates the instance with deps injected\n * await levelManager.loadLevel('Level1');\n * ```\n */\nexport class LevelManager implements Disposable\n{\n private _eventBus : GameEventBus;\n private _gameEngine : GameEngine | null = null;\n private _log : LoggerInterface;\n private _logger ?: LoggingUtility;\n\n /** Map of registered level configurations by name */\n private _levelConfigs = new Map<string, LevelConfig>();\n\n /** Map of registered custom level classes by name */\n private _levelClasses = new Map<string, LevelConstructor>();\n\n /** Map of loaded level instances by name */\n private _loadedLevels = new Map<string, Level>();\n\n /** Map of registered property handlers for scene node metadata */\n private _propertyHandlers = new Map<string, PropertyHandler>();\n\n /** The currently active level */\n private _currentLevel : Level | null = null;\n\n /** The level currently inside load(), if any */\n private _loadingLevel : Level | null = null;\n\n /** Unsubscribe functions for event subscriptions */\n private _eventUnsubscribers : (() => void)[] = [];\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Gets the currently active level\n */\n public get currentLevel() : Level | null\n {\n return this._currentLevel;\n }\n\n /**\n * Gets the level currently being loaded, if any.\n *\n * During a transition, `currentLevel` still points at the outgoing level while the incoming one builds its\n * scene — anything that needs \"the level work is happening in right now\" (dynamic entity mesh creation during\n * spawn processing, for instance) must prefer this over `currentLevel`.\n */\n public get loadingLevel() : Level | null\n {\n return this._loadingLevel;\n }\n\n /**\n * Gets the registered property handlers\n */\n public get propertyHandlers() : Map<string, PropertyHandler>\n {\n return this._propertyHandlers;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(\n eventBus : GameEventBus,\n logger ?: LoggingUtility\n )\n {\n this._eventBus = eventBus;\n this._logger = logger;\n this._log = logger?.getLogger('LevelManager') || new SAGELogger('LevelManager');\n\n // Subscribe to level events and store unsubscribe handles\n this._eventUnsubscribers.push(\n this._eventBus.subscribe('level:progress', (event) =>\n {\n this._handleProgress(event.payload);\n })\n );\n\n this._eventUnsubscribers.push(\n this._eventBus.subscribe('level:complete', (event) =>\n {\n this._handleComplete(event.payload);\n })\n );\n\n this._eventUnsubscribers.push(\n this._eventBus.subscribe('level:error', (event) =>\n {\n this._handleError(event.payload);\n })\n );\n\n this._log.info('LevelManager initialized');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Late-bind the GameEngine after construction.\n * Called by createGameEngine() after the GameEngine instance is created.\n */\n $setGameEngine(gameEngine : GameEngine) : void\n {\n this._gameEngine = gameEngine;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n private _handleProgress(payload : LevelProgressPayload) : void\n {\n this._log.debug(`Level ${ payload.levelName } progress: ${ payload.progress }%`);\n }\n\n private _handleComplete(payload : LevelCompletePayload) : void\n {\n this._log.info(`Level ${ payload.levelName } loaded successfully`);\n }\n\n private _handleError(payload : LevelErrorPayload) : void\n {\n this._log.error(`Level ${ payload.levelName } error: ${ payload.message }`);\n }\n\n /**\n * Creates the LevelContext with all dependencies\n */\n private _createContext() : LevelContext\n {\n if(!this._gameEngine)\n {\n throw new Error('LevelManager: gameEngine not set. Call $setGameEngine() first.');\n }\n\n return {\n gameEngine: this._gameEngine,\n propertyHandlers: this._propertyHandlers,\n logger: this._logger,\n };\n }\n\n /**\n * Creates a Level instance from a config using the appropriate class\n */\n private _createLevelInstance(config : LevelConfig) : Level\n {\n const context = this._createContext();\n\n if(config.class)\n {\n // Look up the registered class\n const LevelClass = this._levelClasses.get(config.class);\n if(!LevelClass)\n {\n throw new Error(`Level class '${ config.class }' is not registered.`);\n }\n\n this._log.debug(`Creating level '${ config.name }' using class '${ config.class }'`);\n return new LevelClass(config, context) as Level;\n }\n\n // Use GameLevel as default\n this._log.debug(`Creating level '${ config.name }' using GameLevel`);\n return new GameLevel(config, context);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Configuration Registration API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Registers a level configuration.\n * The level will be instantiated when loadLevel() is called.\n *\n * @param config\n */\n public registerLevelConfig(config : LevelConfig) : void\n {\n if(this._levelConfigs.has(config.name))\n {\n this._log.warn(`Level config '${ config.name }' is already registered. Overwriting.`);\n }\n\n this._levelConfigs.set(config.name, config);\n this._log.info(`Registered level config: ${ config.name }`);\n }\n\n /**\n * Registers a custom Level class that can be referenced in configs.\n * When a config specifies `class: \"my-level\"`, this class will be instantiated.\n *\n * @param name - The key used in LevelConfig.class to reference this constructor\n * @param levelClass\n */\n public registerLevelClass(name : string, levelClass : LevelConstructor) : void\n {\n if(this._levelClasses.has(name))\n {\n this._log.warn(`Level class '${ name }' is already registered. Overwriting.`);\n }\n\n this._levelClasses.set(name, levelClass);\n this._log.info(`Registered level class: ${ name }`);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Property Handler API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a property handler for processing custom properties on scene nodes.\n * Handlers are called for each node that has the specified property in its metadata.\n *\n * @param property - The metadata property name to match (e.g., 'sound', 'collider')\n * @param handler\n */\n public registerPropertyHandler(property : string, handler : PropertyHandler) : void\n {\n if(this._propertyHandlers.has(property))\n {\n this._log.warn(`Property handler '${ property }' is already registered. Overwriting.`);\n }\n\n this._propertyHandlers.set(property, handler);\n this._log.debug(`Registered property handler: ${ property }`);\n }\n\n /**\n * Unregister a property handler.\n *\n * @param property\n */\n public unregisterPropertyHandler(property : string) : void\n {\n this._propertyHandlers.delete(property);\n }\n\n /**\n * Clear all registered property handlers.\n * Primarily useful for testing to ensure a clean state between tests.\n */\n public clearAllPropertyHandlers() : void\n {\n this._propertyHandlers.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Level Loading API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Gets a loaded level by name.\n * Returns null if the level is not currently loaded.\n *\n * @param name\n */\n public getLevel(name : string) : Level | null\n {\n return this._loadedLevels.get(name) || null;\n }\n\n /**\n * Gets a registered level config by name.\n *\n * @param name\n */\n public getLevelConfig(name : string) : LevelConfig | null\n {\n return this._levelConfigs.get(name) || null;\n }\n\n /**\n * Loads a level by name. The level config must be registered first.\n *\n * @param name\n * @param options\n */\n public async loadLevel(name : string, options ?: LevelLoadOptions) : Promise<Level>;\n\n /**\n * Loads a level from a config. The config will be registered automatically.\n *\n * @param config\n * @param options\n */\n public async loadLevel(config : LevelConfig, options ?: LevelLoadOptions) : Promise<Level>;\n\n /**\n * Loads a level. If a name is passed, the config must be registered.\n * If a config is passed, it will be registered automatically.\n * Returns the existing instance if already loaded.\n *\n * @param configOrName\n * @param options\n */\n public async loadLevel(configOrName : string | LevelConfig, options ?: LevelLoadOptions) : Promise<Level>\n {\n // Resolve the config\n let config : LevelConfig;\n if(typeof configOrName === 'string')\n {\n const registered = this._levelConfigs.get(configOrName);\n if(!registered)\n {\n throw new Error(`Level config '${ configOrName }' is not registered.`);\n }\n config = registered;\n }\n else\n {\n config = configOrName;\n // Auto-register the config\n this.registerLevelConfig(config);\n }\n\n // Check if already loaded\n const existing = this._loadedLevels.get(config.name);\n if(existing)\n {\n this._log.warn(`Level '${ config.name }' is already loaded`);\n return existing;\n }\n\n // Create and load the level\n this._log.debug(`Loading level: ${ config.name }`);\n const level = this._createLevelInstance(config);\n\n // Building a level's scene rebuilds a simulation for it, which binds the engine to that simulation as\n // a side effect. Only activation binds (design/simulation.md#which-simulation-the-engine-holds), so\n // the engine gets back whatever it held until this level is the current one.\n const previousSimulation = this._gameEngine?.simulation ?? null;\n\n this._loadingLevel = level;\n try\n {\n await level.load(options);\n\n // GameLevel records the simulation its own build rebuilt, but a Level subclass is free to call\n // rebuildSimulation itself. Either way the level owns what it built, so anything the load left on\n // the engine that was not there before belongs to this level.\n const built = this._gameEngine?.simulation ?? null;\n\n if(level.$simulation === null && built !== null && built !== previousSimulation)\n {\n level.$adoptSimulation(built);\n }\n }\n catch (error)\n {\n // A load that fails part way has already built a scene and possibly spawned into a simulation;\n // neither is reachable through this manager once the level is not stored, so release both here.\n await level.$dispose();\n throw error;\n }\n finally\n {\n this._loadingLevel = null;\n\n if(this._gameEngine !== null)\n {\n this._gameEngine.simulation = previousSimulation;\n }\n }\n\n // Store the loaded level\n this._loadedLevels.set(config.name, level);\n\n return level;\n }\n\n /**\n * Bind the engine to a level's own simulation. A level that built none leaves the engine as it is.\n *\n * @param level\n */\n private _bindSimulation(level : Level) : void\n {\n if(this._gameEngine === null || level.$simulation === null)\n {\n return;\n }\n\n this._gameEngine.simulation = level.$simulation;\n }\n\n /**\n * Hand the current level a simulation built to replace its own, and bind the engine to it. A save load\n * restores into a fresh simulation, and the level that owns the scene has to follow it.\n *\n * @param simulation\n */\n $bindSimulation(simulation : NodeSimulation) : void\n {\n if(this._currentLevel !== null)\n {\n this._currentLevel.$adoptSimulation(simulation);\n }\n\n if(this._gameEngine !== null)\n {\n this._gameEngine.simulation = simulation;\n }\n }\n\n /**\n * Activates a level by name, loading it if necessary.\n *\n * @param name\n */\n public async activateLevel(name : string) : Promise<Level>;\n\n /**\n * Activates a level from a config, loading it if necessary.\n *\n * @param config\n */\n public async activateLevel(config : LevelConfig) : Promise<Level>;\n\n /**\n * Activates a level, loading it if necessary.\n * Sets it as the currentLevel used by the render loop.\n *\n * @param configOrName\n */\n public async activateLevel(configOrName : string | LevelConfig) : Promise<Level>\n {\n const level = typeof configOrName === 'string'\n ? await this.loadLevel(configOrName)\n : await this.loadLevel(configOrName);\n this._currentLevel = level;\n this._bindSimulation(level);\n this._log.info(`Activated level: ${ level.name }`);\n return level;\n }\n\n /**\n * Unloads a level and disposes its resources.\n * Clears currentLevel if this was the active level.\n *\n * @param name\n */\n public async unloadLevel(name : string) : Promise<void>\n {\n const level = this._loadedLevels.get(name);\n if(level)\n {\n // If this is the current level, clear it\n if(this._currentLevel === level)\n {\n this._currentLevel = null;\n }\n\n // Dispose the level\n await level.$dispose();\n\n // Remove from loaded levels\n this._loadedLevels.delete(name);\n\n this._log.info(`Unloaded level: ${ name }`);\n }\n }\n\n /**\n * Unloads the current level and disposes its resources\n */\n public async unloadCurrentLevel() : Promise<void>\n {\n if(this._currentLevel)\n {\n await this.unloadLevel(this._currentLevel.name);\n }\n else\n {\n this._log.warn('No current level to unload');\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Transitions\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Orchestrates a level-to-level transition with lifecycle hooks and events.\n *\n * Flow: deactivate old -> load new -> dispose old (unless keepAlive) -> activate new.\n * If `preloadOnly` is true, the new level is loaded but not activated.\n *\n * @param levelName - The name of the level to transition to (must be registered)\n * @param options\n */\n public async transition(levelName : string, options ?: TransitionOptions) : Promise<void>\n {\n const oldLevel = this._currentLevel;\n\n // Loading the new level rebinds the engine to the new level's simulation before anything decides\n // whether the transition succeeds; a failure leaves the old level current, so it gets its own back.\n const previousSimulation = this._gameEngine?.simulation ?? null;\n\n try\n {\n // 1. Emit start\n this._eventBus.publish({\n type: 'level:transition:start',\n payload: {\n from: oldLevel?.name,\n to: levelName,\n },\n });\n\n // 2. Deactivate the old level\n if(oldLevel?.onDeactivate)\n {\n await oldLevel.onDeactivate();\n }\n\n // 3. Emit loading progress\n this._eventBus.publish({\n type: 'level:transition:progress',\n payload: {\n stage: 'loading',\n levelName,\n },\n });\n\n // 4. Load the new level\n const newLevel = await this.loadLevel(levelName, options);\n\n // 5. If preloadOnly, stop here\n if(options?.preloadOnly)\n {\n return;\n }\n\n // 6. Dispose old level unless keepAlive\n if(oldLevel && !options?.keepAlive)\n {\n await this.unloadLevel(oldLevel.name);\n }\n\n // 7. Set new level as current\n this._currentLevel = newLevel;\n this._bindSimulation(newLevel);\n\n // 8. Activate the new level\n if(newLevel.onActivate)\n {\n await newLevel.onActivate();\n }\n\n // 9. Emit complete\n this._eventBus.publish({\n type: 'level:transition:complete',\n payload: { levelName },\n });\n }\n catch (error)\n {\n if(this._gameEngine !== null)\n {\n this._gameEngine.simulation = previousSimulation;\n }\n\n // 10. Emit error event and re-throw\n this._eventBus.publish({\n type: 'level:transition:error',\n payload: {\n from: oldLevel?.name,\n to: levelName,\n error,\n },\n });\n\n throw error;\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Tears down the level manager and cleans up any resources\n */\n async $teardown() : Promise<void>\n {\n // Unsubscribe from all events\n for(const unsubscribe of this._eventUnsubscribers)\n {\n unsubscribe();\n }\n this._eventUnsubscribers = [];\n\n // Dispose of all loaded levels\n for(const level of this._loadedLevels.values())\n {\n // eslint-disable-next-line no-await-in-loop\n await level.$dispose();\n }\n\n this._loadedLevels.clear();\n this._levelConfigs.clear();\n this._currentLevel = null;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Audio Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { AudioBus, IStaticSoundOptions, StaticSound } from '@babylonjs/core';\n\n// Engines\nimport type { AudioEngine } from '../engines/audio.ts';\n\n// Interfaces\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Utils\nimport type { LoggingUtility } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Public channel info exposed to consumers (no BabylonJS internals).\n */\nexport interface ChannelInfo\n{\n volume : number;\n muted : boolean;\n}\n\n/**\n * Internal channel state including the BabylonJS bus reference.\n */\ninterface ChannelState extends ChannelInfo\n{\n bus : AudioBus;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class AudioManager implements Disposable\n{\n private _audioEngine : AudioEngine;\n private _channels = new Map<string, ChannelState>();\n private _masterMuted = false;\n private _masterVolumeBeforeMute = 1;\n private _log : LoggerInterface;\n\n constructor(audioEngine : AudioEngine, logger : LoggingUtility)\n {\n this._audioEngine = audioEngine;\n this._log = logger.getLogger('AudioManager');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Lifecycle\n //------------------------------------------------------------------------------------------------------------------\n\n async initialize(channels : string[]) : Promise<void>\n {\n this._log.info(`Initializing audio channels: ${ channels.join(', ') }`);\n\n for(const name of channels)\n {\n // eslint-disable-next-line no-await-in-loop\n const bus = await this._audioEngine.createBus(name);\n this._channels.set(name, { bus, volume: 1, muted: false });\n }\n\n this._log.info('Audio channels initialized.');\n }\n\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down audio manager...');\n\n for(const state of this._channels.values())\n {\n state.bus.dispose();\n }\n\n this._channels.clear();\n this._log.info('Audio manager torn down.');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Sound Creation\n //------------------------------------------------------------------------------------------------------------------\n\n async createSound(\n name : string,\n url : string,\n channel ?: string,\n options ?: Partial<IStaticSoundOptions>\n ) : Promise<StaticSound>\n {\n let bus : AudioBus | undefined;\n\n if(channel)\n {\n const state = this._channels.get(channel);\n if(!state)\n {\n this._log.warn(`Unknown audio channel \"${ channel }\", sound \"${ name }\" will use default bus.`);\n }\n else\n {\n bus = state.bus;\n }\n }\n\n return this._audioEngine.createSound(name, url, bus, options);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Master Volume\n //------------------------------------------------------------------------------------------------------------------\n\n setMasterVolume(volume : number) : void\n {\n if(!this._masterMuted)\n {\n this._audioEngine.setMasterVolume(volume);\n }\n else\n {\n // Store for when we unmute\n this._masterVolumeBeforeMute = volume;\n }\n }\n\n getMasterVolume() : number\n {\n if(this._masterMuted)\n {\n return this._masterVolumeBeforeMute;\n }\n\n return this._audioEngine.getMasterVolume();\n }\n\n setMasterMuted(muted : boolean) : void\n {\n if(muted === this._masterMuted) { return; }\n\n this._masterMuted = muted;\n\n if(muted)\n {\n this._masterVolumeBeforeMute = this._audioEngine.getMasterVolume();\n this._audioEngine.setMasterVolume(0);\n }\n else\n {\n this._audioEngine.setMasterVolume(this._masterVolumeBeforeMute);\n }\n }\n\n isMasterMuted() : boolean\n {\n return this._masterMuted;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Channel Volume\n //------------------------------------------------------------------------------------------------------------------\n\n setChannelVolume(channel : string, volume : number) : void\n {\n const state = this._channels.get(channel);\n if(!state)\n {\n this._log.warn(`Unknown audio channel: ${ channel }`);\n return;\n }\n\n state.volume = volume;\n\n if(!state.muted)\n {\n this._audioEngine.setBusVolume(state.bus, volume);\n }\n }\n\n getChannelVolume(channel : string) : number\n {\n const state = this._channels.get(channel);\n return state?.volume ?? 1;\n }\n\n setChannelMuted(channel : string, muted : boolean) : void\n {\n const state = this._channels.get(channel);\n if(!state)\n {\n this._log.warn(`Unknown audio channel: ${ channel }`);\n return;\n }\n\n if(muted === state.muted) { return; }\n\n state.muted = muted;\n this._audioEngine.setBusVolume(state.bus, muted ? 0 : state.volume);\n }\n\n isChannelMuted(channel : string) : boolean\n {\n const state = this._channels.get(channel);\n return state?.muted ?? false;\n }\n\n getChannels() : string[]\n {\n return Array.from(this._channels.keys());\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Simulation Save Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameSimulationManager, GameSimulationSnapshot } from '@skewedaspect/sage-core';\n\nimport type { GameEngine } from '../classes/gameEngine.ts';\nimport type { NodeSimulation } from '../entities/nodeSimulation.ts';\nimport type { Disposable } from '../interfaces/lifecycle.ts';\nimport type { LevelLoadOptions } from '../interfaces/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nconst saveFormatVersion = 1;\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface LevelLoader\n{\n readonly currentLevel : { name : string } | null;\n transition(levelName : string, options ?: LevelLoadOptions) : Promise<void>;\n $bindSimulation(simulation : NodeSimulation) : void;\n}\n\nexport interface SaveEnvelope\n{\n saveFormatVersion : number;\n engineVersion : string;\n level : string;\n timestamp : number;\n custom : Record<string, unknown>;\n snapshot : GameSimulationSnapshot;\n}\n\nexport type BeforeSaveHook = () => Record<string, unknown>;\n\nexport interface LoadResult\n{\n simulation : NodeSimulation;\n envelope : SaveEnvelope;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class SimulationSaveManager implements Disposable\n{\n readonly #levelLoader : LevelLoader;\n readonly #engineVersion : string;\n readonly #now : () => number;\n\n readonly #beforeSaveHooks : BeforeSaveHook[] = [];\n\n #gameEngine : GameEngine | null = null;\n\n constructor(levelLoader : LevelLoader, engineVersion : string, now : () => number = Date.now)\n {\n this.#levelLoader = levelLoader;\n this.#engineVersion = engineVersion;\n this.#now = now;\n }\n\n /**\n * Late-bind the GameEngine after construction. Called by createGameEngine() after the GameEngine\n * instance is created; load() needs it to reach the simulation a transition just rebuilt.\n */\n $setGameEngine(gameEngine : GameEngine) : void\n {\n this.#gameEngine = gameEngine;\n }\n\n onBeforeSave(hook : BeforeSaveHook) : void\n {\n this.#beforeSaveHooks.push(hook);\n }\n\n save(simulation : GameSimulationManager) : SaveEnvelope\n {\n let custom : Record<string, unknown> = {};\n\n for(const hook of this.#beforeSaveHooks)\n {\n custom = { ...custom, ...hook() };\n }\n\n return {\n saveFormatVersion,\n engineVersion: this.#engineVersion,\n level: this.#levelLoader.currentLevel?.name ?? '',\n timestamp: this.#now(),\n custom,\n snapshot: simulation.snapshot(),\n };\n }\n\n async load(envelope : SaveEnvelope) : Promise<LoadResult>\n {\n if(envelope.saveFormatVersion !== saveFormatVersion)\n {\n throw new Error(\n `The save format version \"${ envelope.saveFormatVersion }\" is not one this engine knows how to load.`\n );\n }\n\n if(this.#gameEngine === null)\n {\n throw new Error('SimulationSaveManager: gameEngine not set. Call $setGameEngine() first.');\n }\n\n await this.#levelLoader.transition(envelope.level, { forRestore: true });\n\n // The transition just rebuilt the level's scene and, with it, a fresh NodeSimulation bound to that\n // scene (GameEngine.rebuildSimulation) -- fromSnapshot has to run on that one, not on whatever\n // simulation was current before the transition, or the restored entities carry a disposed scene.\n const freshSimulation = this.#gameEngine.simulation;\n\n if(freshSimulation === null)\n {\n throw new Error('SimulationSaveManager: the transitioned level built no simulation to restore into.');\n }\n\n // buildReplacementSimulation is virtual (design/simulation.md#snapshot-and-restore): called on a\n // NodeSimulation it always returns a NodeSimulation, a guarantee fromSnapshot's sage-core signature\n // cannot show.\n const restored = freshSimulation.fromSnapshot(envelope.snapshot) as NodeSimulation;\n\n // The level that owns the scene has to follow the restored simulation, or its own spawns and\n // despawns keep going to the one the transition built\n // (design/simulation.md#which-simulation-the-engine-holds).\n this.#levelLoader.$bindSimulation(restored);\n\n return { simulation: restored, envelope };\n }\n\n async $teardown() : Promise<void>\n {\n this.#beforeSaveHooks.length = 0;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Collider Debug Manager\n//\n// Wraps BabylonJS PhysicsViewer to provide per-node collider debug visualization with color support.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { type AbstractMesh, Color3, StandardMaterial, type TransformNode } from '@babylonjs/core';\nimport { PhysicsViewer } from '@babylonjs/core/Debug/physicsViewer';\n\nimport type { Disposable } from '../interfaces/lifecycle.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type ColliderDebugConfig = boolean | string | Record<string, string | false>;\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface ShownEntry\n{\n debugMesh : AbstractMesh;\n colorMaterial ?: StandardMaterial;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class ColliderDebugManager implements Disposable\n{\n private _viewer : PhysicsViewer | null = null;\n private _shown = new Map<TransformNode, ShownEntry>();\n private _materials = new Map<string, StandardMaterial>();\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n showColliderForNode(node : TransformNode, color ?: string) : void\n {\n if(!node.physicsBody || this._shown.has(node))\n {\n return;\n }\n\n const viewer = this._getViewer(node);\n const debugMesh = viewer.showBody(node.physicsBody);\n\n if(!debugMesh)\n {\n return;\n }\n\n const entry : ShownEntry = { debugMesh };\n\n if(color)\n {\n const material = this._getColorMaterial(color, node);\n debugMesh.material = material;\n entry.colorMaterial = material;\n }\n\n this._shown.set(node, entry);\n }\n\n hideColliderForNode(node : TransformNode) : void\n {\n const entry = this._shown.get(node);\n if(!entry || !this._viewer)\n {\n return;\n }\n\n if(node.physicsBody)\n {\n this._viewer.hideBody(node.physicsBody);\n }\n\n this._shown.delete(node);\n }\n\n hideAll() : void\n {\n for(const [ node ] of this._shown)\n {\n this.hideColliderForNode(node);\n }\n }\n\n isShown(node : TransformNode) : boolean\n {\n return this._shown.has(node);\n }\n\n async $teardown() : Promise<void>\n {\n this.dispose();\n }\n\n dispose() : void\n {\n if(this._viewer)\n {\n this.hideAll();\n this._viewer.dispose();\n this._viewer = null;\n }\n\n this._shown.clear();\n\n for(const mat of this._materials.values())\n {\n mat.dispose();\n }\n\n this._materials.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private\n //------------------------------------------------------------------------------------------------------------------\n\n private _getViewer(node : TransformNode) : PhysicsViewer\n {\n if(!this._viewer)\n {\n this._viewer = new PhysicsViewer(node.getScene());\n }\n\n return this._viewer;\n }\n\n private _getColorMaterial(color : string, node : TransformNode) : StandardMaterial\n {\n let material = this._materials.get(color);\n if(material)\n {\n return material;\n }\n\n material = new StandardMaterial(`sage-collider-debug-${ color }`, node.getScene());\n material.emissiveColor = Color3.FromHexString(color);\n material.disableLighting = true;\n material.wireframe = true;\n this._materials.set(color, material);\n\n return material;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Graphics Utility\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n AbstractEngine,\n Engine,\n type EngineOptions,\n NullEngine,\n type NullEngineOptions,\n WebGPUEngine,\n type WebGPUEngineOptions,\n} from '@babylonjs/core';\n\n// Interfaces\nimport type { BabylonEngineOptions, GameCanvas, RenderEngineOptions } from '../interfaces/game.ts';\n\n// Utilities\nimport { hasWebGPU } from './capabilities.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Whether a WebGPU engine can be built here.\n *\n * `hasWebGPU()` only reports that the browser exposes the API. Headless and software-rendering browsers\n * expose it and then hand back no adapter, and Babylon's WebGPU engine claims its window listeners in the\n * constructor, before `initAsync()` fails -- so an engine built on the weaker answer and then dropped holds\n * them for the life of the page. `dispose()` cannot take them back: it reaches for a GPU device that a\n * failure this early never created.\n */\nasync function _canBuildWebGPUEngine() : Promise<boolean>\n{\n if(!hasWebGPU())\n {\n return false;\n }\n\n return WebGPUEngine.IsSupportedAsync;\n}\n\n/**\n * Builds and initializes a WebGPU engine.\n *\n * @param canvas\n * @param options\n */\nasync function _buildWebGPUEngine(\n canvas : HTMLCanvasElement | OffscreenCanvas,\n options : WebGPUEngineOptions\n) : Promise<WebGPUEngine>\n{\n const engine = new WebGPUEngine(canvas, options);\n await engine.initAsync();\n return engine;\n}\n\n/**\n * Builds a WebGL engine.\n *\n * @param canvas\n * @param options\n */\nfunction _buildWebGLEngine(canvas : GameCanvas, options : BabylonEngineOptions) : Engine\n{\n return new Engine(canvas, options.antialias, options.options, options.adaptToDeviceRatio);\n}\n\n/**\n * Builds a Null engine, which is used when no rendering is required.\n *\n * @param options\n */\nfunction _buildNullEngine(options : NullEngineOptions) : NullEngine\n{\n return new NullEngine(options as NullEngineOptions);\n}\n\n//------------------------------------------------------------------------------------------------------------------\n\n/**\n * Creates an appropriate engine based on the provided canvas and options.\n * Passing null for canvas creates a NullEngine. Otherwise auto-detects WebGPU with WebGL fallback,\n * unless a specific engine type is forced via `options.engine`.\n *\n * @param canvas - Pass null to get a NullEngine (headless/testing)\n * @param options\n */\nexport async function createEngine(\n canvas : GameCanvas | null,\n options : RenderEngineOptions,\n largeWorldRendering = false\n) : Promise<AbstractEngine>\n{\n // Check if we should use a null engine (no canvas provided)\n if(canvas === null)\n {\n console.debug('Using Null Engine');\n return _buildNullEngine(options as NullEngineOptions);\n }\n\n // Inject large world rendering into engine options if requested. We have to build new objects\n // here because BabylonJS declares useLargeWorldRendering as readonly on its option types.\n if(largeWorldRendering)\n {\n if('options' in options)\n {\n const babylonOpts = options as BabylonEngineOptions;\n options = {\n ...babylonOpts,\n options: { ...babylonOpts.options, useLargeWorldRendering: true },\n } as RenderEngineOptions;\n }\n else\n {\n // WebGPUEngineOptions -- useLargeWorldRendering lives at the top level\n options = { ...options, useLargeWorldRendering: true } as RenderEngineOptions;\n }\n }\n\n // Extract the forceEngine option if available\n const forceEngine = options.engine || 'auto';\n\n // Force WebGPU engine if specified and available\n if(forceEngine === 'webgpu')\n {\n if(await _canBuildWebGPUEngine())\n {\n try\n {\n console.debug('Using forced WebGPU engine');\n return await _buildWebGPUEngine(canvas, options);\n }\n catch (error)\n {\n console.error('Forced WebGPU initialization failed:', error);\n throw new Error(\n 'Forced WebGPU failed to initialize. If WebGPU is required, check browser compatibility.',\n { cause: error }\n );\n }\n }\n else\n {\n throw new Error('WebGPU was forced but is not available in this browser.');\n }\n }\n\n // Force WebGL engine if specified\n if(forceEngine === 'webgl')\n {\n console.debug('Using forced WebGL engine');\n return _buildWebGLEngine(canvas, options as BabylonEngineOptions);\n }\n\n // Auto detection (default behavior)\n if(await _canBuildWebGPUEngine())\n {\n try\n {\n console.debug('Using WebGPU');\n\n return await _buildWebGPUEngine(canvas, options);\n }\n catch (error)\n {\n console.warn('WebGPU initialization failed, falling back to WebGL:', error);\n }\n }\n else\n {\n console.warn('WebGPU not supported, falling back to WebGL.');\n }\n\n console.debug('Using WebGL');\n\n return _buildWebGLEngine(canvas, options as EngineOptions);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n","//----------------------------------------------------------------------------------------------------------------------\n// Physics Utility\n//----------------------------------------------------------------------------------------------------------------------\n\nimport HavokPhysics, { type HavokPhysicsWithBindings } from '@babylonjs/havok';\nimport { HavokPlugin } from '@babylonjs/core';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Initializes the Havok WASM module and returns the raw bindings.\n *\n * Each BabylonJS scene needs its own HavokPlugin instance (because scene.dispose() disposes the\n * plugin attached to it). The raw WASM module can be shared across plugins safely.\n *\n * @param wasmBinary - Pre-loaded Havok WASM. Left unset, Havok fetches its own over HTTP, which\n * only works in a browser.\n */\nexport async function initHavok(wasmBinary ?: BufferSource) : Promise<HavokPhysicsWithBindings>\n{\n // Emscripten declares the slot as ArrayBuffer, but hands it straight to WebAssembly, which\n // takes any BufferSource. A node Buffer is the common case and is not an ArrayBuffer.\n return await HavokPhysics(wasmBinary ? { wasmBinary: wasmBinary as ArrayBuffer } : undefined);\n}\n\n/**\n * Creates a new HavokPlugin from an existing WASM instance.\n */\nexport function createHavokPlugin(havok : HavokPhysicsWithBindings) : HavokPlugin\n{\n return new HavokPlugin(true, havok);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// GameTimer - Pause-aware timer utilities\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A pollable cooldown handle.\n */\nexport interface CooldownHandle\n{\n readonly ready : boolean;\n reset() : void;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface DelayEntry\n{\n id : number;\n remaining : number;\n callback : () => void;\n}\n\ninterface IntervalEntry\n{\n id : number;\n periodMs : number;\n elapsed : number;\n callback : () => void;\n}\n\ninterface CooldownEntry\n{\n id : number;\n periodMs : number;\n remaining : number;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Pause-aware game timer providing delay, interval, and cooldown primitives.\n *\n * All times are in game-time milliseconds. When the game is paused, `tick()` is simply not called, so all timers\n * freeze automatically.\n */\nexport class GameTimer\n{\n private _nextId = 0;\n private _delays : DelayEntry[] = [];\n private _intervals : IntervalEntry[] = [];\n private _cooldowns : CooldownEntry[] = [];\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * One-shot timer. Fires the callback after `ms` milliseconds of game time.\n *\n * @returns A cancel function that prevents the callback from firing.\n */\n delay(ms : number, callback : () => void) : () => void\n {\n if(ms < 0)\n {\n throw new RangeError(`delay() requires ms >= 0, got ${ ms }`);\n }\n\n const id = this._nextId++;\n this._delays.push({ id, remaining: ms, callback });\n\n return () =>\n {\n this._delays = this._delays.filter((entry) => entry.id !== id);\n };\n }\n\n /**\n * Repeating timer. Fires the callback every `ms` milliseconds of game time.\n *\n * @returns A cancel function that stops the interval.\n */\n interval(ms : number, callback : () => void) : () => void\n {\n if(ms <= 0)\n {\n throw new RangeError(`interval() requires ms > 0, got ${ ms }`);\n }\n\n const id = this._nextId++;\n this._intervals.push({ id, periodMs: ms, elapsed: 0, callback });\n\n return () =>\n {\n this._intervals = this._intervals.filter((entry) => entry.id !== id);\n };\n }\n\n /**\n * Pollable cooldown. Starts ready; after calling `reset()`, becomes not-ready until `ms` elapses.\n */\n cooldown(ms : number) : CooldownHandle\n {\n const id = this._nextId++;\n const entry : CooldownEntry = { id, periodMs: ms, remaining: 0 };\n this._cooldowns.push(entry);\n\n return {\n get ready() : boolean\n {\n return entry.remaining <= 0;\n },\n reset() : void\n {\n entry.remaining = entry.periodMs;\n },\n };\n }\n\n /**\n * Cancel all active delays, intervals, and cooldowns.\n */\n cancelAll() : void\n {\n this._delays = [];\n this._intervals = [];\n\n // Set all cooldowns to ready before clearing, so existing CooldownHandle references aren't stuck\n for(const entry of this._cooldowns)\n {\n entry.remaining = 0;\n }\n\n this._cooldowns = [];\n }\n\n /**\n * Advance all timers by `dtMs` milliseconds. Called once per frame by the game loop.\n */\n tick(dtMs : number) : void\n {\n // Process delays\n const firedDelays : number[] = [];\n for(const entry of this._delays)\n {\n entry.remaining -= dtMs;\n if(entry.remaining <= 0)\n {\n entry.callback();\n firedDelays.push(entry.id);\n }\n }\n\n if(firedDelays.length > 0)\n {\n this._delays = this._delays.filter((entry) => !firedDelays.includes(entry.id));\n }\n\n // Process intervals\n for(const entry of this._intervals)\n {\n entry.elapsed += dtMs;\n while(entry.elapsed >= entry.periodMs)\n {\n entry.callback();\n entry.elapsed -= entry.periodMs;\n }\n }\n\n // Process cooldowns\n for(const entry of this._cooldowns)\n {\n if(entry.remaining > 0)\n {\n entry.remaining = Math.max(0, entry.remaining - dtMs);\n }\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Built-in Debug Console Commands\n//\n// Registers default references and commands onto the debug console namespace, providing convenient\n// access to game engine internals from the browser dev console.\n//----------------------------------------------------------------------------------------------------------------------\n\n/* eslint-disable no-console */\n\nimport { NamedBehavior } from '@skewedaspect/sage-core';\nimport type { GameEntity, StateBag } from '@skewedaspect/sage-core';\n\nimport { DebugConsole } from '../classes/debugConsole.ts';\nimport { NodeEntity } from '../entities/nodeEntity.ts';\n\nimport type { NodeEntityState } from '../entities/nodeEntity.ts';\nimport type { NodeSimulation } from '../entities/nodeSimulation.ts';\n\n// Types\nimport type { GameEngine } from '../classes/gameEngine.ts';\nimport type { Unsubscribe } from '../classes/eventBus.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * The current simulation, or a warning: nothing has called rebuildSimulation yet (no level has loaded, and\n * the game has not built one directly).\n */\nfunction requireSimulation(ge : GameEngine) : NodeSimulation | undefined\n{\n if(ge.simulation === null)\n {\n console.warn('[sage] No simulation yet -- nothing has given the engine a scene to build one against.');\n return undefined;\n }\n\n return ge.simulation;\n}\n\n/**\n * Resolves an entity from an ID string or returns the entity if already a GameEntity instance.\n */\nfunction resolveEntity(\n idOrEntity : string | GameEntity,\n ge : GameEngine\n) : GameEntity | undefined\n{\n if(typeof idOrEntity === 'string')\n {\n return requireSimulation(ge)?.getEntity(idOrEntity);\n }\n\n return idOrEntity;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Registers all default references and commands onto the debug console namespace.\n */\nexport function registerBuiltins(dc : DebugConsole, ge : GameEngine) : void\n{\n const gm = ge.managers.gameManager;\n const lm = ge.managers.levelManager;\n\n // Track active event loggers in a closure\n const activeLoggers : Unsubscribe[] = [];\n\n //------------------------------------------------------------------------------------------------------------------\n // Exposed references\n //------------------------------------------------------------------------------------------------------------------\n\n dc.expose('engine', ge);\n dc.expose('events', ge.eventBus);\n dc.expose('physics', ge.physics);\n dc.expose('colliders', ge.debug.colliders);\n\n dc.expose('entities', new Proxy({} as Record<string, unknown>, {\n get(_target : Record<string, unknown>, prop : string | symbol) : unknown\n {\n if(typeof prop === 'symbol')\n {\n return undefined;\n }\n\n const entity = requireSimulation(ge)?.getEntity(prop);\n if(!entity)\n {\n console.warn(`[sage] Entity not found: ${ prop }`);\n }\n\n return entity;\n },\n }));\n\n // Dynamic getters\n dc.expose('level', { get: () => lm.currentLevel });\n dc.expose('scene', { get: () => lm.currentLevel?.scene ?? null });\n\n //------------------------------------------------------------------------------------------------------------------\n // Commands\n //------------------------------------------------------------------------------------------------------------------\n\n dc.expose('tp', (idOrEntity : string | GameEntity, x : number, y : number, z : number) =>\n {\n const entity = resolveEntity(idOrEntity, ge);\n if(!entity)\n {\n console.warn(`[sage] Entity not found: ${ idOrEntity }`);\n return;\n }\n\n if(!(entity instanceof NodeEntity))\n {\n console.warn(`[sage] Entity \"${ entity.id }\" has no node`);\n return;\n }\n\n const spatial = entity.state as NodeEntityState;\n\n if(spatial.nodeName === undefined)\n {\n // update() writes the node from state.position every fixed step (design/authoring.md), so a\n // write to the node alone is gone by the next tick -- state is what has to move.\n spatial.position = { x, y, z };\n }\n else\n {\n // An adopted entity's update() never touches its node from state\n // (design/authoring.md#position-rotation-and-scale) -- move the node directly, the way any\n // Babylon code moves any node.\n entity.node.position.x = x;\n entity.node.position.y = y;\n entity.node.position.z = z;\n }\n\n console.log(`[sage] Teleported \"${ entity.id }\" to (${ x }, ${ y }, ${ z })`);\n });\n\n dc.expose('spawn', (name : string, x : number, y : number, z : number) =>\n {\n const simulation = requireSimulation(ge);\n if(!simulation)\n {\n return undefined;\n }\n\n const entity = simulation.spawn(name, undefined, { position: { x, y, z } } as StateBag);\n console.log(`[sage] Spawned \"${ name }\" (${ entity.id }) at (${ x }, ${ y }, ${ z })`);\n return entity;\n });\n\n dc.expose('list', (name ?: string, tag ?: string) : GameEntity[] =>\n {\n const simulation = requireSimulation(ge);\n if(!simulation)\n {\n return [];\n }\n\n let entities : readonly GameEntity[] = simulation.entities;\n\n if(name !== undefined)\n {\n entities = entities.filter((ent) => ent.name === name);\n }\n\n if(tag !== undefined)\n {\n entities = entities.filter((ent) => (ent.state as { tags : string[] }).tags.includes(tag));\n }\n\n const rows = entities.map((ent) =>\n {\n const row : Record<string, unknown> = {\n id: ent.id,\n name: ent.name,\n node: ent instanceof NodeEntity ? ent.node.name : '(no node)',\n };\n\n if(ent.hasBehavior(NamedBehavior))\n {\n row.displayName = ent.state.displayName;\n }\n\n return row;\n });\n\n console.table(rows);\n return [ ...entities ];\n });\n\n dc.expose('inspect', (idOrEntity : string | GameEntity) =>\n {\n const entity = resolveEntity(idOrEntity, ge);\n if(!entity)\n {\n console.warn(`[sage] Entity not found: ${ idOrEntity }`);\n return undefined;\n }\n\n const info = {\n id: entity.id,\n state: entity.state,\n node: entity instanceof NodeEntity ? entity.node.name : null,\n position: entity instanceof NodeEntity ? entity.node.position : null,\n };\n\n console.log('[sage] Entity info:', info);\n return info;\n });\n\n dc.expose('logEvents', (pattern ?: string | RegExp) =>\n {\n const resolvedPattern = pattern ?? '*';\n interface EventSub\n {\n subscribe : (\n pat : string | RegExp,\n cb : (event : { type : string; payload ?: unknown }) => void\n ) => Unsubscribe;\n }\n\n const unsub = (ge.eventBus as unknown as EventSub).subscribe(resolvedPattern, (event) =>\n {\n console.log(`[sage] Event: ${ event.type }`, event.payload);\n });\n\n activeLoggers.push(unsub);\n console.log(`[sage] Logging events matching \"${ resolvedPattern }\"`);\n return unsub;\n });\n\n dc.expose('stopLogging', () =>\n {\n for(const unsub of activeLoggers)\n {\n unsub();\n }\n\n console.log(`[sage] Stopped ${ activeLoggers.length } event logger(s)`);\n activeLoggers.length = 0;\n });\n\n dc.expose('pause', () =>\n {\n gm.pause('debug console');\n console.log('[sage] Game paused');\n });\n\n dc.expose('resume', () =>\n {\n gm.resume('debug console');\n console.log('[sage] Game resumed');\n });\n\n dc.expose('slow', (factor : number) =>\n {\n (ge.renderEngine as unknown as Record<string, unknown>).timeStep = (1 / 60) / factor;\n console.log(`[sage] Time scale set to ${ factor }x (timeStep=${ (1 / 60) / factor })`);\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Logger Interfaces\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Defines the available logging levels as string literals.\n */\nexport const LogLevels = [\n 'trace',\n 'debug',\n 'info',\n 'warn',\n 'error',\n 'none',\n] as const;\n\n/**\n * Type for LogLevel values\n */\nexport type LogLevel = typeof LogLevels[number];\n\n/**\n * Interface for a logging backend implementation.\n * Backends handle the actual output of log messages.\n */\nexport interface LoggingBackend\n{\n /**\n * Log a message at TRACE level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n trace(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Log a message at DEBUG level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n debug(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Log a message at INFO level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n info(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Log a message at WARN level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n warn(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Log a message at ERROR level\n *\n * @param category - Typically the class or module name\n * @param message\n * @param args\n */\n error(category : string, message : string, ...args : unknown[]) : void;\n\n /**\n * Start a timer with the specified label.\n *\n * @param category - Typically the class or module name\n * @param label - Must be unique; used to match with timeEnd()\n */\n time(category : string, label : string) : void;\n\n /**\n * End a timer with the specified label and log the time elapsed.\n *\n * @param category - Typically the class or module name\n * @param label - Must match a previous time() call\n */\n timeEnd(category : string, label : string) : void;\n}\n\n/**\n * Interface for a logger instance.\n * Each instance is typically associated with a specific category/component.\n */\nexport interface LoggerInterface\n{\n /** Log a message at TRACE level */\n trace(message : string, ...args : unknown[]) : void;\n\n /** Log a message at DEBUG level */\n debug(message : string, ...args : unknown[]) : void;\n\n /** Log a message at INFO level */\n info(message : string, ...args : unknown[]) : void;\n\n /** Log a message at WARN level */\n warn(message : string, ...args : unknown[]) : void;\n\n /** Log a message at ERROR level */\n error(message : string, ...args : unknown[]) : void;\n\n /** Start a timer with the given label */\n time(label : string) : void;\n\n /** End a timer and log the elapsed time */\n timeEnd(label : string) : void;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Hierarchy Bounds\n//\n// Blender's glTF exporter splits a multi-material model into primitive children, leaving a bare\n// TransformNode parent with no geometry of its own. `PhysicsAggregate` reads such a node's raw bounding\n// info -- a default unit cube -- so anything sizing a collision shape from one of these nodes has to\n// measure the descendants instead.\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Quaternion, type TransformNode, Vector3 } from '@babylonjs/core';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface HierarchyBounds\n{\n /** Full box size across the node's descendants. */\n extents : Vector3;\n\n /** Offset from the node's origin, in the node's local frame. */\n center : Vector3;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Measures a node's descendant geometry as a box, in the format `PhysicsAggregate` takes in its options.\n */\nexport function computeHierarchyBounds(node : TransformNode) : HierarchyBounds\n{\n const { min, max } = node.getHierarchyBoundingVectors(true);\n const extents = max.subtract(min);\n const worldCenter = min.add(max).scale(0.5);\n\n // Undo the node's rotation so the center lands in its local frame; for the common identity case this\n // is just worldCenter - absolutePosition.\n const worldOffset = worldCenter.subtract(node.absolutePosition);\n const inverseRotation = Quaternion.Inverse(node.absoluteRotationQuaternion);\n const center = new Vector3();\n\n worldOffset.rotateByQuaternionToRef(inverseRotation, center);\n\n return { extents, center };\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Collider Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n AbstractMesh,\n Mesh,\n PhysicsAggregate,\n type PhysicsAggregateParameters,\n PhysicsShapeType,\n type TransformNode,\n} from '@babylonjs/core';\n\nimport type { LevelManager } from '../managers/level.ts';\nimport { computeHierarchyBounds } from '../utils/bounds.ts';\nimport { SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nconst logger = new SAGELogger('ColliderHandler');\n\n/**\n * Creates a physics aggregate for a node with the given shape type.\n *\n * Works with both Mesh and TransformNode. For non-Mesh TransformNodes with child geometry\n * (multi-material mesh case), computes explicit extents/center from the hierarchy bounding box\n * to avoid `PhysicsAggregate` falling back to a default unit cube.\n */\nfunction createPhysicsAggregate(node : TransformNode, shapeType : PhysicsShapeType, mass : number) : void\n{\n const scene = node.getScene();\n const options : PhysicsAggregateParameters = { mass };\n\n // Non-Mesh TransformNodes have no geometry of their own; compute bounds from descendants.\n if(!(node instanceof Mesh) && (shapeType === PhysicsShapeType.BOX || shapeType === PhysicsShapeType.SPHERE))\n {\n const { extents, center } = computeHierarchyBounds(node);\n options.center = center;\n\n if(shapeType === PhysicsShapeType.BOX)\n {\n options.extents = extents;\n }\n else\n {\n // Sphere radius: use the largest half-extent so the sphere fully contains the geometry.\n options.radius = Math.max(extents.x, extents.y, extents.z) / 2;\n }\n }\n\n new PhysicsAggregate(node, shapeType, options, scene);\n}\n\n/**\n * Handles mesh collider setup, using a child mesh if marked with collider_mesh.\n */\nfunction handleMeshCollider(mesh : Mesh, mass : number) : void\n{\n const colliderChild = mesh.getChildMeshes().find(\n (child) => child.metadata?.collider_mesh === true\n );\n\n if(colliderChild instanceof Mesh)\n {\n colliderChild.isVisible = false;\n createPhysicsAggregate(colliderChild, PhysicsShapeType.MESH, mass);\n }\n else\n {\n createPhysicsAggregate(mesh, PhysicsShapeType.MESH, mass);\n }\n}\n\n/**\n * Register the collider property handler.\n *\n * Supports the following properties on scene nodes:\n * - `collider` (string): Collider type - \"box\", \"sphere\", \"mesh\", or \"none\"\n * - `collider_mesh` (boolean): On a child node, marks it as the collision source for parent's \"mesh\" collider\n * - `collider_mass` (number): Mass for the physics body. Defaults to 0 (static). Set > 0 for dynamic bodies.\n * - `collider_kinematic` (boolean): When true, sets `disablePreStep = false` so the physics body\n * tracks mesh transform each frame. Required for animated colliders (doors, elevators, platforms).\n *\n * For \"mesh\" colliders, if a child node has `collider_mesh: true`, that child's geometry\n * is used for collision and the child is made invisible.\n *\n * @param levelManager\n */\nexport function registerColliderHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('collider', (node, value, level, _gameEngine) =>\n {\n const colliderType = value as string;\n logger.debug(`Processing collider: ${ node.name } -> ${ colliderType } (isMesh: ${ node instanceof Mesh })`);\n\n if(!level.scene)\n {\n throw new Error('Scene not available for collider handler');\n }\n\n if(!(node instanceof Mesh) && colliderType === 'mesh')\n {\n logger.warn(`Skipping mesh collider for ${ node.name }: not a Mesh instance`);\n return;\n }\n\n const mass = (node.metadata?.collider_mass as number | undefined) ?? 0;\n\n switch (colliderType)\n {\n case 'box':\n createPhysicsAggregate(node, PhysicsShapeType.BOX, mass);\n break;\n\n case 'sphere':\n createPhysicsAggregate(node, PhysicsShapeType.SPHERE, mass);\n break;\n\n case 'mesh':\n handleMeshCollider(node as Mesh, mass);\n break;\n\n case 'none':\n if(node instanceof AbstractMesh)\n {\n node.checkCollisions = false;\n }\n break;\n\n default:\n logger.warn(`Unknown collider type: ${ colliderType }`);\n }\n\n // Enable kinematic mode -- physics body tracks mesh transform each frame\n if(node.metadata?.collider_kinematic && node instanceof AbstractMesh)\n {\n if(node.physicsBody)\n {\n node.physicsBody.disablePreStep = false;\n }\n }\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// LOD (Level of Detail) Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Mesh } from '@babylonjs/core';\n\nimport type { LevelManager } from '../managers/level.ts';\nimport { SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nconst logger = new SAGELogger('LodHandler');\n\n/**\n * Parses a comma-separated string of distances into an array of numbers.\n * Filters out invalid (NaN) values.\n */\nfunction parseDistances(distancesStr : string) : number[]\n{\n return distancesStr\n .split(',')\n .map((str) => parseFloat(str.trim()))\n .filter((num) => !isNaN(num));\n}\n\n/**\n * Register the LOD (Level of Detail) property handler.\n *\n * Supports the following property on scene nodes:\n * - `lod_distances` (string): Comma-separated distances for LOD levels\n *\n * The mesh's children are used as LOD levels in order.\n * Example: \"10,25,50\" means:\n * - 0-10 units: use first child mesh\n * - 10-25 units: use second child mesh\n * - 25-50 units: use third child mesh\n * - 50+ units: use null (not visible)\n *\n * @param levelManager\n */\nexport function registerLodHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('lod_distances', (node, value, _level, _gameEngine) =>\n {\n const distancesStr = value as string;\n\n if(!(node instanceof Mesh))\n {\n return;\n }\n\n const distances = parseDistances(distancesStr);\n\n if(distances.length === 0)\n {\n logger.warn(`Invalid lod_distances value: ${ distancesStr }`);\n return;\n }\n\n const children = node.getChildMeshes(true) as Mesh[];\n const levelCount = Math.min(distances.length, children.length);\n\n // Add LOD levels for each distance/child pair\n for(let i = 0; i < levelCount; i++)\n {\n node.addLODLevel(distances[i], children[i]);\n }\n\n // Add null LOD for beyond the last distance (not visible)\n const lastDistance = distances[distances.length - 1];\n node.addLODLevel(lastDistance, null);\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Occluder Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Mesh } from '@babylonjs/core';\n\nimport type { LevelManager } from '../managers/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Register the occluder property handler.\n *\n * Supports the following property on scene nodes:\n * - `occluder` (boolean): If true, marks the mesh as an occluder for occlusion culling\n *\n * Occluder meshes are used by the engine to hide objects that are behind them,\n * improving rendering performance. The occluder mesh itself is made invisible.\n *\n * @param levelManager\n */\nexport function registerOccluderHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('occluder', (node, value, _level, _gameEngine) =>\n {\n if(!value)\n {\n return;\n }\n\n // Only meshes can be occluders\n if(!(node instanceof Mesh))\n {\n return;\n }\n\n // Mark as occluder and hide\n // Note: isOccluder exists at runtime but is missing from @babylonjs/core type definitions as of 9.0\n (node as Mesh & { isOccluder : boolean }).isOccluder = true;\n node.isVisible = false;\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Metadata Utilities\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Collect properties with a common prefix into a single object.\n * Useful for gathering related properties from node metadata.\n *\n * For example, given metadata with:\n * - sound_volume: 0.5\n * - sound_loop: true\n * - sound_spatial: false\n *\n * Calling collectPrefixedProperties(metadata, 'sound') returns:\n * { volume: 0.5, loop: true, spatial: false }\n *\n * @param metadata - The node metadata to search\n * @param prefix - The prefix to match (without trailing underscore)\n * @returns Object with sub-properties (prefix and underscore removed from keys)\n */\nexport function collectPrefixedProperties(\n metadata : Record<string, unknown>,\n prefix : string\n) : Record<string, unknown>\n{\n const result : Record<string, unknown> = {};\n const prefixWithUnderscore = `${ prefix }_`;\n\n for(const [ key, value ] of Object.entries(metadata))\n {\n if(key.startsWith(prefixWithUnderscore))\n {\n const subKey = key.slice(prefixWithUnderscore.length);\n result[subKey] = value;\n }\n }\n\n return result;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Sound Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { collectPrefixedProperties } from '../utils/metadata.ts';\nimport { SAGELogger } from '../utils/logger.ts';\nimport type { LevelManager } from '../managers/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nconst logger = new SAGELogger('SoundHandler');\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Register the sound property handler.\n *\n * Supports the following properties on scene nodes:\n * - `sound` (string): Path to the sound file\n * - `sound_volume` (number): Volume 0-1, default 1\n * - `sound_loop` (boolean): Whether to loop, default true\n * - `sound_spatial` (boolean): Whether to use 3D spatial audio, default true\n * - `sound_distance` (number): Maximum distance for spatial audio, default 100\n * - `sound_autoplay` (boolean): Whether to autoplay, default true\n * - `sound_channel` (string): Audio channel name, default 'ambient'\n *\n * @param levelManager\n */\nexport function registerSoundHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('sound', (node, value, level, gameEngine) =>\n {\n const soundPath = value as string;\n const scene = level.scene;\n\n if(!scene)\n {\n throw new Error('Scene not available for sound handler');\n }\n\n // Collect prefixed properties\n const extras = collectPrefixedProperties(\n node.metadata as Record<string, unknown>,\n 'sound'\n );\n\n const volume = (extras.volume as number) ?? 1;\n const loop = (extras.loop as boolean) ?? true;\n const spatial = (extras.spatial as boolean) ?? true;\n const maxDistance = (extras.distance as number) ?? 100;\n const autoplay = (extras.autoplay as boolean) ?? true;\n const channel = (extras.channel as string) ?? 'ambient';\n\n const audioManager = gameEngine.managers.audioManager;\n\n if(!audioManager)\n {\n logger.warn(`No AudioManager available. Sound for \"${ node.name }\" skipped.`);\n return;\n }\n\n audioManager.createSound(\n `${ node.name }_sound`,\n soundPath,\n channel,\n {\n loop,\n autoplay,\n volume,\n spatialEnabled: spatial,\n spatialMaxDistance: maxDistance,\n spatialDistanceModel: 'linear',\n }\n )\n .then((sound) =>\n {\n if(spatial)\n {\n sound.spatial.attach(node);\n }\n })\n .catch((err) =>\n {\n logger.error(`Failed to create sound for node \"${ node.name }\": ${ err }`);\n });\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Trigger Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractMesh, ActionManager, ExecuteCodeAction } from '@babylonjs/core';\n\nimport type { GameEventBus } from '../classes/eventBus.ts';\nimport type { LevelManager } from '../managers/level.ts';\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Registers an intersection action (enter or exit) for a trigger mesh against a target mesh.\n */\nfunction registerIntersectionAction(\n actionManager : ActionManager,\n triggerName : string,\n eventBus : GameEventBus,\n targetMesh : AbstractMesh,\n actionType : 'enter' | 'exit'\n) : void\n{\n const triggerType = actionType === 'enter'\n ? ActionManager.OnIntersectionEnterTrigger\n : ActionManager.OnIntersectionExitTrigger;\n\n const eventType = `trigger:${ actionType }` as const;\n\n actionManager.registerAction(\n new ExecuteCodeAction(\n {\n trigger: triggerType,\n parameter: { mesh: targetMesh },\n },\n (evt) =>\n {\n // Babylon fires an intersection action with the trigger mesh as its source and the mesh that\n // crossed it as the additional data.\n eventBus.publish({\n type: eventType,\n payload: {\n trigger: triggerName,\n other: evt.additionalData as AbstractMesh,\n },\n });\n }\n )\n );\n}\n\n/**\n * Sets up intersection triggers between a trigger mesh and a target mesh.\n */\nfunction registerTriggersForMesh(\n actionManager : ActionManager,\n triggerName : string,\n eventBus : GameEventBus,\n targetMesh : AbstractMesh\n) : void\n{\n registerIntersectionAction(actionManager, triggerName, eventBus, targetMesh, 'enter');\n registerIntersectionAction(actionManager, triggerName, eventBus, targetMesh, 'exit');\n}\n\n/**\n * Register the trigger property handler.\n *\n * Supports the following property on scene nodes:\n * - `trigger` (string): The trigger zone name\n *\n * When an object enters or exits the trigger zone, events are published:\n * - `trigger:enter` with payload { trigger: string, other: AbstractMesh }\n * - `trigger:exit` with payload { trigger: string, other: AbstractMesh }\n *\n * The trigger mesh is made invisible but retains collision detection.\n * Intersection checks are registered against all non-trigger meshes in the scene,\n * including meshes added after the handler runs (e.g. dynamically spawned entities).\n *\n * @param levelManager\n */\nexport function registerTriggerHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('trigger', (node, value, level, gameEngine) =>\n {\n const triggerName = value as string;\n const scene = level.scene;\n\n if(!scene)\n {\n throw new Error('Scene not available for trigger handler');\n }\n\n if(!(node instanceof AbstractMesh))\n {\n return;\n }\n\n const eventBus = gameEngine.eventBus;\n\n // Make the trigger invisible but keep collision\n node.isVisible = false;\n node.checkCollisions = true;\n\n // Create action manager if needed\n const actionManager = (node.actionManager as ActionManager) ?? new ActionManager(scene);\n node.actionManager = actionManager;\n\n // Register intersection triggers against all existing non-trigger meshes\n for(const mesh of scene.meshes)\n {\n if(mesh !== node && !mesh.metadata?.trigger)\n {\n registerTriggersForMesh(actionManager, triggerName, eventBus, mesh);\n }\n }\n\n // Register triggers for meshes added later (e.g. dynamically spawned entities)\n const observer = scene.onNewMeshAddedObservable.add((mesh) =>\n {\n if(mesh !== node && !mesh.metadata?.trigger)\n {\n registerTriggersForMesh(actionManager, triggerName, eventBus, mesh);\n }\n });\n\n // Clean up observer when the trigger mesh is disposed\n node.onDisposeObservable.add(() =>\n {\n scene.onNewMeshAddedObservable.remove(observer);\n });\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Visible Property Handler\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractMesh, type TransformNode } from '@babylonjs/core';\nimport type { LevelManager } from '../managers/level.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Register the visible property handler.\n *\n * Supports the following property on scene nodes:\n * - `visible` (boolean | string | number): Controls mesh visibility.\n * Accepts true/false, 'true'/'false', or 1/0.\n *\n * @param levelManager\n */\nexport function registerVisibleHandler(levelManager : LevelManager) : void\n{\n levelManager.registerPropertyHandler('visible', (node : TransformNode, value : unknown, _level, _gameEngine) =>\n {\n // Only meshes have isVisible property\n if(!(node instanceof AbstractMesh))\n {\n return;\n }\n\n // Parse value as boolean\n // Supports: true/false (boolean), 'true'/'false' (string), 1/0 (number)\n let visible = true;\n\n if(typeof value === 'boolean')\n {\n visible = value;\n }\n else if(typeof value === 'string')\n {\n visible = value.toLowerCase() === 'true' || value === '1';\n }\n else if(typeof value === 'number')\n {\n visible = value !== 0;\n }\n\n node.isVisible = visible;\n });\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Property Handlers Index\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LevelManager } from '../managers/level.ts';\n\nimport { registerColliderHandler } from './collider.ts';\nimport { registerLodHandler } from './lod.ts';\nimport { registerOccluderHandler } from './occluder.ts';\nimport { applyPostProcessing } from './postProcessing.ts';\nimport { registerSoundHandler } from './sound.ts';\nimport { registerTriggerHandler } from './trigger.ts';\nimport { registerVisibleHandler } from './visible.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport {\n applyPostProcessing,\n registerColliderHandler,\n registerLodHandler,\n registerOccluderHandler,\n registerSoundHandler,\n registerTriggerHandler,\n registerVisibleHandler,\n};\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Register all built-in property handlers with the given LevelManager.\n * Call this during game initialization to enable default property handling.\n *\n * @param levelManager\n */\nexport function registerAllPropertyHandlers(levelManager : LevelManager) : void\n{\n registerColliderHandler(levelManager);\n registerLodHandler(levelManager);\n registerOccluderHandler(levelManager);\n registerSoundHandler(levelManager);\n registerTriggerHandler(levelManager);\n registerVisibleHandler(levelManager);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// defineEntity\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type {\n Action, BehaviorClass, ComposedOf, GameEntityType, GameSimulationContext, StateBag, StateFor, ValidTuple,\n} from '@skewedaspect/sage-core';\n\nimport { NodeEntity } from './nodeEntity.ts';\n\nimport type { MeshConfig } from './meshConfig.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface GameEntityDefinition<Behaviors extends readonly BehaviorClass[]>\n{\n type : string;\n tags ?: string[];\n defaultState ?: Partial<StateFor<Behaviors>>;\n behaviors : Behaviors;\n mesh ?: MeshConfig;\n actions ?: readonly Action[];\n}\n\nexport type GameEntityDefinitionClass<Behaviors extends readonly BehaviorClass[]> = typeof NodeEntity & {\n readonly behaviors : Behaviors;\n};\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// A supplied field replaces the definition's own default whole (design/entities.md#state); tags merge as a\n// deduplicated union instead, since a spawn's own extra tags are additions to a definition's baseline, not a\n// replacement for it (design/authoring.md#composition-and-the-class-table).\nfunction foldCreateState<Behaviors extends readonly BehaviorClass[]>(\n definition : GameEntityDefinition<Behaviors>,\n supplied : Partial<StateFor<Behaviors>> | undefined\n) : Partial<StateFor<Behaviors>>\n{\n const suppliedState = (supplied ?? {}) as StateBag;\n const definitionTags = definition.tags ?? [];\n const suppliedTags = (suppliedState['tags'] as string[] | undefined) ?? [];\n\n return {\n ...definition.defaultState,\n ...suppliedState,\n tags: [ ...new Set([ ...definitionTags, ...suppliedTags ]) ],\n } as Partial<StateFor<Behaviors>>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport function defineEntity<Behaviors extends readonly BehaviorClass[]>(\n definition : GameEntityDefinition<Behaviors> & { behaviors : ValidTuple<Behaviors> }\n) : GameEntityDefinitionClass<Behaviors>\n{\n class DefinedEntity extends NodeEntity\n {\n static override readonly behaviors = definition.behaviors;\n static override readonly meshConfig = definition.mesh;\n static override readonly actions = definition.actions;\n\n static override create<Type extends GameEntityType>(\n this : Type & { behaviors : ValidTuple<Type['behaviors']> },\n id : string,\n context : GameSimulationContext,\n state ?: Partial<StateFor<Type['behaviors']>>,\n name ?: string\n ) : ComposedOf<Type>\n {\n const folded = foldCreateState(definition, state as Partial<StateFor<Behaviors>> | undefined);\n\n return super.create(id, context, folded as Partial<StateFor<Type['behaviors']>>, name);\n }\n }\n\n return DefinedEntity as unknown as GameEntityDefinitionClass<Behaviors>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Standard Mesh Source\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Color3, MeshBuilder, PBRMaterial, StandardMaterial } from '@babylonjs/core';\n\nimport type { AbstractMesh, AssetContainer, Mesh, Scene } from '@babylonjs/core';\nimport type { GameEntityType } from '@skewedaspect/sage-core';\n\nimport type { ColorConfig } from '../interfaces/level.ts';\nimport type { EntityMeshLoader } from '../interfaces/game.ts';\n\nimport { NodeEntity } from './nodeEntity.ts';\nimport type { MeshConfig, MeshMaterialConfig } from './meshConfig.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// What the standard mesh source needs from the asset manager: loading a glb (or a fragment naming one of its\n// meshes), and cloning a fragment's cached source mesh into a fresh, independent one.\nexport interface MeshAssetSource\n{\n load(path : string) : Promise<AssetContainer | AbstractMesh>;\n clone(path : string) : Mesh;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nfunction toColor3(color : ColorConfig) : Color3\n{\n return new Color3(color.r, color.g, color.b);\n}\n\nfunction buildPrimitiveMesh(scene : Scene, source : string, params : Record<string, unknown> | undefined) : Mesh\n{\n const builderName = `Create${ source.charAt(0).toUpperCase() }${ source.slice(1) }`;\n const builders = MeshBuilder as unknown as Record<string, (name : string, options : object, scene : Scene) => Mesh>;\n const builder = builders[builderName];\n\n if(typeof builder !== 'function')\n {\n throw new Error(`No Babylon mesh builder provides the primitive shape \"${ source }\".`);\n }\n\n return builder(source, params ?? {}, scene);\n}\n\nfunction applyMaterial(scene : Scene, mesh : Mesh, config : MeshMaterialConfig) : void\n{\n if(config.type === 'pbr')\n {\n const material = new PBRMaterial(`${ mesh.name }-material`, scene);\n\n if(config.color !== undefined)\n {\n material.albedoColor = toColor3(config.color);\n }\n\n if(config.emissive !== undefined)\n {\n material.emissiveColor = toColor3(config.emissive);\n }\n\n if(config.metallic !== undefined)\n {\n material.metallic = config.metallic;\n }\n\n if(config.roughness !== undefined)\n {\n material.roughness = config.roughness;\n }\n\n mesh.material = material;\n }\n else\n {\n const material = new StandardMaterial(`${ mesh.name }-material`, scene);\n\n if(config.color !== undefined)\n {\n material.diffuseColor = toColor3(config.color);\n }\n\n if(config.emissive !== undefined)\n {\n material.emissiveColor = toColor3(config.emissive);\n }\n\n mesh.material = material;\n }\n}\n\nasync function buildMesh(scene : Scene, config : MeshConfig, assets : MeshAssetSource) : Promise<Mesh>\n{\n let mesh : Mesh;\n\n if(config.source.includes('#'))\n {\n await assets.load(config.source);\n mesh = assets.clone(config.source);\n }\n else\n {\n mesh = buildPrimitiveMesh(scene, config.source, config.params);\n }\n\n mesh.isVisible = config.visible ?? true;\n\n if(config.material !== undefined)\n {\n applyMaterial(scene, mesh, config.material);\n }\n\n return mesh;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport function buildStandardMeshSource(\n entityClasses : Readonly<Record<string, GameEntityType>>,\n assets : MeshAssetSource\n) : EntityMeshLoader\n{\n return async (scene, name) : Promise<AbstractMesh | undefined> =>\n {\n const entityClass = entityClasses[name] as typeof NodeEntity | undefined;\n const config = entityClass?.meshConfig;\n\n if(config === undefined)\n {\n return undefined;\n }\n\n return buildMesh(scene, config, assets);\n };\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// SoundBehavior\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Behavior } from '@skewedaspect/sage-core';\n\nimport { NodeEntity } from '../entities/nodeEntity.ts';\nimport type { NodeBehaviorHooks } from '../entities/nodeEntity.ts';\n\nimport type { StaticSound } from '@babylonjs/core';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface SoundConfig\n{\n url : string;\n volume ?: number;\n loop ?: boolean;\n spatial ?: boolean;\n maxDistance ?: number;\n channel ?: string;\n}\n\nexport interface SoundState\n{\n sounds : Record<string, SoundConfig>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class SoundBehavior extends Behavior<SoundState> implements NodeBehaviorHooks\n{\n static readonly ops = [ 'play', 'stop' ] as const;\n\n readonly defaults : SoundState = { sounds: {} };\n\n readonly #sounds = new Map<string, StaticSound>();\n #destroyed = false;\n\n onMeshLoaded() : void\n {\n const entity = this.entity as NodeEntity;\n const audio = entity.audio;\n\n if(audio === undefined)\n {\n return;\n }\n\n for(const [ name, config ] of Object.entries(this.state.sounds))\n {\n audio.createSound(name, config.url, config.channel, {\n volume: config.volume ?? 1,\n loop: config.loop ?? false,\n }).then((sound) =>\n {\n this.#onSoundBuilt(name, config, entity, sound);\n });\n }\n }\n\n onDestroy() : void\n {\n this.#destroyed = true;\n\n for(const sound of this.#sounds.values())\n {\n sound.stop();\n sound.dispose();\n }\n\n this.#sounds.clear();\n }\n\n play(name : string) : void\n {\n this.#find(name)?.play();\n }\n\n stop(name : string) : void\n {\n this.#find(name)?.stop();\n }\n\n // A sound whose entity is already gone by the time it finishes building is disposed on arrival rather\n // than kept -- destruction is not undone by a late arrival, unlike a mesh\n // (design/audio.md#pooling-and-destruction).\n #onSoundBuilt(name : string, config : SoundConfig, entity : NodeEntity, sound : StaticSound) : void\n {\n if(this.#destroyed)\n {\n sound.dispose();\n return;\n }\n\n if(config.spatial ?? false)\n {\n sound.spatial.attach(entity.node);\n sound.spatial.maxDistance = config.maxDistance ?? 100;\n }\n\n this.#sounds.set(name, sound);\n\n if(config.loop ?? false)\n {\n sound.play();\n }\n }\n\n #find(name : string) : StaticSound | undefined\n {\n if(!(name in this.state.sounds))\n {\n throw new Error(`SoundBehavior: no sound named \"${ name }\" is declared.`);\n }\n\n return this.#sounds.get(name);\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Keyboard Device Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { KeyboardState } from '../deviceState.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// Reads one key by its DOM code (input.md#device-readers).\nexport class KeyboardReader implements DeviceReader<KeyboardState>\n{\n readonly sourceType = 'key';\n readonly sourceKey : string;\n\n constructor(sourceKey : string)\n {\n this.sourceKey = sourceKey;\n }\n\n getValue(state : KeyboardState) : boolean | undefined\n {\n return state.keys[this.sourceKey];\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Mouse Device Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { MouseState } from '../deviceState.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type MouseSourceType = 'button' | 'position' | 'wheel' | 'movement';\n\n// Reads a mouse button, the cursor position, or a wheel axis (input.md#device-readers).\nexport class MouseReader implements DeviceReader<MouseState>\n{\n readonly sourceType : MouseSourceType;\n readonly sourceKey : string;\n\n constructor(sourceType : MouseSourceType, sourceKey : string)\n {\n this.sourceType = sourceType;\n this.sourceKey = sourceKey;\n }\n\n getValue(state : MouseState) : boolean | number | undefined\n {\n if(this.sourceType === 'button')\n {\n return state.buttons[this.sourceKey];\n }\n\n if(this.sourceType === 'movement')\n {\n if(this.sourceKey === 'x')\n {\n return state.movement.x;\n }\n\n if(this.sourceKey === 'y')\n {\n return state.movement.y;\n }\n\n return undefined;\n }\n\n if(this.sourceType === 'position')\n {\n if(this.sourceKey === 'x')\n {\n return state.position.x;\n }\n\n if(this.sourceKey === 'y')\n {\n return state.position.y;\n }\n\n return undefined;\n }\n\n if(this.sourceType === 'wheel')\n {\n if(this.sourceKey === 'deltaX')\n {\n return state.wheel.deltaX;\n }\n\n if(this.sourceKey === 'deltaY')\n {\n return state.wheel.deltaY;\n }\n\n if(this.sourceKey === 'deltaZ')\n {\n return state.wheel.deltaZ;\n }\n\n return undefined;\n }\n\n return undefined;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Gamepad Device Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GamepadState } from '../deviceState.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type GamepadSourceType = 'button' | 'axis';\n\n// Reads a gamepad button or stick axis (input.md#device-readers).\nexport class GamepadReader implements DeviceReader<GamepadState>\n{\n readonly sourceType : GamepadSourceType;\n readonly sourceKey : string;\n\n constructor(sourceType : GamepadSourceType, sourceKey : string)\n {\n this.sourceType = sourceType;\n this.sourceKey = sourceKey;\n }\n\n getValue(state : GamepadState) : boolean | number | undefined\n {\n if(this.sourceType === 'button')\n {\n return state.buttons[this.sourceKey];\n }\n\n if(this.sourceType === 'axis')\n {\n return state.axes[this.sourceKey];\n }\n\n return undefined;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Trigger Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition, BindingFire, BindingInput, DeviceID } from '../binding.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type EdgeMode = 'rising' | 'falling' | 'both';\n\nexport interface TriggerOptions\n{\n edge ?: EdgeMode;\n threshold ?: number;\n\n /** Takes this input from the browser while the binding is live (input.md#claiming-an-input-from-the-browser). */\n preventDefault ?: boolean;\n}\n\n// Fires once on the rising edge, the falling edge, or both. An analog input crossing the threshold counts\n// as the change (input.md#bindings).\nexport class TriggerBinding<State> implements Binding<State>\n{\n readonly kind = 'trigger' as const;\n readonly action : Action;\n readonly device : DeviceID;\n readonly context ?: string;\n readonly preventDefault : boolean;\n\n readonly #reader : DeviceReader<State>;\n readonly #edge : EdgeMode;\n readonly #threshold ?: number;\n\n // undefined until the first process()/prime() call establishes a baseline, so that call never fires.\n #pressed : boolean | undefined;\n\n constructor(\n action : Action,\n device : DeviceID,\n reader : DeviceReader<State>,\n options : TriggerOptions = {},\n context ?: string\n )\n {\n this.action = action;\n this.device = device;\n this.context = context;\n this.preventDefault = options.preventDefault ?? false;\n\n this.#reader = reader;\n this.#edge = options.edge ?? 'rising';\n this.#threshold = options.threshold;\n }\n\n get input() : BindingInput\n {\n return { device: this.device, sourceType: this.#reader.sourceType, sourceKey: this.#reader.sourceKey };\n }\n\n process(state : State) : BindingFire | undefined\n {\n const pressed = this.#isPressed(this.#reader.getValue(state));\n const previous = this.#pressed;\n\n this.#pressed = pressed;\n\n if(previous === undefined)\n {\n return undefined;\n }\n\n if(!this.#crossesEdge(previous, pressed))\n {\n return undefined;\n }\n\n if(this.action.type === 'analog')\n {\n return { value: this.#numericValue(this.#reader.getValue(state)) };\n }\n\n return { value: pressed };\n }\n\n prime(state : State) : void\n {\n this.#pressed = this.#isPressed(this.#reader.getValue(state));\n }\n\n toDefinition() : BindingDefinition\n {\n const options : Record<string, unknown> = { edge: this.#edge };\n\n if(this.#threshold !== undefined)\n {\n options.threshold = this.#threshold;\n }\n\n if(this.preventDefault)\n {\n options.preventDefault = true;\n }\n\n return {\n kind: 'trigger',\n action: this.action.name,\n input: this.input,\n context: this.context,\n options,\n };\n }\n\n #crossesEdge(previous : boolean, pressed : boolean) : boolean\n {\n if(this.#edge === 'rising')\n {\n return !previous && pressed;\n }\n\n if(this.#edge === 'falling')\n {\n return previous && !pressed;\n }\n\n return previous !== pressed;\n }\n\n #isPressed(raw : boolean | number | undefined) : boolean\n {\n if(typeof raw === 'boolean')\n {\n return raw;\n }\n\n if(typeof raw === 'number')\n {\n if(this.#threshold !== undefined)\n {\n return raw >= this.#threshold;\n }\n\n return raw !== 0;\n }\n\n return false;\n }\n\n #numericValue(raw : boolean | number | undefined) : number\n {\n if(typeof raw === 'number')\n {\n return raw;\n }\n\n if(raw === true)\n {\n return 1;\n }\n\n return 0;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Toggle Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition, BindingFire, BindingInput, DeviceID } from '../binding.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface ToggleOptions\n{\n onValue ?: boolean | number;\n offValue ?: boolean | number;\n initialState ?: boolean;\n threshold ?: number;\n invert ?: boolean;\n\n /** Takes this input from the browser while the binding is live (input.md#claiming-an-input-from-the-browser). */\n preventDefault ?: boolean;\n}\n\n// Alternates each time its input is pressed and holds its state between presses (input.md#bindings).\nexport class ToggleBinding<State> implements Binding<State>\n{\n readonly kind = 'toggle' as const;\n readonly action : Action;\n readonly device : DeviceID;\n readonly context ?: string;\n readonly preventDefault : boolean;\n\n readonly #reader : DeviceReader<State>;\n readonly #onValue : boolean | number;\n readonly #offValue : boolean | number;\n readonly #threshold ?: number;\n readonly #invert : boolean;\n\n // No declared default (input.md#bindings): left undeclared, the on/off bit that alternation flips starts\n // undefined, and the first flip lands on true the same way any boolean negation of undefined does.\n #isOn : boolean | undefined;\n\n // undefined until the first process()/prime() call establishes a baseline, so that call never fires.\n #previousEffective : boolean | undefined;\n\n constructor(\n action : Action,\n device : DeviceID,\n reader : DeviceReader<State>,\n options : ToggleOptions = {},\n context ?: string\n )\n {\n this.action = action;\n this.device = device;\n this.context = context;\n this.preventDefault = options.preventDefault ?? false;\n\n this.#reader = reader;\n this.#onValue = options.onValue ?? true;\n this.#offValue = options.offValue ?? false;\n this.#threshold = options.threshold;\n this.#invert = options.invert ?? false;\n this.#isOn = options.initialState;\n }\n\n get input() : BindingInput\n {\n return { device: this.device, sourceType: this.#reader.sourceType, sourceKey: this.#reader.sourceKey };\n }\n\n process(state : State) : BindingFire | undefined\n {\n const effective = this.#effective(this.#reader.getValue(state));\n const previous = this.#previousEffective;\n\n this.#previousEffective = effective;\n\n if(previous === undefined)\n {\n return undefined;\n }\n\n if(previous || !effective)\n {\n return undefined;\n }\n\n this.#isOn = !this.#isOn;\n\n if(this.#isOn)\n {\n return { value: this.#onValue };\n }\n\n return { value: this.#offValue };\n }\n\n prime(state : State) : void\n {\n this.#previousEffective = this.#effective(this.#reader.getValue(state));\n }\n\n toDefinition() : BindingDefinition\n {\n const options : Record<string, unknown> = {\n onValue: this.#onValue,\n offValue: this.#offValue,\n invert: this.#invert,\n };\n\n if(this.#threshold !== undefined)\n {\n options.threshold = this.#threshold;\n }\n\n if(this.#isOn !== undefined)\n {\n options.initialState = this.#isOn;\n }\n\n if(this.preventDefault)\n {\n options.preventDefault = true;\n }\n\n return {\n kind: 'toggle',\n action: this.action.name,\n input: this.input,\n context: this.context,\n options,\n };\n }\n\n // The pressed state, inverted when the binding alternates on a release instead of a press.\n #effective(raw : boolean | number | undefined) : boolean\n {\n const pressed = this.#isPressed(raw);\n\n if(this.#invert)\n {\n return !pressed;\n }\n\n return pressed;\n }\n\n #isPressed(raw : boolean | number | undefined) : boolean\n {\n if(typeof raw === 'boolean')\n {\n return raw;\n }\n\n if(typeof raw === 'number')\n {\n if(this.#threshold !== undefined)\n {\n return raw >= this.#threshold;\n }\n\n return raw !== 0;\n }\n\n return false;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Value Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition, BindingFire, BindingInput, DeviceID } from '../binding.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface ValueOptions\n{\n scale ?: number;\n offset ?: number;\n invert ?: boolean;\n deadzone ?: number;\n min ?: number;\n max ?: number;\n\n /** Takes this input from the browser while the binding is live (input.md#claiming-an-input-from-the-browser). */\n preventDefault ?: boolean;\n}\n\n// Passes its input through continuously: scales, offsets, clamps, inverts, and ignores magnitudes inside\n// a deadzone. What it computes is a contribution to its action's value, not a delivery of its own, so it\n// always answers with one (input.md#bindings, input.md#value-actions).\nexport class ValueBinding<State> implements Binding<State>\n{\n readonly kind = 'value' as const;\n readonly action : Action;\n readonly device : DeviceID;\n readonly context ?: string;\n readonly preventDefault : boolean;\n\n readonly #reader : DeviceReader<State>;\n readonly #scale : number;\n readonly #offset : number;\n readonly #invert : boolean;\n readonly #deadzone : number;\n readonly #min ?: number;\n readonly #max ?: number;\n\n constructor(\n action : Action,\n device : DeviceID,\n reader : DeviceReader<State>,\n options : ValueOptions = {},\n context ?: string\n )\n {\n this.action = action;\n this.device = device;\n this.context = context;\n this.preventDefault = options.preventDefault ?? false;\n\n this.#reader = reader;\n this.#scale = options.scale ?? 1;\n this.#offset = options.offset ?? 0;\n this.#invert = options.invert ?? false;\n this.#deadzone = options.deadzone ?? 0;\n this.#min = options.min;\n this.#max = options.max;\n }\n\n get input() : BindingInput\n {\n return { device: this.device, sourceType: this.#reader.sourceType, sourceKey: this.#reader.sourceKey };\n }\n\n process(state : State) : BindingFire\n {\n return { value: this.#compute(state) };\n }\n\n // A value binding has no edge to miss, so an input already held when its context activates needs no\n // baseline: it reports its value on the next pass like any other (input.md#action-contexts).\n prime(_state : State) : void\n {\n // Nothing to prime.\n }\n\n toDefinition() : BindingDefinition\n {\n const options : Record<string, unknown> = {\n scale: this.#scale,\n offset: this.#offset,\n invert: this.#invert,\n deadzone: this.#deadzone,\n };\n\n if(this.#min !== undefined)\n {\n options.min = this.#min;\n }\n\n if(this.#max !== undefined)\n {\n options.max = this.#max;\n }\n\n if(this.preventDefault)\n {\n options.preventDefault = true;\n }\n\n return {\n kind: 'value',\n action: this.action.name,\n input: this.input,\n context: this.context,\n options,\n };\n }\n\n #compute(state : State) : number\n {\n let value = this.#toNumber(this.#reader.getValue(state));\n\n value = value * this.#scale;\n value = value + this.#offset;\n\n if(this.#min !== undefined && value < this.#min)\n {\n value = this.#min;\n }\n\n if(this.#max !== undefined && value > this.#max)\n {\n value = this.#max;\n }\n\n if(this.#invert)\n {\n value = -value;\n }\n\n if(Math.abs(value) < this.#deadzone)\n {\n value = 0;\n }\n\n return value;\n }\n\n #toNumber(raw : boolean | number | undefined) : number\n {\n if(typeof raw === 'number')\n {\n return raw;\n }\n\n if(raw === true)\n {\n return 1;\n }\n\n return 0;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Delta Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition, BindingFire, BindingInput, DeviceID } from '../binding.ts';\nimport type { DeviceReader } from '../deviceReader.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface DeltaOptions\n{\n scale ?: number;\n invert ?: boolean;\n deadzone ?: number;\n\n /** Takes this input from the browser while the binding is live (input.md#claiming-an-input-from-the-browser). */\n preventDefault ?: boolean;\n}\n\n// Fires once for a one-shot sample its input reports -- a wheel notch, a frame of raw mouse movement --\n// treating the sample as consumed. Applies scale, invert, and deadzone; never offset or clamp\n// (input.md#bindings).\nexport class DeltaBinding<State> implements Binding<State>\n{\n readonly kind = 'delta' as const;\n readonly action : Action;\n readonly device : DeviceID;\n readonly context ?: string;\n readonly preventDefault : boolean;\n\n readonly #reader : DeviceReader<State>;\n readonly #scale : number;\n readonly #invert : boolean;\n readonly #deadzone : number;\n\n constructor(\n action : Action,\n device : DeviceID,\n reader : DeviceReader<State>,\n options : DeltaOptions = {},\n context ?: string\n )\n {\n this.action = action;\n this.device = device;\n this.context = context;\n this.preventDefault = options.preventDefault ?? false;\n\n this.#reader = reader;\n this.#scale = options.scale ?? 1;\n this.#invert = options.invert ?? false;\n this.#deadzone = options.deadzone ?? 0;\n }\n\n get input() : BindingInput\n {\n return { device: this.device, sourceType: this.#reader.sourceType, sourceKey: this.#reader.sourceKey };\n }\n\n process(state : State) : BindingFire | undefined\n {\n let value = this.#toNumber(this.#reader.getValue(state));\n\n value = value * this.#scale;\n\n if(this.#invert)\n {\n value = -value;\n }\n\n if(Math.abs(value) < this.#deadzone)\n {\n value = 0;\n }\n\n // No held value to compare against -- a delta fires whenever the sample computes nonzero, every\n // time, and stays silent otherwise (input.md#bindings).\n if(value === 0)\n {\n return undefined;\n }\n\n return { value };\n }\n\n // A delta has no held state to prime -- it fires from a fresh sample every time, never from what a\n // context activation reports (input.md#bindings).\n prime(_state : State) : void\n {\n // Nothing to prime.\n }\n\n toDefinition() : BindingDefinition\n {\n const options : Record<string, unknown> = {\n scale: this.#scale, invert: this.#invert, deadzone: this.#deadzone,\n };\n\n if(this.preventDefault)\n {\n options.preventDefault = true;\n }\n\n return {\n kind: 'delta',\n action: this.action.name,\n input: this.input,\n context: this.context,\n options,\n };\n }\n\n #toNumber(raw : boolean | number | undefined) : number\n {\n if(typeof raw === 'number')\n {\n return raw;\n }\n\n if(raw === true)\n {\n return 1;\n }\n\n return 0;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Binding Resolution\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { Binding, BindingDefinition } from './binding.ts';\nimport type { DeviceReader } from './deviceReader.ts';\n\nimport { TriggerBinding, type TriggerOptions } from './bindings/trigger.ts';\nimport { ToggleBinding, type ToggleOptions } from './bindings/toggle.ts';\nimport { ValueBinding, type ValueOptions } from './bindings/value.ts';\nimport { DeltaBinding, type DeltaOptions } from './bindings/delta.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n// Builds the live binding a definition describes: the kind picks trigger, toggle, value, or delta; the\n// action and the reader are already resolved by the caller (input.md#bindings).\nexport function resolveBinding<State>(\n definition : BindingDefinition,\n action : Action,\n reader : DeviceReader<State>\n) : Binding<State>\n{\n const device = definition.input.device;\n const options = definition.options ?? {};\n\n if(definition.kind === 'trigger')\n {\n return new TriggerBinding(action, device, reader, options as TriggerOptions, definition.context);\n }\n\n if(definition.kind === 'toggle')\n {\n return new ToggleBinding(action, device, reader, options as ToggleOptions, definition.context);\n }\n\n if(definition.kind === 'delta')\n {\n return new DeltaBinding(action, device, reader, options as DeltaOptions, definition.context);\n }\n\n return new ValueBinding(action, device, reader, options as ValueOptions, definition.context);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Input Capture\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { BindingInput, DeviceType } from './binding.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface CaptureOptions\n{\n deviceTypes ?: readonly DeviceType[];\n sourceTypes ?: readonly string[];\n signal ?: AbortSignal;\n}\n\nexport interface CandidateInput\n{\n device : { type : DeviceType; index : number };\n sourceType : string;\n sourceKey : string;\n value : boolean | number;\n}\n\n// Waits for the next deliberate input and answers with the definition that would bind it (input.md#input-capture).\nexport class InputCapture\n{\n readonly #deviceTypes ?: readonly DeviceType[];\n readonly #sourceTypes ?: readonly string[];\n\n #resolveWait : ((input : BindingInput) => void) | undefined;\n #rejectWait : ((reason : unknown) => void) | undefined;\n\n constructor(options : CaptureOptions = {})\n {\n this.#deviceTypes = options.deviceTypes;\n this.#sourceTypes = options.sourceTypes;\n\n options.signal?.addEventListener('abort', () =>\n {\n this.#rejectWait?.(options.signal?.reason ?? new Error('The capture was aborted.'));\n this.#resolveWait = undefined;\n this.#rejectWait = undefined;\n });\n }\n\n // A rebinding screen feeds every candidate input it sees here as it arrives.\n feed(candidate : CandidateInput) : void\n {\n if(this.#resolveWait === undefined)\n {\n return;\n }\n\n if(!this.#isDeliberate(candidate.value))\n {\n return;\n }\n\n if(this.#deviceTypes !== undefined && !this.#deviceTypes.includes(candidate.device.type))\n {\n return;\n }\n\n if(this.#sourceTypes !== undefined && !this.#sourceTypes.includes(candidate.sourceType))\n {\n return;\n }\n\n this.#resolveWait({\n device: candidate.device,\n sourceType: candidate.sourceType,\n sourceKey: candidate.sourceKey,\n });\n\n this.#resolveWait = undefined;\n this.#rejectWait = undefined;\n }\n\n wait() : Promise<BindingInput>\n {\n return new Promise((resolve, reject) =>\n {\n this.#resolveWait = resolve;\n this.#rejectWait = reject;\n });\n }\n\n #isDeliberate(value : boolean | number) : boolean\n {\n if(value === true)\n {\n return true;\n }\n\n if(typeof value === 'number' && value !== 0)\n {\n return true;\n }\n\n return false;\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Configuration\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { Action } from '@skewedaspect/sage-core';\n\nimport type { ActionContextRegistry } from './actionContext.ts';\nimport type { ActionRegistry } from './actionRegistry.ts';\nimport type { Binding, BindingDefinition } from './binding.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface ContextDefinition\n{\n name : string;\n exclusive : boolean;\n}\n\n// The whole set of actions, bindings, and contexts, JSON-compatible (input.md#bindings).\nexport interface Configuration\n{\n actions : readonly Action[];\n bindings : readonly BindingDefinition[];\n contexts : readonly ContextDefinition[];\n}\n\nexport function exportConfiguration(\n actions : readonly Action[],\n bindings : readonly Binding<unknown>[],\n contexts : readonly ContextDefinition[]\n) : Configuration\n{\n return {\n actions: [ ...actions ],\n bindings: bindings.map((binding) => binding.toDefinition()),\n contexts: [ ...contexts ],\n };\n}\n\n// Registers every action and every context a configuration names. Binding definitions are handed back\n// for the caller to resolve, since resolution needs a device reader the configuration itself does not carry.\nexport function importConfiguration(\n config : Configuration,\n actions : ActionRegistry,\n contexts : ActionContextRegistry\n) : readonly BindingDefinition[]\n{\n for(const action of config.actions)\n {\n actions.register(action);\n }\n\n for(const context of config.contexts)\n {\n contexts.registerContext(context.name, context.exclusive);\n }\n\n return [ ...config.bindings ];\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","for(var r=256,n=[];r--;)n[r]=(r+256).toString(16).substring(1);export function hexoid(r){r=r||16;var t=\"\",o=0;return function(){if(!t||256===o){for(t=\"\",o=(1+r)/2|0;o--;)t+=n[256*Math.random()|0];t=t.substring(o=0,r-2)}return t+n[o++]}}","//----------------------------------------------------------------------------------------------------------------------\n// ID Generation Utility\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { hexoid } from 'hexoid';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Fast, unique ID generator for entities.\n * Uses hexoid for high-performance ID generation (~200M IDs/sec).\n * Generates 16-character hexadecimal strings.\n *\n * @example\n * ```typescript\n * const id = generateId(); // \"8cd88e1a9b13aac0\"\n * ```\n */\nexport const generateId = hexoid(16);\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// State Machine\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../classes/eventBus.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\ninterface TransitionDef\n{\n guard ?: () => boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A generic finite state machine with optional guard functions, lifecycle callbacks, and event bus integration.\n *\n * @template States - A string union of valid state names.\n */\nexport class StateMachine<States extends string>\n{\n private _currentState : States;\n private readonly transitions = new Map<string, TransitionDef>();\n private readonly wildcardTransitions = new Map<States, TransitionDef>();\n private readonly enterCallbacks = new Map<States, (() => void)[]>();\n private readonly exitCallbacks = new Map<States, (() => void)[]>();\n private readonly eventBus ?: GameEventBus<Record<string, unknown>>;\n\n constructor(initialState : States, eventBus ?: GameEventBus<Record<string, unknown>>)\n {\n this._currentState = initialState;\n this.eventBus = eventBus;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Properties\n //------------------------------------------------------------------------------------------------------------------\n\n get currentState() : States\n {\n return this._currentState;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Configuration\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a valid transition from one state to another, with an optional guard function.\n */\n addTransition(from : States, to : States, guard ?: () => boolean) : void\n {\n const key = `${ from }->${ to }`;\n this.transitions.set(key, { guard });\n }\n\n /**\n * Register a wildcard transition: allows transitioning to the given state from any state.\n */\n addTransitionFromAny(to : States, guard ?: () => boolean) : void\n {\n this.wildcardTransitions.set(to, { guard });\n }\n\n /**\n * Register a callback to run when entering a state.\n */\n onEnter(state : States, callback : () => void) : void\n {\n const callbacks = this.enterCallbacks.get(state) ?? [];\n callbacks.push(callback);\n this.enterCallbacks.set(state, callbacks);\n }\n\n /**\n * Register a callback to run when exiting a state.\n */\n onExit(state : States, callback : () => void) : void\n {\n const callbacks = this.exitCallbacks.get(state) ?? [];\n callbacks.push(callback);\n this.exitCallbacks.set(state, callbacks);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Queries\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Check if a transition to the given state is defined (ignoring guards).\n */\n canTransition(to : States) : boolean\n {\n const key = `${ this._currentState }->${ to }`;\n return this.transitions.has(key) || this.wildcardTransitions.has(to);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Transitions\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Execute a transition to the target state.\n * Flow: validate -> run guard -> call exit callbacks -> change state -> call enter callbacks.\n *\n * @throws If no transition is defined from the current state to the target, or if a guard rejects.\n */\n transition(to : States) : void\n {\n const key = `${ this._currentState }->${ to }`;\n let transitionDef = this.transitions.get(key);\n\n // Fall back to wildcard\n if(!transitionDef)\n {\n transitionDef = this.wildcardTransitions.get(to);\n }\n\n if(!transitionDef)\n {\n throw new Error(\n `No transition defined from '${ this._currentState }' to '${ to }'.`\n );\n }\n\n // Run guard\n if(transitionDef.guard && !transitionDef.guard())\n {\n throw new Error(\n `Guard rejected transition from '${ this._currentState }' to '${ to }'.`\n );\n }\n\n const previousState = this._currentState;\n\n // Exit callbacks\n const exitCbs = this.exitCallbacks.get(previousState);\n if(exitCbs)\n {\n for(const cb of exitCbs)\n {\n cb();\n }\n }\n\n // Emit exit event -- cast needed because template literal event types can't resolve conditionally\n if(this.eventBus)\n {\n this.eventBus.publish({\n type: `state:exit:${ previousState }`,\n payload: { from: previousState, to },\n } as { type : string; payload : unknown });\n }\n\n // Change state\n this._currentState = to;\n\n // Enter callbacks\n const enterCbs = this.enterCallbacks.get(to);\n if(enterCbs)\n {\n for(const cb of enterCbs)\n {\n cb();\n }\n }\n\n // Emit enter event -- cast needed because template literal event types can't resolve conditionally\n if(this.eventBus)\n {\n this.eventBus.publish({\n type: `state:enter:${ to }`,\n payload: { from: previousState, to },\n } as { type : string; payload : unknown });\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// SkewedAspect Game Engine\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { EntityMeshLoader, GameCanvas, RenderEngineOptions, SageOptions } from './interfaces/game.ts';\n\n// Base Classes\nimport { DebugConsole } from './classes/debugConsole.ts';\nimport { GameEngine, type SimulationFactory } from './classes/gameEngine.ts';\nimport { GameEventBus } from './classes/eventBus.ts';\n\n// Entities\nimport { NodeSimulation } from './entities/nodeSimulation.ts';\nimport { NodePool } from './entities/nodePool.ts';\n\nimport type { GameEntityType } from '@skewedaspect/sage-core';\nimport type { Scene } from '@babylonjs/core';\n\n// Engines\nimport { AudioEngine } from './engines/audio.ts';\nimport { SceneEngine } from './engines/scene.ts';\n\n// Managers\nimport { AssetManager } from './managers/asset.ts';\nimport { GameManager } from './managers/game.ts';\nimport { InputManager } from './managers/input.ts';\nimport { LevelManager } from './managers/level.ts';\nimport { AudioManager } from './managers/audio.ts';\nimport { SimulationSaveManager } from './managers/simulationSave.ts';\nimport { ColliderDebugManager } from './managers/colliderDebug.ts';\n\n// Utils\nimport { createEngine } from './utils/graphics.ts';\nimport { createHavokPlugin, initHavok } from './utils/physics.ts';\nimport { LoggingUtility } from './utils/logger.ts';\nimport { GameTimer } from './utils/timer.ts';\nimport { VERSION } from './utils/version.ts';\n\n// Debug\nimport { registerBuiltins } from './debug/builtins.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Creates and initializes a fully-wired GameEngine with rendering, physics, and entity management. The\n * engine owns one InputManager (`engine.managers.inputManager`): its action registry, its action context\n * registry, device state capture, and the routing loop that runs before each frame's fixed steps\n * (docs/design/input.md). A game declares its own actions, contexts, and bindings onto it.\n *\n * @param canvas\n * @param entityClasses - The entity class table, keyed by class name, that the simulation spawns from\n * @param options\n */\nexport async function createGameEngine(\n canvas : GameCanvas,\n entityClasses : Readonly<Record<string, GameEntityType>>,\n options : SageOptions = {}\n) : Promise<GameEngine>\n{\n // Initialize logging\n const logger = new LoggingUtility(options.logLevel || 'debug');\n const engineLogger = logger.getLogger('SAGE');\n\n engineLogger.info(`Initializing SAGE Game Engine v${ VERSION }...`);\n\n // Initialize BabylonJS stuff\n engineLogger.debug('Creating rendering engine...');\n\n const renderOptions : RenderEngineOptions = {\n antialias: true,\n adaptToDeviceRatio: true,\n ...options.renderOptions,\n };\n\n const engine = await createEngine(canvas, renderOptions, options.largeWorldRendering ?? false);\n\n engineLogger.debug('Creating physics engine...');\n const havok = await initHavok(options.havokBinary);\n const physics = createHavokPlugin(havok);\n\n // Initialize internals\n engineLogger.debug('Creating event bus...');\n const eventBus = new GameEventBus(logger);\n\n // Initialize SAGE Engines\n engineLogger.debug('Creating scene engine...');\n const sceneEngine = new SceneEngine(canvas, engine, havok, logger);\n\n // Initialize SAGE Managers\n engineLogger.debug('Creating managers...');\n const assetManager = new AssetManager(eventBus, sceneEngine, logger);\n const levelManager = new LevelManager(eventBus, logger);\n const saveManager = new SimulationSaveManager(levelManager, VERSION);\n const gameManager = new GameManager(engine, eventBus, levelManager, logger);\n const inputManager = new InputManager();\n gameManager.setInputManager(inputManager);\n\n // Initialize audio system (opt-in)\n let audioEngine : AudioEngine | undefined;\n let audioManager : AudioManager | undefined;\n\n if(options.audioChannels && options.audioChannels.length > 0)\n {\n engineLogger.debug('Creating audio engine...');\n audioEngine = new AudioEngine(logger);\n await audioEngine.initialize();\n\n engineLogger.debug('Creating audio manager...');\n audioManager = new AudioManager(audioEngine, logger);\n await audioManager.initialize(options.audioChannels);\n }\n\n // A factory rather than an instance: no scene exists yet at boot, so there is nothing to build a\n // NodeSimulation against until a level (GameLevel.buildScene) or the game itself calls rebuildSimulation.\n const fixedStepDuration = options.fixedStepDuration ?? 1 / 60;\n const noMesh : EntityMeshLoader = async () => undefined;\n\n engineLogger.debug('Creating collider debug manager...');\n const colliderDebugManager = new ColliderDebugManager();\n\n const buildSimulation : SimulationFactory = (scene : Scene, meshSource ?: EntityMeshLoader) => new NodeSimulation({\n fixedStepDuration,\n entityClasses,\n scene,\n pool: new NodePool(),\n\n // NodeSimulationContext's MeshLoader carries no scene of its own -- bind the scene this simulation\n // was just built for, so the mesh source does not have to track which scene is current.\n loadMesh: (name, state) => (meshSource ?? noMesh)(scene, name, state),\n\n audio: audioManager,\n colliders: colliderDebugManager,\n });\n\n // Initialize debug tools\n let debugConsole : DebugConsole | null = null;\n\n if(options.debug !== false)\n {\n engineLogger.debug('Creating debug console...');\n const namespaceName = (typeof options.debug === 'object' ? options.debug.namespace : undefined) ?? 'sage';\n debugConsole = new DebugConsole(namespaceName);\n }\n\n // Initialize timer\n engineLogger.debug('Creating game timer...');\n const timer = new GameTimer();\n gameManager.registerFrameCallback((dt) => timer.tick(dt * 1000));\n\n engineLogger.info(`SAGE Game Engine v${ VERSION } initialized successfully.`);\n\n const ge = new GameEngine(\n canvas,\n engine,\n physics,\n eventBus,\n logger,\n timer,\n buildSimulation,\n\n // Engines\n {\n sceneEngine,\n audioEngine,\n },\n\n // Managers\n {\n assetManager,\n gameManager,\n inputManager,\n levelManager,\n saveManager,\n audioManager,\n },\n\n options.largeWorldRendering ?? false,\n\n // Debug tools\n {\n colliders: colliderDebugManager,\n console: debugConsole,\n expose: (name : string, value : unknown) =>\n {\n debugConsole?.expose(name, value);\n },\n }\n );\n\n // Late-bind: these managers are constructed before GameEngine (because GameEngine takes them as\n // constructor args), but they need a back-reference at runtime. This must happen before any level\n // loads or entity attaches, so do it immediately after construction. Do not reorder.\n levelManager.$setGameEngine(ge);\n gameManager.$setGameEngine(ge);\n saveManager.$setGameEngine(ge);\n\n // Register debug console built-ins\n if(debugConsole)\n {\n registerBuiltins(debugConsole, ge);\n }\n\n return ge;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// Exports\n//----------------------------------------------------------------------------------------------------------------------\n\nexport * from './interfaces/game.ts';\nexport * from './interfaces/logger.ts';\n\n// Export base classes\nexport { AssetManager, AudioEngine, GameEngine, GameEventBus, SceneEngine };\nexport type { SimulationFactory } from './classes/gameEngine.ts';\n\nexport * from './classes/eventBus.ts';\nexport * from './classes/level.ts';\nexport * from './classes/gameLevel.ts';\nexport * from './events/index.ts';\nexport * from './handlers/index.ts';\nexport * from './interfaces/level.ts';\nexport * from './interfaces/lifecycle.ts';\nexport * from './utils/logger.ts';\nexport * from './utils/metadata.ts';\nexport * from './utils/timer.ts';\nexport * from './utils/version.ts';\n\n// Export the entity layer -- sage-core's own base classes, so a game can author entities and behaviors\n// without a direct sage-core dependency. EventMap augmentation still has to target sage-core directly (a\n// `declare module` only merges into the module that declares the interface), so this re-export is for\n// reading the type, not for augmenting it.\n//\n// sage-core's `GameEvent` is renamed to `GameEntityEvent` here: `./classes/eventBus.ts` below already\n// exports its own, unrelated `GameEvent` -- 0.9's library event bus shape -- under that name, and this\n// package holds both systems at once.\nexport {\n Behavior, EventBus, GameEntity, GameSimulationManager, NamedBehavior, StateMachineBehavior, defineStates,\n dilationLimit, drainPassLimit, operationCallDepthLimit,\n} from '@skewedaspect/sage-core';\nexport type {\n BehaviorClass, ComposedOf, DeepReadonly, EventMap, EventSubscription, GameEntityState, GameEntityType,\n GameEvent as GameEntityEvent, GameSimulationContext, GameSimulationOptions, GameSimulationSnapshot,\n NamedState, NarrowedTo, OpsFor, OpsOf, StateBag, StateFor, StateSliceOf,\n} from '@skewedaspect/sage-core';\nexport { NodeEntity } from './entities/nodeEntity.ts';\nexport type { NodeEntityState, QuaternionState, Vector3State } from './entities/nodeEntity.ts';\nexport { NodePool } from './entities/nodePool.ts';\nexport { NodeSimulation } from './entities/nodeSimulation.ts';\nexport type { NodeSimulationOptions } from './entities/nodeSimulation.ts';\nexport type { MeshLoader, NodeSet, NodeSimulationContext } from './entities/nodeSimulationContext.ts';\nexport type { EntityPickFilter, EntityPickResult } from './entities/entityPicking.ts';\n\n// Export the authoring surface: entity definitions compiling onto the class table, and the standard mesh\n// source built from it (docs/design/authoring.md).\nexport { defineEntity } from './entities/defineEntity.ts';\nexport type { GameEntityDefinition, GameEntityDefinitionClass } from './entities/defineEntity.ts';\nexport { buildStandardMeshSource } from './entities/standardMeshSource.ts';\nexport type { MeshAssetSource } from './entities/standardMeshSource.ts';\nexport type { MeshConfig, MeshMaterialConfig } from './entities/meshConfig.ts';\n\n// Export the provided behaviors (docs/design/audio.md).\nexport { SoundBehavior } from './behaviors/sound.ts';\nexport type { SoundConfig, SoundState } from './behaviors/sound.ts';\n\n// Export the input system: the action registry, action contexts, the four binding kinds, device readers,\n// binding resolution and delivery, capture, and configuration import/export (docs/design/input.md,\n// docs/design/events.md#delivery). `engine.managers.inputManager` is the one instance a game declares its\n// own actions, contexts, and bindings onto; these exports are the pieces it builds them from.\nexport { ActionRegistry } from './input/actionRegistry.ts';\nexport { ActionContextRegistry } from './input/actionContext.ts';\nexport type {\n Binding, BindingDefinition, BindingFire, BindingInput, BindingKind, DeviceID, DeviceType,\n} from './input/binding.ts';\nexport type { DeviceReader } from './input/deviceReader.ts';\nexport type { GamepadState, KeyboardState, MouseState } from './input/deviceState.ts';\nexport { KeyboardReader } from './input/deviceReaders/keyboard.ts';\nexport { MouseReader } from './input/deviceReaders/mouse.ts';\nexport type { MouseSourceType } from './input/deviceReaders/mouse.ts';\nexport { GamepadReader } from './input/deviceReaders/gamepad.ts';\nexport type { GamepadSourceType } from './input/deviceReaders/gamepad.ts';\nexport { TriggerBinding } from './input/bindings/trigger.ts';\nexport type { EdgeMode, TriggerOptions } from './input/bindings/trigger.ts';\nexport { ToggleBinding } from './input/bindings/toggle.ts';\nexport type { ToggleOptions } from './input/bindings/toggle.ts';\nexport { ValueBinding } from './input/bindings/value.ts';\nexport type { ValueOptions } from './input/bindings/value.ts';\nexport { DeltaBinding } from './input/bindings/delta.ts';\nexport type { DeltaOptions } from './input/bindings/delta.ts';\nexport { resolveBinding } from './input/resolveBinding.ts';\nexport { deliverAction } from './input/deliverAction.ts';\nexport { InputCapture } from './input/inputCapture.ts';\nexport type { CandidateInput, CaptureOptions } from './input/inputCapture.ts';\nexport { exportConfiguration, importConfiguration } from './input/configuration.ts';\nexport type { Configuration, ContextDefinition } from './input/configuration.ts';\nexport type { Action, AnalogAction, DigitalAction } from '@skewedaspect/sage-core';\n\n// Export manager types\nexport type { FrameCallback } from './managers/game.ts';\nexport { InputManager, ROUTING_LOOP_SENDER_ID } from './managers/input.ts';\nexport type { TransitionOptions } from './managers/level.ts';\nexport { AudioManager } from './managers/audio.ts';\nexport type { ChannelInfo } from './managers/audio.ts';\nexport { ColliderDebugManager } from './managers/colliderDebug.ts';\nexport type { ColliderDebugConfig } from './managers/colliderDebug.ts';\nexport { OutlineManager } from './managers/outline.ts';\nexport { SimulationSaveManager } from './managers/simulationSave.ts';\nexport type { BeforeSaveHook, LevelLoader, LoadResult, SaveEnvelope } from './managers/simulationSave.ts';\n\n// Export debug tools\nexport { DebugConsole } from './classes/debugConsole.ts';\n\n// Export utilities\nexport { computeHierarchyBounds } from './utils/bounds.ts';\nexport type { HierarchyBounds } from './utils/bounds.ts';\nexport { generateId } from './utils/id.ts';\nexport { StateMachine } from './utils/stateMachine.ts';\nexport {\n getCanonicalTransform,\n toQuatObject, toQuaternion, toVec3Object, toVector3,\n} from './utils/vectors.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n"],"x_google_ignoreList":[58],"mappings":"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"}