@skewedaspect/sage 0.6.2 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"sage.es.js","sources":["../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","../src/engines/scene.ts","../src/interfaces/binding.ts","../src/classes/bindings/trigger.ts","../src/classes/bindings/toggle.ts","../src/classes/bindings/value.ts","../src/classes/input/readers/keyboard.ts","../src/classes/input/readers/mouse.ts","../src/classes/input/readers/gamepad.ts","../src/managers/binding.ts","../src/utils/capabilities.ts","../src/managers/game.ts","../src/classes/entity.ts","../src/managers/entity.ts","../src/classes/input/keyboard.ts","../src/classes/input/mouse.ts","../src/classes/input/gamepad.ts","../src/managers/input.ts","../src/utils/graphics.ts","../src/utils/physics.ts","../src/interfaces/input.ts","../src/interfaces/logger.ts","../src/sage.ts"],"sourcesContent":["//----------------------------------------------------------------------------------------------------------------------\n// Version utility for SAGE\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * The current version of the SAGE engine.\n * This is automatically pulled from package.json during build.\n */\nexport const VERSION = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0-dev';\n\n//----------------------------------------------------------------------------------------------------------------------\n","//------------------------------------------------------------------------------------------------------------------\n// SkewedAspect Game Engine\n//------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractEngine, HavokPlugin } from '@babylonjs/core';\n\n// Interfaces\nimport type { GameCanvas } from '../interfaces/game.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Engines\nimport { SceneEngine } from '../engines/scene.ts';\n\n// Managers\nimport { BindingManager } from '../managers/binding.ts';\nimport { GameManager } from '../managers/game.ts';\nimport { GameEntityManager } from '../managers/entity.ts';\nimport { UserInputManager } from '../managers/input.ts';\n\n// Utils\nimport { GameEventBus } from './eventBus.ts';\nimport { LoggingUtility } from '../utils/logger.ts';\nimport { VERSION } from '../utils/version.ts';\n\n//------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type definition for game lifecycle hook functions\n */\n// eslint-disable-next-line no-use-before-define\nexport type GameHook = (gameEngine : SkewedAspectGameEngine) => Promise<void>;\n\n/**\n * Interface representing the engines used in the game.\n */\ninterface Engines\n{\n sceneEngine : SceneEngine;\n}\n\n/**\n * Interface representing the managers used in the game.\n */\ninterface Managers\n{\n bindingManager : BindingManager;\n gameManager : GameManager;\n entityManager : GameEntityManager;\n inputManager : UserInputManager;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Class representing the SkewedAspect Game Engine.\n */\nexport class SkewedAspectGameEngine\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 public started = false;\n\n private _log : LoggerInterface;\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 SkewedAspectGameEngine.\n * @param canvas - The game canvas.\n * @param renderEngine - The rendering engine.\n * @param physics - The physics engine.\n * @param eventBus - The event bus.\n * @param logger - The logging utility.\n * @param engines - The engines used in the game.\n * @param managers - The managers used in the game.\n */\n constructor(\n canvas : GameCanvas,\n renderEngine : AbstractEngine,\n physics : HavokPlugin,\n eventBus : GameEventBus,\n logger : LoggingUtility,\n engines : Engines,\n managers : Managers\n )\n {\n this.canvas = canvas;\n this.renderEngine = renderEngine;\n this.physics = physics;\n this.eventBus = eventBus;\n this.logger = logger;\n this.engines = engines;\n this.managers = managers;\n\n this._log = logger.getLogger('GameEngine');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a function to be called before the game engine starts\n * @param hook - Async function to be called before start\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 - Async function to be called after start\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 - Async function to be called during teardown\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 * Starts the game engine.\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(this.canvas);\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 * Stops the game engine.\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 // Teardown all managers if they have a $teardown function\n for(const managerKey of Object.keys(this.managers) as (keyof Managers)[])\n {\n const manager = this.managers[managerKey];\n if(typeof (manager as any).$teardown === 'function')\n {\n try\n {\n this._log.debug(`Tearing down manager: ${ managerKey }`);\n // eslint-disable-next-line no-await-in-loop\n await (manager as any).$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 if they have a $teardown function\n for(const engineKey of Object.keys(this.engines) as (keyof Engines)[])\n {\n const engine = this.engines[engineKey];\n if(typeof (engine as any).$teardown === 'function')\n {\n try\n {\n this._log.debug(`Tearing down engine: ${ engineKey }`);\n // eslint-disable-next-line no-await-in-loop\n await (engine as any).$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 // Stop the game manager\n try\n {\n await this.managers.gameManager.stop();\n }\n catch (err)\n {\n this._log.error(`Error stopping game manager: ${ err }`);\n teardownError = teardownError || (err as Error);\n }\n\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 : any[]) : 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 : any[]) : 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 : any[]) : 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 : any[]) : 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 : any[]) : 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 : any[]) : void\n {\n // Do nothing\n }\n\n debug(_category : string, _message : string, ..._args : any[]) : void\n {\n // Do nothing\n }\n\n info(_category : string, _message : string, ..._args : any[]) : void\n {\n // Do nothing\n }\n\n warn(_category : string, _message : string, ..._args : any[]) : void\n {\n // Do nothing\n }\n\n error(_category : string, _message : string, ..._args : any[]) : 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 : any[]) : void\n {\n if(this.shouldLog('trace'))\n {\n this.backend.trace(this.category, message, ...args);\n }\n }\n\n debug(message : string, ...args : any[]) : void\n {\n if(this.shouldLog('debug'))\n {\n this.backend.debug(this.category, message, ...args);\n }\n }\n\n info(message : string, ...args : any[]) : void\n {\n if(this.shouldLog('info'))\n {\n this.backend.info(this.category, message, ...args);\n }\n }\n\n warn(message : string, ...args : any[]) : void\n {\n if(this.shouldLog('warn'))\n {\n this.backend.warn(this.category, message, ...args);\n }\n }\n\n error(message : string, ...args : any[]) : 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.\n *\n * @param backend - The logging backend to use\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.\n *\n * @param level - The minimum logging level to display\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 a logger for the specified category.\n *\n * @param category - The category for this logger (typically class/module name)\n * @returns A Logger instance for the specified category\n */\n public getLogger(category : string) : LoggerInterface\n {\n if(!this.loggers.has(category))\n {\n this.loggers.set(category, new SAGELogger(category, this.level, this.backend));\n }\n\n const logger = this.loggers.get(category);\n // Check for undefined instead of using non-null assertion\n if(logger === undefined)\n {\n throw new Error(`Failed to create logger for category: ${ category }`);\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 { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A generic game event type.\n *\n * @template P - The shape of the payload. Default is a generic object if omitted.\n */\nexport interface GameEvent<P extends Record<string, any> = Record<string, any>>\n{\n /** The string identifier/category of the event (e.g. \"input:keydown\"). */\n type : string;\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, with user-defined shape. */\n payload ?: P;\n}\n\n/**\n * A callback function that receives a {@link GameEvent} with a given payload type.\n *\n * @template P - The shape of the payload in the event.\n */\nexport type GameEventCallback<P extends Record<string, any> = Record<string, any>> =\n (event : GameEvent<P>) => 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>) => void | Promise<void>;\n}\n\n/**\n * An unsubscribe function, returned by any subscribe method.\n */\nexport type Unsubscribe = () => void;\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A high-performance event bus that supports exact or pattern-based subscriptions,\n * and allows subscribers to specify a more specific `payload` type for convenience.\n *\n * **Note**: This does NOT enforce that a given `type` always has a certain payload shape.\n * It's a best-effort approach that lets you avoid `as` casts in callbacks.\n */\nexport class GameEventBus\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 - The logging utility to use\n */\n constructor(logger ?: LoggingUtility)\n {\n this._log = logger?.getLogger('EventBus') || new SAGELogger('EventBus');\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 P - The payload shape you want to expect in the callback.\n * @param {string | RegExp} eventTypeOrPattern - The exact event type or a wildcard/RegExp pattern.\n * @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.\n * @returns {Unsubscribe} function that, when called, removes this subscription.\n */\n public subscribe<P extends Record<string, any> = Record<string, any>>(\n eventTypeOrPattern : string | RegExp,\n callback : GameEventCallback<P>\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(eventTypeOrPattern, callback);\n }\n else\n {\n return this.subscribeExact(eventTypeOrPattern, callback);\n }\n }\n\n /**\n * Subscribes to an exact event type (e.g. \"input:keydown\").\n *\n * @template P - The payload shape you want to expect in the callback.\n * @param {string} eventType - The exact string to match.\n * @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.\n * @returns {Unsubscribe} function that unsubscribes this exact subscription.\n */\n public subscribeExact<P extends Record<string, any> = Record<string, any>>(\n eventType : string,\n callback : GameEventCallback<P>\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<P>), // runtime \"type-lie\" cast\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 * @template P - The payload shape you want to expect in the callback.\n * @param {string | RegExp} patternOrRegExp - The pattern to match (`\"input:*\"` or `/^input:/`).\n * @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.\n * @returns {Unsubscribe} function that unsubscribes this pattern-based subscription.\n */\n public subscribePattern<P extends Record<string, any> = Record<string, any>>(\n patternOrRegExp : string | RegExp,\n callback : GameEventCallback<P>\n ) : Unsubscribe\n {\n let regex : RegExp;\n\n if(typeof patternOrRegExp === 'string')\n {\n // MAIN BUG FIX:\n // We replace '*' with '.*' (not '\\*' or '\\\\*').\n // The prior code was .replace('\\\\*', '.*'), which never matched 'input:*'\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<P>),\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 * Publishes an event, invoking all matching callbacks.\n *\n * Callbacks are called asynchronously (microtask) via `Promise.resolve()`.\n *\n * @param {GameEvent<any>} event - The event object to publish.\n */\n public publish(event : GameEvent<any>) : 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().then(() => sub.callback(event));\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().then(() => sub.callback(event));\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//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Scene Engine\n//----------------------------------------------------------------------------------------------------------------------\n\nimport {\n AbstractEngine,\n FreeCamera,\n HavokPlugin,\n HemisphericLight,\n MeshBuilder,\n PhysicsAggregate,\n PhysicsShapeType,\n Scene,\n Vector3,\n} from '@babylonjs/core';\nimport type { GameCanvas } from '../interfaces/game.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport { type LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class SceneEngine\n{\n private _engine : AbstractEngine;\n private _physics : HavokPlugin;\n private _log : LoggerInterface;\n\n constructor(engine : AbstractEngine, physics : HavokPlugin, logger ?: LoggingUtility)\n {\n this._engine = engine;\n this._physics = physics;\n this._log = logger?.getLogger('SceneEngine') || new SAGELogger('SceneEngine');\n }\n\n private async _buildDemoScene(scene : Scene, canvas : GameCanvas) : Promise<void>\n {\n this._log.debug('Building demo scene...');\n\n // This creates and positions a free camera (non-mesh)\n this._log.trace('Creating camera...');\n const camera = new FreeCamera('camera1', new Vector3(0, 5, -10), scene);\n\n // This targets the camera to scene origin\n camera.setTarget(Vector3.Zero());\n\n // This attaches the camera to the canvas\n camera.attachControl(canvas, true);\n\n // This creates a light, aiming 0,1,0 - to the sky (non-mesh)\n this._log.trace('Creating light...');\n const light = new HemisphericLight('light', new Vector3(0, 1, 0), scene);\n\n // Default intensity is 1. Let's dim the light a small amount\n light.intensity = 0.7;\n\n // Our built-in 'sphere' shape.\n this._log.trace('Creating sphere...');\n const sphere = MeshBuilder.CreateSphere('sphere', { diameter: 2, segments: 32 }, scene);\n\n // Move the sphere upward at 4 units\n sphere.position.y = 4;\n\n // Our built-in 'ground' shape.\n this._log.trace('Creating ground...');\n const ground = MeshBuilder.CreateGround('ground', { width: 10, height: 10 }, scene);\n\n // Create a sphere shape and the associated body. Size will be determined automatically.\n this._log.trace('Adding physics to sphere...');\n // @ts-expect-error Need these, or they don't show up in the scene.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const sphereAggregate = new PhysicsAggregate(\n sphere,\n PhysicsShapeType.SPHERE,\n { mass: 1, restitution: 0.75 },\n scene\n );\n\n // Create a static box shape.\n this._log.trace('Adding physics to ground...');\n // @ts-expect-error Need these, or they don't show up in the scene.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const groundAggregate = new PhysicsAggregate(ground, PhysicsShapeType.BOX, { mass: 0 }, scene);\n\n this._log.debug('Demo scene built successfully');\n }\n\n // TODO: This needs more logic to handle different scenes\n async loadScene(canvas : GameCanvas) : Promise<Scene>\n {\n this._log.info('Loading scene...');\n this._log.time('sceneLoad');\n\n // Create a new scene\n this._log.debug('Creating new scene...');\n const scene = new Scene(this._engine);\n\n // TODO: Some scenes may not need physics, or may need different gravity settings\n // Initialize the scene with physics\n this._log.debug('Enabling physics with gravity (0, -9.8, 0)...');\n scene.enablePhysics(new Vector3(0, -9.8, 0), this._physics);\n\n // TODO: Need to set up a real scene...\n await this._buildDemoScene(scene, canvas);\n\n this._log.timeEnd('sceneLoad');\n this._log.info('Scene loaded successfully');\n\n return scene;\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 return Promise.resolve();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Binding Interfaces\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../classes/eventBus.ts';\nimport type { DeviceValueReader, DeviceValueReaderDefinition, InputState } from './input.ts';\nimport type { Action } from './action.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Types of bindings supported by the system\n */\nexport type BindingType = 'trigger' | 'toggle' | 'value';\n\n/**\n * Array of all supported binding types for validation\n */\nexport const bindingTypes : BindingType[] = [\n 'trigger',\n 'toggle',\n 'value',\n];\n\n/**\n * Context for organizing bindings into groups that can be activated/deactivated together\n */\nexport interface Context\n{\n /**\n * Name of the context for identification\n */\n name : string;\n\n /**\n * Whether this context is exclusive (only one exclusive context can be active at a time)\n */\n exclusive : boolean;\n}\n\n/**\n * Input definition that combines device ID and reader configuration into a single object\n */\nexport type InputDefinition = DeviceValueReaderDefinition & {\n deviceID : string;\n};\n\n/**\n * Base interface for binding definitions with object-based sources\n */\nexport interface BindingDefinitionBase\n{\n /**\n * Type of binding\n */\n type : BindingType;\n\n /**\n * Name of the (already registered) action this binding triggers\n */\n action : string;\n\n /**\n * Input definition combining device ID and reader configuration\n */\n input : InputDefinition;\n\n /**\n * Optional context name this binding belongs to\n */\n context ?: string;\n\n /**\n * Optional options for the binding\n */\n options ?: Record<string, any>;\n}\n\n/**\n * Trigger binding definition\n */\nexport interface TriggerBindingDefinition extends BindingDefinitionBase\n{\n type : 'trigger';\n\n options ?: {\n edgeMode ?: 'rising' | 'falling' | 'both';\n threshold ?: number;\n passthrough ?: boolean;\n };\n}\n\n/**\n * Toggle binding definition\n */\nexport interface ToggleBindingDefinition extends BindingDefinitionBase\n{\n type : 'toggle';\n state ?: boolean;\n\n options ?: {\n invert ?: boolean;\n initialState ?: boolean;\n threshold ?: number;\n onValue ?: boolean | number;\n offValue ?: boolean | number;\n };\n}\n\n/**\n * Value binding definition\n */\nexport interface ValueBindingDefinition extends BindingDefinitionBase\n{\n type : 'value';\n options ?: {\n scale ?: number;\n offset ?: number;\n min ?: number;\n max ?: number;\n invert ?: boolean;\n emitOnChange ?: boolean;\n deadzone ?: number;\n };\n}\n\n/**\n * Union type of all binding definitions\n */\nexport type BindingDefinition =\n | TriggerBindingDefinition\n | ToggleBindingDefinition\n | ValueBindingDefinition;\n\n/**\n * Base interface for all input bindings\n */\nexport interface Binding\n{\n /**\n * Type of binding\n */\n readonly type : BindingType;\n\n /**\n * Action object this binding triggers\n */\n readonly action : Action;\n\n /**\n * Optional context name this binding belongs to\n */\n readonly context ?: string;\n\n /**\n * Device ID this binding is associated with\n */\n readonly deviceID : string;\n\n /**\n * Input source that provides values for this binding\n */\n readonly reader : DeviceValueReader;\n\n /**\n * Process input state and potentially emit an action event\n *\n * @param state - Current input state for the device\n * @param eventBus - Event bus to emit action events to\n */\n process(state : InputState, eventBus : GameEventBus) : void;\n\n /**\n * Convert the binding to a JSON-serializable object\n *\n * @returns A JSON-serializable representation of the binding\n */\n toJSON() : BindingDefinition;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Trigger Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../eventBus.ts';\n\n// Interfaces\nimport type { Action } from '../../interfaces/action.ts';\nimport type { Binding, TriggerBindingDefinition } from '../../interfaces/binding.ts';\nimport type { DeviceValueReader, InputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * All supported edge modes for validation\n */\nexport const edgeModes = [ 'rising', 'falling', 'both' ] as const;\n\n/**\n * Defines when a trigger binding will fire based on input state changes\n */\nexport type EdgeMode = typeof edgeModes[number];\n\n/**\n * Options for configuring a trigger binding\n */\nexport interface TriggerBindingOptions\n{\n /**\n * Which edge(s) should cause the trigger to fire\n * - 'rising': Only trigger on false -> true transitions\n * - 'falling': Only trigger on true -> false transitions\n * - 'both': Trigger on any state change\n */\n edgeMode ?: EdgeMode;\n\n /**\n * Threshold for analog inputs (0.0 to 1.0)\n * When input value is >= threshold, it's considered \"active\" (true)\n * When input value is < threshold, it's considered \"inactive\" (false)\n * Defaults to 0.5 for analog inputs\n */\n threshold ?: number;\n\n /**\n * Optional context name this binding belongs to\n */\n context ?: string;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Implementation of a trigger binding, which emits an action when a button/key is pressed or released.\n * Can handle both digital and analog inputs with configurable threshold.\n * Can output either digital or analog values based on the action type.\n */\nexport class TriggerBinding implements Binding\n{\n public readonly type = 'trigger' as const;\n public readonly action : Action;\n public readonly context ?: string;\n public readonly deviceID : string;\n public readonly reader : DeviceValueReader;\n\n // Trigger-specific options\n private readonly _edgeMode : EdgeMode;\n private readonly _threshold : number;\n\n // State tracking\n private _lastDigitalState = false;\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Getters\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get the current options for this trigger binding.\n *\n * @returns The current options for this trigger binding\n */\n get options() : TriggerBindingOptions\n {\n return {\n edgeMode: this._edgeMode,\n threshold: this._threshold,\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new trigger binding\n *\n * @param action - Action to trigger\n * @param deviceID - Device ID this binding is for\n * @param reader - Input reader to read values from\n * @param options - Binding configuration options\n */\n constructor(\n action : Action,\n deviceID : string,\n reader : DeviceValueReader,\n options : TriggerBindingOptions = {}\n )\n {\n this.action = action;\n this.deviceID = deviceID;\n this.reader = reader;\n this.context = options.context;\n\n // Set options with defaults\n this._edgeMode = options.edgeMode ?? 'rising';\n this._threshold = options.threshold ?? 0.5;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process input state and emit an action if triggered\n *\n * @param state - Current input state\n * @param eventBus - Event bus to emit action events to\n * @returns True if an action was triggered, false otherwise\n */\n public process(state : InputState, eventBus : GameEventBus) : void\n {\n // Extract the raw value using our source\n const rawValue = this.reader.getValue(state) ?? false;\n\n // Determine the digital state based on the threshold\n const digitalState = typeof rawValue === 'boolean'\n ? rawValue\n : rawValue >= this._threshold;\n\n // Determine if we should trigger based on the configured edge mode\n let shouldTrigger = false;\n\n switch (this._edgeMode)\n {\n case 'rising':\n shouldTrigger = digitalState && !this._lastDigitalState;\n break;\n case 'falling':\n shouldTrigger = !digitalState && this._lastDigitalState;\n break;\n case 'both':\n shouldTrigger = digitalState !== this._lastDigitalState;\n break;\n }\n\n // Update last known state for next time\n this._lastDigitalState = digitalState;\n\n // If triggered, emit the action event\n if(shouldTrigger)\n {\n let outputValue : boolean | number;\n if(this.action.type === 'analog')\n {\n // Use raw value and convert to a number if needed\n let finalValue = typeof rawValue === 'number' ? rawValue : (rawValue ? 1.0 : 0.0);\n\n // Apply scaling if action parameters exist\n if(this.action.minValue !== undefined || this.action.maxValue !== undefined)\n {\n const min = this.action.minValue ?? 0;\n const max = this.action.maxValue ?? 1;\n finalValue = min + (finalValue * (max - min));\n }\n outputValue = finalValue;\n }\n else\n {\n outputValue = true;\n }\n const actionEvent = {\n type: `action:${ this.action.name }`,\n payload: {\n value: outputValue,\n deviceId: this.deviceID,\n context: this.context,\n },\n };\n eventBus.publish(actionEvent);\n }\n }\n\n /**\n * Returns a JSON-serializable representation of this trigger binding\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : TriggerBindingDefinition\n {\n return {\n type: this.type,\n action: this.action.name,\n input: {\n deviceID: this.deviceID,\n ...this.reader.toJSON(),\n },\n context: this.context,\n options: this.options,\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Toggle Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../eventBus.ts';\n\n// Interfaces\nimport type { Action } from '../../interfaces/action.ts';\nimport type { Binding, ToggleBindingDefinition } from '../../interfaces/binding.ts';\nimport type { DeviceValueReader, InputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Options for configuring a toggle binding\n */\nexport interface ToggleBindingOptions\n{\n /**\n * If true, toggle on falling edge (true -> false) instead of rising edge (false -> true)\n */\n invert ?: boolean;\n\n /**\n * Initial toggle state (defaults to false - off)\n */\n initialState ?: boolean;\n\n /**\n * Threshold for analog inputs (0.0 to 1.0)\n * When input value is >= threshold, it's considered \"active\" (true)\n * When input value is < threshold, it's considered \"inactive\" (false)\n * Defaults to 0.5 for analog inputs\n */\n threshold ?: number;\n\n /**\n * Value to emit when the toggle is in the \"on\" state for digital actions\n * If undefined, will emit boolean true\n * Ignored for analog actions\n */\n onValue ?: boolean | number;\n\n /**\n * Value to emit when the toggle is in the \"off\" state for digital actions\n * If undefined, will emit boolean false\n * Ignored for analog actions\n */\n offValue ?: boolean | number;\n\n /**\n * Optional context name this binding belongs to\n */\n context ?: string;\n}\n\n/**\n * Implementation of a toggle binding, which maintains and toggles state between true/false\n * when a button/key is pressed or released, remaining in that state until toggled again.\n * Can handle both digital and analog inputs with configurable threshold.\n * Can output either digital or analog values based on the action type.\n */\nexport class ToggleBinding implements Binding\n{\n public readonly type = 'toggle' as const;\n public readonly action : Action;\n public readonly context ?: string;\n public readonly deviceID : string;\n public readonly reader : DeviceValueReader;\n\n // Toggle-specific options\n private readonly _invert : boolean;\n private readonly _threshold : number;\n private readonly _initialState : boolean;\n private readonly _onValue : boolean | number;\n private readonly _offValue : boolean | number;\n\n // State tracking\n private _lastDigitalState = false;\n private _toggleState : boolean;\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Getters\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get the current toggle state\n *\n * @returns Current toggle state (true = on, false = off)\n */\n public get state() : boolean\n {\n return this._toggleState;\n }\n\n /**\n * Set the toggle state directly (useful for programmatic control)\n *\n * @param value - New toggle state\n */\n public set state(value : boolean)\n {\n this._toggleState = value;\n }\n\n /**\n * Get the value emitted when toggle is in the \"on\" state\n *\n * @returns The value for the \"on\" state\n */\n public get onValue() : boolean | number\n {\n return this._onValue;\n }\n\n /**\n * Get the value emitted when toggle is in the \"off\" state\n *\n * @returns The value for the \"off\" state\n */\n public get offValue() : boolean | number\n {\n return this._offValue;\n }\n\n /**\n * Get the current value based on toggle state\n *\n * @returns The current value (boolean or number)\n */\n public get value() : boolean | number\n {\n if(this.action.type === 'analog')\n {\n return this._toggleState\n ? (this.action.maxValue ?? 1)\n : (this.action.minValue ?? 0);\n }\n else\n {\n return this._toggleState ? this._onValue : this._offValue;\n }\n }\n\n /**\n * Get the current options for this toggle binding.\n *\n * @returns The current options for this toggle binding\n */\n public get options() : ToggleBindingOptions\n {\n return {\n invert: this._invert,\n threshold: this._threshold,\n initialState: this._initialState,\n onValue: this._onValue,\n offValue: this._offValue,\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new toggle binding\n *\n * @param action - Action to toggle\n * @param deviceID - Device ID this binding is for\n * @param reader - Input reader to read values from\n * @param options - Binding configuration options\n */\n constructor(\n action : Action,\n deviceID : string,\n reader : DeviceValueReader,\n options : ToggleBindingOptions = {}\n )\n {\n this.action = action;\n this.deviceID = deviceID;\n this.reader = reader;\n this.context = options.context;\n\n // Set options with defaults\n this._invert = options.invert ?? false;\n this._threshold = options.threshold ?? 0.5;\n this._initialState = options.initialState ?? false;\n this._toggleState = this._initialState;\n\n // For digital actions, these control the output value\n // For analog actions, these are not used (we use action properties instead)\n this._onValue = options.onValue ?? true;\n this._offValue = options.offValue ?? false;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process input state and emit a digital action if toggled\n *\n * @param state - Current input state\n * @param eventBus - Event bus to emit action events to\n */\n public process(state : InputState, eventBus : GameEventBus) : void\n {\n const rawValue = this.reader.getValue(state) ?? false;\n const digitalState = typeof rawValue === 'boolean'\n ? rawValue\n : rawValue >= this._threshold;\n\n const shouldToggle = this._invert\n ? !digitalState && this._lastDigitalState\n : digitalState && !this._lastDigitalState;\n\n this._lastDigitalState = digitalState;\n\n if(!shouldToggle) { return; }\n\n this._toggleState = !this._toggleState;\n\n eventBus.publish({\n type: `action:${ this.action.name }`,\n payload: {\n value: this.value,\n deviceId: this.deviceID,\n context: this.context,\n },\n });\n }\n\n /**\n * Reset toggle to its initial state\n */\n public reset() : void\n {\n this._toggleState = this._initialState;\n }\n\n /**\n * Returns a JSON-serializable representation of this toggle binding\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : ToggleBindingDefinition\n {\n return {\n type: this.type,\n action: this.action.name,\n input: {\n deviceID: this.deviceID,\n ...this.reader.toJSON(),\n },\n state: this._toggleState,\n context: this.context,\n options: this.options,\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Value Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../eventBus.ts';\n\n// Interfaces\nimport type { Action } from '../../interfaces/action.ts';\nimport type { Binding, ValueBindingDefinition } from '../../interfaces/binding.ts';\nimport type { DeviceValueReader, InputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Options for configuring a value binding\n */\nexport interface ValueBindingOptions\n{\n /**\n * Factor to scale the input value by (1.0 = no scaling)\n */\n scale ?: number;\n\n /**\n * Value to add to the input value after scaling\n */\n offset ?: number;\n\n /**\n * Whether to invert the input value (multiply by -1)\n */\n invert ?: boolean;\n\n /**\n * Optional context name this binding belongs to\n */\n context ?: string;\n\n /**\n * If true, only emit values when they change\n * If false, always emit values on each process call\n */\n emitOnChange ?: boolean;\n\n /**\n * Deadzone threshold (0.0 to 1.0)\n * Input values below this threshold will be treated as 0\n */\n deadzone ?: number;\n\n /**\n * Value to emit when the input is digital and true\n * Only used when action is digital\n */\n onValue ?: boolean | number;\n\n /**\n * Value to emit when the input is digital and false\n * Only used when action is digital\n */\n offValue ?: boolean | number;\n\n /**\n * Minimum value to clamp output to\n */\n min ?: number;\n\n /**\n * Maximum value to clamp output to\n */\n max ?: number;\n}\n\n/**\n * Implementation of a value binding, which directly converts an analog input\n * to an analog output with optional scaling, offset, and clamping.\n * Can handle both digital and analog inputs/outputs based on action type.\n */\nexport class ValueBinding implements Binding\n{\n public readonly type = 'value' as const;\n public readonly action : Action;\n public readonly context ?: string;\n public readonly deviceID : string;\n public readonly reader : DeviceValueReader;\n\n // Value-specific options\n private readonly _scale : number;\n private readonly _offset : number;\n private readonly _invert : boolean;\n private readonly _emitOnChange : boolean;\n private readonly _deadzone : number;\n private readonly _onValue : boolean | number;\n private readonly _offValue : boolean | number;\n private readonly _min : number;\n private readonly _max : number;\n\n // State tracking\n private _lastValue : number | boolean | undefined;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get the current options for this value binding.\n *\n * @returns The current options for this value binding\n */\n get options() : ValueBindingOptions\n {\n return {\n scale: this._scale,\n offset: this._offset,\n invert: this._invert,\n emitOnChange: this._emitOnChange,\n deadzone: this._deadzone,\n onValue: this._onValue,\n offValue: this._offValue,\n min: this._min,\n max: this._max,\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new value binding\n *\n * @param action - Action to emit values for\n * @param deviceID - Device ID this binding is for\n * @param reader - Input reader to read values from\n * @param options - Binding configuration options\n */\n constructor(\n action : Action,\n deviceID : string,\n reader : DeviceValueReader,\n options : ValueBindingOptions = {}\n )\n {\n this.action = action;\n this.deviceID = deviceID;\n this.reader = reader;\n this.context = options.context;\n\n // Common options that apply to both analog and digital\n this._scale = options.scale ?? 1.0;\n this._offset = options.offset ?? 0.0;\n this._invert = options.invert ?? false;\n this._emitOnChange = options.emitOnChange ?? true;\n this._deadzone = options.deadzone ?? 0.0;\n\n // For digital actions, these control the output value\n // For analog actions, these are not used\n this._onValue = options.onValue ?? true;\n this._offValue = options.offValue ?? false;\n\n // Store min/max values explicitly\n const defaultMin = this.action.type === 'analog'\n ? this.action.minValue ?? Number.NEGATIVE_INFINITY\n : Number.NEGATIVE_INFINITY;\n this._min = options.min ?? defaultMin;\n\n const defaultMax = this.action.type === 'analog'\n ? this.action.maxValue ?? Number.POSITIVE_INFINITY\n : Number.POSITIVE_INFINITY;\n this._max = options.max ?? defaultMax;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process input state and emit an action value\n *\n * @param state - Current input state\n * @param eventBus - Event bus to emit action events to\n * @returns True if a value was emitted, false otherwise\n */\n public process(state : InputState, eventBus : GameEventBus) : void\n {\n const rawValue = this.reader.getValue(state);\n if(rawValue === undefined) { return; }\n\n const numericValue = typeof rawValue === 'boolean' ? (rawValue ? 1.0 : 0.0) : rawValue;\n if(numericValue === undefined) { return; }\n\n let value = this._deadzone > 0 && Math.abs(numericValue) < this._deadzone ? 0 : numericValue;\n value = ((this._invert ? -value : value) * this._scale) + this._offset;\n\n // Apply min/max clamping\n value = Math.max(this._min, Math.min(this._max, value));\n\n if(this._emitOnChange && this._lastValue === value) { return; }\n this._lastValue = value;\n\n eventBus.publish({\n type: `action:${ this.action.name }`,\n payload: {\n value,\n deviceId: this.deviceID,\n context: this.context,\n },\n });\n }\n\n /**\n * Returns a JSON-serializable representation of this value binding\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : ValueBindingDefinition\n {\n return {\n type: this.type,\n action: this.action.name,\n input: {\n deviceID: this.deviceID,\n ...this.reader.toJSON(),\n },\n context: this.context,\n options: this.options,\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Keyboard Value Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type {\n DeviceValueReader,\n InputState,\n KeyboardInputState,\n KeyboardValueReaderDefinition,\n} from '../../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Types of keyboard input sources\n */\nexport type KeyboardSourceType = 'key';\n\n/**\n * Options for configuring a keyboard value reader\n */\nexport interface KeyboardReaderOptions\n{\n /**\n * Whether to use delta state instead of current state\n * When true, the source will only return true on the initial key press\n */\n useDelta ?: boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Reads values from keyboard input states\n */\nexport class KeyboardValueReader implements DeviceValueReader\n{\n /**\n * The type of keyboard input (always 'key')\n */\n public readonly sourceType : KeyboardSourceType = 'key';\n\n /**\n * The key code to monitor (e.g., \"KeyA\", \"Space\")\n */\n public readonly sourceKey : string;\n\n /**\n * Whether to use delta state instead of current state\n */\n private readonly useDelta : boolean;\n\n /**\n * Creates a new KeyboardValueReader\n *\n * @param keyCode - The key code to monitor (e.g., \"KeyA\", \"Space\")\n * @param options - Configuration options\n */\n constructor(keyCode : string, options : KeyboardReaderOptions = {})\n {\n this.sourceKey = keyCode;\n this.useDelta = options.useDelta || false;\n }\n\n /**\n * Gets the value of the key state\n *\n * @param state - The input state\n * @returns The key state value or undefined if the state type is not keyboard\n */\n public getValue(state : InputState) : boolean | undefined\n {\n // Check if the state is from the correct device type\n if(state.type !== 'keyboard')\n {\n return undefined;\n }\n\n const keyboardState = state as KeyboardInputState;\n\n // Get value from either the current state or delta state based on configuration\n if(this.useDelta)\n {\n return keyboardState.delta[this.sourceKey];\n }\n else\n {\n return keyboardState.keys[this.sourceKey];\n }\n }\n\n /**\n * Creates a KeyboardValueReader from a string representation\n *\n * @param sourceKey - The key code (e.g., \"KeyA\", \"Space\")\n * @param options - Optional configuration\n * @returns A new KeyboardValueReader instance\n */\n public static fromString(sourceKey : string, options : KeyboardReaderOptions = {}) : KeyboardValueReader\n {\n return new KeyboardValueReader(sourceKey, options);\n }\n\n /**\n * Returns a JSON-serializable representation of this keyboard value reader\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : KeyboardValueReaderDefinition\n {\n return {\n\n type: 'keyboard',\n sourceType: this.sourceType,\n sourceKey: this.sourceKey,\n options: {\n useDelta: this.useDelta,\n },\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Mouse Value Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { DeviceValueReader, InputState, MouseValueReaderDefinition } from '../../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Types of mouse input sources\n */\nexport type MouseSourceType = 'button' | 'position' | 'wheel';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Reads values from mouse input states\n */\nexport class MouseValueReader implements DeviceValueReader\n{\n /**\n * The type of mouse input\n */\n readonly sourceType : MouseSourceType;\n\n /**\n * The specific key for this input\n */\n readonly sourceKey : string;\n\n /**\n * Creates a new MouseValueReader\n *\n * @param sourceType - Type of mouse input to monitor (button, position, wheel)\n * @param sourceKey - The specific key for this input type\n */\n constructor(sourceType : MouseSourceType, sourceKey : string)\n {\n this.sourceType = sourceType;\n this.sourceKey = sourceKey;\n }\n\n /**\n * Gets the value of the mouse input source\n *\n * @param state - The current input state\n * @returns The value of the input source\n */\n public getValue(state : InputState) : boolean | number | undefined\n {\n // Check if the state is from the correct device type\n if(state.type !== 'mouse')\n {\n return undefined;\n }\n\n switch (this.sourceType)\n {\n case 'button':\n {\n const buttonState = state.buttons[this.sourceKey];\n return buttonState ? buttonState.pressed : undefined;\n }\n\n case 'position':\n {\n // Position keys are in format \"positionType:axis\" (e.g., \"absolute:x\")\n const [ posType, axis ] = this.sourceKey.split(':');\n\n if(!posType || !axis || (posType !== 'absolute' && posType !== 'relative')\n || (axis !== 'x' && axis !== 'y'))\n {\n return undefined;\n }\n\n return state.position[posType][axis];\n }\n\n case 'wheel':\n {\n const isValidWheelKey = this.sourceKey === 'deltaX' || this.sourceKey === 'deltaY'\n || this.sourceKey === 'deltaZ';\n if(state.wheel && isValidWheelKey)\n {\n return state.wheel[this.sourceKey];\n }\n return undefined;\n }\n\n default:\n return undefined;\n }\n }\n\n /**\n * Creates a MouseValueReader from a string representation\n *\n * @param sourceTypeString - String in format \"sourceType:sourceKey\" (e.g., \"button:0\", \"position:absolute:x\")\n * @returns A new MouseValueReader instance\n */\n public static fromString(sourceTypeString : string) : MouseValueReader\n {\n const [ sourceType, ...keyParts ] = sourceTypeString.split(':');\n const sourceKey = keyParts.join(':');\n\n if(!sourceType || !sourceKey)\n {\n throw new Error(`Invalid mouse source format: ${ sourceTypeString }`);\n }\n\n return new MouseValueReader(\n sourceType as MouseSourceType,\n sourceKey\n );\n }\n\n /**\n * Returns a JSON-serializable representation of this mouse value reader\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : MouseValueReaderDefinition\n {\n // Always return consistent 'mouse' type with sourceType and sourceKey\n return {\n type: 'mouse',\n sourceType: this.sourceType,\n sourceKey: this.sourceKey,\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Gamepad Value Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { DeviceValueReader, GamepadValueReaderDefinition, InputState } from '../../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Types of gamepad input sources\n */\nexport type GamepadSourceType = 'button' | 'axis';\n\n/**\n * Options for configuring a gamepad value reader\n */\nexport interface GamepadReaderOptions\n{\n /**\n * Whether to use the analog value instead of boolean pressed state for buttons\n */\n useAnalogValue ?: boolean;\n\n /**\n * Deadzone value (0.0 - 1.0) for analog axes\n * Input values below this threshold will be treated as 0\n */\n deadzone ?: number;\n\n /**\n * Whether to invert the axis values\n */\n invert ?: boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Reads values from gamepad input states\n */\nexport class GamepadValueReader implements DeviceValueReader\n{\n /**\n * The type of gamepad input\n */\n readonly sourceType : GamepadSourceType;\n\n /**\n * The specific key for this input\n */\n readonly sourceKey : string;\n\n /**\n * Whether to use analog value for buttons\n */\n private readonly useAnalogValue : boolean;\n\n /**\n * Deadzone value for axes\n */\n private readonly deadzone : number;\n\n /**\n * Whether to invert axis values\n */\n private readonly invert : boolean;\n\n /**\n * Creates a new GamepadValueReader\n *\n * @param sourceType - Type of gamepad input to monitor (button or axis)\n * @param sourceKey - The specific key for this input type\n * @param options - Configuration options\n */\n constructor(sourceType : GamepadSourceType, sourceKey : string, options : GamepadReaderOptions = {})\n {\n this.sourceType = sourceType;\n this.sourceKey = sourceKey;\n this.useAnalogValue = options.useAnalogValue || false;\n this.deadzone = options.deadzone || 0.1; // Default 10% deadzone\n this.invert = options.invert || false;\n }\n\n /**\n * Gets the value of the input source based on the current state\n *\n * @param state - The current input state\n * @returns The value of the input source\n */\n public getValue(state : InputState) : boolean | number | undefined\n {\n // Check if the state is from the correct device type\n if(state.type !== 'gamepad')\n {\n return undefined;\n }\n\n switch (this.sourceType)\n {\n case 'button':\n {\n const buttonState = state.buttons[this.sourceKey];\n\n if(!buttonState)\n {\n return undefined;\n }\n\n return this.useAnalogValue ? buttonState.value : buttonState.pressed;\n }\n\n case 'axis':\n {\n let value = state.axes[this.sourceKey];\n\n if(value === undefined)\n {\n return undefined;\n }\n\n // Apply deadzone\n if(Math.abs(value) < this.deadzone)\n {\n value = 0;\n }\n\n // Apply inversion if needed\n return this.invert ? -value : value;\n }\n\n default:\n return undefined;\n }\n }\n\n /**\n * Creates a GamepadValueReader from a string representation\n *\n * @param sourceTypeString - String in format \"sourceType:sourceKey\" (e.g., \"button:0\", \"axis:1\")\n * @param options - Optional configuration options\n * @returns A new GamepadValueReader instance\n */\n public static fromString(sourceTypeString : string, options : GamepadReaderOptions = {}) : GamepadValueReader\n {\n const [ sourceType, sourceKey ] = sourceTypeString.split(':');\n\n if(!sourceType || !sourceKey)\n {\n throw new Error(`Invalid gamepad source format: ${ sourceTypeString }`);\n }\n\n return new GamepadValueReader(\n sourceType as GamepadSourceType,\n sourceKey,\n options\n );\n }\n\n /**\n * Returns a JSON-serializable representation of this gamepad value reader\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : GamepadValueReaderDefinition\n {\n return {\n type: 'gamepad',\n sourceType: this.sourceType,\n sourceKey: this.sourceKey,\n options: {\n useAnalogValue: this.useAnalogValue,\n deadzone: this.deadzone,\n invert: this.invert,\n },\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Binding Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { GameEventBus } from '../classes/eventBus.ts';\n\n// Interfaces\nimport { type Binding, type BindingDefinition, type Context, bindingTypes } from '../interfaces/binding.ts';\nimport type {\n DeviceValueReader,\n DeviceValueReaderDefinition,\n InputDevice,\n InputState,\n} from '../interfaces/input.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type { Action } from '../interfaces/action.ts';\n\n// Bindings\nimport { TriggerBinding } from '../classes/bindings/trigger.ts';\nimport { ToggleBinding } from '../classes/bindings/toggle.ts';\nimport { ValueBinding } from '../classes/bindings/value.ts';\n\n// Value Readers\nimport { KeyboardValueReader } from '../classes/input/readers/keyboard.ts';\nimport { MouseValueReader } from '../classes/input/readers/mouse.ts';\nimport { GamepadValueReader } from '../classes/input/readers/gamepad.ts';\n\n// Utils\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Configuration object structure for binding manager\n */\nexport interface BindingConfiguration\n{\n /** All registered actions */\n actions : Action[];\n\n /** All registered bindings */\n bindings : BindingDefinition[];\n\n /** All registered contexts */\n contexts : {\n name : string;\n exclusive : boolean;\n active : boolean;\n }[];\n}\n\n/* eslint-disable no-continue */\n\n/**\n * Manages input bindings and actions for the game\n */\nexport class BindingManager\n{\n /** Map of device IDs to their bindings */\n private _bindings = new Map<string, Binding[]>();\n\n /** Map of action names to their action definitions */\n private _actions = new Map<string, Action>();\n\n /** Map of all registered contexts by name for O(1) lookup */\n private _contexts = new Map<string, Context>();\n\n /**\n * Set of all active contexts (both exclusive and non-exclusive)\n */\n private _activeContexts = new Set<string>();\n\n /** Event bus for handling game events */\n private _eventBus : GameEventBus;\n\n /** Logger instance */\n private _log : LoggerInterface;\n\n /**\n * Creates an instance of BindingManager.\n *\n * @param eventBus - The game event bus to publish events to\n * @param logger - The logging utility to use\n */\n constructor(eventBus : GameEventBus, logger ?: LoggingUtility)\n {\n this._eventBus = eventBus;\n\n // Bind to input events\n this._eventBus.subscribe<{ device : InputDevice, state : InputState }>('input:changed', (event) =>\n {\n if(event.payload)\n {\n this.$handleInput(event.payload.device, event.payload.state);\n }\n });\n\n this._log = logger?.getLogger('BindingManager') || new SAGELogger('BindingManager');\n this._log.debug('BindingManager initialized');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Checks if a binding's context is currently active\n *\n * @param binding - The binding to check\n * @returns True if binding's context is active or has no context\n */\n private _isBindingContextActive(binding : Binding) : boolean\n {\n // If no context is specified, binding is always active regardless of active contexts\n if(!binding.context)\n {\n return true;\n }\n\n // Otherwise check if the binding's specific context is active\n return this._activeContexts.has(binding.context);\n }\n\n /**\n * Get a context by name, creating it if it doesn't exist\n *\n * @param contextName - The name of the context to get or create\n * @param exclusive - Whether the context is exclusive (only used if creating)\n * @returns The context object\n */\n private _getOrCreateContext(contextName : string, exclusive = true) : Context\n {\n let context = this._contexts.get(contextName);\n\n if(!context)\n {\n context = {\n name: contextName,\n exclusive,\n };\n\n this._contexts.set(contextName, context);\n this._log.debug(`Auto-created context \"${ contextName }\" (exclusive: ${ exclusive })`);\n }\n\n return context;\n }\n\n /**\n * Deactivate all exclusive contexts except the specified one\n *\n * @param exceptContextName - Name of context to not deactivate\n * @returns Array of deactivated context names\n */\n private _deactivateExclusiveContexts(exceptContextName ?: string) : string[]\n {\n const deactivatedContexts : string[] = [];\n\n // Find all active exclusive contexts\n for(const contextName of this._activeContexts)\n {\n // Skip the context we want to keep\n if(contextName === exceptContextName)\n {\n continue;\n }\n\n const context = this._contexts.get(contextName);\n\n // If this is an exclusive context, deactivate it\n if(context?.exclusive)\n {\n this._activeContexts.delete(contextName);\n deactivatedContexts.push(contextName);\n }\n }\n\n return deactivatedContexts;\n }\n\n /**\n * Create a binding from a binding definition\n *\n * @param definition - The binding definition to create a binding from\n * @returns A new binding instance or null if the type is not supported\n */\n private _createBindingFromDefinition(definition : BindingDefinition) : Binding | null\n {\n // Get the full action object from the action name\n const action = this._actions.get(definition.action);\n\n // If the action doesn't exist, log a warning and return null\n if(!action)\n {\n this._log.warn(`Cannot create binding: Action \"${ definition.action }\" not found.`);\n return null;\n }\n\n // Extract deviceID and create reader from the input definition\n const { deviceID, ...readerDef } = definition.input;\n\n switch (definition.type)\n {\n case 'trigger': {\n // Create the binding with the device value reader based on definition's source\n return new TriggerBinding(\n action,\n deviceID,\n this._createInputSourceFromDefinition(readerDef),\n {\n ...(definition.options || {}),\n context: definition.context,\n }\n );\n }\n\n case 'toggle': {\n // Create the binding with the device value reader based on definition's source\n return new ToggleBinding(\n action,\n deviceID,\n this._createInputSourceFromDefinition(readerDef),\n {\n ...(definition.options || {}),\n context: definition.context,\n }\n );\n }\n\n case 'value': {\n // Create the binding with the device value reader based on definition's source\n return new ValueBinding(\n action,\n deviceID,\n this._createInputSourceFromDefinition(readerDef),\n {\n ...(definition.options || {}),\n context: definition.context,\n }\n );\n }\n\n default:\n this._log.error(`Binding type not implemented: ${ (definition as any).type }`);\n return null;\n }\n }\n\n /**\n * Create a device value reader from a definition object\n *\n * @param definition - The device value reader definition\n * @returns A new device value reader instance\n * @throws Error if the reader type is not supported\n */\n private _createInputSourceFromDefinition(definition : DeviceValueReaderDefinition) : DeviceValueReader\n {\n // Create reader based on device type\n switch (definition.type)\n {\n case 'keyboard':\n return new KeyboardValueReader(\n definition.sourceKey,\n definition.options\n );\n\n case 'mouse':\n {\n // Check if sourceType is valid\n const sourceType = definition.sourceType;\n if(!(sourceType === 'button' || sourceType === 'position' || sourceType === 'wheel'))\n {\n throw new Error(`Invalid mouse source type: ${ sourceType }`);\n }\n\n return new MouseValueReader(\n sourceType,\n definition.sourceKey\n );\n }\n\n case 'gamepad':\n {\n // Check if sourceType is valid\n const sourceType = definition.sourceType;\n if(!(sourceType === 'button' || sourceType === 'axis'))\n {\n throw new Error(`Invalid gamepad source type: ${ sourceType }`);\n }\n return new GamepadValueReader(\n sourceType,\n definition.sourceKey,\n definition.options\n );\n }\n\n default:\n throw new Error(`Unsupported input source type: ${ (definition as any).type }`);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Handle input from a device and process all relevant bindings\n *\n * @param device - The input device\n * @param state - Current input state\n */\n public $handleInput(device : InputDevice, state : InputState) : void\n {\n const bindings = this._bindings.get(device.id);\n if(!bindings || bindings.length === 0)\n {\n return;\n }\n\n if(this._activeContexts.size === 0 && bindings.some((binding) => binding.context))\n {\n return;\n }\n\n for(const binding of bindings)\n {\n if(!this._isBindingContextActive(binding))\n {\n continue;\n }\n\n binding.process(state, this._eventBus);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Action Management API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Registers an action\n *\n * @param action - The action to register\n * @throws Error if action is already registered\n */\n public registerAction(action : Action) : void\n {\n this._log.debug(`Registering action \"${ action.name }\"`);\n\n if(this._actions.has(action.name))\n {\n const errorMsg = `Action \"${ action.name }\" already registered.`;\n this._log.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n this._actions.set(action.name, action);\n this._log.debug(`Action \"${ action.name }\" registered successfully`);\n }\n\n /**\n * Gets an action by name\n *\n * @param actionName - The name of the action to get\n * @returns The action or null if not found\n */\n public getAction(actionName : string) : Action | null\n {\n this._log.trace(`Getting action \"${ actionName }\"`);\n\n const action = this._actions.get(actionName) ?? null;\n if(!action)\n {\n this._log.debug(`Action \"${ actionName }\" not found`);\n }\n\n return action;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Context Management API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Registers a context with specific options\n *\n * @param contextName - The name of the context to register\n * @param exclusive - Whether the context is exclusive (default: true)\n * @returns The registered context\n */\n public registerContext(contextName : string, exclusive = true) : Context\n {\n const context = this._getOrCreateContext(contextName, exclusive);\n\n // Update context's exclusivity if it already exists\n if(context.exclusive !== exclusive)\n {\n context.exclusive = exclusive;\n this._log.info(`Updated context \"${ contextName }\" exclusivity: ${ exclusive }`);\n }\n\n return context;\n }\n\n /**\n * Activates a context, enabling all bindings associated with it.\n * If the context is exclusive, all other exclusive contexts will be deactivated.\n *\n * @param contextName - The name of the context to activate\n */\n public activateContext(contextName : string) : void\n {\n // Get or create context\n const context = this._getOrCreateContext(contextName);\n\n // Get the context's exclusivity setting\n const isExclusive = context.exclusive;\n\n this._log.debug(`Activating context \"${ contextName }\" (exclusive: ${ isExclusive })`);\n\n // If already active, nothing to do unless we're making an exclusive activation\n const isAlreadyActive = this._activeContexts.has(contextName);\n\n if(isExclusive)\n {\n // Deactivate all other exclusive contexts\n const deactivated = this._deactivateExclusiveContexts(contextName);\n\n if(deactivated.length > 0)\n {\n this._log.info(`Deactivated exclusive contexts: ${ deactivated.join(', ') }`);\n }\n }\n\n // Add to active contexts if not already there\n if(!isAlreadyActive)\n {\n this._activeContexts.add(contextName);\n this._log.info(`Context \"${ contextName }\" activated${ isExclusive ? ' as exclusive' : '' }`);\n }\n }\n\n /**\n * Deactivates a context, disabling all bindings associated with it\n *\n * @param contextName - The name of the context to deactivate\n */\n public deactivateContext(contextName : string) : void\n {\n this._log.debug(`Deactivating context \"${ contextName }\"`);\n\n if(this._activeContexts.has(contextName))\n {\n this._activeContexts.delete(contextName);\n this._log.info(`Context \"${ contextName }\" deactivated`);\n }\n else\n {\n this._log.debug(`Context \"${ contextName }\" was not active`);\n }\n }\n\n /**\n * Returns a list of all active contexts\n *\n * @returns Array of active context names\n */\n public getActiveContexts() : string[]\n {\n return [ ...this._activeContexts ];\n }\n\n /**\n * Returns whether a context is active\n *\n * @param contextName - The context to check\n * @returns True if the context is active\n */\n public isContextActive(contextName : string) : boolean\n {\n return this._activeContexts.has(contextName);\n }\n\n /**\n * Gets a context by name\n *\n * @param contextName - The context name to get\n * @returns The context or null if not found\n */\n public getContext(contextName : string) : Context | null\n {\n return this._contexts.get(contextName) || null;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Binding Management API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a binding for an input device\n *\n * @param binding - The binding to register\n * @throws Error if binding type is invalid\n */\n public $registerBinding(binding : Binding) : void\n {\n if(!bindingTypes.includes(binding.type))\n {\n throw new Error(`Invalid binding type: ${ binding.type }`);\n }\n\n // If binding has a context but context isn't registered yet, register it with default settings\n if(binding.context && !this._contexts.has(binding.context))\n {\n this.registerContext(binding.context);\n }\n\n // Initialize device bindings array if it doesn't exist\n if(!this._bindings.has(binding.deviceID))\n {\n this._bindings.set(binding.deviceID, []);\n }\n\n // Add the binding\n this._bindings.get(binding.deviceID)?.push(binding);\n this._log.debug(`Registered ${ binding.type } binding for \"${ binding.action.name }\" in context `\n + `\"${ binding.context || null }\"`);\n }\n\n /**\n * Register a binding using a binding definition object\n *\n * @param definition - The binding definition to register\n */\n public registerBinding(definition : BindingDefinition) : void\n {\n const binding = this._createBindingFromDefinition(definition);\n\n if(binding)\n {\n this.$registerBinding(binding);\n }\n else\n {\n this._log.error(`Failed to create binding for action \"${ definition.action }\" with type `\n + `\"${ definition.type }\"`);\n }\n }\n\n /**\n * Unregister all bindings for an action within a context\n *\n * @param actionName - The name of the action\n * @param context - The context to unregister from (defaults to null)\n */\n public unregisterBindings(actionName : string, context : string | null = null) : void\n {\n this._log.debug(`Unregistering all bindings for action \"${ actionName }\" in context \"${ context }\"`);\n\n let bindingsRemoved = 0;\n\n // Iterate through all devices and their bindings\n for(const [ deviceId, deviceBindings ] of this._bindings.entries())\n {\n const newBindings = deviceBindings.filter((binding) =>\n {\n const bindingContext = binding.context || null;\n const shouldKeep = binding.action.name !== actionName || bindingContext !== context;\n if(!shouldKeep)\n {\n bindingsRemoved++;\n }\n return shouldKeep;\n });\n\n // Only update the map if we actually removed something\n if(newBindings.length !== deviceBindings.length)\n {\n this._bindings.set(deviceId, newBindings);\n }\n }\n\n this._log.info(`Removed ${ bindingsRemoved } bindings for action \"${ actionName }\" in context \"${ context }\"`);\n }\n\n /**\n * Gets all bindings for a specific action\n *\n * @param actionName - The name of the action\n * @param context - The context to get bindings from (optional)\n * @returns Array of bindings that match the criteria\n */\n public getBindingsForAction(actionName : string, context ?: string | null) : Binding[]\n {\n const result : Binding[] = [];\n\n for(const deviceBindings of this._bindings.values())\n {\n for(const binding of deviceBindings)\n {\n const bindingContext = binding.context || null;\n if(binding.action.name === actionName)\n {\n // If context specified, only include bindings from that context\n if(!context || bindingContext === context)\n {\n result.push(binding);\n }\n }\n }\n }\n\n return result;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Configuration Management API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Exports the current configuration to a serializable object\n *\n * @returns A BindingConfiguration object representing the current state\n */\n public exportConfiguration() : BindingConfiguration\n {\n this._log.debug('Exporting binding configuration');\n\n // Export all registered actions\n const actions = [ ...this._actions.values() ].map((action) =>\n {\n // Ensure we include all analog action properties when exporting\n if(action.type === 'analog')\n {\n return {\n name: action.name,\n type: action.type,\n minValue: action.minValue ?? 0,\n maxValue: action.maxValue ?? 1,\n };\n }\n return action;\n });\n\n // Export all registered bindings using their toJSON methods\n const bindings : BindingDefinition[] = [];\n\n // Iterate through all device bindings\n for(const deviceBindings of this._bindings.values())\n {\n for(const binding of deviceBindings)\n {\n // Let the binding serialize itself to the BindingDefinition format\n bindings.push(binding.toJSON());\n }\n }\n\n // Export all contexts with their active state\n const contexts = [ ...this._contexts.values() ].map((context) => ({\n name: context.name,\n exclusive: context.exclusive,\n active: this._activeContexts.has(context.name),\n }));\n\n this._log.info(`Configuration exported: ${ actions.length } actions, ${ bindings.length } bindings, `\n + `${ contexts.length } contexts`);\n\n return {\n actions,\n bindings,\n contexts,\n };\n }\n\n /**\n * Imports a configuration from an object\n *\n * @param config - The configuration to import\n */\n public importConfiguration(config : BindingConfiguration) : void\n {\n this._log.debug('Importing binding configuration');\n\n // Clear existing configuration\n this._bindings.clear();\n this._actions.clear();\n this._contexts.clear();\n this._activeContexts.clear();\n\n // Import actions\n for(const action of config.actions)\n {\n this.registerAction(action);\n }\n\n // Import contexts first so they exist before bindings reference them\n for(const contextData of config.contexts)\n {\n this.registerContext(contextData.name, contextData.exclusive);\n\n // Activate contexts that were active\n if(contextData.active)\n {\n this.activateContext(contextData.name);\n }\n }\n\n // Import bindings\n for(const bindingDef of config.bindings)\n {\n try\n {\n this.registerBinding(bindingDef);\n }\n catch (err : unknown)\n {\n if(err instanceof Error)\n {\n this._log.error(`Failed to import binding for action \"${ bindingDef.action }\": ${ err.message }`);\n }\n }\n }\n\n this._log.info(`Configuration imported: ${ config.actions.length } actions, `\n + `${ config.bindings.length } bindings, ${ config.contexts.length } contexts`);\n }\n\n /**\n * Tears down the binding manager and cleans up any resources\n */\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down BindingManager');\n\n // Clear all bindings\n this._bindings.clear();\n\n // Clear all actions\n this._actions.clear();\n\n // Clear all contexts\n this._contexts.clear();\n this._activeContexts.clear();\n\n this._log.info('BindingManager torn down successfully');\n\n return Promise.resolve();\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 { GameCanvas } from '../interfaces/game.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Managers\nimport { GameEntityManager } from './entity.ts';\nimport { UserInputManager } from './input.ts';\n\n// Engines\nimport { SceneEngine } from '../engines/scene.ts';\n\n// Utils\nimport { isBrowser } from '../utils/capabilities.ts';\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class GameManager\n{\n private _engine : AbstractEngine;\n private _entityManager : GameEntityManager;\n private _inputManager : UserInputManager;\n private _sceneEngine : SceneEngine;\n private _currentScene : Scene | null = null;\n private _log : LoggerInterface;\n private _boundResizeHandler : () => void;\n\n public started = false;\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(\n engine : AbstractEngine,\n sceneEngine : SceneEngine,\n entityManager : GameEntityManager,\n inputManager : UserInputManager,\n logger ?: LoggingUtility\n )\n {\n this._engine = engine;\n this._sceneEngine = sceneEngine;\n this._entityManager = entityManager;\n this._inputManager = inputManager;\n this._log = logger?.getLogger('GameManager') || new SAGELogger('GameManager');\n\n // Store the bound function reference for later cleanup\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 private _renderLoop() : void\n {\n // Update entities and entity manager\n const deltaTime = this._engine.getDeltaTime();\n this._entityManager.$frameUpdate(deltaTime);\n\n // Update input devices\n if(this._inputManager)\n {\n this._inputManager.pollGamepads();\n }\n\n // Render the current scene\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(canvas : GameCanvas) : Promise<void>\n {\n // TODO: There needs to be some sort of initial scene we load\n this._currentScene = await this._sceneEngine.loadScene(canvas);\n\n // Start the render loop\n this._engine.runRenderLoop(this._renderLoop.bind(this));\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 * 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 this._log.info('GameManager torn down successfully');\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Game Entity\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEntityBehaviorConstructor } from '../interfaces/entity.ts';\nimport { type GameEvent, GameEventBus } from './eventBus.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Interface representing a subscription to a game event.\n */\ninterface GameEventSubscription\n{\n count : number;\n unsubscribe : () => void;\n}\n\n/**\n * Interface representing the context of a game entity.\n * @template EntityState - The type of the state object.\n */\ninterface SimpleGameEntity<EntityState extends object = object>\n{\n id : string;\n type : string;\n state : EntityState;\n eventBus : GameEventBus;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Base class for game entity behaviors.\n */\nexport abstract class GameEntityBehavior<RequiredState extends object = object>\n{\n abstract name : string;\n abstract eventSubscriptions : string[];\n\n protected entity : SimpleGameEntity | null = null;\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal API\n //------------------------------------------------------------------------------------------------------------------\n\n $emit(event : GameEvent) : void\n {\n if(!this.entity)\n {\n throw new Error('Entity is not set for this behavior.');\n }\n\n event.senderID = this.entity.id;\n this.entity.eventBus.publish(event);\n }\n\n /**\n * Sets the entity for this behavior.\n * @param entity - The entity to set.\n */\n $setEntity(entity : SimpleGameEntity | null) : void\n {\n this.entity = entity;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Implementation API\n //------------------------------------------------------------------------------------------------------------------\n\n abstract processEvent(event : GameEvent, state : RequiredState) : Promise<boolean> | boolean;\n\n update?(dt : number, state : RequiredState) : void;\n\n destroy?() : Promise<void>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Base class for game entities.\n * @template EntityState - The type of the state object.\n */\nexport class GameEntity<EntityState extends object = object> implements SimpleGameEntity<EntityState>\n{\n /** The unique identifier of the entity. */\n public readonly id : string;\n\n /** The type of the entity. */\n public readonly type : string;\n\n /** The state of the entity. */\n public state : EntityState;\n\n /** A map of behaviors attached to the entity. */\n public behaviors : Map<string, GameEntityBehavior> = new Map<string, GameEntityBehavior>();\n\n /** The event bus for the entity. */\n public eventBus : GameEventBus;\n\n /** The event subscriptions for the entity. */\n private subscriptions : Map<string, GameEventSubscription> = new Map<string, GameEventSubscription>();\n\n /**\n * Creates an instance of GameEntityBase.\n * @param type - The type of the entity.\n * @param initialState - The initial state of the entity.\n * @param behaviors - An array of behaviors to attach to the entity.\n * @param eventBus\n */\n constructor(\n type : string,\n eventBus : GameEventBus,\n initialState : EntityState,\n behaviors : GameEntityBehaviorConstructor[]\n )\n {\n this.id = crypto.randomUUID();\n this.type = type;\n this.state = initialState;\n this.eventBus = eventBus;\n\n // Attach behaviors\n for(const behaviorCtor of behaviors)\n {\n this.attachBehavior(new behaviorCtor());\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Processes a game event by passing it to the entity's behaviors.\n * @param event - The game event to process.\n * @returns A promise that resolves when the event has been processed.\n */\n async $processEvent(event : GameEvent) : Promise<void>\n {\n for(const behavior of this.behaviors.values())\n {\n // eslint-disable-next-line no-await-in-loop\n const result = await behavior.processEvent(event, this.state);\n if(result)\n {\n // If the behavior returns true, stop other behaviors from processing the event.\n break;\n }\n }\n }\n\n /**\n * Updates the entity by calling the update method of its behaviors.\n * @param dt - The delta time since the last update.\n */\n $update(dt : number) : void\n {\n for(const behavior of this.behaviors.values())\n {\n behavior.update?.(dt, this.state);\n }\n }\n\n /**\n * Destroys the entity by calling the destroy method of its behaviors.\n * @returns A promise that resolves when the entity has been destroyed.\n */\n async $destroy() : Promise<void>\n {\n for(const behavior of this.behaviors.values())\n {\n behavior.destroy?.();\n }\n\n // Unsubscribe from all event subscriptions\n for(const subscription of this.subscriptions.values())\n {\n subscription.unsubscribe();\n }\n\n this.behaviors.clear();\n this.subscriptions.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Attaches a behavior to the entity.\n * @param behavior - The behavior to attach.\n * @throws Will throw an error if the behavior is already attached.\n */\n attachBehavior(behavior : GameEntityBehavior) : void\n {\n if(this.behaviors.has(behavior.name))\n {\n throw new Error(`Behavior ${ behavior.name } is already attached to this entity.`);\n }\n\n // Subscribe to the behavior's event subscriptions\n for(const event of behavior.eventSubscriptions)\n {\n // Check if the event is already subscribed\n const existingSubscription = this.subscriptions.get(event);\n if(existingSubscription)\n {\n existingSubscription.count++;\n }\n else\n {\n // Create a new subscription\n const unsubscribe = this.eventBus.subscribe(event, this.$processEvent.bind(this));\n this.subscriptions.set(event, { count: 1, unsubscribe });\n }\n }\n\n // Attach the behavior\n this.behaviors.set(behavior.name, behavior);\n behavior.$setEntity(this);\n }\n\n /**\n * Detaches a behavior from the entity.\n * @param behaviorName - The behavior to detach.\n */\n detachBehavior(behaviorName : string) : void\n {\n const behavior = this.behaviors.get(behaviorName);\n if(behavior)\n {\n // Remove the behavior's event subscriptions\n for(const event of behavior.eventSubscriptions)\n {\n const subscription = this.subscriptions.get(event);\n if(subscription)\n {\n subscription.count--;\n if(subscription.count <= 0)\n {\n subscription.unsubscribe();\n this.subscriptions.delete(event);\n }\n }\n }\n\n // Remove the behavior\n this.behaviors.delete(behavior.name);\n behavior.$setEntity(null);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n","//----------------------------------------------------------------------------------------------------------------------\n// Entity Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { GameEntity } from '../classes/entity.ts';\nimport { GameEventBus } from '../classes/eventBus.ts';\n\n// Interfaces\nimport type { Action } from '../interfaces/action.ts';\nimport type { GameEntityDefinition } from '../interfaces/entity.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type { BindingManager } from '../managers/binding.ts';\n\n// Utils\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class GameEntityManager\n{\n /** The event bus for the entity manager. */\n private eventBus : GameEventBus;\n\n /** A map of entities managed by the entity manager. */\n private entities : Map<string, GameEntity> = new Map<string, GameEntity>();\n\n /** A map of entity definitions registered with the entity manager. */\n private entityDefinitions : Map<string, GameEntityDefinition> = new Map<string, GameEntityDefinition>();\n\n /** Reference to the binding manager for registering actions */\n private bindingManager : BindingManager;\n\n /** Logger instance */\n private _log : LoggerInterface;\n\n /**\n * Creates an instance of EntityManager.\n * @param eventBus - The event bus for the entity manager.\n * @param logger - The logging utility to use\n * @param bindingManager - The binding manager for registering actions\n */\n constructor(eventBus : GameEventBus, logger : LoggingUtility | undefined, bindingManager : BindingManager)\n {\n this.eventBus = eventBus;\n this.bindingManager = bindingManager;\n this._log = logger?.getLogger('EntityManager') || new SAGELogger('EntityManager');\n this._log.info('EntityManager initialized');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Checks if two actions are compatible\n * @param existingAction - The action already registered\n * @param newAction - The action being registered\n * @returns true if the actions are compatible, false if they have conflicting options\n */\n private _areActionsCompatible(existingAction : Action, newAction : Action) : boolean\n {\n // If types don't match, they're incompatible\n if(existingAction.type !== newAction.type)\n {\n return false;\n }\n\n // For analog actions, check min and max values\n if(existingAction.type === 'analog' && newAction.type === 'analog')\n {\n // If minValue or maxValue is specified and differs, they're incompatible\n if((newAction.minValue !== undefined && existingAction.minValue !== newAction.minValue)\n || (newAction.maxValue !== undefined && existingAction.maxValue !== newAction.maxValue))\n {\n return false;\n }\n }\n\n // Digital actions are always compatible if they have the same name\n return true;\n }\n\n /**\n * Registers actions defined in the entity definition with the binding manager\n * @param entityDef - The entity definition containing actions to register\n */\n private _registerEntityActions(entityDef : GameEntityDefinition) : void\n {\n if(!entityDef.actions)\n {\n return;\n }\n\n for(const action of entityDef.actions)\n {\n try\n {\n // Check if the action is already registered\n const existingAction = this.bindingManager.getAction(action.name);\n\n if(!existingAction)\n {\n // Action doesn't exist yet, register it\n this._log.debug(`Registering action \"${ action.name }\" from entity type \"${ entityDef.type }\"`);\n this.bindingManager.registerAction(action);\n }\n else if(!this._areActionsCompatible(existingAction, action))\n {\n this._log.warn(\n `Action \"${ action.name }\" already registered with different options. `\n + `Entity \"${ entityDef.type }\" requires: ${ JSON.stringify(action) }, `\n + `but found: ${ JSON.stringify(existingAction) }`\n );\n }\n else\n {\n this._log.trace(\n `Action \"${ action.name }\" already registered with compatible options, skipping registration`\n );\n }\n }\n catch (err)\n {\n this._log.debug(`Failed to register action \"${ action.name }\": `\n + `${ err instanceof Error ? err.message : String(err) }`);\n }\n }\n }\n\n $frameUpdate(dt : number) : void\n {\n this._log.trace(`Updating ${ this.entities.size } entities with dt=${ dt }`);\n for(const entity of this.entities.values())\n {\n entity.$update(dt);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Registers a new entity definition.\n * @param entityDef - The definition of the entity.\n */\n registerEntityDefinition(entityDef : GameEntityDefinition) : void\n {\n this._log.debug(`Registering entity definition: ${ entityDef.type }`);\n\n // Register any actions this entity requires\n this._registerEntityActions(entityDef);\n\n // Store the entity definition\n this.entityDefinitions.set(entityDef.type, entityDef);\n }\n\n /**\n * Creates a new entity of the given type.\n * @param type - The type of the entity to create.\n * @param initialState - The initial state of the entity.\n * @returns The created entity.\n */\n async createEntity<\n State extends object = object,\n >\n (\n type : string,\n initialState : Partial<State> = {}\n ) : Promise<GameEntity<State>>\n {\n this._log.debug(`Creating entity of type: ${ type }`);\n const entityDef = this.entityDefinitions.get(type);\n if(!entityDef)\n {\n const errorMsg = `Entity type ${ type } is not registered.`;\n this._log.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n this._log.trace(`Using entity definition with ${ entityDef.behaviors?.length || 0 } behaviors`);\n let mergedState = { ...entityDef.defaultState, ...initialState } as State;\n\n // Call onBeforeCreate hook if it exists\n if(entityDef.onBeforeCreate)\n {\n this._log.trace(`Calling onBeforeCreate hook for entity type: ${ type }`);\n const result = await entityDef.onBeforeCreate(mergedState);\n // If the hook returns a new state, use it\n if(result !== undefined)\n {\n mergedState = result as State;\n }\n }\n\n // Create the entity instance with possibly modified state\n const entity = new GameEntity<State>(\n entityDef.type,\n this.eventBus,\n mergedState,\n entityDef.behaviors\n );\n\n // Add entity to the manager\n this.entities.set(entity.id, entity);\n this._log.debug(`Entity created with ID: ${ entity.id }`);\n\n // Call onCreate hook if it exists\n if(entityDef.onCreate)\n {\n this._log.trace(`Calling onCreate hook for entity type: ${ type }`);\n const result = await entityDef.onCreate(entity.state);\n // If the hook returns a new state, update the entity's state\n if(result !== undefined)\n {\n entity.state = result as State;\n }\n }\n\n return entity;\n }\n\n /**\n * Destroys the entity with the given ID.\n * @param entityID - The ID of the entity to destroy.\n */\n async destroyEntity(entityID : string) : Promise<void>\n {\n this._log.debug(`Destroying entity: ${ entityID }`);\n const entity = this.entities.get(entityID);\n if(entity)\n {\n // Get entity definition\n const entityDef = this.entityDefinitions.get(entity.type);\n\n // Call onBeforeDestroy hook if it exists\n if(entityDef?.onBeforeDestroy)\n {\n this._log.trace(`Calling onBeforeDestroy hook for entity: ${ entityID }`);\n const result = await entityDef.onBeforeDestroy(entity.state);\n // If the hook returns a new state, update the entity's state\n if(result !== undefined)\n {\n entity.state = result;\n }\n }\n\n // Destroy entity (call $destroy internally)\n await entity.$destroy();\n\n // Call onDestroy hook if it exists\n if(entityDef?.onDestroy)\n {\n this._log.trace(`Calling onDestroy hook for entity: ${ entityID }`);\n await entityDef.onDestroy(entity.state);\n }\n\n // Remove entity from manager\n this.entities.delete(entityID);\n\n this._log.debug(`Entity ${ entityID } destroyed`);\n }\n else\n {\n this._log.warn(`Attempted to destroy non-existent entity: ${ entityID }`);\n }\n }\n\n /**\n * Gets the entity with the given ID.\n * @param entityID - The ID of the entity to get.\n * @returns The entity with the given ID, or null if it does not exist.\n */\n getEntity(entityID : string) : GameEntity | null\n {\n this._log.trace(`Getting entity: ${ entityID }`);\n return this.entities.get(entityID) ?? null;\n }\n\n /**\n * Adds an existing entity to the entity manager.\n * @param entity - The entity to add.\n */\n addEntity(entity : GameEntity) : void\n {\n this._log.debug(`Adding existing entity: ${ entity.id } (type: ${ entity.type })`);\n this.entities.set(entity.id, entity);\n }\n\n /**\n * Removes the entity with the given ID, without destroying it.\n * @param entityID - The ID of the entity to remove.\n */\n removeEntity(entityID : string) : void\n {\n this._log.debug(`Removing entity: ${ entityID }`);\n const entity = this.entities.get(entityID);\n if(entity)\n {\n this.entities.delete(entityID);\n this._log.debug(`Entity ${ entityID } removed`);\n }\n else\n {\n this._log.warn(`Attempted to remove non-existent entity: ${ entityID }`);\n }\n }\n\n /**\n * Tears down all entities and releases resources\n * @returns A promise that resolves when all entities have been destroyed\n */\n async $teardown() : Promise<void>\n {\n this._log.info(`Tearing down EntityManager with ${ this.entities.size } entities`);\n\n // Create a copy of the entity IDs to avoid modification during iteration\n const entityIds = [ ...this.entities.keys() ];\n\n // Destroy all entities\n for(const entityId of entityIds)\n {\n try\n {\n // eslint-disable-next-line no-await-in-loop\n await this.destroyEntity(entityId);\n }\n catch (err)\n {\n this._log.error(`Error destroying entity ${ entityId }: ${ err }`);\n }\n }\n\n // Clear entity definitions\n this.entityDefinitions.clear();\n\n this._log.info('EntityManager torn down successfully');\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n","//----------------------------------------------------------------------------------------------------------------------\n// Keyboard Input Resource Access\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { KeyboardDevice, KeyboardInputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Callback type for keyboard device connection events\n */\nexport type KeyboardDeviceCallback = (device : KeyboardDevice) => void;\n\n/**\n * Callback type for keyboard input events\n */\nexport type KeyboardInputCallback = (device : KeyboardDevice, state : KeyboardInputState) => void;\n\n/**\n * Responsible for tracking keyboard state and notifying subscribers of changes.\n */\nexport class KeyboardInputPlugin\n{\n private _keyboardDevice : KeyboardDevice;\n private _keysState : Record<string, boolean> = {};\n\n // Callbacks\n private _onDeviceConnected ?: KeyboardDeviceCallback;\n private _onInputChanged ?: KeyboardInputCallback;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new KeyboardResourceAccess\n */\n constructor()\n {\n // Initialize keyboard device\n this._keyboardDevice = {\n id: 'keyboard-0',\n name: 'Keyboard',\n type: 'keyboard',\n connected: true,\n };\n\n // Set up event listeners\n this._setupKeyboardEvents();\n\n // Notify that device is connected (on next tick to allow callback registration)\n setTimeout(() => this._notifyDeviceConnected(), 0);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a callback for device connected events\n *\n * @param callback - The callback to register\n */\n public onDeviceConnected(callback : KeyboardDeviceCallback) : void\n {\n this._onDeviceConnected = callback;\n }\n\n /**\n * Register a callback for input changed events\n *\n * @param callback - The callback to register\n */\n public onInputChanged(callback : KeyboardInputCallback) : void\n {\n this._onInputChanged = callback;\n }\n\n /**\n * Get the current keyboard state\n */\n public getState() : KeyboardInputState\n {\n return {\n type: 'keyboard',\n keys: { ...this._keysState },\n delta: {},\n };\n }\n\n /**\n * Get the keyboard device\n */\n public getDevice() : KeyboardDevice\n {\n return { ...this._keyboardDevice };\n }\n\n /**\n * Tears down the keyboard resource access and cleans up event listeners\n */\n public $teardown() : Promise<void>\n {\n // Remove keyboard event listeners\n window.removeEventListener('keydown', this._handleKeyDown);\n window.removeEventListener('keyup', this._handleKeyUp);\n\n return Promise.resolve();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Set up keyboard event listeners\n */\n private _setupKeyboardEvents() : void\n {\n // Bind handlers to this instance\n this._handleKeyDown = this._handleKeyDown.bind(this);\n this._handleKeyUp = this._handleKeyUp.bind(this);\n\n // Add event listeners\n window.addEventListener('keydown', this._handleKeyDown);\n window.addEventListener('keyup', this._handleKeyUp);\n }\n\n /**\n * Handle keyboard key down events\n */\n private _handleKeyDown(event : KeyboardEvent) : void\n {\n this._keysState[event.code] = true;\n\n const delta : Record<string, boolean> = {};\n delta[event.code] = true;\n\n const state : KeyboardInputState = {\n type: 'keyboard',\n keys: { ...this._keysState },\n delta,\n event,\n };\n\n this._notifyInputChanged(state);\n }\n\n /**\n * Handle keyboard key up events\n */\n private _handleKeyUp(event : KeyboardEvent) : void\n {\n this._keysState[event.code] = false;\n\n const delta : Record<string, boolean> = {};\n delta[event.code] = false;\n\n const state : KeyboardInputState = {\n type: 'keyboard',\n keys: { ...this._keysState },\n delta,\n event,\n };\n\n this._notifyInputChanged(state);\n }\n\n /**\n * Notify subscribers of device connected event\n */\n private _notifyDeviceConnected() : void\n {\n if(this._onDeviceConnected)\n {\n this._onDeviceConnected(this._keyboardDevice);\n }\n }\n\n /**\n * Notify subscribers of input changed event\n */\n private _notifyInputChanged(state : KeyboardInputState) : void\n {\n if(this._onInputChanged)\n {\n this._onInputChanged(this._keyboardDevice, state);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Mouse Input Resource Access\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { ButtonState, MouseDevice, MouseInputState, Position } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Callback type for mouse device connection events\n */\nexport type MouseDeviceCallback = (device : MouseDevice) => void;\n\n/**\n * Callback type for mouse input events\n */\nexport type MouseInputCallback = (device : MouseDevice, state : MouseInputState) => void;\n\n/**\n * Responsible for tracking mouse state and notifying subscribers of changes.\n */\nexport class MouseInputPlugin\n{\n private _targetElement : HTMLElement;\n private _mouseDevice : MouseDevice;\n private _buttonState : Record<string, ButtonState> = {};\n private _axesState : Record<string, number> = {};\n private _position : Position = {\n absolute: { x: 0, y: 0 },\n relative: { x: 0, y: 0 },\n };\n\n private _wheelState = {\n deltaX: 0,\n deltaY: 0,\n deltaZ: 0,\n deltaMode: 0,\n };\n\n // Callbacks\n private _onDeviceConnected ?: MouseDeviceCallback;\n private _onInputChanged ?: MouseInputCallback;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new MouseResourceAccess\n *\n * @param targetElement - The DOM element to attach mouse listeners to (defaults to document.body)\n */\n constructor(targetElement : HTMLElement = document.body)\n {\n this._targetElement = targetElement;\n\n // Initialize mouse device\n this._mouseDevice = {\n id: 'mouse-0',\n name: 'Mouse',\n type: 'mouse',\n connected: true,\n };\n\n // Set up event listeners\n this._setupMouseEvents();\n\n // Initialize axes to zero\n this._axesState['axis-x'] = 0;\n this._axesState['axis-y'] = 0;\n\n // Notify that device is connected (on next tick to allow callback registration)\n setTimeout(() => this._notifyDeviceConnected(), 0);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a callback for device connected events\n *\n * @param callback - The callback to register\n */\n public onDeviceConnected(callback : MouseDeviceCallback) : void\n {\n this._onDeviceConnected = callback;\n }\n\n /**\n * Register a callback for input changed events\n *\n * @param callback - The callback to register\n */\n public onInputChanged(callback : MouseInputCallback) : void\n {\n this._onInputChanged = callback;\n }\n\n /**\n * Get the current mouse state\n */\n public getState() : MouseInputState\n {\n return {\n type: 'mouse',\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: {\n absolute: { ...this._position.absolute },\n relative: { ...this._position.relative },\n },\n wheel: { ...this._wheelState },\n };\n }\n\n /**\n * Get the mouse device\n */\n public getDevice() : MouseDevice\n {\n return { ...this._mouseDevice };\n }\n\n /**\n * Tears down the mouse resource access and cleans up event listeners\n */\n public $teardown() : Promise<void>\n {\n // Remove mouse event listeners\n this._targetElement.removeEventListener('mousedown', this._handleMouseDown);\n this._targetElement.removeEventListener('mouseup', this._handleMouseUp);\n this._targetElement.removeEventListener('mousemove', this._handleMouseMove);\n this._targetElement.removeEventListener('wheel', this._handleMouseWheel);\n\n return Promise.resolve();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Set up mouse event listeners\n */\n private _setupMouseEvents() : void\n {\n // Bind handlers to this instance\n this._handleMouseDown = this._handleMouseDown.bind(this);\n this._handleMouseUp = this._handleMouseUp.bind(this);\n this._handleMouseMove = this._handleMouseMove.bind(this);\n this._handleMouseWheel = this._handleMouseWheel.bind(this);\n\n // Add event listeners\n this._targetElement.addEventListener('mousedown', this._handleMouseDown);\n this._targetElement.addEventListener('mouseup', this._handleMouseUp);\n this._targetElement.addEventListener('mousemove', this._handleMouseMove);\n this._targetElement.addEventListener('wheel', this._handleMouseWheel);\n }\n\n /**\n * Handle mouse button down events\n */\n private _handleMouseDown(event : MouseEvent) : void\n {\n const buttonKey = `button-${ event.button }`;\n const buttonState : ButtonState = {\n pressed: true,\n };\n\n this._buttonState[buttonKey] = buttonState;\n\n this._notifyInputChanged({\n type: 'mouse',\n event,\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: { ...this._position },\n });\n }\n\n /**\n * Handle mouse button up events\n */\n private _handleMouseUp(event : MouseEvent) : void\n {\n const buttonKey = `button-${ event.button }`;\n const buttonState : ButtonState = {\n pressed: false,\n };\n\n this._buttonState[buttonKey] = buttonState;\n\n this._notifyInputChanged({\n type: 'mouse',\n event,\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: { ...this._position },\n });\n }\n\n /**\n * Handle mouse move events\n */\n private _handleMouseMove(event : MouseEvent) : void\n {\n // Update position\n this._position = {\n absolute: {\n x: event.clientX,\n y: event.clientY,\n },\n relative: {\n x: event.movementX,\n y: event.movementY,\n },\n };\n\n // Update axis values\n this._axesState['axis-x'] = event.clientX;\n this._axesState['axis-y'] = event.clientY;\n\n this._notifyInputChanged({\n type: 'mouse',\n event,\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: { ...this._position },\n });\n }\n\n /**\n * Handle mouse wheel events\n */\n private _handleMouseWheel(event : WheelEvent) : void\n {\n // Update wheel state\n this._wheelState = {\n deltaX: event.deltaX,\n deltaY: event.deltaY,\n deltaZ: event.deltaZ,\n deltaMode: event.deltaMode,\n };\n\n this._notifyInputChanged({\n type: 'mouse',\n event,\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: { ...this._position },\n wheel: { ...this._wheelState },\n });\n }\n\n /**\n * Notify subscribers of device connected event\n */\n private _notifyDeviceConnected() : void\n {\n if(this._onDeviceConnected)\n {\n this._onDeviceConnected(this._mouseDevice);\n }\n }\n\n /**\n * Notify subscribers of input changed event\n */\n private _notifyInputChanged(state : MouseInputState) : void\n {\n if(this._onInputChanged)\n {\n this._onInputChanged(this._mouseDevice, state);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Gamepad Input\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { ButtonState, GamepadDevice, GamepadInputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Callback type for gamepad device connection events\n */\nexport type GamepadDeviceCallback = (device : GamepadDevice) => void;\n\n/**\n * Callback type for gamepad input events\n */\nexport type GamepadInputCallback = (device : GamepadDevice, state : GamepadInputState) => void;\n\n/**\n * Responsible for tracking gamepad state and notifying subscribers of changes.\n */\nexport class GamepadInputPlugin\n{\n private _gamepadDevices : Record<number, GamepadDevice> = {};\n private _buttonStates : Record<number, Record<string, ButtonState>> = {};\n private _axesStates : Record<number, Record<string, number>> = {};\n\n // Callbacks\n private _onDeviceConnected ?: GamepadDeviceCallback;\n private _onDeviceDisconnected ?: GamepadDeviceCallback;\n private _onInputChanged ?: GamepadInputCallback;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new GamepadResourceAccess\n */\n constructor()\n {\n // Set up event listeners for gamepad connection and disconnection\n this._setupGamepadEvents();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a callback for device connected events\n *\n * @param callback - The callback to register\n */\n public onDeviceConnected(callback : GamepadDeviceCallback) : void\n {\n this._onDeviceConnected = callback;\n }\n\n /**\n * Register a callback for device disconnected events\n *\n * @param callback - The callback to register\n */\n public onDeviceDisconnected(callback : GamepadDeviceCallback) : void\n {\n this._onDeviceDisconnected = callback;\n }\n\n /**\n * Register a callback for input changed events\n *\n * @param callback - The callback to register\n */\n public onInputChanged(callback : GamepadInputCallback) : void\n {\n this._onInputChanged = callback;\n }\n\n /**\n * Get all connected gamepad devices\n */\n public getDevices() : GamepadDevice[]\n {\n return Object.values(this._gamepadDevices).map((device) => ({ ...device }));\n }\n\n /**\n * Get all connected gamepad states\n */\n public getStates() : Record<number, GamepadInputState>\n {\n const states : Record<number, GamepadInputState> = {};\n\n Object.keys(this._buttonStates).forEach((indexStr) =>\n {\n const index = Number(indexStr);\n const buttons = this._buttonStates[index] || {};\n const axes = this._axesStates[index] || {};\n\n states[index] = {\n type: 'gamepad',\n buttons: { ...buttons },\n axes: { ...axes },\n };\n });\n\n return states;\n }\n\n /**\n * Get a specific gamepad device by index\n *\n * @param index - The index of the gamepad to get\n */\n public getDevice(index : number) : GamepadDevice | null\n {\n const device = this._gamepadDevices[index];\n return device ? { ...device } : null;\n }\n\n /**\n * Get a specific gamepad state by index\n *\n * @param index - The index of the gamepad state to get\n */\n public getState(index : number) : GamepadInputState | null\n {\n const buttons = this._buttonStates[index];\n const axes = this._axesStates[index];\n\n if(!buttons && !axes) { return null; }\n\n return {\n type: 'gamepad',\n buttons: buttons ? { ...buttons } : {},\n axes: axes ? { ...axes } : {},\n };\n }\n\n /**\n * Poll for gamepad state updates - call this in your game loop\n */\n public pollGamepads() : void\n {\n /* eslint-disable no-continue */\n\n if(!navigator.getGamepads) { return; }\n\n const gamepads = navigator.getGamepads();\n for(const gamepad of gamepads)\n {\n if(!gamepad) { continue; }\n\n const index = gamepad.index;\n\n if(!this._gamepadDevices[index])\n {\n this._handleGamepadConnected(gamepad);\n continue;\n }\n\n // Get device\n const device = this._gamepadDevices[index];\n if(!device) { continue; }\n\n // Get current button and axis states or initialize them\n const currentButtons = this._buttonStates[index] || {};\n const currentAxes = this._axesStates[index] || {};\n\n // Build new button state object\n const newButtons : Record<string, ButtonState> = {};\n let buttonsChanged = false;\n\n gamepad.buttons.forEach((btn, i) =>\n {\n const buttonKey = `button-${ i }`;\n const buttonState : ButtonState = {\n pressed: btn.pressed,\n touched: btn.touched,\n value: btn.value,\n };\n\n newButtons[buttonKey] = buttonState;\n\n const prevButton = currentButtons[buttonKey];\n if(!prevButton\n || prevButton.pressed !== buttonState.pressed\n || prevButton.touched !== buttonState.touched\n || prevButton.value !== buttonState.value)\n {\n buttonsChanged = true;\n }\n });\n\n // Build new axis state object\n const newAxes : Record<string, number> = {};\n let axesChanged = false;\n\n gamepad.axes.forEach((axisValue, i) =>\n {\n const axisKey = `axis-${ i }`;\n newAxes[axisKey] = axisValue;\n\n const prevAxis = currentAxes[axisKey];\n if(prevAxis !== axisValue)\n {\n axesChanged = true;\n }\n });\n\n // Update state objects\n this._buttonStates[index] = newButtons;\n this._axesStates[index] = newAxes;\n\n // Notify if state changed\n if(buttonsChanged || axesChanged)\n {\n const state : GamepadInputState = {\n type: 'gamepad',\n buttons: { ...newButtons },\n axes: { ...newAxes },\n };\n\n this._notifyInputChanged(device, state);\n }\n }\n\n /* eslint-enable no-continue */\n }\n\n /**\n * Tears down the gamepad resource access and cleans up event listeners\n */\n public $teardown() : Promise<void>\n {\n // Remove gamepad event listeners\n window.removeEventListener('gamepadconnected', this._handleGamepadConnected);\n window.removeEventListener('gamepaddisconnected', this._handleGamepadDisconnected);\n\n return Promise.resolve();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Set up gamepad event listeners\n */\n private _setupGamepadEvents() : void\n {\n // Bind handlers to this instance\n this._handleGamepadConnected = this._handleGamepadConnected.bind(this);\n this._handleGamepadDisconnected = this._handleGamepadDisconnected.bind(this);\n\n // Add event listeners\n window.addEventListener('gamepadconnected', this._handleGamepadConnected);\n window.addEventListener('gamepaddisconnected', this._handleGamepadDisconnected);\n\n // Check for already connected gamepads (browsers sometimes miss the connected event)\n if(navigator.getGamepads)\n {\n const gamepads = navigator.getGamepads();\n for(const gamepad of gamepads)\n {\n if(gamepad)\n {\n this._handleGamepadConnected(gamepad);\n }\n }\n }\n }\n\n /**\n * Handle gamepad connected event\n */\n private _handleGamepadConnected(event : GamepadEvent | Gamepad) : void\n {\n const gamepad = event instanceof GamepadEvent ? event.gamepad : event;\n const index = gamepad.index;\n\n // Create gamepad device object\n const gamepadDevice : GamepadDevice = {\n id: `gamepad-${ index }`,\n name: gamepad.id,\n type: 'gamepad',\n connected: true,\n index,\n mapping: gamepad.mapping,\n axes: Array.from(gamepad.axes),\n buttons: Array.from(gamepad.buttons),\n };\n\n // Store the device\n this._gamepadDevices[index] = gamepadDevice;\n\n // Initialize button state object\n const buttonStateObj : Record<string, ButtonState> = {};\n Array.from(gamepad.buttons).forEach((btn, i) =>\n {\n buttonStateObj[`button-${ i }`] = {\n pressed: btn.pressed,\n touched: btn.touched,\n value: btn.value,\n };\n });\n this._buttonStates[index] = buttonStateObj;\n\n // Initialize axis state object\n const axesStateObj : Record<string, number> = {};\n Array.from(gamepad.axes).forEach((axisValue, i) =>\n {\n axesStateObj[`axis-${ i }`] = axisValue;\n });\n this._axesStates[index] = axesStateObj;\n\n // Notify device connected with initial state\n this._notifyDeviceConnected(gamepadDevice);\n\n // Emit input changed event with initial state\n this._notifyInputChanged(gamepadDevice, {\n type: 'gamepad',\n buttons: { ...buttonStateObj },\n axes: { ...axesStateObj },\n event: event instanceof GamepadEvent ? event : undefined,\n });\n }\n\n /**\n * Handle gamepad disconnected event\n */\n private _handleGamepadDisconnected(event : GamepadEvent) : void\n {\n const gamepad = event.gamepad;\n const index = gamepad.index;\n\n // Get the device\n const gamepadDevice = this._gamepadDevices[index];\n\n if(gamepadDevice)\n {\n // Update connection status\n gamepadDevice.connected = false;\n\n // Notify device disconnected\n this._notifyDeviceDisconnected(gamepadDevice);\n\n // Remove from objects\n // Using object property assignment with undefined instead of delete\n this._gamepadDevices[index] = undefined as unknown as GamepadDevice;\n this._buttonStates[index] = undefined as unknown as Record<string, ButtonState>;\n this._axesStates[index] = undefined as unknown as Record<string, number>;\n }\n }\n\n /**\n * Notify subscribers of device connected event\n */\n private _notifyDeviceConnected(device : GamepadDevice) : void\n {\n if(this._onDeviceConnected)\n {\n this._onDeviceConnected(device);\n }\n }\n\n /**\n * Notify subscribers of device disconnected event\n */\n private _notifyDeviceDisconnected(device : GamepadDevice) : void\n {\n if(this._onDeviceDisconnected)\n {\n this._onDeviceDisconnected(device);\n }\n }\n\n /**\n * Notify subscribers of input changed event\n */\n private _notifyInputChanged(device : GamepadDevice, state : GamepadInputState) : void\n {\n if(this._onInputChanged)\n {\n this._onInputChanged(device, state);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// User Input Manager\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { GamepadInputState, InputDevice, KeyboardInputState, MouseInputState } from '../interfaces/input.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Classes\nimport { type GameEvent, GameEventBus } from '../classes/eventBus.ts';\n\n// Utils\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n// Resource Access\nimport { KeyboardInputPlugin } from '../classes/input/keyboard.ts';\nimport { MouseInputPlugin } from '../classes/input/mouse.ts';\nimport { GamepadInputPlugin } from '../classes/input/gamepad.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Manager for handling user input from various devices (keyboard, mouse, gamepad)\n */\nexport class UserInputManager\n{\n private _eventBus : GameEventBus;\n\n private _keyboardRA : KeyboardInputPlugin;\n private _mouseRA : MouseInputPlugin;\n private _gamepadRA : GamepadInputPlugin;\n\n /** Logger instance */\n private _log : LoggerInterface;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new UserInputManager\n *\n * @param eventBus - The game event bus to publish events to\n * @param canvas - The DOM element to attach input listeners to\n * @param logger - The logging utility to use\n */\n constructor(\n eventBus : GameEventBus,\n canvas : HTMLElement,\n logger ?: LoggingUtility\n )\n {\n this._eventBus = eventBus;\n\n // Initialize logger\n this._log = logger?.getLogger('UserInputManager') || new SAGELogger('UserInputManager');\n\n this._log.info('Initializing UserInputManager');\n\n // Initialize resource access classes\n this._log.debug('Initializing input resource access classes');\n this._keyboardRA = new KeyboardInputPlugin();\n this._mouseRA = new MouseInputPlugin(canvas);\n this._gamepadRA = new GamepadInputPlugin();\n\n // Register callbacks\n this._log.debug('Registering input event callbacks');\n this._keyboardRA.onDeviceConnected(this._publishDeviceConnected.bind(this));\n this._keyboardRA.onInputChanged(this._publishInputChanged.bind(this));\n this._mouseRA.onDeviceConnected(this._publishDeviceConnected.bind(this));\n this._mouseRA.onInputChanged(this._publishInputChanged.bind(this));\n this._gamepadRA.onDeviceConnected(this._publishDeviceConnected.bind(this));\n this._gamepadRA.onDeviceDisconnected(this._publishDeviceDisconnected.bind(this));\n this._gamepadRA.onInputChanged(this._publishInputChanged.bind(this));\n\n this._log.info('UserInputManager initialized successfully');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Publish device connected event to the event bus\n */\n private _publishDeviceConnected(device : InputDevice) : void\n {\n this._log.debug(`Device connected: ${ device.id } (${ device.name })`);\n\n const gameEvent : GameEvent = {\n type: 'input:device:connected',\n payload: { device },\n };\n\n this._eventBus.publish(gameEvent);\n }\n\n /**\n * Publish device disconnected event to the event bus\n */\n private _publishDeviceDisconnected(device : InputDevice) : void\n {\n this._log.debug(`Device disconnected: ${ device.id } (${ device.name })`);\n\n const gameEvent : GameEvent = {\n type: 'input:device:disconnected',\n payload: { device },\n };\n\n this._eventBus.publish(gameEvent);\n }\n\n /**\n * Publish input changed event to the event bus, used by all device types\n */\n private _publishInputChanged(\n device : InputDevice,\n state : KeyboardInputState | MouseInputState | GamepadInputState\n ) : void\n {\n this._log.trace(`Input changed: ${ device.id }`, state);\n\n const gameEvent : GameEvent = {\n type: 'input:changed',\n payload: {\n deviceId: device.id,\n device,\n state,\n },\n };\n\n // Publish the event to the event bus\n this._eventBus.publish(gameEvent);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Tears down the input manager and clean up event listeners\n */\n public $teardown() : Promise<void>\n {\n this._log.info('Tearing down UserInputManager');\n\n // Cleanup all resource access instances\n this._log.debug('Cleaning up input resource access instances');\n\n // Using Promise.all to handle multiple async teardowns\n return Promise.all([\n this._keyboardRA.$teardown(),\n this._mouseRA.$teardown(),\n this._gamepadRA.$teardown(),\n ]).then(() =>\n {\n this._log.info('UserInputManager torn down successfully');\n });\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get all input devices\n *\n * @return An array of input devices\n */\n public listDevices() : InputDevice[]\n {\n this._log.debug('Getting all connected input devices');\n\n const devices : InputDevice[] = [];\n\n // Add keyboard and mouse\n devices.push(this._keyboardRA.getDevice());\n devices.push(this._mouseRA.getDevice());\n\n // Add all gamepads\n devices.push(...this._gamepadRA.getDevices());\n\n this._log.debug(`Found ${ devices.length } connected devices`);\n return devices;\n }\n\n /**\n * Get a specific input device by ID\n *\n * @param deviceId - The ID of the device to get\n *\n * @return The input device, or null if not found\n */\n public getDevice(deviceId : string) : InputDevice | null\n {\n this._log.debug(`Getting device: ${ deviceId }`);\n\n // Shortcut for keyboard and mouse\n if(deviceId.startsWith('keyboard-'))\n {\n return this._keyboardRA.getDevice();\n }\n else if(deviceId.startsWith('mouse-'))\n {\n return this._mouseRA.getDevice();\n }\n else if(deviceId.startsWith('gamepad-'))\n {\n const index = parseInt(deviceId.split('-')[1], 10);\n return this._gamepadRA.getDevice(index);\n }\n else\n {\n // This is the slow path, and should never be hit, but it is here for completeness\n const devices = this.listDevices();\n\n for(const device of devices)\n {\n if(device.id === deviceId)\n {\n return device;\n }\n }\n }\n\n this._log.warn(`Device not found: ${ deviceId }`);\n return null;\n }\n\n /**\n * Poll for gamepad state updates - call this in your game loop\n */\n public pollGamepads() : void\n {\n this._gamepadRA.pollGamepads();\n }\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 - The canvas element to render on (HTMLCanvasElement or OffscreenCanvas)\n * @param options - Options specific to the WebGPU engine\n * @returns A promise that resolves to an initialized WebGPUEngine\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 - The canvas element to render on (GameCanvas)\n * @param options - Options for configuring the engine\n * @returns An initialized WebGL Engine\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 - Options specific to the Null engine\n * @returns An initialized Null Engine\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 *\n * @param canvas - The canvas element to render on, or null for a Null engine\n * @param options - Options for configuring the engine (EngineOptions, WebGPUEngineOptions, or NullEngineOptions)\n * @returns A promise that resolves to an initialized AbstractEngine\n */\nexport async function createEngine(\n canvas : GameCanvas | null,\n options : RenderEngineOptions\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 // 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 );\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 from '@babylonjs/havok';\nimport { HavokPlugin } from '@babylonjs/core';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport async function createPhysics() : Promise<HavokPlugin>\n{\n const hk = await HavokPhysics();\n return new HavokPlugin(true, hk);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Input Device Interfaces\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Valid input device types\n */\nexport const validDeviceTypes = [ 'keyboard', 'mouse', 'gamepad' ] as const;\nexport type DeviceType = typeof validDeviceTypes[number];\n\n/**\n * Base input device interface\n */\nexport interface InputDevice\n{\n id : string;\n name : string;\n type : DeviceType;\n connected : boolean;\n}\n\n/**\n * Keyboard device information\n */\nexport interface KeyboardDevice extends InputDevice\n{\n type : 'keyboard';\n}\n\n/**\n * Mouse device information\n */\nexport interface MouseDevice extends InputDevice\n{\n type : 'mouse';\n}\n\n/**\n * Gamepad device information\n */\nexport interface GamepadDevice extends InputDevice\n{\n type : 'gamepad';\n index : number;\n mapping : string;\n axes : number[];\n buttons : GamepadButton[];\n}\n\n/**\n * Base interface for all input device states\n */\nexport interface BaseInputState\n{\n event ?: Event;\n}\n\n/**\n * Button state interface (for both mouse and gamepad)\n */\nexport interface ButtonState\n{\n pressed : boolean;\n touched ?: boolean;\n value ?: number;\n}\n\n/**\n * Mouse position interface\n */\nexport interface Position\n{\n absolute : { x : number, y : number };\n relative : { x : number, y : number };\n}\n\n/**\n * Unified keyboard input state with object literals instead of Maps\n */\nexport interface KeyboardInputState extends BaseInputState\n{\n type : 'keyboard';\n keys : Record<string, boolean>;\n delta : Record<string, boolean>;\n event ?: KeyboardEvent;\n}\n\n/**\n * Unified mouse input state with object literals instead of Maps\n */\nexport interface MouseInputState extends BaseInputState\n{\n type : 'mouse';\n buttons : Record<string, ButtonState>;\n axes : Record<string, number>;\n position : Position;\n wheel ?: {\n deltaX : number;\n deltaY : number;\n deltaZ : number;\n deltaMode : number;\n };\n event ?: MouseEvent | WheelEvent;\n}\n\n/**\n * Unified gamepad input state with object literals instead of Maps\n */\nexport interface GamepadInputState extends BaseInputState\n{\n type : 'gamepad';\n buttons : Record<string, ButtonState>;\n axes : Record<string, number>;\n event ?: GamepadEvent;\n}\n\n/**\n * Unified input state interface that can represent any input device\n */\nexport type InputState = KeyboardInputState | MouseInputState | GamepadInputState;\n\n/**\n * Base interface for serialized device value readers\n */\nexport interface DeviceValueReaderDefinitionBase\n{\n /**\n * The device type of input source\n */\n type : DeviceType;\n\n /**\n * The input source type (e.g., 'key', 'button', 'axis')\n */\n sourceType : string;\n\n /**\n * The specific identifier for this input source (e.g., 'KeyA', '0', 'absolute:x')\n */\n sourceKey : string;\n}\n\n/**\n * Keyboard value reader definition\n */\nexport interface KeyboardValueReaderDefinition extends DeviceValueReaderDefinitionBase\n{\n type : 'keyboard';\n sourceType : 'key';\n sourceKey : string;\n options ?: {\n useDelta ?: boolean;\n };\n}\n\n/**\n * Mouse value reader definition\n */\nexport interface MouseValueReaderDefinition extends DeviceValueReaderDefinitionBase\n{\n type : 'mouse';\n sourceType : string;\n sourceKey : string;\n}\n\n/**\n * Gamepad value reader definition\n */\nexport interface GamepadValueReaderDefinition extends DeviceValueReaderDefinitionBase\n{\n type : 'gamepad';\n sourceType : string;\n sourceKey : string;\n options ?: {\n useAnalogValue ?: boolean;\n deadzone ?: number;\n invert ?: boolean;\n };\n}\n\n/**\n * Union type of all device value reader definitions\n */\nexport type DeviceValueReaderDefinition =\n | KeyboardValueReaderDefinition\n | MouseValueReaderDefinition\n | GamepadValueReaderDefinition;\n\n/**\n * Interface for all device value readers\n * Unified interface for all types of input sources that can extract values from device states\n */\nexport interface DeviceValueReader\n{\n /**\n * The type of input source (e.g., 'key', 'button', 'axis')\n */\n readonly sourceType : string;\n\n /**\n * The specific identifier for this input source (e.g., 'KeyA', '0', 'absolute:x')\n */\n readonly sourceKey : string;\n\n /**\n * Gets the value from an input state\n *\n * @param state - The current input state\n * @returns The value from the input (boolean or number) or undefined if not applicable to this state\n */\n getValue(state : InputState) : boolean | number | undefined;\n\n /**\n * Convert the reader to a JSON-serializable object\n *\n * @returns A JSON-serializable representation of the reader\n */\n toJSON() : DeviceValueReaderDefinition;\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 - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n trace(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Log a message at DEBUG level\n *\n * @param category - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n debug(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Log a message at INFO level\n *\n * @param category - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n info(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Log a message at WARN level\n *\n * @param category - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n warn(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Log a message at ERROR level\n *\n * @param category - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n error(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Start a timer with the specified label.\n *\n * @param category - The category for this timer (typically class/module name)\n * @param label - A unique label to identify the timer\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 - The category for this timer (typically class/module name)\n * @param label - The label identifying the timer to end\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 : any[]) : void;\n\n /** Log a message at DEBUG level */\n debug(message : string, ...args : any[]) : void;\n\n /** Log a message at INFO level */\n info(message : string, ...args : any[]) : void;\n\n /** Log a message at WARN level */\n warn(message : string, ...args : any[]) : void;\n\n /** Log a message at ERROR level */\n error(message : string, ...args : any[]) : 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// SkewedAspect Game Engine\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport { GameCanvas, RenderEngineOptions, SageOptions } from './interfaces/game.ts';\n\n// Base Classes\nimport { SkewedAspectGameEngine } from './classes/gameEngine.ts';\nimport { GameEventBus } from './classes/eventBus.ts';\n\n// Engines\nimport { SceneEngine } from './engines/scene.ts';\n\n// Managers\nimport { BindingManager } from './managers/binding.ts';\nimport { GameManager } from './managers/game.ts';\nimport { GameEntityManager } from './managers/entity.ts';\nimport { UserInputManager } from './managers/input.ts';\n\n// Utils\nimport { createEngine } from './utils/graphics.ts';\nimport { createPhysics } from './utils/physics.ts';\nimport { LoggingUtility } from './utils/logger.ts';\nimport { VERSION } from './utils/version.ts';\nimport { GameEntityDefinition } from './interfaces/entity.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Creates an instance of SkewedAspectGameEngine.\n * @param canvas - The game canvas.\n * @param entities - The game entities.\n * @param options - The game engine options including rendering options and log level.\n * @returns A promise that resolves to an instance of SkewedAspectGameEngine.\n */\nexport async function createGameEngine(\n canvas : GameCanvas,\n entities : GameEntityDefinition[],\n options : SageOptions = {}\n) : Promise<SkewedAspectGameEngine>\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 const engine = await createEngine(canvas, options.renderOptions || {});\n\n engineLogger.debug('Creating physics engine...');\n const physics = await createPhysics();\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(engine, physics, logger);\n\n // Initialize SAGE Managers\n engineLogger.debug('Creating managers...');\n const inputManager = new UserInputManager(eventBus, canvas as HTMLElement, logger);\n const bindingManager = new BindingManager(eventBus, logger);\n const entityManager = new GameEntityManager(eventBus, logger, bindingManager);\n const gameManager = new GameManager(engine, sceneEngine, entityManager, inputManager, logger);\n\n engineLogger.info(`SAGE Game Engine v${ VERSION } initialized successfully.`);\n\n // Register entities\n engineLogger.debug('Loading entities...');\n for(const entity of entities)\n {\n entityManager.registerEntityDefinition(entity);\n }\n\n // Register default input bindings\n engineLogger.debug('Registering default input bindings...');\n if(options.bindings)\n {\n for(const binding of options.bindings)\n {\n bindingManager.registerBinding(binding);\n }\n }\n\n return new SkewedAspectGameEngine(\n canvas,\n engine,\n physics,\n eventBus,\n logger,\n\n // Engines\n {\n sceneEngine,\n },\n\n // Managers\n {\n bindingManager,\n entityManager,\n gameManager,\n inputManager,\n }\n );\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// Exports\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type { GameCanvas, RenderEngineOptions, SageOptions };\n\nexport * from './interfaces/action.ts';\nexport * from './interfaces/binding.ts';\nexport * from './interfaces/entity.ts';\nexport * from './interfaces/game.ts';\nexport * from './interfaces/input.ts';\nexport * from './interfaces/logger.ts';\n\n// Export base classes\nexport { GameEventBus, SkewedAspectGameEngine };\nexport * from './classes/eventBus.ts';\nexport * from './classes/entity.ts';\nexport * from './utils/logger.ts';\nexport * from './utils/version.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n"],"names":["VERSION","SkewedAspectGameEngine","canvas","renderEngine","physics","eventBus","logger","engines","managers","__publicField","hook","teardownError","err","managerKey","manager","engineKey","engine","ConsoleBackend","level","styles","category","now","hours","minutes","seconds","ampm","timestamp","upperLevel","message","args","format","defaultStyle","levelStyle","resetStyle","label","timerKey","startTime","duration","NullBackend","_category","_message","_args","_label","SAGELogger","minLevel","backend","LoggingUtility","GameEventBus","eventTypeOrPattern","callback","eventType","subs","subscription","event","patternOrRegExp","regex","escaped","callbackCount","directSubs","sub","SceneEngine","scene","camera","FreeCamera","Vector3","light","HemisphericLight","sphere","MeshBuilder","ground","PhysicsAggregate","PhysicsShapeType","Scene","bindingTypes","TriggerBinding","action","deviceID","reader","options","state","rawValue","digitalState","shouldTrigger","outputValue","finalValue","min","max","actionEvent","ToggleBinding","value","shouldToggle","ValueBinding","defaultMin","defaultMax","numericValue","KeyboardValueReader","keyCode","keyboardState","sourceKey","MouseValueReader","sourceType","buttonState","posType","axis","isValidWheelKey","sourceTypeString","keyParts","GamepadValueReader","BindingManager","binding","contextName","exclusive","context","exceptContextName","deactivatedContexts","definition","readerDef","device","bindings","errorMsg","actionName","isExclusive","isAlreadyActive","deactivated","_a","bindingsRemoved","deviceId","deviceBindings","newBindings","bindingContext","shouldKeep","result","actions","contexts","config","contextData","bindingDef","isBrowser","hasWebGPU","GameManager","sceneEngine","entityManager","inputManager","deltaTime","GameEntityBehavior","entity","GameEntity","type","initialState","behaviors","behaviorCtor","behavior","dt","existingSubscription","unsubscribe","behaviorName","GameEntityManager","bindingManager","existingAction","newAction","entityDef","mergedState","entityID","entityIds","entityId","KeyboardInputPlugin","delta","MouseInputPlugin","targetElement","buttonKey","GamepadInputPlugin","states","indexStr","index","buttons","axes","gamepads","gamepad","currentButtons","currentAxes","newButtons","buttonsChanged","btn","i","prevButton","newAxes","axesChanged","axisValue","axisKey","gamepadDevice","buttonStateObj","axesStateObj","UserInputManager","gameEvent","devices","_buildWebGPUEngine","WebGPUEngine","_buildWebGLEngine","Engine","_buildNullEngine","NullEngine","createEngine","forceEngine","error","createPhysics","hk","HavokPhysics","HavokPlugin","validDeviceTypes","LogLevels","createGameEngine","entities","engineLogger","gameManager"],"mappings":"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"}
1
+ {"version":3,"file":"sage.es.js","sources":["../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","../src/engines/scene.ts","../src/interfaces/binding.ts","../src/classes/bindings/trigger.ts","../src/classes/bindings/toggle.ts","../src/classes/bindings/value.ts","../src/classes/input/readers/keyboard.ts","../src/classes/input/readers/mouse.ts","../src/classes/input/readers/gamepad.ts","../src/managers/binding.ts","../src/utils/capabilities.ts","../src/managers/game.ts","../src/classes/entity.ts","../src/managers/entity.ts","../src/managers/level.ts","../src/classes/input/keyboard.ts","../src/classes/input/mouse.ts","../src/classes/input/gamepad.ts","../src/managers/input.ts","../src/utils/graphics.ts","../src/utils/physics.ts","../src/interfaces/input.ts","../src/interfaces/logger.ts","../src/classes/level.ts","../src/sage.ts"],"sourcesContent":["//----------------------------------------------------------------------------------------------------------------------\n// Version utility for SAGE\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * The current version of the SAGE engine.\n * This is automatically pulled from package.json during build.\n */\nexport const VERSION = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0-dev';\n\n//----------------------------------------------------------------------------------------------------------------------\n","//------------------------------------------------------------------------------------------------------------------\n// SkewedAspect Game Engine\n//------------------------------------------------------------------------------------------------------------------\n\nimport { AbstractEngine, HavokPlugin } from '@babylonjs/core';\n\n// Interfaces\nimport type { GameCanvas } from '../interfaces/game.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Engines\nimport { SceneEngine } from '../engines/scene.ts';\n\n// Managers\nimport { BindingManager } from '../managers/binding.ts';\nimport { GameManager } from '../managers/game.ts';\nimport { GameEntityManager } from '../managers/entity.ts';\nimport { LevelManager } from '../managers/level.ts';\nimport { UserInputManager } from '../managers/input.ts';\n\n// Utils\nimport { GameEventBus } from './eventBus.ts';\nimport { LoggingUtility } from '../utils/logger.ts';\nimport { VERSION } from '../utils/version.ts';\n\n//------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type definition for game lifecycle hook functions\n */\n// eslint-disable-next-line no-use-before-define\nexport type GameHook = (gameEngine : SkewedAspectGameEngine) => Promise<void>;\n\n/**\n * Interface representing the engines used in the game.\n */\ninterface Engines\n{\n sceneEngine : SceneEngine;\n}\n\n/**\n * Interface representing the managers used in the game.\n */\ninterface Managers\n{\n bindingManager : BindingManager;\n gameManager : GameManager;\n entityManager : GameEntityManager;\n inputManager : UserInputManager;\n levelManager : LevelManager;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Class representing the SkewedAspect Game Engine.\n */\nexport class SkewedAspectGameEngine\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 public started = false;\n\n private _log : LoggerInterface;\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 SkewedAspectGameEngine.\n * @param canvas - The game canvas.\n * @param renderEngine - The rendering engine.\n * @param physics - The physics engine.\n * @param eventBus - The event bus.\n * @param logger - The logging utility.\n * @param engines - The engines used in the game.\n * @param managers - The managers used in the game.\n */\n constructor(\n canvas : GameCanvas,\n renderEngine : AbstractEngine,\n physics : HavokPlugin,\n eventBus : GameEventBus,\n logger : LoggingUtility,\n engines : Engines,\n managers : Managers\n )\n {\n this.canvas = canvas;\n this.renderEngine = renderEngine;\n this.physics = physics;\n this.eventBus = eventBus;\n this.logger = logger;\n this.engines = engines;\n this.managers = managers;\n\n this._log = logger.getLogger('GameEngine');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a function to be called before the game engine starts\n * @param hook - Async function to be called before start\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 - Async function to be called after start\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 - Async function to be called during teardown\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 * Starts the game engine.\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 * Stops the game engine.\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 // Teardown all managers if they have a $teardown function\n for(const managerKey of Object.keys(this.managers) as (keyof Managers)[])\n {\n const manager = this.managers[managerKey];\n if(typeof (manager as any).$teardown === 'function')\n {\n try\n {\n this._log.debug(`Tearing down manager: ${ managerKey }`);\n // eslint-disable-next-line no-await-in-loop\n await (manager as any).$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 if they have a $teardown function\n for(const engineKey of Object.keys(this.engines) as (keyof Engines)[])\n {\n const engine = this.engines[engineKey];\n if(typeof (engine as any).$teardown === 'function')\n {\n try\n {\n this._log.debug(`Tearing down engine: ${ engineKey }`);\n // eslint-disable-next-line no-await-in-loop\n await (engine as any).$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 // Stop the game manager\n try\n {\n await this.managers.gameManager.stop();\n }\n catch (err)\n {\n this._log.error(`Error stopping game manager: ${ err }`);\n teardownError = teardownError || (err as Error);\n }\n\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 : any[]) : 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 : any[]) : 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 : any[]) : 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 : any[]) : 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 : any[]) : 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 : any[]) : void\n {\n // Do nothing\n }\n\n debug(_category : string, _message : string, ..._args : any[]) : void\n {\n // Do nothing\n }\n\n info(_category : string, _message : string, ..._args : any[]) : void\n {\n // Do nothing\n }\n\n warn(_category : string, _message : string, ..._args : any[]) : void\n {\n // Do nothing\n }\n\n error(_category : string, _message : string, ..._args : any[]) : 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 : any[]) : void\n {\n if(this.shouldLog('trace'))\n {\n this.backend.trace(this.category, message, ...args);\n }\n }\n\n debug(message : string, ...args : any[]) : void\n {\n if(this.shouldLog('debug'))\n {\n this.backend.debug(this.category, message, ...args);\n }\n }\n\n info(message : string, ...args : any[]) : void\n {\n if(this.shouldLog('info'))\n {\n this.backend.info(this.category, message, ...args);\n }\n }\n\n warn(message : string, ...args : any[]) : void\n {\n if(this.shouldLog('warn'))\n {\n this.backend.warn(this.category, message, ...args);\n }\n }\n\n error(message : string, ...args : any[]) : 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.\n *\n * @param backend - The logging backend to use\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.\n *\n * @param level - The minimum logging level to display\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 a logger for the specified category.\n *\n * @param category - The category for this logger (typically class/module name)\n * @returns A Logger instance for the specified category\n */\n public getLogger(category : string) : LoggerInterface\n {\n if(!this.loggers.has(category))\n {\n this.loggers.set(category, new SAGELogger(category, this.level, this.backend));\n }\n\n const logger = this.loggers.get(category);\n // Check for undefined instead of using non-null assertion\n if(logger === undefined)\n {\n throw new Error(`Failed to create logger for category: ${ category }`);\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 { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A generic game event type.\n *\n * @template P - The shape of the payload. Default is a generic object if omitted.\n */\nexport interface GameEvent<P extends Record<string, any> = Record<string, any>>\n{\n /** The string identifier/category of the event (e.g. \"input:keydown\"). */\n type : string;\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, with user-defined shape. */\n payload ?: P;\n}\n\n/**\n * A callback function that receives a {@link GameEvent} with a given payload type.\n *\n * @template P - The shape of the payload in the event.\n */\nexport type GameEventCallback<P extends Record<string, any> = Record<string, any>> =\n (event : GameEvent<P>) => 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>) => void | Promise<void>;\n}\n\n/**\n * An unsubscribe function, returned by any subscribe method.\n */\nexport type Unsubscribe = () => void;\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * A high-performance event bus that supports exact or pattern-based subscriptions,\n * and allows subscribers to specify a more specific `payload` type for convenience.\n *\n * **Note**: This does NOT enforce that a given `type` always has a certain payload shape.\n * It's a best-effort approach that lets you avoid `as` casts in callbacks.\n */\nexport class GameEventBus\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 - The logging utility to use\n */\n constructor(logger ?: LoggingUtility)\n {\n this._log = logger?.getLogger('EventBus') || new SAGELogger('EventBus');\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 P - The payload shape you want to expect in the callback.\n * @param {string | RegExp} eventTypeOrPattern - The exact event type or a wildcard/RegExp pattern.\n * @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.\n * @returns {Unsubscribe} function that, when called, removes this subscription.\n */\n public subscribe<P extends Record<string, any> = Record<string, any>>(\n eventTypeOrPattern : string | RegExp,\n callback : GameEventCallback<P>\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(eventTypeOrPattern, callback);\n }\n else\n {\n return this.subscribeExact(eventTypeOrPattern, callback);\n }\n }\n\n /**\n * Subscribes to an exact event type (e.g. \"input:keydown\").\n *\n * @template P - The payload shape you want to expect in the callback.\n * @param {string} eventType - The exact string to match.\n * @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.\n * @returns {Unsubscribe} function that unsubscribes this exact subscription.\n */\n public subscribeExact<P extends Record<string, any> = Record<string, any>>(\n eventType : string,\n callback : GameEventCallback<P>\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<P>), // runtime \"type-lie\" cast\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 * @template P - The payload shape you want to expect in the callback.\n * @param {string | RegExp} patternOrRegExp - The pattern to match (`\"input:*\"` or `/^input:/`).\n * @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.\n * @returns {Unsubscribe} function that unsubscribes this pattern-based subscription.\n */\n public subscribePattern<P extends Record<string, any> = Record<string, any>>(\n patternOrRegExp : string | RegExp,\n callback : GameEventCallback<P>\n ) : Unsubscribe\n {\n let regex : RegExp;\n\n if(typeof patternOrRegExp === 'string')\n {\n // MAIN BUG FIX:\n // We replace '*' with '.*' (not '\\*' or '\\\\*').\n // The prior code was .replace('\\\\*', '.*'), which never matched 'input:*'\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<P>),\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 * Publishes an event, invoking all matching callbacks.\n *\n * Callbacks are called asynchronously (microtask) via `Promise.resolve()`.\n *\n * @param {GameEvent<any>} event - The event object to publish.\n */\n public publish(event : GameEvent<any>) : 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().then(() => sub.callback(event));\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().then(() => sub.callback(event));\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//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Scene Engine\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 Scene,\n Sound,\n SpotLight,\n Vector3,\n} from '@babylonjs/core';\nimport type { GameCanvas } from '../interfaces/game.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport { type LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class SceneEngine\n{\n private _canvas : GameCanvas;\n private _engine : AbstractEngine;\n private _physics : HavokPlugin;\n private _log : LoggerInterface;\n\n constructor(canvas : GameCanvas, engine : AbstractEngine, physics : HavokPlugin, logger ?: LoggingUtility)\n {\n this._canvas = canvas;\n this._engine = engine;\n this._physics = physics;\n this._log = logger?.getLogger('SceneEngine') || new SAGELogger('SceneEngine');\n }\n\n /**\n * Creates a new scene with physics enabled\n * @returns A new Scene instance with physics enabled\n */\n public createScene() : Scene\n {\n this._log.debug('Creating new scene...');\n return new Scene(this._engine);\n }\n\n public enablePhysics(scene : Scene, gravityVector : Vector3 = new Vector3(0, -9.8, 0)) : void\n {\n this._log.debug(\n `Enabling physics with gravity (${ gravityVector.x }, ${ gravityVector.y }, ${ gravityVector.z })...`\n );\n scene.enablePhysics(gravityVector, this._physics);\n }\n\n // Camera Utilities\n /**\n * Creates a free camera at the specified position\n * @param name - The name for the camera\n * @param position - The position of the camera\n * @param scene - The scene to add the camera to\n * @param canvas - The canvas to attach the camera controls to\n * @returns A new FreeCamera instance\n */\n public createFreeCamera(\n name : string,\n position : Vector3,\n scene : Scene,\n canvas ?: GameCanvas\n ) : FreeCamera\n {\n this._log.debug(`Creating free camera: ${ name }`);\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 - The name for the light\n * @param direction - The direction of the light\n * @param scene - The scene to add the light to\n * @param intensity - The intensity of the light (default: 0.7)\n * @returns A new HemisphericLight instance\n */\n public createHemisphericLight(\n name : string,\n direction : Vector3,\n scene : Scene,\n intensity = 0.7\n ) : HemisphericLight\n {\n this._log.debug(`Creating hemispheric light: ${ name }`);\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 - The name for the light\n * @param direction - The direction of the light\n * @param scene - The scene to add the light to\n * @param intensity - The intensity of the light (default: 1.0)\n * @returns A new DirectionalLight instance\n */\n public createDirectionalLight(\n name : string,\n direction : Vector3,\n scene : Scene,\n intensity = 1.0\n ) : DirectionalLight\n {\n this._log.debug(`Creating directional light: ${ name }`);\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 - The name for the light\n * @param position - The position of the light\n * @param scene - The scene to add the light to\n * @param intensity - The intensity of the light (default: 1.0)\n * @returns A new PointLight instance\n */\n public createPointLight(\n name : string,\n position : Vector3,\n scene : Scene,\n intensity = 1.0\n ) : PointLight\n {\n this._log.debug(`Creating point light: ${ name }`);\n const light = new PointLight(name, position, scene);\n light.intensity = intensity;\n return light;\n }\n\n /**\n * Creates a spot light (flashlight-like lighting)\n * @param name - The name for the light\n * @param position - The position of the light\n * @param direction - The direction of the light\n * @param angle - The angle of the light cone\n * @param exponent - The exponent of the light falloff\n * @param scene - The scene to add the light to\n * @param intensity - The intensity of the light (default: 1.0)\n * @returns A new SpotLight instance\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 this._log.debug(`Creating spot light: ${ name }`);\n const light = new SpotLight(name, position, direction, angle, exponent, scene);\n light.intensity = intensity;\n return light;\n }\n\n // Mesh Creation Utilities\n /**\n * Creates a sphere mesh\n * @param name - The name for the mesh\n * @param options - Options for creating the sphere\n * @param scene - The scene to add the mesh to\n * @returns A new sphere Mesh instance\n */\n public createSphere(\n name : string,\n options : { diameter ?: number; segments ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n this._log.debug(`Creating sphere: ${ name }`);\n const defaultOptions = { diameter: 1, segments: 32 };\n return MeshBuilder.CreateSphere(name, { ...defaultOptions, ...options }, scene);\n }\n\n /**\n * Creates a box mesh\n * @param name - The name for the mesh\n * @param options - Options for creating the box\n * @param scene - The scene to add the mesh to\n * @returns A new box Mesh instance\n */\n public createBox(\n name : string,\n options : { size ?: number; width ?: number; height ?: number; depth ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n this._log.debug(`Creating box: ${ name }`);\n const defaultOptions = { size: 1 };\n return MeshBuilder.CreateBox(name, { ...defaultOptions, ...options }, scene);\n }\n\n /**\n * Creates a ground mesh\n * @param name - The name for the mesh\n * @param options - Options for creating the ground\n * @param scene - The scene to add the mesh to\n * @returns A new ground Mesh instance\n */\n public createGround(\n name : string,\n options : { width ?: number; height ?: number; subdivisions ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n this._log.debug(`Creating ground: ${ name }`);\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\n * @param name - The name for the mesh\n * @param options - Options for creating the cylinder\n * @param scene - The scene to add the mesh to\n * @returns A new cylinder Mesh instance\n */\n public createCylinder(\n name : string,\n options : { height ?: number; diameterTop ?: number; diameterBottom ?: number } = {},\n scene : Scene\n ) : Mesh\n {\n this._log.debug(`Creating cylinder: ${ name }`);\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\n * @param mesh - The mesh to add physics to\n * @param shapeType - The physics shape type\n * @param physicsProps - The physics properties (mass, restitution, etc.)\n * @param scene - The scene the mesh belongs to\n * @returns A new PhysicsAggregate instance\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 this._log.debug(`Adding physics to mesh: ${ mesh.name }`);\n const defaultProps = { mass: 1, restitution: 0.75, friction: 0.5 };\n return new PhysicsAggregate(mesh, shapeType, { ...defaultProps, ...physicsProps }, scene);\n }\n\n // Audio Utilities\n /**\n * Creates and loads a sound\n * @param name - The name for the sound\n * @param url - The URL or file path of the sound\n * @param scene - The scene to add the sound to\n * @param options - Additional sound options\n * @returns A new Sound instance\n */\n public createSound(\n name : string,\n url : string,\n scene : Scene,\n options : {\n loop ?: boolean;\n autoplay ?: boolean;\n volume ?: number;\n playbackRate ?: number;\n } = {}\n ) : Sound\n {\n this._log.debug(`Creating sound: ${ name }`);\n const defaultOptions = { loop: false, autoplay: false, volume: 1.0, playbackRate: 1.0 };\n const soundOptions = { ...defaultOptions, ...options };\n\n return new Sound(\n name,\n url,\n scene,\n undefined, // readyToPlayCallback\n soundOptions\n );\n }\n\n // Model Loading Utilities\n /**\n * Loads a 3D model/asset from a file\n * @param rootUrl - The root URL where the file is located\n * @param sceneFilename - The filename of the scene file\n * @param scene - The scene to load the model into\n * @returns Promise that resolves with the loaded AssetContainer\n */\n public async loadModel(\n rootUrl : string,\n sceneFilename : string,\n scene : Scene\n ) : Promise<AssetContainer>\n {\n this._log.debug(`Loading model: ${ rootUrl }${ sceneFilename }`);\n\n try\n {\n const result = await LoadAssetContainerAsync(`${ rootUrl }${ 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\n * @param meshNames - Array of mesh names to import (empty array imports all)\n * @param rootUrl - The root URL where the file is located\n * @param sceneFilename - The filename of the scene file\n * @param scene - The scene to import the meshes into\n * @returns Promise that resolves with the import result\n */\n public async importMeshes(\n meshNames : string[],\n rootUrl : string,\n sceneFilename : string,\n scene : Scene\n ) : Promise<{\n meshes : AbstractMesh[];\n particleSystems : any[];\n skeletons : any[];\n animationGroups : any[];\n transformNodes : any[];\n geometries : any[];\n lights : Light[];\n }>\n {\n this._log.debug(`Importing meshes from: ${ rootUrl }${ sceneFilename }`);\n\n try\n {\n const result = await ImportMeshAsync(`${ rootUrl }${ sceneFilename }`, scene, { meshNames });\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 return Promise.resolve();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Binding Interfaces\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../classes/eventBus.ts';\nimport type { DeviceValueReader, DeviceValueReaderDefinition, InputState } from './input.ts';\nimport type { Action } from './action.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Types of bindings supported by the system\n */\nexport type BindingType = 'trigger' | 'toggle' | 'value';\n\n/**\n * Array of all supported binding types for validation\n */\nexport const bindingTypes : BindingType[] = [\n 'trigger',\n 'toggle',\n 'value',\n];\n\n/**\n * Context for organizing bindings into groups that can be activated/deactivated together\n */\nexport interface Context\n{\n /**\n * Name of the context for identification\n */\n name : string;\n\n /**\n * Whether this context is exclusive (only one exclusive context can be active at a time)\n */\n exclusive : boolean;\n}\n\n/**\n * Input definition that combines device ID and reader configuration into a single object\n */\nexport type InputDefinition = DeviceValueReaderDefinition & {\n deviceID : string;\n};\n\n/**\n * Base interface for binding definitions with object-based sources\n */\nexport interface BindingDefinitionBase\n{\n /**\n * Type of binding\n */\n type : BindingType;\n\n /**\n * Name of the (already registered) action this binding triggers\n */\n action : string;\n\n /**\n * Input definition combining device ID and reader configuration\n */\n input : InputDefinition;\n\n /**\n * Optional context name this binding belongs to\n */\n context ?: string;\n\n /**\n * Optional options for the binding\n */\n options ?: Record<string, any>;\n}\n\n/**\n * Trigger binding definition\n */\nexport interface TriggerBindingDefinition extends BindingDefinitionBase\n{\n type : 'trigger';\n\n options ?: {\n edgeMode ?: 'rising' | 'falling' | 'both';\n threshold ?: number;\n passthrough ?: boolean;\n };\n}\n\n/**\n * Toggle binding definition\n */\nexport interface ToggleBindingDefinition extends BindingDefinitionBase\n{\n type : 'toggle';\n state ?: boolean;\n\n options ?: {\n invert ?: boolean;\n initialState ?: boolean;\n threshold ?: number;\n onValue ?: boolean | number;\n offValue ?: boolean | number;\n };\n}\n\n/**\n * Value binding definition\n */\nexport interface ValueBindingDefinition extends BindingDefinitionBase\n{\n type : 'value';\n options ?: {\n scale ?: number;\n offset ?: number;\n min ?: number;\n max ?: number;\n invert ?: boolean;\n emitOnChange ?: boolean;\n deadzone ?: number;\n };\n}\n\n/**\n * Union type of all binding definitions\n */\nexport type BindingDefinition =\n | TriggerBindingDefinition\n | ToggleBindingDefinition\n | ValueBindingDefinition;\n\n/**\n * Base interface for all input bindings\n */\nexport interface Binding\n{\n /**\n * Type of binding\n */\n readonly type : BindingType;\n\n /**\n * Action object this binding triggers\n */\n readonly action : Action;\n\n /**\n * Optional context name this binding belongs to\n */\n readonly context ?: string;\n\n /**\n * Device ID this binding is associated with\n */\n readonly deviceID : string;\n\n /**\n * Input source that provides values for this binding\n */\n readonly reader : DeviceValueReader;\n\n /**\n * Process input state and potentially emit an action event\n *\n * @param state - Current input state for the device\n * @param eventBus - Event bus to emit action events to\n */\n process(state : InputState, eventBus : GameEventBus) : void;\n\n /**\n * Convert the binding to a JSON-serializable object\n *\n * @returns A JSON-serializable representation of the binding\n */\n toJSON() : BindingDefinition;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Trigger Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../eventBus.ts';\n\n// Interfaces\nimport type { Action } from '../../interfaces/action.ts';\nimport type { Binding, TriggerBindingDefinition } from '../../interfaces/binding.ts';\nimport type { DeviceValueReader, InputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * All supported edge modes for validation\n */\nexport const edgeModes = [ 'rising', 'falling', 'both' ] as const;\n\n/**\n * Defines when a trigger binding will fire based on input state changes\n */\nexport type EdgeMode = typeof edgeModes[number];\n\n/**\n * Options for configuring a trigger binding\n */\nexport interface TriggerBindingOptions\n{\n /**\n * Which edge(s) should cause the trigger to fire\n * - 'rising': Only trigger on false -> true transitions\n * - 'falling': Only trigger on true -> false transitions\n * - 'both': Trigger on any state change\n */\n edgeMode ?: EdgeMode;\n\n /**\n * Threshold for analog inputs (0.0 to 1.0)\n * When input value is >= threshold, it's considered \"active\" (true)\n * When input value is < threshold, it's considered \"inactive\" (false)\n * Defaults to 0.5 for analog inputs\n */\n threshold ?: number;\n\n /**\n * Optional context name this binding belongs to\n */\n context ?: string;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Implementation of a trigger binding, which emits an action when a button/key is pressed or released.\n * Can handle both digital and analog inputs with configurable threshold.\n * Can output either digital or analog values based on the action type.\n */\nexport class TriggerBinding implements Binding\n{\n public readonly type = 'trigger' as const;\n public readonly action : Action;\n public readonly context ?: string;\n public readonly deviceID : string;\n public readonly reader : DeviceValueReader;\n\n // Trigger-specific options\n private readonly _edgeMode : EdgeMode;\n private readonly _threshold : number;\n\n // State tracking\n private _lastDigitalState = false;\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Getters\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get the current options for this trigger binding.\n *\n * @returns The current options for this trigger binding\n */\n get options() : TriggerBindingOptions\n {\n return {\n edgeMode: this._edgeMode,\n threshold: this._threshold,\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new trigger binding\n *\n * @param action - Action to trigger\n * @param deviceID - Device ID this binding is for\n * @param reader - Input reader to read values from\n * @param options - Binding configuration options\n */\n constructor(\n action : Action,\n deviceID : string,\n reader : DeviceValueReader,\n options : TriggerBindingOptions = {}\n )\n {\n this.action = action;\n this.deviceID = deviceID;\n this.reader = reader;\n this.context = options.context;\n\n // Set options with defaults\n this._edgeMode = options.edgeMode ?? 'rising';\n this._threshold = options.threshold ?? 0.5;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process input state and emit an action if triggered\n *\n * @param state - Current input state\n * @param eventBus - Event bus to emit action events to\n * @returns True if an action was triggered, false otherwise\n */\n public process(state : InputState, eventBus : GameEventBus) : void\n {\n // Extract the raw value using our source\n const rawValue = this.reader.getValue(state) ?? false;\n\n // Determine the digital state based on the threshold\n const digitalState = typeof rawValue === 'boolean'\n ? rawValue\n : rawValue >= this._threshold;\n\n // Determine if we should trigger based on the configured edge mode\n let shouldTrigger = false;\n\n switch (this._edgeMode)\n {\n case 'rising':\n shouldTrigger = digitalState && !this._lastDigitalState;\n break;\n case 'falling':\n shouldTrigger = !digitalState && this._lastDigitalState;\n break;\n case 'both':\n shouldTrigger = digitalState !== this._lastDigitalState;\n break;\n }\n\n // Update last known state for next time\n this._lastDigitalState = digitalState;\n\n // If triggered, emit the action event\n if(shouldTrigger)\n {\n let outputValue : boolean | number;\n if(this.action.type === 'analog')\n {\n // Use raw value and convert to a number if needed\n let finalValue = typeof rawValue === 'number' ? rawValue : (rawValue ? 1.0 : 0.0);\n\n // Apply scaling if action parameters exist\n if(this.action.minValue !== undefined || this.action.maxValue !== undefined)\n {\n const min = this.action.minValue ?? 0;\n const max = this.action.maxValue ?? 1;\n finalValue = min + (finalValue * (max - min));\n }\n outputValue = finalValue;\n }\n else\n {\n outputValue = true;\n }\n const actionEvent = {\n type: `action:${ this.action.name }`,\n payload: {\n value: outputValue,\n deviceId: this.deviceID,\n context: this.context,\n },\n };\n eventBus.publish(actionEvent);\n }\n }\n\n /**\n * Returns a JSON-serializable representation of this trigger binding\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : TriggerBindingDefinition\n {\n return {\n type: this.type,\n action: this.action.name,\n input: {\n deviceID: this.deviceID,\n ...this.reader.toJSON(),\n },\n context: this.context,\n options: this.options,\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Toggle Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../eventBus.ts';\n\n// Interfaces\nimport type { Action } from '../../interfaces/action.ts';\nimport type { Binding, ToggleBindingDefinition } from '../../interfaces/binding.ts';\nimport type { DeviceValueReader, InputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Options for configuring a toggle binding\n */\nexport interface ToggleBindingOptions\n{\n /**\n * If true, toggle on falling edge (true -> false) instead of rising edge (false -> true)\n */\n invert ?: boolean;\n\n /**\n * Initial toggle state (defaults to false - off)\n */\n initialState ?: boolean;\n\n /**\n * Threshold for analog inputs (0.0 to 1.0)\n * When input value is >= threshold, it's considered \"active\" (true)\n * When input value is < threshold, it's considered \"inactive\" (false)\n * Defaults to 0.5 for analog inputs\n */\n threshold ?: number;\n\n /**\n * Value to emit when the toggle is in the \"on\" state for digital actions\n * If undefined, will emit boolean true\n * Ignored for analog actions\n */\n onValue ?: boolean | number;\n\n /**\n * Value to emit when the toggle is in the \"off\" state for digital actions\n * If undefined, will emit boolean false\n * Ignored for analog actions\n */\n offValue ?: boolean | number;\n\n /**\n * Optional context name this binding belongs to\n */\n context ?: string;\n}\n\n/**\n * Implementation of a toggle binding, which maintains and toggles state between true/false\n * when a button/key is pressed or released, remaining in that state until toggled again.\n * Can handle both digital and analog inputs with configurable threshold.\n * Can output either digital or analog values based on the action type.\n */\nexport class ToggleBinding implements Binding\n{\n public readonly type = 'toggle' as const;\n public readonly action : Action;\n public readonly context ?: string;\n public readonly deviceID : string;\n public readonly reader : DeviceValueReader;\n\n // Toggle-specific options\n private readonly _invert : boolean;\n private readonly _threshold : number;\n private readonly _initialState : boolean;\n private readonly _onValue : boolean | number;\n private readonly _offValue : boolean | number;\n\n // State tracking\n private _lastDigitalState = false;\n private _toggleState : boolean;\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Getters\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get the current toggle state\n *\n * @returns Current toggle state (true = on, false = off)\n */\n public get state() : boolean\n {\n return this._toggleState;\n }\n\n /**\n * Set the toggle state directly (useful for programmatic control)\n *\n * @param value - New toggle state\n */\n public set state(value : boolean)\n {\n this._toggleState = value;\n }\n\n /**\n * Get the value emitted when toggle is in the \"on\" state\n *\n * @returns The value for the \"on\" state\n */\n public get onValue() : boolean | number\n {\n return this._onValue;\n }\n\n /**\n * Get the value emitted when toggle is in the \"off\" state\n *\n * @returns The value for the \"off\" state\n */\n public get offValue() : boolean | number\n {\n return this._offValue;\n }\n\n /**\n * Get the current value based on toggle state\n *\n * @returns The current value (boolean or number)\n */\n public get value() : boolean | number\n {\n if(this.action.type === 'analog')\n {\n return this._toggleState\n ? (this.action.maxValue ?? 1)\n : (this.action.minValue ?? 0);\n }\n else\n {\n return this._toggleState ? this._onValue : this._offValue;\n }\n }\n\n /**\n * Get the current options for this toggle binding.\n *\n * @returns The current options for this toggle binding\n */\n public get options() : ToggleBindingOptions\n {\n return {\n invert: this._invert,\n threshold: this._threshold,\n initialState: this._initialState,\n onValue: this._onValue,\n offValue: this._offValue,\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new toggle binding\n *\n * @param action - Action to toggle\n * @param deviceID - Device ID this binding is for\n * @param reader - Input reader to read values from\n * @param options - Binding configuration options\n */\n constructor(\n action : Action,\n deviceID : string,\n reader : DeviceValueReader,\n options : ToggleBindingOptions = {}\n )\n {\n this.action = action;\n this.deviceID = deviceID;\n this.reader = reader;\n this.context = options.context;\n\n // Set options with defaults\n this._invert = options.invert ?? false;\n this._threshold = options.threshold ?? 0.5;\n this._initialState = options.initialState ?? false;\n this._toggleState = this._initialState;\n\n // For digital actions, these control the output value\n // For analog actions, these are not used (we use action properties instead)\n this._onValue = options.onValue ?? true;\n this._offValue = options.offValue ?? false;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process input state and emit a digital action if toggled\n *\n * @param state - Current input state\n * @param eventBus - Event bus to emit action events to\n */\n public process(state : InputState, eventBus : GameEventBus) : void\n {\n const rawValue = this.reader.getValue(state) ?? false;\n const digitalState = typeof rawValue === 'boolean'\n ? rawValue\n : rawValue >= this._threshold;\n\n const shouldToggle = this._invert\n ? !digitalState && this._lastDigitalState\n : digitalState && !this._lastDigitalState;\n\n this._lastDigitalState = digitalState;\n\n if(!shouldToggle) { return; }\n\n this._toggleState = !this._toggleState;\n\n eventBus.publish({\n type: `action:${ this.action.name }`,\n payload: {\n value: this.value,\n deviceId: this.deviceID,\n context: this.context,\n },\n });\n }\n\n /**\n * Reset toggle to its initial state\n */\n public reset() : void\n {\n this._toggleState = this._initialState;\n }\n\n /**\n * Returns a JSON-serializable representation of this toggle binding\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : ToggleBindingDefinition\n {\n return {\n type: this.type,\n action: this.action.name,\n input: {\n deviceID: this.deviceID,\n ...this.reader.toJSON(),\n },\n state: this._toggleState,\n context: this.context,\n options: this.options,\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Value Binding\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEventBus } from '../eventBus.ts';\n\n// Interfaces\nimport type { Action } from '../../interfaces/action.ts';\nimport type { Binding, ValueBindingDefinition } from '../../interfaces/binding.ts';\nimport type { DeviceValueReader, InputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Options for configuring a value binding\n */\nexport interface ValueBindingOptions\n{\n /**\n * Factor to scale the input value by (1.0 = no scaling)\n */\n scale ?: number;\n\n /**\n * Value to add to the input value after scaling\n */\n offset ?: number;\n\n /**\n * Whether to invert the input value (multiply by -1)\n */\n invert ?: boolean;\n\n /**\n * Optional context name this binding belongs to\n */\n context ?: string;\n\n /**\n * If true, only emit values when they change\n * If false, always emit values on each process call\n */\n emitOnChange ?: boolean;\n\n /**\n * Deadzone threshold (0.0 to 1.0)\n * Input values below this threshold will be treated as 0\n */\n deadzone ?: number;\n\n /**\n * Value to emit when the input is digital and true\n * Only used when action is digital\n */\n onValue ?: boolean | number;\n\n /**\n * Value to emit when the input is digital and false\n * Only used when action is digital\n */\n offValue ?: boolean | number;\n\n /**\n * Minimum value to clamp output to\n */\n min ?: number;\n\n /**\n * Maximum value to clamp output to\n */\n max ?: number;\n}\n\n/**\n * Implementation of a value binding, which directly converts an analog input\n * to an analog output with optional scaling, offset, and clamping.\n * Can handle both digital and analog inputs/outputs based on action type.\n */\nexport class ValueBinding implements Binding\n{\n public readonly type = 'value' as const;\n public readonly action : Action;\n public readonly context ?: string;\n public readonly deviceID : string;\n public readonly reader : DeviceValueReader;\n\n // Value-specific options\n private readonly _scale : number;\n private readonly _offset : number;\n private readonly _invert : boolean;\n private readonly _emitOnChange : boolean;\n private readonly _deadzone : number;\n private readonly _onValue : boolean | number;\n private readonly _offValue : boolean | number;\n private readonly _min : number;\n private readonly _max : number;\n\n // State tracking\n private _lastValue : number | boolean | undefined;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get the current options for this value binding.\n *\n * @returns The current options for this value binding\n */\n get options() : ValueBindingOptions\n {\n return {\n scale: this._scale,\n offset: this._offset,\n invert: this._invert,\n emitOnChange: this._emitOnChange,\n deadzone: this._deadzone,\n onValue: this._onValue,\n offValue: this._offValue,\n min: this._min,\n max: this._max,\n };\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new value binding\n *\n * @param action - Action to emit values for\n * @param deviceID - Device ID this binding is for\n * @param reader - Input reader to read values from\n * @param options - Binding configuration options\n */\n constructor(\n action : Action,\n deviceID : string,\n reader : DeviceValueReader,\n options : ValueBindingOptions = {}\n )\n {\n this.action = action;\n this.deviceID = deviceID;\n this.reader = reader;\n this.context = options.context;\n\n // Common options that apply to both analog and digital\n this._scale = options.scale ?? 1.0;\n this._offset = options.offset ?? 0.0;\n this._invert = options.invert ?? false;\n this._emitOnChange = options.emitOnChange ?? true;\n this._deadzone = options.deadzone ?? 0.0;\n\n // For digital actions, these control the output value\n // For analog actions, these are not used\n this._onValue = options.onValue ?? true;\n this._offValue = options.offValue ?? false;\n\n // Store min/max values explicitly\n const defaultMin = this.action.type === 'analog'\n ? this.action.minValue ?? Number.NEGATIVE_INFINITY\n : Number.NEGATIVE_INFINITY;\n this._min = options.min ?? defaultMin;\n\n const defaultMax = this.action.type === 'analog'\n ? this.action.maxValue ?? Number.POSITIVE_INFINITY\n : Number.POSITIVE_INFINITY;\n this._max = options.max ?? defaultMax;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Process input state and emit an action value\n *\n * @param state - Current input state\n * @param eventBus - Event bus to emit action events to\n * @returns True if a value was emitted, false otherwise\n */\n public process(state : InputState, eventBus : GameEventBus) : void\n {\n const rawValue = this.reader.getValue(state);\n if(rawValue === undefined) { return; }\n\n const numericValue = typeof rawValue === 'boolean' ? (rawValue ? 1.0 : 0.0) : rawValue;\n if(numericValue === undefined) { return; }\n\n let value = this._deadzone > 0 && Math.abs(numericValue) < this._deadzone ? 0 : numericValue;\n value = ((this._invert ? -value : value) * this._scale) + this._offset;\n\n // Apply min/max clamping\n value = Math.max(this._min, Math.min(this._max, value));\n\n if(this._emitOnChange && this._lastValue === value) { return; }\n this._lastValue = value;\n\n eventBus.publish({\n type: `action:${ this.action.name }`,\n payload: {\n value,\n deviceId: this.deviceID,\n context: this.context,\n },\n });\n }\n\n /**\n * Returns a JSON-serializable representation of this value binding\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : ValueBindingDefinition\n {\n return {\n type: this.type,\n action: this.action.name,\n input: {\n deviceID: this.deviceID,\n ...this.reader.toJSON(),\n },\n context: this.context,\n options: this.options,\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Keyboard Value Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type {\n DeviceValueReader,\n InputState,\n KeyboardInputState,\n KeyboardValueReaderDefinition,\n} from '../../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Types of keyboard input sources\n */\nexport type KeyboardSourceType = 'key';\n\n/**\n * Options for configuring a keyboard value reader\n */\nexport interface KeyboardReaderOptions\n{\n /**\n * Whether to use delta state instead of current state\n * When true, the source will only return true on the initial key press\n */\n useDelta ?: boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Reads values from keyboard input states\n */\nexport class KeyboardValueReader implements DeviceValueReader\n{\n /**\n * The type of keyboard input (always 'key')\n */\n public readonly sourceType : KeyboardSourceType = 'key';\n\n /**\n * The key code to monitor (e.g., \"KeyA\", \"Space\")\n */\n public readonly sourceKey : string;\n\n /**\n * Whether to use delta state instead of current state\n */\n private readonly useDelta : boolean;\n\n /**\n * Creates a new KeyboardValueReader\n *\n * @param keyCode - The key code to monitor (e.g., \"KeyA\", \"Space\")\n * @param options - Configuration options\n */\n constructor(keyCode : string, options : KeyboardReaderOptions = {})\n {\n this.sourceKey = keyCode;\n this.useDelta = options.useDelta || false;\n }\n\n /**\n * Gets the value of the key state\n *\n * @param state - The input state\n * @returns The key state value or undefined if the state type is not keyboard\n */\n public getValue(state : InputState) : boolean | undefined\n {\n // Check if the state is from the correct device type\n if(state.type !== 'keyboard')\n {\n return undefined;\n }\n\n const keyboardState = state as KeyboardInputState;\n\n // Get value from either the current state or delta state based on configuration\n if(this.useDelta)\n {\n return keyboardState.delta[this.sourceKey];\n }\n else\n {\n return keyboardState.keys[this.sourceKey];\n }\n }\n\n /**\n * Creates a KeyboardValueReader from a string representation\n *\n * @param sourceKey - The key code (e.g., \"KeyA\", \"Space\")\n * @param options - Optional configuration\n * @returns A new KeyboardValueReader instance\n */\n public static fromString(sourceKey : string, options : KeyboardReaderOptions = {}) : KeyboardValueReader\n {\n return new KeyboardValueReader(sourceKey, options);\n }\n\n /**\n * Returns a JSON-serializable representation of this keyboard value reader\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : KeyboardValueReaderDefinition\n {\n return {\n\n type: 'keyboard',\n sourceType: this.sourceType,\n sourceKey: this.sourceKey,\n options: {\n useDelta: this.useDelta,\n },\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Mouse Value Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { DeviceValueReader, InputState, MouseValueReaderDefinition } from '../../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Types of mouse input sources\n */\nexport type MouseSourceType = 'button' | 'position' | 'wheel';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Reads values from mouse input states\n */\nexport class MouseValueReader implements DeviceValueReader\n{\n /**\n * The type of mouse input\n */\n readonly sourceType : MouseSourceType;\n\n /**\n * The specific key for this input\n */\n readonly sourceKey : string;\n\n /**\n * Creates a new MouseValueReader\n *\n * @param sourceType - Type of mouse input to monitor (button, position, wheel)\n * @param sourceKey - The specific key for this input type\n */\n constructor(sourceType : MouseSourceType, sourceKey : string)\n {\n this.sourceType = sourceType;\n this.sourceKey = sourceKey;\n }\n\n /**\n * Gets the value of the mouse input source\n *\n * @param state - The current input state\n * @returns The value of the input source\n */\n public getValue(state : InputState) : boolean | number | undefined\n {\n // Check if the state is from the correct device type\n if(state.type !== 'mouse')\n {\n return undefined;\n }\n\n switch (this.sourceType)\n {\n case 'button':\n {\n const buttonState = state.buttons[this.sourceKey];\n return buttonState ? buttonState.pressed : undefined;\n }\n\n case 'position':\n {\n // Position keys are in format \"positionType:axis\" (e.g., \"absolute:x\")\n const [ posType, axis ] = this.sourceKey.split(':');\n\n if(!posType || !axis || (posType !== 'absolute' && posType !== 'relative')\n || (axis !== 'x' && axis !== 'y'))\n {\n return undefined;\n }\n\n return state.position[posType][axis];\n }\n\n case 'wheel':\n {\n const isValidWheelKey = this.sourceKey === 'deltaX' || this.sourceKey === 'deltaY'\n || this.sourceKey === 'deltaZ';\n if(state.wheel && isValidWheelKey)\n {\n return state.wheel[this.sourceKey];\n }\n return undefined;\n }\n\n default:\n return undefined;\n }\n }\n\n /**\n * Creates a MouseValueReader from a string representation\n *\n * @param sourceTypeString - String in format \"sourceType:sourceKey\" (e.g., \"button:0\", \"position:absolute:x\")\n * @returns A new MouseValueReader instance\n */\n public static fromString(sourceTypeString : string) : MouseValueReader\n {\n const [ sourceType, ...keyParts ] = sourceTypeString.split(':');\n const sourceKey = keyParts.join(':');\n\n if(!sourceType || !sourceKey)\n {\n throw new Error(`Invalid mouse source format: ${ sourceTypeString }`);\n }\n\n return new MouseValueReader(\n sourceType as MouseSourceType,\n sourceKey\n );\n }\n\n /**\n * Returns a JSON-serializable representation of this mouse value reader\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : MouseValueReaderDefinition\n {\n // Always return consistent 'mouse' type with sourceType and sourceKey\n return {\n type: 'mouse',\n sourceType: this.sourceType,\n sourceKey: this.sourceKey,\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Gamepad Value Reader\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { DeviceValueReader, GamepadValueReaderDefinition, InputState } from '../../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Types of gamepad input sources\n */\nexport type GamepadSourceType = 'button' | 'axis';\n\n/**\n * Options for configuring a gamepad value reader\n */\nexport interface GamepadReaderOptions\n{\n /**\n * Whether to use the analog value instead of boolean pressed state for buttons\n */\n useAnalogValue ?: boolean;\n\n /**\n * Deadzone value (0.0 - 1.0) for analog axes\n * Input values below this threshold will be treated as 0\n */\n deadzone ?: number;\n\n /**\n * Whether to invert the axis values\n */\n invert ?: boolean;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Reads values from gamepad input states\n */\nexport class GamepadValueReader implements DeviceValueReader\n{\n /**\n * The type of gamepad input\n */\n readonly sourceType : GamepadSourceType;\n\n /**\n * The specific key for this input\n */\n readonly sourceKey : string;\n\n /**\n * Whether to use analog value for buttons\n */\n private readonly useAnalogValue : boolean;\n\n /**\n * Deadzone value for axes\n */\n private readonly deadzone : number;\n\n /**\n * Whether to invert axis values\n */\n private readonly invert : boolean;\n\n /**\n * Creates a new GamepadValueReader\n *\n * @param sourceType - Type of gamepad input to monitor (button or axis)\n * @param sourceKey - The specific key for this input type\n * @param options - Configuration options\n */\n constructor(sourceType : GamepadSourceType, sourceKey : string, options : GamepadReaderOptions = {})\n {\n this.sourceType = sourceType;\n this.sourceKey = sourceKey;\n this.useAnalogValue = options.useAnalogValue || false;\n this.deadzone = options.deadzone || 0.1; // Default 10% deadzone\n this.invert = options.invert || false;\n }\n\n /**\n * Gets the value of the input source based on the current state\n *\n * @param state - The current input state\n * @returns The value of the input source\n */\n public getValue(state : InputState) : boolean | number | undefined\n {\n // Check if the state is from the correct device type\n if(state.type !== 'gamepad')\n {\n return undefined;\n }\n\n switch (this.sourceType)\n {\n case 'button':\n {\n const buttonState = state.buttons[this.sourceKey];\n\n if(!buttonState)\n {\n return undefined;\n }\n\n return this.useAnalogValue ? buttonState.value : buttonState.pressed;\n }\n\n case 'axis':\n {\n let value = state.axes[this.sourceKey];\n\n if(value === undefined)\n {\n return undefined;\n }\n\n // Apply deadzone\n if(Math.abs(value) < this.deadzone)\n {\n value = 0;\n }\n\n // Apply inversion if needed\n return this.invert ? -value : value;\n }\n\n default:\n return undefined;\n }\n }\n\n /**\n * Creates a GamepadValueReader from a string representation\n *\n * @param sourceTypeString - String in format \"sourceType:sourceKey\" (e.g., \"button:0\", \"axis:1\")\n * @param options - Optional configuration options\n * @returns A new GamepadValueReader instance\n */\n public static fromString(sourceTypeString : string, options : GamepadReaderOptions = {}) : GamepadValueReader\n {\n const [ sourceType, sourceKey ] = sourceTypeString.split(':');\n\n if(!sourceType || !sourceKey)\n {\n throw new Error(`Invalid gamepad source format: ${ sourceTypeString }`);\n }\n\n return new GamepadValueReader(\n sourceType as GamepadSourceType,\n sourceKey,\n options\n );\n }\n\n /**\n * Returns a JSON-serializable representation of this gamepad value reader\n *\n * @returns A simple object representation that can be converted to JSON\n */\n public toJSON() : GamepadValueReaderDefinition\n {\n return {\n type: 'gamepad',\n sourceType: this.sourceType,\n sourceKey: this.sourceKey,\n options: {\n useAnalogValue: this.useAnalogValue,\n deadzone: this.deadzone,\n invert: this.invert,\n },\n };\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Binding Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { GameEventBus } from '../classes/eventBus.ts';\n\n// Interfaces\nimport { type Binding, type BindingDefinition, type Context, bindingTypes } from '../interfaces/binding.ts';\nimport type {\n DeviceValueReader,\n DeviceValueReaderDefinition,\n InputDevice,\n InputState,\n} from '../interfaces/input.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type { Action } from '../interfaces/action.ts';\n\n// Bindings\nimport { TriggerBinding } from '../classes/bindings/trigger.ts';\nimport { ToggleBinding } from '../classes/bindings/toggle.ts';\nimport { ValueBinding } from '../classes/bindings/value.ts';\n\n// Value Readers\nimport { KeyboardValueReader } from '../classes/input/readers/keyboard.ts';\nimport { MouseValueReader } from '../classes/input/readers/mouse.ts';\nimport { GamepadValueReader } from '../classes/input/readers/gamepad.ts';\n\n// Utils\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Configuration object structure for binding manager\n */\nexport interface BindingConfiguration\n{\n /** All registered actions */\n actions : Action[];\n\n /** All registered bindings */\n bindings : BindingDefinition[];\n\n /** All registered contexts */\n contexts : {\n name : string;\n exclusive : boolean;\n active : boolean;\n }[];\n}\n\n/* eslint-disable no-continue */\n\n/**\n * Manages input bindings and actions for the game\n */\nexport class BindingManager\n{\n /** Map of device IDs to their bindings */\n private _bindings = new Map<string, Binding[]>();\n\n /** Map of action names to their action definitions */\n private _actions = new Map<string, Action>();\n\n /** Map of all registered contexts by name for O(1) lookup */\n private _contexts = new Map<string, Context>();\n\n /**\n * Set of all active contexts (both exclusive and non-exclusive)\n */\n private _activeContexts = new Set<string>();\n\n /** Event bus for handling game events */\n private _eventBus : GameEventBus;\n\n /** Logger instance */\n private _log : LoggerInterface;\n\n /**\n * Creates an instance of BindingManager.\n *\n * @param eventBus - The game event bus to publish events to\n * @param logger - The logging utility to use\n */\n constructor(eventBus : GameEventBus, logger ?: LoggingUtility)\n {\n this._eventBus = eventBus;\n\n // Bind to input events\n this._eventBus.subscribe<{ device : InputDevice, state : InputState }>('input:changed', (event) =>\n {\n if(event.payload)\n {\n this.$handleInput(event.payload.device, event.payload.state);\n }\n });\n\n this._log = logger?.getLogger('BindingManager') || new SAGELogger('BindingManager');\n this._log.debug('BindingManager initialized');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Checks if a binding's context is currently active\n *\n * @param binding - The binding to check\n * @returns True if binding's context is active or has no context\n */\n private _isBindingContextActive(binding : Binding) : boolean\n {\n // If no context is specified, binding is always active regardless of active contexts\n if(!binding.context)\n {\n return true;\n }\n\n // Otherwise check if the binding's specific context is active\n return this._activeContexts.has(binding.context);\n }\n\n /**\n * Get a context by name, creating it if it doesn't exist\n *\n * @param contextName - The name of the context to get or create\n * @param exclusive - Whether the context is exclusive (only used if creating)\n * @returns The context object\n */\n private _getOrCreateContext(contextName : string, exclusive = true) : Context\n {\n let context = this._contexts.get(contextName);\n\n if(!context)\n {\n context = {\n name: contextName,\n exclusive,\n };\n\n this._contexts.set(contextName, context);\n this._log.debug(`Auto-created context \"${ contextName }\" (exclusive: ${ exclusive })`);\n }\n\n return context;\n }\n\n /**\n * Deactivate all exclusive contexts except the specified one\n *\n * @param exceptContextName - Name of context to not deactivate\n * @returns Array of deactivated context names\n */\n private _deactivateExclusiveContexts(exceptContextName ?: string) : string[]\n {\n const deactivatedContexts : string[] = [];\n\n // Find all active exclusive contexts\n for(const contextName of this._activeContexts)\n {\n // Skip the context we want to keep\n if(contextName === exceptContextName)\n {\n continue;\n }\n\n const context = this._contexts.get(contextName);\n\n // If this is an exclusive context, deactivate it\n if(context?.exclusive)\n {\n this._activeContexts.delete(contextName);\n deactivatedContexts.push(contextName);\n }\n }\n\n return deactivatedContexts;\n }\n\n /**\n * Create a binding from a binding definition\n *\n * @param definition - The binding definition to create a binding from\n * @returns A new binding instance or null if the type is not supported\n */\n private _createBindingFromDefinition(definition : BindingDefinition) : Binding | null\n {\n // Get the full action object from the action name\n const action = this._actions.get(definition.action);\n\n // If the action doesn't exist, log a warning and return null\n if(!action)\n {\n this._log.warn(`Cannot create binding: Action \"${ definition.action }\" not found.`);\n return null;\n }\n\n // Extract deviceID and create reader from the input definition\n const { deviceID, ...readerDef } = definition.input;\n\n switch (definition.type)\n {\n case 'trigger': {\n // Create the binding with the device value reader based on definition's source\n return new TriggerBinding(\n action,\n deviceID,\n this._createInputSourceFromDefinition(readerDef),\n {\n ...(definition.options || {}),\n context: definition.context,\n }\n );\n }\n\n case 'toggle': {\n // Create the binding with the device value reader based on definition's source\n return new ToggleBinding(\n action,\n deviceID,\n this._createInputSourceFromDefinition(readerDef),\n {\n ...(definition.options || {}),\n context: definition.context,\n }\n );\n }\n\n case 'value': {\n // Create the binding with the device value reader based on definition's source\n return new ValueBinding(\n action,\n deviceID,\n this._createInputSourceFromDefinition(readerDef),\n {\n ...(definition.options || {}),\n context: definition.context,\n }\n );\n }\n\n default:\n this._log.error(`Binding type not implemented: ${ (definition as any).type }`);\n return null;\n }\n }\n\n /**\n * Create a device value reader from a definition object\n *\n * @param definition - The device value reader definition\n * @returns A new device value reader instance\n * @throws Error if the reader type is not supported\n */\n private _createInputSourceFromDefinition(definition : DeviceValueReaderDefinition) : DeviceValueReader\n {\n // Create reader based on device type\n switch (definition.type)\n {\n case 'keyboard':\n return new KeyboardValueReader(\n definition.sourceKey,\n definition.options\n );\n\n case 'mouse':\n {\n // Check if sourceType is valid\n const sourceType = definition.sourceType;\n if(!(sourceType === 'button' || sourceType === 'position' || sourceType === 'wheel'))\n {\n throw new Error(`Invalid mouse source type: ${ sourceType }`);\n }\n\n return new MouseValueReader(\n sourceType,\n definition.sourceKey\n );\n }\n\n case 'gamepad':\n {\n // Check if sourceType is valid\n const sourceType = definition.sourceType;\n if(!(sourceType === 'button' || sourceType === 'axis'))\n {\n throw new Error(`Invalid gamepad source type: ${ sourceType }`);\n }\n return new GamepadValueReader(\n sourceType,\n definition.sourceKey,\n definition.options\n );\n }\n\n default:\n throw new Error(`Unsupported input source type: ${ (definition as any).type }`);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Handle input from a device and process all relevant bindings\n *\n * @param device - The input device\n * @param state - Current input state\n */\n public $handleInput(device : InputDevice, state : InputState) : void\n {\n const bindings = this._bindings.get(device.id);\n if(!bindings || bindings.length === 0)\n {\n return;\n }\n\n if(this._activeContexts.size === 0 && bindings.some((binding) => binding.context))\n {\n return;\n }\n\n for(const binding of bindings)\n {\n if(!this._isBindingContextActive(binding))\n {\n continue;\n }\n\n binding.process(state, this._eventBus);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Action Management API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Registers an action\n *\n * @param action - The action to register\n * @throws Error if action is already registered\n */\n public registerAction(action : Action) : void\n {\n this._log.debug(`Registering action \"${ action.name }\"`);\n\n if(this._actions.has(action.name))\n {\n const errorMsg = `Action \"${ action.name }\" already registered.`;\n this._log.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n this._actions.set(action.name, action);\n this._log.debug(`Action \"${ action.name }\" registered successfully`);\n }\n\n /**\n * Gets an action by name\n *\n * @param actionName - The name of the action to get\n * @returns The action or null if not found\n */\n public getAction(actionName : string) : Action | null\n {\n this._log.trace(`Getting action \"${ actionName }\"`);\n\n const action = this._actions.get(actionName) ?? null;\n if(!action)\n {\n this._log.debug(`Action \"${ actionName }\" not found`);\n }\n\n return action;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Context Management API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Registers a context with specific options\n *\n * @param contextName - The name of the context to register\n * @param exclusive - Whether the context is exclusive (default: true)\n * @returns The registered context\n */\n public registerContext(contextName : string, exclusive = true) : Context\n {\n const context = this._getOrCreateContext(contextName, exclusive);\n\n // Update context's exclusivity if it already exists\n if(context.exclusive !== exclusive)\n {\n context.exclusive = exclusive;\n this._log.info(`Updated context \"${ contextName }\" exclusivity: ${ exclusive }`);\n }\n\n return context;\n }\n\n /**\n * Activates a context, enabling all bindings associated with it.\n * If the context is exclusive, all other exclusive contexts will be deactivated.\n *\n * @param contextName - The name of the context to activate\n */\n public activateContext(contextName : string) : void\n {\n // Get or create context\n const context = this._getOrCreateContext(contextName);\n\n // Get the context's exclusivity setting\n const isExclusive = context.exclusive;\n\n this._log.debug(`Activating context \"${ contextName }\" (exclusive: ${ isExclusive })`);\n\n // If already active, nothing to do unless we're making an exclusive activation\n const isAlreadyActive = this._activeContexts.has(contextName);\n\n if(isExclusive)\n {\n // Deactivate all other exclusive contexts\n const deactivated = this._deactivateExclusiveContexts(contextName);\n\n if(deactivated.length > 0)\n {\n this._log.info(`Deactivated exclusive contexts: ${ deactivated.join(', ') }`);\n }\n }\n\n // Add to active contexts if not already there\n if(!isAlreadyActive)\n {\n this._activeContexts.add(contextName);\n this._log.info(`Context \"${ contextName }\" activated${ isExclusive ? ' as exclusive' : '' }`);\n }\n }\n\n /**\n * Deactivates a context, disabling all bindings associated with it\n *\n * @param contextName - The name of the context to deactivate\n */\n public deactivateContext(contextName : string) : void\n {\n this._log.debug(`Deactivating context \"${ contextName }\"`);\n\n if(this._activeContexts.has(contextName))\n {\n this._activeContexts.delete(contextName);\n this._log.info(`Context \"${ contextName }\" deactivated`);\n }\n else\n {\n this._log.debug(`Context \"${ contextName }\" was not active`);\n }\n }\n\n /**\n * Returns a list of all active contexts\n *\n * @returns Array of active context names\n */\n public getActiveContexts() : string[]\n {\n return [ ...this._activeContexts ];\n }\n\n /**\n * Returns whether a context is active\n *\n * @param contextName - The context to check\n * @returns True if the context is active\n */\n public isContextActive(contextName : string) : boolean\n {\n return this._activeContexts.has(contextName);\n }\n\n /**\n * Gets a context by name\n *\n * @param contextName - The context name to get\n * @returns The context or null if not found\n */\n public getContext(contextName : string) : Context | null\n {\n return this._contexts.get(contextName) || null;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Binding Management API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a binding for an input device\n *\n * @param binding - The binding to register\n * @throws Error if binding type is invalid\n */\n public $registerBinding(binding : Binding) : void\n {\n if(!bindingTypes.includes(binding.type))\n {\n throw new Error(`Invalid binding type: ${ binding.type }`);\n }\n\n // If binding has a context but context isn't registered yet, register it with default settings\n if(binding.context && !this._contexts.has(binding.context))\n {\n this.registerContext(binding.context);\n }\n\n // Initialize device bindings array if it doesn't exist\n if(!this._bindings.has(binding.deviceID))\n {\n this._bindings.set(binding.deviceID, []);\n }\n\n // Add the binding\n this._bindings.get(binding.deviceID)?.push(binding);\n this._log.debug(`Registered ${ binding.type } binding for \"${ binding.action.name }\" in context `\n + `\"${ binding.context || null }\"`);\n }\n\n /**\n * Register a binding using a binding definition object\n *\n * @param definition - The binding definition to register\n */\n public registerBinding(definition : BindingDefinition) : void\n {\n const binding = this._createBindingFromDefinition(definition);\n\n if(binding)\n {\n this.$registerBinding(binding);\n }\n else\n {\n this._log.error(`Failed to create binding for action \"${ definition.action }\" with type `\n + `\"${ definition.type }\"`);\n }\n }\n\n /**\n * Unregister all bindings for an action within a context\n *\n * @param actionName - The name of the action\n * @param context - The context to unregister from (defaults to null)\n */\n public unregisterBindings(actionName : string, context : string | null = null) : void\n {\n this._log.debug(`Unregistering all bindings for action \"${ actionName }\" in context \"${ context }\"`);\n\n let bindingsRemoved = 0;\n\n // Iterate through all devices and their bindings\n for(const [ deviceId, deviceBindings ] of this._bindings.entries())\n {\n const newBindings = deviceBindings.filter((binding) =>\n {\n const bindingContext = binding.context || null;\n const shouldKeep = binding.action.name !== actionName || bindingContext !== context;\n if(!shouldKeep)\n {\n bindingsRemoved++;\n }\n return shouldKeep;\n });\n\n // Only update the map if we actually removed something\n if(newBindings.length !== deviceBindings.length)\n {\n this._bindings.set(deviceId, newBindings);\n }\n }\n\n this._log.info(`Removed ${ bindingsRemoved } bindings for action \"${ actionName }\" in context \"${ context }\"`);\n }\n\n /**\n * Gets all bindings for a specific action\n *\n * @param actionName - The name of the action\n * @param context - The context to get bindings from (optional)\n * @returns Array of bindings that match the criteria\n */\n public getBindingsForAction(actionName : string, context ?: string | null) : Binding[]\n {\n const result : Binding[] = [];\n\n for(const deviceBindings of this._bindings.values())\n {\n for(const binding of deviceBindings)\n {\n const bindingContext = binding.context || null;\n if(binding.action.name === actionName)\n {\n // If context specified, only include bindings from that context\n if(!context || bindingContext === context)\n {\n result.push(binding);\n }\n }\n }\n }\n\n return result;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Configuration Management API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Exports the current configuration to a serializable object\n *\n * @returns A BindingConfiguration object representing the current state\n */\n public exportConfiguration() : BindingConfiguration\n {\n this._log.debug('Exporting binding configuration');\n\n // Export all registered actions\n const actions = [ ...this._actions.values() ].map((action) =>\n {\n // Ensure we include all analog action properties when exporting\n if(action.type === 'analog')\n {\n return {\n name: action.name,\n type: action.type,\n minValue: action.minValue ?? 0,\n maxValue: action.maxValue ?? 1,\n };\n }\n return action;\n });\n\n // Export all registered bindings using their toJSON methods\n const bindings : BindingDefinition[] = [];\n\n // Iterate through all device bindings\n for(const deviceBindings of this._bindings.values())\n {\n for(const binding of deviceBindings)\n {\n // Let the binding serialize itself to the BindingDefinition format\n bindings.push(binding.toJSON());\n }\n }\n\n // Export all contexts with their active state\n const contexts = [ ...this._contexts.values() ].map((context) => ({\n name: context.name,\n exclusive: context.exclusive,\n active: this._activeContexts.has(context.name),\n }));\n\n this._log.info(`Configuration exported: ${ actions.length } actions, ${ bindings.length } bindings, `\n + `${ contexts.length } contexts`);\n\n return {\n actions,\n bindings,\n contexts,\n };\n }\n\n /**\n * Imports a configuration from an object\n *\n * @param config - The configuration to import\n */\n public importConfiguration(config : BindingConfiguration) : void\n {\n this._log.debug('Importing binding configuration');\n\n // Clear existing configuration\n this._bindings.clear();\n this._actions.clear();\n this._contexts.clear();\n this._activeContexts.clear();\n\n // Import actions\n for(const action of config.actions)\n {\n this.registerAction(action);\n }\n\n // Import contexts first so they exist before bindings reference them\n for(const contextData of config.contexts)\n {\n this.registerContext(contextData.name, contextData.exclusive);\n\n // Activate contexts that were active\n if(contextData.active)\n {\n this.activateContext(contextData.name);\n }\n }\n\n // Import bindings\n for(const bindingDef of config.bindings)\n {\n try\n {\n this.registerBinding(bindingDef);\n }\n catch (err : unknown)\n {\n if(err instanceof Error)\n {\n this._log.error(`Failed to import binding for action \"${ bindingDef.action }\": ${ err.message }`);\n }\n }\n }\n\n this._log.info(`Configuration imported: ${ config.actions.length } actions, `\n + `${ config.bindings.length } bindings, ${ config.contexts.length } contexts`);\n }\n\n /**\n * Tears down the binding manager and cleans up any resources\n */\n async $teardown() : Promise<void>\n {\n this._log.info('Tearing down BindingManager');\n\n // Clear all bindings\n this._bindings.clear();\n\n // Clear all actions\n this._actions.clear();\n\n // Clear all contexts\n this._contexts.clear();\n this._activeContexts.clear();\n\n this._log.info('BindingManager torn down successfully');\n\n return Promise.resolve();\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 { LoggerInterface } from '../interfaces/logger.ts';\n\n// Managers\nimport { GameEntityManager } from './entity.ts';\nimport { LevelManager } from './level.ts';\nimport { UserInputManager } from './input.ts';\n\n// Utils\nimport { isBrowser } from '../utils/capabilities.ts';\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class GameManager\n{\n private _engine : AbstractEngine;\n private _entityManager : GameEntityManager;\n private _inputManager : UserInputManager;\n private _levelManager : LevelManager;\n private _log : LoggerInterface;\n private _boundResizeHandler : () => void;\n\n public started = false;\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(\n engine : AbstractEngine,\n entityManager : GameEntityManager,\n inputManager : UserInputManager,\n levelManager : LevelManager,\n logger ?: LoggingUtility\n )\n {\n this._engine = engine;\n this._entityManager = entityManager;\n this._inputManager = inputManager;\n this._levelManager = levelManager;\n this._log = logger?.getLogger('GameManager') || new SAGELogger('GameManager');\n\n // Store the bound function reference for later cleanup\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 private _renderLoop() : void\n {\n // Update entities and entity manager\n const deltaTime = this._engine.getDeltaTime();\n this._entityManager.$frameUpdate(deltaTime);\n\n // Update input devices\n if(this._inputManager)\n {\n this._inputManager.pollGamepads();\n }\n\n // Render the current scene\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._renderLoop.bind(this));\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 * 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 this._log.info('GameManager torn down successfully');\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Game Entity\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { GameEntityBehaviorConstructor } from '../interfaces/entity.ts';\nimport { type GameEvent, GameEventBus } from './eventBus.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Interface representing a subscription to a game event.\n */\ninterface GameEventSubscription\n{\n count : number;\n unsubscribe : () => void;\n}\n\n/**\n * Interface representing the context of a game entity.\n * @template EntityState - The type of the state object.\n */\ninterface SimpleGameEntity<EntityState extends object = object>\n{\n id : string;\n type : string;\n state : EntityState;\n eventBus : GameEventBus;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Base class for game entity behaviors.\n */\nexport abstract class GameEntityBehavior<RequiredState extends object = object>\n{\n abstract name : string;\n abstract eventSubscriptions : string[];\n\n protected entity : SimpleGameEntity | null = null;\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal API\n //------------------------------------------------------------------------------------------------------------------\n\n $emit(event : GameEvent) : void\n {\n if(!this.entity)\n {\n throw new Error('Entity is not set for this behavior.');\n }\n\n event.senderID = this.entity.id;\n this.entity.eventBus.publish(event);\n }\n\n /**\n * Sets the entity for this behavior.\n * @param entity - The entity to set.\n */\n $setEntity(entity : SimpleGameEntity | null) : void\n {\n this.entity = entity;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Implementation API\n //------------------------------------------------------------------------------------------------------------------\n\n abstract processEvent(event : GameEvent, state : RequiredState) : Promise<boolean> | boolean;\n\n update?(dt : number, state : RequiredState) : void;\n\n destroy?() : Promise<void>;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Base class for game entities.\n * @template EntityState - The type of the state object.\n */\nexport class GameEntity<EntityState extends object = object> implements SimpleGameEntity<EntityState>\n{\n /** The unique identifier of the entity. */\n public readonly id : string;\n\n /** The type of the entity. */\n public readonly type : string;\n\n /** The state of the entity. */\n public state : EntityState;\n\n /** A map of behaviors attached to the entity. */\n public behaviors : Map<string, GameEntityBehavior> = new Map<string, GameEntityBehavior>();\n\n /** The event bus for the entity. */\n public eventBus : GameEventBus;\n\n /** The event subscriptions for the entity. */\n private subscriptions : Map<string, GameEventSubscription> = new Map<string, GameEventSubscription>();\n\n /**\n * Creates an instance of GameEntityBase.\n * @param type - The type of the entity.\n * @param initialState - The initial state of the entity.\n * @param behaviors - An array of behaviors to attach to the entity.\n * @param eventBus\n */\n constructor(\n type : string,\n eventBus : GameEventBus,\n initialState : EntityState,\n behaviors : GameEntityBehaviorConstructor[]\n )\n {\n this.id = crypto.randomUUID();\n this.type = type;\n this.state = initialState;\n this.eventBus = eventBus;\n\n // Attach behaviors\n for(const behaviorCtor of behaviors)\n {\n this.attachBehavior(new behaviorCtor());\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Processes a game event by passing it to the entity's behaviors.\n * @param event - The game event to process.\n * @returns A promise that resolves when the event has been processed.\n */\n async $processEvent(event : GameEvent) : Promise<void>\n {\n for(const behavior of this.behaviors.values())\n {\n // eslint-disable-next-line no-await-in-loop\n const result = await behavior.processEvent(event, this.state);\n if(result)\n {\n // If the behavior returns true, stop other behaviors from processing the event.\n break;\n }\n }\n }\n\n /**\n * Updates the entity by calling the update method of its behaviors.\n * @param dt - The delta time since the last update.\n */\n $update(dt : number) : void\n {\n for(const behavior of this.behaviors.values())\n {\n behavior.update?.(dt, this.state);\n }\n }\n\n /**\n * Destroys the entity by calling the destroy method of its behaviors.\n * @returns A promise that resolves when the entity has been destroyed.\n */\n async $destroy() : Promise<void>\n {\n for(const behavior of this.behaviors.values())\n {\n behavior.destroy?.();\n }\n\n // Unsubscribe from all event subscriptions\n for(const subscription of this.subscriptions.values())\n {\n subscription.unsubscribe();\n }\n\n this.behaviors.clear();\n this.subscriptions.clear();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Attaches a behavior to the entity.\n * @param behavior - The behavior to attach.\n * @throws Will throw an error if the behavior is already attached.\n */\n attachBehavior(behavior : GameEntityBehavior) : void\n {\n if(this.behaviors.has(behavior.name))\n {\n throw new Error(`Behavior ${ behavior.name } is already attached to this entity.`);\n }\n\n // Subscribe to the behavior's event subscriptions\n for(const event of behavior.eventSubscriptions)\n {\n // Check if the event is already subscribed\n const existingSubscription = this.subscriptions.get(event);\n if(existingSubscription)\n {\n existingSubscription.count++;\n }\n else\n {\n // Create a new subscription\n const unsubscribe = this.eventBus.subscribe(event, this.$processEvent.bind(this));\n this.subscriptions.set(event, { count: 1, unsubscribe });\n }\n }\n\n // Attach the behavior\n this.behaviors.set(behavior.name, behavior);\n behavior.$setEntity(this);\n }\n\n /**\n * Detaches a behavior from the entity.\n * @param behaviorName - The behavior to detach.\n */\n detachBehavior(behaviorName : string) : void\n {\n const behavior = this.behaviors.get(behaviorName);\n if(behavior)\n {\n // Remove the behavior's event subscriptions\n for(const event of behavior.eventSubscriptions)\n {\n const subscription = this.subscriptions.get(event);\n if(subscription)\n {\n subscription.count--;\n if(subscription.count <= 0)\n {\n subscription.unsubscribe();\n this.subscriptions.delete(event);\n }\n }\n }\n\n // Remove the behavior\n this.behaviors.delete(behavior.name);\n behavior.$setEntity(null);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n","//----------------------------------------------------------------------------------------------------------------------\n// Entity Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { GameEntity } from '../classes/entity.ts';\nimport { GameEventBus } from '../classes/eventBus.ts';\n\n// Interfaces\nimport type { Action } from '../interfaces/action.ts';\nimport type { GameEntityDefinition } from '../interfaces/entity.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport type { BindingManager } from '../managers/binding.ts';\n\n// Utils\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport class GameEntityManager\n{\n /** The event bus for the entity manager. */\n private eventBus : GameEventBus;\n\n /** A map of entities managed by the entity manager. */\n private entities : Map<string, GameEntity> = new Map<string, GameEntity>();\n\n /** A map of entity definitions registered with the entity manager. */\n private entityDefinitions : Map<string, GameEntityDefinition> = new Map<string, GameEntityDefinition>();\n\n /** Reference to the binding manager for registering actions */\n private bindingManager : BindingManager;\n\n /** Logger instance */\n private _log : LoggerInterface;\n\n /**\n * Creates an instance of EntityManager.\n * @param eventBus - The event bus for the entity manager.\n * @param logger - The logging utility to use\n * @param bindingManager - The binding manager for registering actions\n */\n constructor(eventBus : GameEventBus, logger : LoggingUtility | undefined, bindingManager : BindingManager)\n {\n this.eventBus = eventBus;\n this.bindingManager = bindingManager;\n this._log = logger?.getLogger('EntityManager') || new SAGELogger('EntityManager');\n this._log.info('EntityManager initialized');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Checks if two actions are compatible\n * @param existingAction - The action already registered\n * @param newAction - The action being registered\n * @returns true if the actions are compatible, false if they have conflicting options\n */\n private _areActionsCompatible(existingAction : Action, newAction : Action) : boolean\n {\n // If types don't match, they're incompatible\n if(existingAction.type !== newAction.type)\n {\n return false;\n }\n\n // For analog actions, check min and max values\n if(existingAction.type === 'analog' && newAction.type === 'analog')\n {\n // If minValue or maxValue is specified and differs, they're incompatible\n if((newAction.minValue !== undefined && existingAction.minValue !== newAction.minValue)\n || (newAction.maxValue !== undefined && existingAction.maxValue !== newAction.maxValue))\n {\n return false;\n }\n }\n\n // Digital actions are always compatible if they have the same name\n return true;\n }\n\n /**\n * Registers actions defined in the entity definition with the binding manager\n * @param entityDef - The entity definition containing actions to register\n */\n private _registerEntityActions(entityDef : GameEntityDefinition) : void\n {\n if(!entityDef.actions)\n {\n return;\n }\n\n for(const action of entityDef.actions)\n {\n try\n {\n // Check if the action is already registered\n const existingAction = this.bindingManager.getAction(action.name);\n\n if(!existingAction)\n {\n // Action doesn't exist yet, register it\n this._log.debug(`Registering action \"${ action.name }\" from entity type \"${ entityDef.type }\"`);\n this.bindingManager.registerAction(action);\n }\n else if(!this._areActionsCompatible(existingAction, action))\n {\n this._log.warn(\n `Action \"${ action.name }\" already registered with different options. `\n + `Entity \"${ entityDef.type }\" requires: ${ JSON.stringify(action) }, `\n + `but found: ${ JSON.stringify(existingAction) }`\n );\n }\n else\n {\n this._log.trace(\n `Action \"${ action.name }\" already registered with compatible options, skipping registration`\n );\n }\n }\n catch (err)\n {\n this._log.debug(`Failed to register action \"${ action.name }\": `\n + `${ err instanceof Error ? err.message : String(err) }`);\n }\n }\n }\n\n $frameUpdate(dt : number) : void\n {\n this._log.trace(`Updating ${ this.entities.size } entities with dt=${ dt }`);\n for(const entity of this.entities.values())\n {\n entity.$update(dt);\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Registers a new entity definition.\n * @param entityDef - The definition of the entity.\n */\n registerEntityDefinition(entityDef : GameEntityDefinition) : void\n {\n this._log.debug(`Registering entity definition: ${ entityDef.type }`);\n\n // Register any actions this entity requires\n this._registerEntityActions(entityDef);\n\n // Store the entity definition\n this.entityDefinitions.set(entityDef.type, entityDef);\n }\n\n /**\n * Creates a new entity of the given type.\n * @param type - The type of the entity to create.\n * @param initialState - The initial state of the entity.\n * @returns The created entity.\n */\n async createEntity<\n State extends object = object,\n >\n (\n type : string,\n initialState : Partial<State> = {}\n ) : Promise<GameEntity<State>>\n {\n this._log.debug(`Creating entity of type: ${ type }`);\n const entityDef = this.entityDefinitions.get(type);\n if(!entityDef)\n {\n const errorMsg = `Entity type ${ type } is not registered.`;\n this._log.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n this._log.trace(`Using entity definition with ${ entityDef.behaviors?.length || 0 } behaviors`);\n let mergedState = { ...entityDef.defaultState, ...initialState } as State;\n\n // Call onBeforeCreate hook if it exists\n if(entityDef.onBeforeCreate)\n {\n this._log.trace(`Calling onBeforeCreate hook for entity type: ${ type }`);\n const result = await entityDef.onBeforeCreate(mergedState);\n // If the hook returns a new state, use it\n if(result !== undefined)\n {\n mergedState = result as State;\n }\n }\n\n // Create the entity instance with possibly modified state\n const entity = new GameEntity<State>(\n entityDef.type,\n this.eventBus,\n mergedState,\n entityDef.behaviors\n );\n\n // Add entity to the manager\n this.entities.set(entity.id, entity);\n this._log.debug(`Entity created with ID: ${ entity.id }`);\n\n // Call onCreate hook if it exists\n if(entityDef.onCreate)\n {\n this._log.trace(`Calling onCreate hook for entity type: ${ type }`);\n const result = await entityDef.onCreate(entity.state);\n // If the hook returns a new state, update the entity's state\n if(result !== undefined)\n {\n entity.state = result as State;\n }\n }\n\n return entity;\n }\n\n /**\n * Destroys the entity with the given ID.\n * @param entityID - The ID of the entity to destroy.\n */\n async destroyEntity(entityID : string) : Promise<void>\n {\n this._log.debug(`Destroying entity: ${ entityID }`);\n const entity = this.entities.get(entityID);\n if(entity)\n {\n // Get entity definition\n const entityDef = this.entityDefinitions.get(entity.type);\n\n // Call onBeforeDestroy hook if it exists\n if(entityDef?.onBeforeDestroy)\n {\n this._log.trace(`Calling onBeforeDestroy hook for entity: ${ entityID }`);\n const result = await entityDef.onBeforeDestroy(entity.state);\n // If the hook returns a new state, update the entity's state\n if(result !== undefined)\n {\n entity.state = result;\n }\n }\n\n // Destroy entity (call $destroy internally)\n await entity.$destroy();\n\n // Call onDestroy hook if it exists\n if(entityDef?.onDestroy)\n {\n this._log.trace(`Calling onDestroy hook for entity: ${ entityID }`);\n await entityDef.onDestroy(entity.state);\n }\n\n // Remove entity from manager\n this.entities.delete(entityID);\n\n this._log.debug(`Entity ${ entityID } destroyed`);\n }\n else\n {\n this._log.warn(`Attempted to destroy non-existent entity: ${ entityID }`);\n }\n }\n\n /**\n * Gets the entity with the given ID.\n * @param entityID - The ID of the entity to get.\n * @returns The entity with the given ID, or null if it does not exist.\n */\n getEntity(entityID : string) : GameEntity | null\n {\n this._log.trace(`Getting entity: ${ entityID }`);\n return this.entities.get(entityID) ?? null;\n }\n\n /**\n * Adds an existing entity to the entity manager.\n * @param entity - The entity to add.\n */\n addEntity(entity : GameEntity) : void\n {\n this._log.debug(`Adding existing entity: ${ entity.id } (type: ${ entity.type })`);\n this.entities.set(entity.id, entity);\n }\n\n /**\n * Removes the entity with the given ID, without destroying it.\n * @param entityID - The ID of the entity to remove.\n */\n removeEntity(entityID : string) : void\n {\n this._log.debug(`Removing entity: ${ entityID }`);\n const entity = this.entities.get(entityID);\n if(entity)\n {\n this.entities.delete(entityID);\n this._log.debug(`Entity ${ entityID } removed`);\n }\n else\n {\n this._log.warn(`Attempted to remove non-existent entity: ${ entityID }`);\n }\n }\n\n /**\n * Tears down all entities and releases resources\n * @returns A promise that resolves when all entities have been destroyed\n */\n async $teardown() : Promise<void>\n {\n this._log.info(`Tearing down EntityManager with ${ this.entities.size } entities`);\n\n // Create a copy of the entity IDs to avoid modification during iteration\n const entityIds = [ ...this.entities.keys() ];\n\n // Destroy all entities\n for(const entityId of entityIds)\n {\n try\n {\n // eslint-disable-next-line no-await-in-loop\n await this.destroyEntity(entityId);\n }\n catch (err)\n {\n this._log.error(`Error destroying entity ${ entityId }: ${ err }`);\n }\n }\n\n // Clear entity definitions\n this.entityDefinitions.clear();\n\n this._log.info('EntityManager torn down successfully');\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n","//----------------------------------------------------------------------------------------------------------------------\n// Level Manager\n//----------------------------------------------------------------------------------------------------------------------\n\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\nimport { Level, type LevelProgressEvent } from '../classes/level.ts';\nimport { type GameEvent, GameEventBus } from '../classes/eventBus.ts';\n\n// Engines\nimport { SceneEngine } from '../engines/scene.ts';\n\n// Utils\nimport { type LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Manages game levels, tracking loaded levels and the current active scene.\n * Provides centralized access to level functionality and scene management.\n */\nexport class LevelManager\n{\n private _eventBus : GameEventBus;\n private _sceneEngine : SceneEngine;\n private _log : LoggerInterface;\n\n /** Map of registered levels by name */\n private _registeredLevels = new Map<string, Level>();\n\n /** The currently active level */\n private _currentLevel : Level | null = null;\n\n constructor(\n eventBus : GameEventBus,\n sceneEngine : SceneEngine,\n logger ?: LoggingUtility\n )\n {\n this._eventBus = eventBus;\n this._sceneEngine = sceneEngine;\n this._log = logger?.getLogger('LevelManager') || new SAGELogger('LevelManager');\n\n // Subscribe to level events\n this._eventBus.subscribe<LevelProgressEvent>('level:*', (event : GameEvent<LevelProgressEvent>) =>\n {\n if(event.payload)\n {\n this._handleLevelEvent(event.payload);\n }\n });\n\n this._log.info('LevelManager initialized');\n }\n\n /**\n * Handles level events from the event bus\n */\n private _handleLevelEvent(event : LevelProgressEvent) : void\n {\n switch (event.type)\n {\n case 'level:progress':\n this._log.debug(`Level ${ event.levelName } progress: ${ event.progress }%`);\n break;\n case 'level:complete':\n this._log.info(`Level ${ event.levelName } loaded successfully`);\n break;\n case 'level:error':\n this._log.error(`Level ${ event.levelName } error: ${ event.message }`);\n break;\n }\n }\n\n /**\n * Registers a level class that can be instantiated later\n * @param name - The name to register the level under\n * @param level - The level class instance\n */\n public registerLevel(name : string, level : Level) : void\n {\n if(this._registeredLevels.has(name))\n {\n this._log.warn(`Level '${ name }' is already registered. Overwriting.`);\n }\n\n this._registeredLevels.set(name, level);\n this._log.info(`Registered level: ${ name }`);\n }\n\n /**\n * Loads and activates a level by name\n * @param levelName - The name of the level to load\n * @returns Promise that resolves when the level is loaded\n */\n public async loadLevel(levelName : string) : Promise<void>\n {\n // Get the level instance\n const level = this._registeredLevels.get(levelName);\n\n if(!level)\n {\n throw new Error(`Level '${ levelName }' is not registered.`);\n }\n\n // Load the level's scene if not already loaded\n if(!level.isLoaded)\n {\n this._log.debug(`Loading level: ${ levelName }`);\n await level.loadScene(this._eventBus, this._sceneEngine);\n }\n\n // Set as current level\n this._currentLevel = level;\n this._log.info(`Activated level: ${ levelName }`);\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 a loaded level by name\n */\n public getLevel(name : string) : Level | null\n {\n return this._registeredLevels.get(name) || null;\n }\n\n /**\n * Unloads a level and disposes its resources\n */\n public unloadLevel(name : string) : void\n {\n const level = this._registeredLevels.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 level.dispose();\n this._log.info(`Unloaded level: ${ name }`);\n }\n }\n\n /**\n * Unloads the current level and disposes its resources\n */\n public unloadCurrentLevel() : void\n {\n if(this._currentLevel)\n {\n this.unloadLevel(this._currentLevel.name);\n }\n else\n {\n this._log.warn('No current level to unload');\n }\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Tears down the level manager and cleans up any resources\n */\n async $teardown() : Promise<void>\n {\n // Dispose of all loaded levels\n for(const level of this._registeredLevels.values())\n {\n level.dispose();\n }\n\n this._registeredLevels.clear();\n this._currentLevel = null;\n\n return Promise.resolve();\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Keyboard Input Resource Access\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { KeyboardDevice, KeyboardInputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Callback type for keyboard device connection events\n */\nexport type KeyboardDeviceCallback = (device : KeyboardDevice) => void;\n\n/**\n * Callback type for keyboard input events\n */\nexport type KeyboardInputCallback = (device : KeyboardDevice, state : KeyboardInputState) => void;\n\n/**\n * Responsible for tracking keyboard state and notifying subscribers of changes.\n */\nexport class KeyboardInputPlugin\n{\n private _keyboardDevice : KeyboardDevice;\n private _keysState : Record<string, boolean> = {};\n\n // Callbacks\n private _onDeviceConnected ?: KeyboardDeviceCallback;\n private _onInputChanged ?: KeyboardInputCallback;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new KeyboardResourceAccess\n */\n constructor()\n {\n // Initialize keyboard device\n this._keyboardDevice = {\n id: 'keyboard-0',\n name: 'Keyboard',\n type: 'keyboard',\n connected: true,\n };\n\n // Set up event listeners\n this._setupKeyboardEvents();\n\n // Notify that device is connected (on next tick to allow callback registration)\n setTimeout(() => this._notifyDeviceConnected(), 0);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a callback for device connected events\n *\n * @param callback - The callback to register\n */\n public onDeviceConnected(callback : KeyboardDeviceCallback) : void\n {\n this._onDeviceConnected = callback;\n }\n\n /**\n * Register a callback for input changed events\n *\n * @param callback - The callback to register\n */\n public onInputChanged(callback : KeyboardInputCallback) : void\n {\n this._onInputChanged = callback;\n }\n\n /**\n * Get the current keyboard state\n */\n public getState() : KeyboardInputState\n {\n return {\n type: 'keyboard',\n keys: { ...this._keysState },\n delta: {},\n };\n }\n\n /**\n * Get the keyboard device\n */\n public getDevice() : KeyboardDevice\n {\n return { ...this._keyboardDevice };\n }\n\n /**\n * Tears down the keyboard resource access and cleans up event listeners\n */\n public $teardown() : Promise<void>\n {\n // Remove keyboard event listeners\n window.removeEventListener('keydown', this._handleKeyDown);\n window.removeEventListener('keyup', this._handleKeyUp);\n\n return Promise.resolve();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Set up keyboard event listeners\n */\n private _setupKeyboardEvents() : void\n {\n // Bind handlers to this instance\n this._handleKeyDown = this._handleKeyDown.bind(this);\n this._handleKeyUp = this._handleKeyUp.bind(this);\n\n // Add event listeners\n window.addEventListener('keydown', this._handleKeyDown);\n window.addEventListener('keyup', this._handleKeyUp);\n }\n\n /**\n * Handle keyboard key down events\n */\n private _handleKeyDown(event : KeyboardEvent) : void\n {\n this._keysState[event.code] = true;\n\n const delta : Record<string, boolean> = {};\n delta[event.code] = true;\n\n const state : KeyboardInputState = {\n type: 'keyboard',\n keys: { ...this._keysState },\n delta,\n event,\n };\n\n this._notifyInputChanged(state);\n }\n\n /**\n * Handle keyboard key up events\n */\n private _handleKeyUp(event : KeyboardEvent) : void\n {\n this._keysState[event.code] = false;\n\n const delta : Record<string, boolean> = {};\n delta[event.code] = false;\n\n const state : KeyboardInputState = {\n type: 'keyboard',\n keys: { ...this._keysState },\n delta,\n event,\n };\n\n this._notifyInputChanged(state);\n }\n\n /**\n * Notify subscribers of device connected event\n */\n private _notifyDeviceConnected() : void\n {\n if(this._onDeviceConnected)\n {\n this._onDeviceConnected(this._keyboardDevice);\n }\n }\n\n /**\n * Notify subscribers of input changed event\n */\n private _notifyInputChanged(state : KeyboardInputState) : void\n {\n if(this._onInputChanged)\n {\n this._onInputChanged(this._keyboardDevice, state);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Mouse Input Resource Access\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { ButtonState, MouseDevice, MouseInputState, Position } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Callback type for mouse device connection events\n */\nexport type MouseDeviceCallback = (device : MouseDevice) => void;\n\n/**\n * Callback type for mouse input events\n */\nexport type MouseInputCallback = (device : MouseDevice, state : MouseInputState) => void;\n\n/**\n * Responsible for tracking mouse state and notifying subscribers of changes.\n */\nexport class MouseInputPlugin\n{\n private _targetElement : HTMLElement;\n private _mouseDevice : MouseDevice;\n private _buttonState : Record<string, ButtonState> = {};\n private _axesState : Record<string, number> = {};\n private _position : Position = {\n absolute: { x: 0, y: 0 },\n relative: { x: 0, y: 0 },\n };\n\n private _wheelState = {\n deltaX: 0,\n deltaY: 0,\n deltaZ: 0,\n deltaMode: 0,\n };\n\n // Callbacks\n private _onDeviceConnected ?: MouseDeviceCallback;\n private _onInputChanged ?: MouseInputCallback;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new MouseResourceAccess\n *\n * @param targetElement - The DOM element to attach mouse listeners to (defaults to document.body)\n */\n constructor(targetElement : HTMLElement = document.body)\n {\n this._targetElement = targetElement;\n\n // Initialize mouse device\n this._mouseDevice = {\n id: 'mouse-0',\n name: 'Mouse',\n type: 'mouse',\n connected: true,\n };\n\n // Set up event listeners\n this._setupMouseEvents();\n\n // Initialize axes to zero\n this._axesState['axis-x'] = 0;\n this._axesState['axis-y'] = 0;\n\n // Notify that device is connected (on next tick to allow callback registration)\n setTimeout(() => this._notifyDeviceConnected(), 0);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a callback for device connected events\n *\n * @param callback - The callback to register\n */\n public onDeviceConnected(callback : MouseDeviceCallback) : void\n {\n this._onDeviceConnected = callback;\n }\n\n /**\n * Register a callback for input changed events\n *\n * @param callback - The callback to register\n */\n public onInputChanged(callback : MouseInputCallback) : void\n {\n this._onInputChanged = callback;\n }\n\n /**\n * Get the current mouse state\n */\n public getState() : MouseInputState\n {\n return {\n type: 'mouse',\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: {\n absolute: { ...this._position.absolute },\n relative: { ...this._position.relative },\n },\n wheel: { ...this._wheelState },\n };\n }\n\n /**\n * Get the mouse device\n */\n public getDevice() : MouseDevice\n {\n return { ...this._mouseDevice };\n }\n\n /**\n * Tears down the mouse resource access and cleans up event listeners\n */\n public $teardown() : Promise<void>\n {\n // Remove mouse event listeners\n this._targetElement.removeEventListener('mousedown', this._handleMouseDown);\n this._targetElement.removeEventListener('mouseup', this._handleMouseUp);\n this._targetElement.removeEventListener('mousemove', this._handleMouseMove);\n this._targetElement.removeEventListener('wheel', this._handleMouseWheel);\n\n return Promise.resolve();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Set up mouse event listeners\n */\n private _setupMouseEvents() : void\n {\n // Bind handlers to this instance\n this._handleMouseDown = this._handleMouseDown.bind(this);\n this._handleMouseUp = this._handleMouseUp.bind(this);\n this._handleMouseMove = this._handleMouseMove.bind(this);\n this._handleMouseWheel = this._handleMouseWheel.bind(this);\n\n // Add event listeners\n this._targetElement.addEventListener('mousedown', this._handleMouseDown);\n this._targetElement.addEventListener('mouseup', this._handleMouseUp);\n this._targetElement.addEventListener('mousemove', this._handleMouseMove);\n this._targetElement.addEventListener('wheel', this._handleMouseWheel);\n }\n\n /**\n * Handle mouse button down events\n */\n private _handleMouseDown(event : MouseEvent) : void\n {\n const buttonKey = `button-${ event.button }`;\n const buttonState : ButtonState = {\n pressed: true,\n };\n\n this._buttonState[buttonKey] = buttonState;\n\n this._notifyInputChanged({\n type: 'mouse',\n event,\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: { ...this._position },\n });\n }\n\n /**\n * Handle mouse button up events\n */\n private _handleMouseUp(event : MouseEvent) : void\n {\n const buttonKey = `button-${ event.button }`;\n const buttonState : ButtonState = {\n pressed: false,\n };\n\n this._buttonState[buttonKey] = buttonState;\n\n this._notifyInputChanged({\n type: 'mouse',\n event,\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: { ...this._position },\n });\n }\n\n /**\n * Handle mouse move events\n */\n private _handleMouseMove(event : MouseEvent) : void\n {\n // Update position\n this._position = {\n absolute: {\n x: event.clientX,\n y: event.clientY,\n },\n relative: {\n x: event.movementX,\n y: event.movementY,\n },\n };\n\n // Update axis values\n this._axesState['axis-x'] = event.clientX;\n this._axesState['axis-y'] = event.clientY;\n\n this._notifyInputChanged({\n type: 'mouse',\n event,\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: { ...this._position },\n });\n }\n\n /**\n * Handle mouse wheel events\n */\n private _handleMouseWheel(event : WheelEvent) : void\n {\n // Update wheel state\n this._wheelState = {\n deltaX: event.deltaX,\n deltaY: event.deltaY,\n deltaZ: event.deltaZ,\n deltaMode: event.deltaMode,\n };\n\n this._notifyInputChanged({\n type: 'mouse',\n event,\n buttons: { ...this._buttonState },\n axes: { ...this._axesState },\n position: { ...this._position },\n wheel: { ...this._wheelState },\n });\n }\n\n /**\n * Notify subscribers of device connected event\n */\n private _notifyDeviceConnected() : void\n {\n if(this._onDeviceConnected)\n {\n this._onDeviceConnected(this._mouseDevice);\n }\n }\n\n /**\n * Notify subscribers of input changed event\n */\n private _notifyInputChanged(state : MouseInputState) : void\n {\n if(this._onInputChanged)\n {\n this._onInputChanged(this._mouseDevice, state);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Gamepad Input\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { ButtonState, GamepadDevice, GamepadInputState } from '../../interfaces/input.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Callback type for gamepad device connection events\n */\nexport type GamepadDeviceCallback = (device : GamepadDevice) => void;\n\n/**\n * Callback type for gamepad input events\n */\nexport type GamepadInputCallback = (device : GamepadDevice, state : GamepadInputState) => void;\n\n/**\n * Responsible for tracking gamepad state and notifying subscribers of changes.\n */\nexport class GamepadInputPlugin\n{\n private _gamepadDevices : Record<number, GamepadDevice> = {};\n private _buttonStates : Record<number, Record<string, ButtonState>> = {};\n private _axesStates : Record<number, Record<string, number>> = {};\n\n // Callbacks\n private _onDeviceConnected ?: GamepadDeviceCallback;\n private _onDeviceDisconnected ?: GamepadDeviceCallback;\n private _onInputChanged ?: GamepadInputCallback;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new GamepadResourceAccess\n */\n constructor()\n {\n // Set up event listeners for gamepad connection and disconnection\n this._setupGamepadEvents();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public API\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Register a callback for device connected events\n *\n * @param callback - The callback to register\n */\n public onDeviceConnected(callback : GamepadDeviceCallback) : void\n {\n this._onDeviceConnected = callback;\n }\n\n /**\n * Register a callback for device disconnected events\n *\n * @param callback - The callback to register\n */\n public onDeviceDisconnected(callback : GamepadDeviceCallback) : void\n {\n this._onDeviceDisconnected = callback;\n }\n\n /**\n * Register a callback for input changed events\n *\n * @param callback - The callback to register\n */\n public onInputChanged(callback : GamepadInputCallback) : void\n {\n this._onInputChanged = callback;\n }\n\n /**\n * Get all connected gamepad devices\n */\n public getDevices() : GamepadDevice[]\n {\n return Object.values(this._gamepadDevices).map((device) => ({ ...device }));\n }\n\n /**\n * Get all connected gamepad states\n */\n public getStates() : Record<number, GamepadInputState>\n {\n const states : Record<number, GamepadInputState> = {};\n\n Object.keys(this._buttonStates).forEach((indexStr) =>\n {\n const index = Number(indexStr);\n const buttons = this._buttonStates[index] || {};\n const axes = this._axesStates[index] || {};\n\n states[index] = {\n type: 'gamepad',\n buttons: { ...buttons },\n axes: { ...axes },\n };\n });\n\n return states;\n }\n\n /**\n * Get a specific gamepad device by index\n *\n * @param index - The index of the gamepad to get\n */\n public getDevice(index : number) : GamepadDevice | null\n {\n const device = this._gamepadDevices[index];\n return device ? { ...device } : null;\n }\n\n /**\n * Get a specific gamepad state by index\n *\n * @param index - The index of the gamepad state to get\n */\n public getState(index : number) : GamepadInputState | null\n {\n const buttons = this._buttonStates[index];\n const axes = this._axesStates[index];\n\n if(!buttons && !axes) { return null; }\n\n return {\n type: 'gamepad',\n buttons: buttons ? { ...buttons } : {},\n axes: axes ? { ...axes } : {},\n };\n }\n\n /**\n * Poll for gamepad state updates - call this in your game loop\n */\n public pollGamepads() : void\n {\n /* eslint-disable no-continue */\n\n if(!navigator.getGamepads) { return; }\n\n const gamepads = navigator.getGamepads();\n for(const gamepad of gamepads)\n {\n if(!gamepad) { continue; }\n\n const index = gamepad.index;\n\n if(!this._gamepadDevices[index])\n {\n this._handleGamepadConnected(gamepad);\n continue;\n }\n\n // Get device\n const device = this._gamepadDevices[index];\n if(!device) { continue; }\n\n // Get current button and axis states or initialize them\n const currentButtons = this._buttonStates[index] || {};\n const currentAxes = this._axesStates[index] || {};\n\n // Build new button state object\n const newButtons : Record<string, ButtonState> = {};\n let buttonsChanged = false;\n\n gamepad.buttons.forEach((btn, i) =>\n {\n const buttonKey = `button-${ i }`;\n const buttonState : ButtonState = {\n pressed: btn.pressed,\n touched: btn.touched,\n value: btn.value,\n };\n\n newButtons[buttonKey] = buttonState;\n\n const prevButton = currentButtons[buttonKey];\n if(!prevButton\n || prevButton.pressed !== buttonState.pressed\n || prevButton.touched !== buttonState.touched\n || prevButton.value !== buttonState.value)\n {\n buttonsChanged = true;\n }\n });\n\n // Build new axis state object\n const newAxes : Record<string, number> = {};\n let axesChanged = false;\n\n gamepad.axes.forEach((axisValue, i) =>\n {\n const axisKey = `axis-${ i }`;\n newAxes[axisKey] = axisValue;\n\n const prevAxis = currentAxes[axisKey];\n if(prevAxis !== axisValue)\n {\n axesChanged = true;\n }\n });\n\n // Update state objects\n this._buttonStates[index] = newButtons;\n this._axesStates[index] = newAxes;\n\n // Notify if state changed\n if(buttonsChanged || axesChanged)\n {\n const state : GamepadInputState = {\n type: 'gamepad',\n buttons: { ...newButtons },\n axes: { ...newAxes },\n };\n\n this._notifyInputChanged(device, state);\n }\n }\n\n /* eslint-enable no-continue */\n }\n\n /**\n * Tears down the gamepad resource access and cleans up event listeners\n */\n public $teardown() : Promise<void>\n {\n // Remove gamepad event listeners\n window.removeEventListener('gamepadconnected', this._handleGamepadConnected);\n window.removeEventListener('gamepaddisconnected', this._handleGamepadDisconnected);\n\n return Promise.resolve();\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private Methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Set up gamepad event listeners\n */\n private _setupGamepadEvents() : void\n {\n // Bind handlers to this instance\n this._handleGamepadConnected = this._handleGamepadConnected.bind(this);\n this._handleGamepadDisconnected = this._handleGamepadDisconnected.bind(this);\n\n // Add event listeners\n window.addEventListener('gamepadconnected', this._handleGamepadConnected);\n window.addEventListener('gamepaddisconnected', this._handleGamepadDisconnected);\n\n // Check for already connected gamepads (browsers sometimes miss the connected event)\n if(navigator.getGamepads)\n {\n const gamepads = navigator.getGamepads();\n for(const gamepad of gamepads)\n {\n if(gamepad)\n {\n this._handleGamepadConnected(gamepad);\n }\n }\n }\n }\n\n /**\n * Handle gamepad connected event\n */\n private _handleGamepadConnected(event : GamepadEvent | Gamepad) : void\n {\n const gamepad = event instanceof GamepadEvent ? event.gamepad : event;\n const index = gamepad.index;\n\n // Create gamepad device object\n const gamepadDevice : GamepadDevice = {\n id: `gamepad-${ index }`,\n name: gamepad.id,\n type: 'gamepad',\n connected: true,\n index,\n mapping: gamepad.mapping,\n axes: Array.from(gamepad.axes),\n buttons: Array.from(gamepad.buttons),\n };\n\n // Store the device\n this._gamepadDevices[index] = gamepadDevice;\n\n // Initialize button state object\n const buttonStateObj : Record<string, ButtonState> = {};\n Array.from(gamepad.buttons).forEach((btn, i) =>\n {\n buttonStateObj[`button-${ i }`] = {\n pressed: btn.pressed,\n touched: btn.touched,\n value: btn.value,\n };\n });\n this._buttonStates[index] = buttonStateObj;\n\n // Initialize axis state object\n const axesStateObj : Record<string, number> = {};\n Array.from(gamepad.axes).forEach((axisValue, i) =>\n {\n axesStateObj[`axis-${ i }`] = axisValue;\n });\n this._axesStates[index] = axesStateObj;\n\n // Notify device connected with initial state\n this._notifyDeviceConnected(gamepadDevice);\n\n // Emit input changed event with initial state\n this._notifyInputChanged(gamepadDevice, {\n type: 'gamepad',\n buttons: { ...buttonStateObj },\n axes: { ...axesStateObj },\n event: event instanceof GamepadEvent ? event : undefined,\n });\n }\n\n /**\n * Handle gamepad disconnected event\n */\n private _handleGamepadDisconnected(event : GamepadEvent) : void\n {\n const gamepad = event.gamepad;\n const index = gamepad.index;\n\n // Get the device\n const gamepadDevice = this._gamepadDevices[index];\n\n if(gamepadDevice)\n {\n // Update connection status\n gamepadDevice.connected = false;\n\n // Notify device disconnected\n this._notifyDeviceDisconnected(gamepadDevice);\n\n // Remove from objects\n // Using object property assignment with undefined instead of delete\n this._gamepadDevices[index] = undefined as unknown as GamepadDevice;\n this._buttonStates[index] = undefined as unknown as Record<string, ButtonState>;\n this._axesStates[index] = undefined as unknown as Record<string, number>;\n }\n }\n\n /**\n * Notify subscribers of device connected event\n */\n private _notifyDeviceConnected(device : GamepadDevice) : void\n {\n if(this._onDeviceConnected)\n {\n this._onDeviceConnected(device);\n }\n }\n\n /**\n * Notify subscribers of device disconnected event\n */\n private _notifyDeviceDisconnected(device : GamepadDevice) : void\n {\n if(this._onDeviceDisconnected)\n {\n this._onDeviceDisconnected(device);\n }\n }\n\n /**\n * Notify subscribers of input changed event\n */\n private _notifyInputChanged(device : GamepadDevice, state : GamepadInputState) : void\n {\n if(this._onInputChanged)\n {\n this._onInputChanged(device, state);\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// User Input Manager\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport type { GamepadInputState, InputDevice, KeyboardInputState, MouseInputState } from '../interfaces/input.ts';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\n\n// Classes\nimport { type GameEvent, GameEventBus } from '../classes/eventBus.ts';\n\n// Utils\nimport { LoggingUtility, SAGELogger } from '../utils/logger.ts';\n\n// Resource Access\nimport { KeyboardInputPlugin } from '../classes/input/keyboard.ts';\nimport { MouseInputPlugin } from '../classes/input/mouse.ts';\nimport { GamepadInputPlugin } from '../classes/input/gamepad.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Manager for handling user input from various devices (keyboard, mouse, gamepad)\n */\nexport class UserInputManager\n{\n private _eventBus : GameEventBus;\n\n private _keyboardRA : KeyboardInputPlugin;\n private _mouseRA : MouseInputPlugin;\n private _gamepadRA : GamepadInputPlugin;\n\n /** Logger instance */\n private _log : LoggerInterface;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Create a new UserInputManager\n *\n * @param eventBus - The game event bus to publish events to\n * @param canvas - The DOM element to attach input listeners to\n * @param logger - The logging utility to use\n */\n constructor(\n eventBus : GameEventBus,\n canvas : HTMLElement,\n logger ?: LoggingUtility\n )\n {\n this._eventBus = eventBus;\n\n // Initialize logger\n this._log = logger?.getLogger('UserInputManager') || new SAGELogger('UserInputManager');\n\n this._log.info('Initializing UserInputManager');\n\n // Initialize resource access classes\n this._log.debug('Initializing input resource access classes');\n this._keyboardRA = new KeyboardInputPlugin();\n this._mouseRA = new MouseInputPlugin(canvas);\n this._gamepadRA = new GamepadInputPlugin();\n\n // Register callbacks\n this._log.debug('Registering input event callbacks');\n this._keyboardRA.onDeviceConnected(this._publishDeviceConnected.bind(this));\n this._keyboardRA.onInputChanged(this._publishInputChanged.bind(this));\n this._mouseRA.onDeviceConnected(this._publishDeviceConnected.bind(this));\n this._mouseRA.onInputChanged(this._publishInputChanged.bind(this));\n this._gamepadRA.onDeviceConnected(this._publishDeviceConnected.bind(this));\n this._gamepadRA.onDeviceDisconnected(this._publishDeviceDisconnected.bind(this));\n this._gamepadRA.onInputChanged(this._publishInputChanged.bind(this));\n\n this._log.info('UserInputManager initialized successfully');\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Private methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Publish device connected event to the event bus\n */\n private _publishDeviceConnected(device : InputDevice) : void\n {\n this._log.debug(`Device connected: ${ device.id } (${ device.name })`);\n\n const gameEvent : GameEvent = {\n type: 'input:device:connected',\n payload: { device },\n };\n\n this._eventBus.publish(gameEvent);\n }\n\n /**\n * Publish device disconnected event to the event bus\n */\n private _publishDeviceDisconnected(device : InputDevice) : void\n {\n this._log.debug(`Device disconnected: ${ device.id } (${ device.name })`);\n\n const gameEvent : GameEvent = {\n type: 'input:device:disconnected',\n payload: { device },\n };\n\n this._eventBus.publish(gameEvent);\n }\n\n /**\n * Publish input changed event to the event bus, used by all device types\n */\n private _publishInputChanged(\n device : InputDevice,\n state : KeyboardInputState | MouseInputState | GamepadInputState\n ) : void\n {\n this._log.trace(`Input changed: ${ device.id }`, state);\n\n const gameEvent : GameEvent = {\n type: 'input:changed',\n payload: {\n deviceId: device.id,\n device,\n state,\n },\n };\n\n // Publish the event to the event bus\n this._eventBus.publish(gameEvent);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Internal methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Tears down the input manager and clean up event listeners\n */\n public $teardown() : Promise<void>\n {\n this._log.info('Tearing down UserInputManager');\n\n // Cleanup all resource access instances\n this._log.debug('Cleaning up input resource access instances');\n\n // Using Promise.all to handle multiple async teardowns\n return Promise.all([\n this._keyboardRA.$teardown(),\n this._mouseRA.$teardown(),\n this._gamepadRA.$teardown(),\n ]).then(() =>\n {\n this._log.info('UserInputManager torn down successfully');\n });\n }\n\n //------------------------------------------------------------------------------------------------------------------\n // Public methods\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get all input devices\n *\n * @return An array of input devices\n */\n public listDevices() : InputDevice[]\n {\n this._log.debug('Getting all connected input devices');\n\n const devices : InputDevice[] = [];\n\n // Add keyboard and mouse\n devices.push(this._keyboardRA.getDevice());\n devices.push(this._mouseRA.getDevice());\n\n // Add all gamepads\n devices.push(...this._gamepadRA.getDevices());\n\n this._log.debug(`Found ${ devices.length } connected devices`);\n return devices;\n }\n\n /**\n * Get a specific input device by ID\n *\n * @param deviceId - The ID of the device to get\n *\n * @return The input device, or null if not found\n */\n public getDevice(deviceId : string) : InputDevice | null\n {\n this._log.debug(`Getting device: ${ deviceId }`);\n\n // Shortcut for keyboard and mouse\n if(deviceId.startsWith('keyboard-'))\n {\n return this._keyboardRA.getDevice();\n }\n else if(deviceId.startsWith('mouse-'))\n {\n return this._mouseRA.getDevice();\n }\n else if(deviceId.startsWith('gamepad-'))\n {\n const index = parseInt(deviceId.split('-')[1], 10);\n return this._gamepadRA.getDevice(index);\n }\n else\n {\n // This is the slow path, and should never be hit, but it is here for completeness\n const devices = this.listDevices();\n\n for(const device of devices)\n {\n if(device.id === deviceId)\n {\n return device;\n }\n }\n }\n\n this._log.warn(`Device not found: ${ deviceId }`);\n return null;\n }\n\n /**\n * Poll for gamepad state updates - call this in your game loop\n */\n public pollGamepads() : void\n {\n this._gamepadRA.pollGamepads();\n }\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 - The canvas element to render on (HTMLCanvasElement or OffscreenCanvas)\n * @param options - Options specific to the WebGPU engine\n * @returns A promise that resolves to an initialized WebGPUEngine\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 - The canvas element to render on (GameCanvas)\n * @param options - Options for configuring the engine\n * @returns An initialized WebGL Engine\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 - Options specific to the Null engine\n * @returns An initialized Null Engine\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 *\n * @param canvas - The canvas element to render on, or null for a Null engine\n * @param options - Options for configuring the engine (EngineOptions, WebGPUEngineOptions, or NullEngineOptions)\n * @returns A promise that resolves to an initialized AbstractEngine\n */\nexport async function createEngine(\n canvas : GameCanvas | null,\n options : RenderEngineOptions\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 // 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 );\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 from '@babylonjs/havok';\nimport { HavokPlugin } from '@babylonjs/core';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport async function createPhysics() : Promise<HavokPlugin>\n{\n const hk = await HavokPhysics();\n return new HavokPlugin(true, hk);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// Input Device Interfaces\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Valid input device types\n */\nexport const validDeviceTypes = [ 'keyboard', 'mouse', 'gamepad' ] as const;\nexport type DeviceType = typeof validDeviceTypes[number];\n\n/**\n * Base input device interface\n */\nexport interface InputDevice\n{\n id : string;\n name : string;\n type : DeviceType;\n connected : boolean;\n}\n\n/**\n * Keyboard device information\n */\nexport interface KeyboardDevice extends InputDevice\n{\n type : 'keyboard';\n}\n\n/**\n * Mouse device information\n */\nexport interface MouseDevice extends InputDevice\n{\n type : 'mouse';\n}\n\n/**\n * Gamepad device information\n */\nexport interface GamepadDevice extends InputDevice\n{\n type : 'gamepad';\n index : number;\n mapping : string;\n axes : number[];\n buttons : GamepadButton[];\n}\n\n/**\n * Base interface for all input device states\n */\nexport interface BaseInputState\n{\n event ?: Event;\n}\n\n/**\n * Button state interface (for both mouse and gamepad)\n */\nexport interface ButtonState\n{\n pressed : boolean;\n touched ?: boolean;\n value ?: number;\n}\n\n/**\n * Mouse position interface\n */\nexport interface Position\n{\n absolute : { x : number, y : number };\n relative : { x : number, y : number };\n}\n\n/**\n * Unified keyboard input state with object literals instead of Maps\n */\nexport interface KeyboardInputState extends BaseInputState\n{\n type : 'keyboard';\n keys : Record<string, boolean>;\n delta : Record<string, boolean>;\n event ?: KeyboardEvent;\n}\n\n/**\n * Unified mouse input state with object literals instead of Maps\n */\nexport interface MouseInputState extends BaseInputState\n{\n type : 'mouse';\n buttons : Record<string, ButtonState>;\n axes : Record<string, number>;\n position : Position;\n wheel ?: {\n deltaX : number;\n deltaY : number;\n deltaZ : number;\n deltaMode : number;\n };\n event ?: MouseEvent | WheelEvent;\n}\n\n/**\n * Unified gamepad input state with object literals instead of Maps\n */\nexport interface GamepadInputState extends BaseInputState\n{\n type : 'gamepad';\n buttons : Record<string, ButtonState>;\n axes : Record<string, number>;\n event ?: GamepadEvent;\n}\n\n/**\n * Unified input state interface that can represent any input device\n */\nexport type InputState = KeyboardInputState | MouseInputState | GamepadInputState;\n\n/**\n * Base interface for serialized device value readers\n */\nexport interface DeviceValueReaderDefinitionBase\n{\n /**\n * The device type of input source\n */\n type : DeviceType;\n\n /**\n * The input source type (e.g., 'key', 'button', 'axis')\n */\n sourceType : string;\n\n /**\n * The specific identifier for this input source (e.g., 'KeyA', '0', 'absolute:x')\n */\n sourceKey : string;\n}\n\n/**\n * Keyboard value reader definition\n */\nexport interface KeyboardValueReaderDefinition extends DeviceValueReaderDefinitionBase\n{\n type : 'keyboard';\n sourceType : 'key';\n sourceKey : string;\n options ?: {\n useDelta ?: boolean;\n };\n}\n\n/**\n * Mouse value reader definition\n */\nexport interface MouseValueReaderDefinition extends DeviceValueReaderDefinitionBase\n{\n type : 'mouse';\n sourceType : string;\n sourceKey : string;\n}\n\n/**\n * Gamepad value reader definition\n */\nexport interface GamepadValueReaderDefinition extends DeviceValueReaderDefinitionBase\n{\n type : 'gamepad';\n sourceType : string;\n sourceKey : string;\n options ?: {\n useAnalogValue ?: boolean;\n deadzone ?: number;\n invert ?: boolean;\n };\n}\n\n/**\n * Union type of all device value reader definitions\n */\nexport type DeviceValueReaderDefinition =\n | KeyboardValueReaderDefinition\n | MouseValueReaderDefinition\n | GamepadValueReaderDefinition;\n\n/**\n * Interface for all device value readers\n * Unified interface for all types of input sources that can extract values from device states\n */\nexport interface DeviceValueReader\n{\n /**\n * The type of input source (e.g., 'key', 'button', 'axis')\n */\n readonly sourceType : string;\n\n /**\n * The specific identifier for this input source (e.g., 'KeyA', '0', 'absolute:x')\n */\n readonly sourceKey : string;\n\n /**\n * Gets the value from an input state\n *\n * @param state - The current input state\n * @returns The value from the input (boolean or number) or undefined if not applicable to this state\n */\n getValue(state : InputState) : boolean | number | undefined;\n\n /**\n * Convert the reader to a JSON-serializable object\n *\n * @returns A JSON-serializable representation of the reader\n */\n toJSON() : DeviceValueReaderDefinition;\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 - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n trace(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Log a message at DEBUG level\n *\n * @param category - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n debug(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Log a message at INFO level\n *\n * @param category - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n info(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Log a message at WARN level\n *\n * @param category - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n warn(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Log a message at ERROR level\n *\n * @param category - The category for this message (typically class/module name)\n * @param message - The message text to log\n * @param args - Optional additional arguments to include\n */\n error(category : string, message : string, ...args : any[]) : void;\n\n /**\n * Start a timer with the specified label.\n *\n * @param category - The category for this timer (typically class/module name)\n * @param label - A unique label to identify the timer\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 - The category for this timer (typically class/module name)\n * @param label - The label identifying the timer to end\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 : any[]) : void;\n\n /** Log a message at DEBUG level */\n debug(message : string, ...args : any[]) : void;\n\n /** Log a message at INFO level */\n info(message : string, ...args : any[]) : void;\n\n /** Log a message at WARN level */\n warn(message : string, ...args : any[]) : void;\n\n /** Log a message at ERROR level */\n error(message : string, ...args : any[]) : 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// Abstract Level Class\n//----------------------------------------------------------------------------------------------------------------------\n\nimport { Scene } from '@babylonjs/core';\nimport type { LoggerInterface } from '../interfaces/logger.ts';\nimport { SAGELogger } from '../utils/logger.ts';\nimport { SceneEngine } from '../engines/scene.ts';\nimport { GameEventBus } from './eventBus.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\nexport interface LevelProgressEvent\n{\n type : 'level:progress' | 'level:complete' | 'level:error';\n levelName : string;\n progress ?: number; // 0-100 for progress events\n message ?: string;\n error ?: any;\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract base class for game levels.\n * Levels are responsible for creating and configuring their own scenes.\n */\nexport abstract class Level\n{\n protected _name : string;\n protected _sceneEngine ?: SceneEngine;\n protected _eventBus ?: GameEventBus;\n protected _scene : Scene | null = null;\n protected _log : LoggerInterface;\n\n //------------------------------------------------------------------------------------------------------------------\n\n constructor(name : string)\n {\n this._name = name;\n this._log = new SAGELogger(`Level:${ name }`, 'info');\n }\n\n /**\n * The name of this level\n */\n get name() : string\n {\n return this._name;\n }\n\n /**\n * The current scene for this level (null if not loaded)\n */\n get scene() : Scene | null\n {\n return this._scene;\n }\n\n /**\n * Whether this level has been loaded\n */\n get isLoaded() : boolean\n {\n return this._scene !== null;\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Emit a progress event during loading\n */\n protected _emitProgress(progress : number, message : string) : void\n {\n if(!this._eventBus)\n {\n throw new Error('Event bus is not initialized. Call loadScene() first.');\n }\n\n this._eventBus.publish({\n type: 'level:progress',\n levelName: this._name,\n progress,\n message,\n } as LevelProgressEvent);\n }\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Load and create the scene for this level.\n * Progress events will be emitted during loading.\n *\n * @param eventBus - The game event bus for publishing events\n * @param sceneEngine - The scene engine used to create the scene\n *\n * @returns Promise that resolves to the loaded scene\n */\n async loadScene(eventBus : GameEventBus, sceneEngine : SceneEngine) : 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 // Set up our internal instances\n this._eventBus = eventBus;\n this._sceneEngine = sceneEngine;\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._eventBus.publish({\n type: 'level:complete',\n levelName: this._name,\n message: 'Level loaded successfully',\n } as LevelProgressEvent);\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._eventBus.publish({\n type: 'level:error',\n levelName: this._name,\n message: 'Failed to load level',\n error,\n } as LevelProgressEvent);\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 * @returns Promise that resolves to the created scene\n */\n protected abstract buildScene() : Promise<Scene>;\n\n //------------------------------------------------------------------------------------------------------------------\n\n /**\n * Dispose of this level's resources\n */\n dispose() : void\n {\n if(this._scene)\n {\n this._log.info(`Disposing level: ${ this._name }`);\n this._scene.dispose();\n this._scene = null;\n }\n }\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n","//----------------------------------------------------------------------------------------------------------------------\n// SkewedAspect Game Engine\n//----------------------------------------------------------------------------------------------------------------------\n\n// Interfaces\nimport { GameCanvas, RenderEngineOptions, SageOptions } from './interfaces/game.ts';\n\n// Base Classes\nimport { SkewedAspectGameEngine } from './classes/gameEngine.ts';\nimport { GameEventBus } from './classes/eventBus.ts';\n\n// Engines\nimport { SceneEngine } from './engines/scene.ts';\n\n// Managers\nimport { BindingManager } from './managers/binding.ts';\nimport { GameManager } from './managers/game.ts';\nimport { GameEntityManager } from './managers/entity.ts';\nimport { LevelManager } from './managers/level.ts';\nimport { UserInputManager } from './managers/input.ts';\n\n// Utils\nimport { createEngine } from './utils/graphics.ts';\nimport { createPhysics } from './utils/physics.ts';\nimport { LoggingUtility } from './utils/logger.ts';\nimport { VERSION } from './utils/version.ts';\nimport { GameEntityDefinition } from './interfaces/entity.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n\n/**\n * Creates an instance of SkewedAspectGameEngine.\n * @param canvas - The game canvas.\n * @param entities - The game entities.\n * @param options - The game engine options including rendering options and log level.\n * @returns A promise that resolves to an instance of SkewedAspectGameEngine.\n */\nexport async function createGameEngine(\n canvas : GameCanvas,\n entities : GameEntityDefinition[],\n options : SageOptions = {}\n) : Promise<SkewedAspectGameEngine>\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 const engine = await createEngine(canvas, options.renderOptions || {});\n\n engineLogger.debug('Creating physics engine...');\n const physics = await createPhysics();\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, physics, logger);\n\n // Initialize SAGE Managers\n engineLogger.debug('Creating managers...');\n const inputManager = new UserInputManager(eventBus, canvas as HTMLElement, logger);\n const bindingManager = new BindingManager(eventBus, logger);\n const entityManager = new GameEntityManager(eventBus, logger, bindingManager);\n const levelManager = new LevelManager(eventBus, sceneEngine, logger);\n const gameManager = new GameManager(engine, entityManager, inputManager, levelManager, logger);\n\n engineLogger.info(`SAGE Game Engine v${ VERSION } initialized successfully.`);\n\n // Register entities\n engineLogger.debug('Loading entities...');\n for(const entity of entities)\n {\n entityManager.registerEntityDefinition(entity);\n }\n\n // Register default input bindings\n engineLogger.debug('Registering default input bindings...');\n if(options.bindings)\n {\n for(const binding of options.bindings)\n {\n bindingManager.registerBinding(binding);\n }\n }\n\n return new SkewedAspectGameEngine(\n canvas,\n engine,\n physics,\n eventBus,\n logger,\n\n // Engines\n {\n sceneEngine,\n },\n\n // Managers\n {\n bindingManager,\n entityManager,\n gameManager,\n inputManager,\n levelManager,\n }\n );\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// Exports\n//----------------------------------------------------------------------------------------------------------------------\n\nexport type { GameCanvas, RenderEngineOptions, SageOptions };\n\nexport * from './interfaces/action.ts';\nexport * from './interfaces/binding.ts';\nexport * from './interfaces/entity.ts';\nexport * from './interfaces/game.ts';\nexport * from './interfaces/input.ts';\nexport * from './interfaces/logger.ts';\n\n// Export base classes\nexport { GameEventBus, SkewedAspectGameEngine };\nexport * from './classes/eventBus.ts';\nexport * from './classes/entity.ts';\nexport * from './classes/level.ts';\nexport * from './utils/logger.ts';\nexport * from './utils/version.ts';\n\n//----------------------------------------------------------------------------------------------------------------------\n"],"names":["VERSION","SkewedAspectGameEngine","canvas","renderEngine","physics","eventBus","logger","engines","managers","__publicField","hook","teardownError","err","managerKey","manager","engineKey","engine","ConsoleBackend","level","styles","category","now","hours","minutes","seconds","ampm","timestamp","upperLevel","message","args","format","defaultStyle","levelStyle","resetStyle","label","timerKey","startTime","duration","NullBackend","_category","_message","_args","_label","SAGELogger","minLevel","backend","LoggingUtility","GameEventBus","eventTypeOrPattern","callback","eventType","subs","subscription","event","patternOrRegExp","regex","escaped","callbackCount","directSubs","sub","SceneEngine","Scene","scene","gravityVector","Vector3","name","position","camera","FreeCamera","direction","intensity","light","HemisphericLight","DirectionalLight","PointLight","angle","exponent","SpotLight","options","defaultOptions","MeshBuilder","mesh","shapeType","physicsProps","defaultProps","PhysicsAggregate","url","soundOptions","Sound","rootUrl","sceneFilename","result","LoadAssetContainerAsync","error","meshNames","ImportMeshAsync","bindingTypes","TriggerBinding","action","deviceID","reader","state","rawValue","digitalState","shouldTrigger","outputValue","finalValue","min","max","actionEvent","ToggleBinding","value","shouldToggle","ValueBinding","defaultMin","defaultMax","numericValue","KeyboardValueReader","keyCode","keyboardState","sourceKey","MouseValueReader","sourceType","buttonState","posType","axis","isValidWheelKey","sourceTypeString","keyParts","GamepadValueReader","BindingManager","binding","contextName","exclusive","context","exceptContextName","deactivatedContexts","definition","readerDef","device","bindings","errorMsg","actionName","isExclusive","isAlreadyActive","deactivated","_a","bindingsRemoved","deviceId","deviceBindings","newBindings","bindingContext","shouldKeep","actions","contexts","config","contextData","bindingDef","isBrowser","hasWebGPU","GameManager","entityManager","inputManager","levelManager","deltaTime","GameEntityBehavior","entity","GameEntity","type","initialState","behaviors","behaviorCtor","behavior","dt","existingSubscription","unsubscribe","behaviorName","GameEntityManager","bindingManager","existingAction","newAction","entityDef","mergedState","entityID","entityIds","entityId","LevelManager","sceneEngine","levelName","KeyboardInputPlugin","delta","MouseInputPlugin","targetElement","buttonKey","GamepadInputPlugin","states","indexStr","index","buttons","axes","gamepads","gamepad","currentButtons","currentAxes","newButtons","buttonsChanged","btn","i","prevButton","newAxes","axesChanged","axisValue","axisKey","gamepadDevice","buttonStateObj","axesStateObj","UserInputManager","gameEvent","devices","_buildWebGPUEngine","WebGPUEngine","_buildWebGLEngine","Engine","_buildNullEngine","NullEngine","createEngine","forceEngine","createPhysics","hk","HavokPhysics","HavokPlugin","validDeviceTypes","LogLevels","Level","progress","createGameEngine","entities","engineLogger","gameManager"],"mappings":"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"}