{"version":3,"file":"ironclad-engine.umd.cjs","sources":["../src/engine/core/GameLoop.js","../src/engine/core/SceneManager.js","../src/engine/core/AssetLoader.js","../src/engine/core/Keyboard.js","../src/engine/core/Mouse.js","../src/engine/core/Gamepad.js","../src/engine/core/InputManager.js","../src/engine/core/EventManager.js","../src/engine/ecs/EntityManager.js","../src/engine/ecs/PrefabManager.js","../src/engine/rendering/Camera.js","../src/engine/core/SaveLoadManager.js","../src/engine/fx/BaseEffect.js","../src/engine/fx/FlashEffect.js","../src/engine/fx/ShakeEffect.js","../src/engine/fx/TintEffect.js","../src/engine/core/EffectsManager.js","../src/engine/core/AudioManager.js","../src/engine/dialogue/DialogueManager.js","../src/engine/core/IroncladEngine.js","../src/engine/core/BaseScene.js","../src/engine/ecs/BaseEntity.js","../src/engine/ecs/System.js","../src/engine/rendering/Sprite.js","../src/engine/ecs/systems/RenderSystem.js","../src/engine/ecs/systems/AnimationSystem.js","../src/engine/physics/PhysicsConstants.js","../src/engine/physics/PhysicsSystem.js","../src/engine/rendering/TileLayerRenderer.js","../src/engine/ui/BaseUIElement.js","../src/engine/ui/Label.js","../src/engine/ui/Button.js","../src/engine/ui/Checkbox.js","../src/engine/ui/Panel.js","../src/engine/ui/ValueBar.js","../src/engine/ui/Slider.js","../src/engine/ui/ScrollablePanel.js","../src/engine/ui/TextInputField.js","../src/engine/ui/DialogueBox.js"],"sourcesContent":["// src/engine/core/GameLoop.js\n\n/**\n * @file GameLoop.js\n * @description Defines the core game loop using requestAnimationFrame.\n * Manages update and render cycles and calculates delta time.\n */\n\n/**\n * @class GameLoop\n * @description Manages the main game loop, calling update and render functions\n * at a consistent rate.\n */\nclass GameLoop {\n  /**\n   * Creates an instance of GameLoop.\n   * @param {function(number): void} update - The function to call for game logic updates.\n   * It receives deltaTime in seconds.\n   * @param {function(): void} render - The function to call for rendering the game.\n   */\n  constructor(update, render) {\n    /** @private {boolean} Indicates if the game loop is currently running. */\n    this.isRunning = false\n    /** @private {number} The timestamp of the last frame. Used to calculate deltaTime. */\n    this.lastTime = 0\n    /** @private {number} The accumulated time for fixed updates, if we were to implement them. Not used in basic loop. */\n    // this.accumulator = 0;\n    /** @private {number} The ID returned by requestAnimationFrame. Used to cancel the loop. */\n    this.rafId = null\n\n    /** @private {function(number): void} The game's main update function. */\n    this.update =\n      update ||\n      ((deltaTime) => {\n        console.warn('GameLoop: Update function not provided.', deltaTime)\n      })\n    /** @private {function(): void} The game's main render function. */\n    this.render =\n      render ||\n      (() => {\n        console.warn('GameLoop: Render function not provided.')\n      })\n\n    // Bind the loop method to this instance to maintain context in requestAnimationFrame\n    this.loop = this.loop.bind(this)\n  }\n\n  /**\n   * The main loop method that gets called by requestAnimationFrame.\n   * @param {number} currentTime - The current time in milliseconds, provided by requestAnimationFrame.\n   * @private\n   */\n  loop(currentTime) {\n    if (!this.isRunning) {\n      return\n    }\n\n    // Convert time to seconds\n    const currentInSeconds = currentTime * 0.001\n    const deltaTime = currentInSeconds - this.lastTime * 0.001\n    this.lastTime = currentTime\n\n    // Call the provided update function with deltaTime\n    this.update(deltaTime)\n\n    // Call the provided render function\n    this.render()\n\n    // Request the next frame\n    this.rafId = requestAnimationFrame(this.loop)\n  }\n\n  /**\n   * Starts the game loop.\n   * Logs a warning if the loop is already running.\n   */\n  start() {\n    if (this.isRunning) {\n      console.warn('GameLoop is already running.')\n      return\n    }\n    this.isRunning = true\n    this.lastTime = performance.now() // Initialize lastTime before the first loop call\n    console.log('GameLoop started.')\n    this.rafId = requestAnimationFrame(this.loop)\n  }\n\n  /**\n   * Stops the game loop.\n   */\n  stop() {\n    if (!this.isRunning) {\n      console.warn('GameLoop is not running.')\n      return\n    }\n    if (this.rafId) {\n      cancelAnimationFrame(this.rafId)\n    }\n    this.isRunning = false\n    this.rafId = null\n    console.log('GameLoop stopped.')\n  }\n\n  /**\n   * Sets a new update function for the game loop.\n   * @param {function(number): void} newUpdateFunction - The new function to call for game logic updates.\n   */\n  setUpdate(newUpdateFunction) {\n    if (typeof newUpdateFunction === 'function') {\n      this.update = newUpdateFunction\n    } else {\n      console.error('Failed to set update function: provided argument is not a function.')\n    }\n  }\n\n  /**\n   * Sets a new render function for the game loop.\n   * @param {function(): void} newRenderFunction - The new function to call for rendering the game.\n   */\n  setRender(newRenderFunction) {\n    if (typeof newRenderFunction === 'function') {\n      this.render = newRenderFunction\n    } else {\n      console.error('Failed to set render function: provided argument is not a function.')\n    }\n  }\n}\n\nexport default GameLoop\n","// src/engine/core/SceneManager.js\n\n/**\n * @file SceneManager.js\n * @description Manages a stack of game scenes, controlling which scene(s) are active,\n * updating, and rendering. Passes engine instance to scene methods.\n */\n\n// JSDoc typedefs as before...\n\nclass SceneManager {\n  constructor() {\n    this.scenes = {}\n    this.sceneStack = []\n    this.context = null\n    this.engine = null\n    // console.log('SceneManager: Initialized with stack support.');\n  }\n\n  setContextAndEngine(context, engineInstance) {\n    if (!context) console.error('SceneManager: Provided context is invalid.')\n    this.context = context\n\n    if (!engineInstance) {\n      console.error('SceneManager: Provided engine instance is invalid.')\n      return\n    }\n    this.engine = engineInstance\n    // console.log('SceneManager: Engine instance and context set.');\n  }\n\n  add(name, sceneInstance) {\n    if (typeof name !== 'string' || !name) {\n      console.error('SceneManager: Scene name invalid.')\n      return\n    }\n    if (typeof sceneInstance !== 'object' || sceneInstance === null) {\n      console.error(`SceneManager: Scene object for \"${name}\" invalid.`)\n      return\n    }\n    if (\n      typeof sceneInstance.initialize !== 'function' ||\n      typeof sceneInstance.update !== 'function' ||\n      typeof sceneInstance.render !== 'function' ||\n      typeof sceneInstance.unload !== 'function'\n    ) {\n      console.error(\n        `SceneManager: Scene \"${name}\" missing required methods. Check JSDoc SceneInstance typedef.`,\n      )\n      return\n    }\n    if (this.scenes[name]) {\n      console.warn(`SceneManager: Scene \"${name}\" already exists. Overwriting.`)\n    }\n    this.scenes[name] = sceneInstance\n    // console.log(`SceneManager: Scene \"${name}\" registered.`);\n  }\n\n  getActiveSceneInstance() {\n    if (this.sceneStack.length > 0) {\n      return this.sceneStack[this.sceneStack.length - 1].instance\n    }\n    return null\n  }\n\n  getActiveSceneName() {\n    if (this.sceneStack.length > 0) {\n      return this.sceneStack[this.sceneStack.length - 1].name\n    }\n    return null\n  }\n\n  switchTo(sceneName, data = {}) {\n    if (!this.engine || !this.context) {\n      console.error('SceneManager.switchTo: Engine or context not set.')\n      return\n    }\n    const newSceneInstance = this.scenes[sceneName]\n    if (!newSceneInstance) {\n      console.error(`SceneManager.switchTo: Scene \"${sceneName}\" not found.`)\n      return\n    }\n\n    // console.log(`SceneManager: Switching to base scene \"${sceneName}\". Unloading current stack...`);\n    while (this.sceneStack.length > 0) {\n      const sceneWrapper = this.sceneStack.pop()\n      if (typeof sceneWrapper.instance.unload === 'function') {\n        // console.log(`SceneManager: Unloading \"${sceneWrapper.name}\" from stack.`);\n        sceneWrapper.instance.unload(this.engine)\n      }\n    }\n\n    this.sceneStack.push({ name: sceneName, instance: newSceneInstance })\n    if (typeof newSceneInstance.initialize === 'function') {\n      // Ensure consistency with scenes expecting initialize(engine, data)\n      // Your BaseScene expects (engine, data).\n      // The old SceneManager JSDoc had (engine, context, data)\n      // For now, matching pushScene's call signature which works with BaseScene\n      newSceneInstance.initialize(this.engine, data)\n    }\n    if (this.engine.events) this.engine.events.emit('scene:switched', { name: sceneName, data })\n    // console.log(`SceneManager: Switched to scene \"${sceneName}\". Stack size: ${this.sceneStack.length}`);\n  }\n\n  pushScene(sceneName, data = {}) {\n    if (!this.engine || !this.context) {\n      // context check might be redundant if scenes don't get it directly\n      console.error('SceneManager.pushScene: Engine or context not set.')\n      return\n    }\n    const newSceneInstance = this.scenes[sceneName]\n    if (!newSceneInstance) {\n      console.error(`SceneManager.pushScene: Scene \"${sceneName}\" not found.`)\n      return\n    }\n\n    // MODIFIED CONSOLE LOG to prevent circular structure error\n    if (typeof data === 'object' && data !== null) {\n      console.log(\n        `SceneManager: Pushing scene \"${sceneName}\". Data keys being passed: ${Object.keys(data).join(', ')}`,\n      )\n    } else {\n      console.log(`SceneManager: Pushing scene \"${sceneName}\". Data being passed:`, data)\n    }\n\n    const currentTopSceneInstance = this.getActiveSceneInstance()\n    if (currentTopSceneInstance && typeof currentTopSceneInstance.pause === 'function') {\n      // console.log(`SceneManager: Pausing current top scene \"${this.getActiveSceneName()}\".`);\n      currentTopSceneInstance.pause(this.engine)\n    }\n\n    this.sceneStack.push({ name: sceneName, instance: newSceneInstance })\n    // console.log(`SceneManager: Pushed scene \"${sceneName}\". Stack size: ${this.sceneStack.length}`);\n    if (typeof newSceneInstance.initialize === 'function') {\n      // This assumes scenes (like BaseScene) expect initialize(engine, data)\n      newSceneInstance.initialize(this.engine, data)\n    }\n    if (this.engine.events) this.engine.events.emit('scene:pushed', { name: sceneName, data })\n  }\n\n  popScene(dataToPassDown = {}) {\n    if (!this.engine /*|| !this.context*/) {\n      // context check might be redundant\n      console.error('SceneManager.popScene: Engine not set.')\n      return\n    }\n    if (this.sceneStack.length === 0) {\n      console.warn('SceneManager.popScene: No scene to pop from stack.')\n      return\n    }\n\n    const poppedSceneWrapper = this.sceneStack.pop()\n    const poppedSceneName = poppedSceneWrapper.name\n    // console.log(`SceneManager: Popping scene \"${poppedSceneName}\". Stack size: ${this.sceneStack.length}`);\n    if (typeof poppedSceneWrapper.instance.unload === 'function') {\n      poppedSceneWrapper.instance.unload(this.engine)\n    }\n    if (this.engine.events) {\n      this.engine.events.emit('scene:popped', { name: poppedSceneName, dataPassed: dataToPassDown })\n    }\n\n    const newTopSceneInstance = this.getActiveSceneInstance()\n    if (newTopSceneInstance && typeof newTopSceneInstance.resume === 'function') {\n      // console.log(`SceneManager: Resuming new top scene \"${this.getActiveSceneName()}\".`);\n      // BaseScene expects resume(engine, data)\n      newTopSceneInstance.resume(this.engine, dataToPassDown)\n    }\n  }\n\n  update(deltaTime) {\n    for (let i = this.sceneStack.length - 1; i >= 0; i--) {\n      const sceneEntry = this.sceneStack[i]\n      if (sceneEntry.instance && typeof sceneEntry.instance.update === 'function') {\n        sceneEntry.instance.update(deltaTime, this.engine)\n      }\n      const isModalScene =\n        sceneEntry.instance.isModal !== undefined ? sceneEntry.instance.isModal : true\n      if (isModalScene) {\n        break\n      }\n    }\n  }\n\n  render() {\n    if (!this.engine || !this.context) return\n    for (const sceneWrapper of this.sceneStack) {\n      if (sceneWrapper.instance && typeof sceneWrapper.instance.render === 'function') {\n        sceneWrapper.instance.render(this.context, this.engine)\n      }\n    }\n  }\n}\n\nexport default SceneManager\n","// src/engine/core/AssetLoader.js\n\n/**\n * @file AssetLoader.js\n * @description Manages asynchronous loading and caching of game assets.\n * Emits events for loading progress, completion, and errors.\n */\n\n/**\n * @class AssetLoader\n * @description Handles loading and storage of game assets.\n */\nclass AssetLoader {\n  constructor() {\n    /** @private @type {Map<string, any>} */\n    this.cache = new Map()\n    /** @private @type {Array<{type: string, name: string, path: string, group?: string}>} */\n    this.loadQueue = []\n    /** @type {number} */\n    this.loadedCount = 0\n\n    /** @private @type {AudioContext | null} Web Audio API context for decoding audio. */\n    this.audioContext = null\n    /** @type {number} */\n    this.totalCountInCurrentBatch = 0 // Renamed for clarity\n    /** @private @type {string | null} */\n    this.currentBatchId = null // To identify ongoing batch loadAll operations\n\n    console.log('AssetLoader: Initialized.')\n  }\n\n  /**\n   * Gets the global AudioContext, creating it if it doesn't exist.\n   * @private\n   * @returns {AudioContext | null}\n   */\n  _getAudioContext() {\n    if (!this.audioContext) {\n      try {\n        this.audioContext = new (window.AudioContext || window.webkitAudioContext)()\n        // console.log(\"AssetLoader: AudioContext initialized.\");\n      } catch (e) {\n        console.error('AssetLoader: Web Audio API is not supported in this browser.', e)\n        return null\n      }\n    }\n    return this.audioContext\n  }\n  /** @private */\n  _getEventManager() {\n    if (window.gameEngine && typeof window.gameEngine.getEventManager === 'function') {\n      return window.gameEngine.getEventManager()\n    }\n    // console.warn(\"AssetLoader: EventManager not found via window.gameEngine. Events will not be emitted.\");\n    return null\n  }\n\n  /**\n   * Queues an image for loading.\n   * @param {string} name - A unique name to identify the asset.\n   * @param {string} path - The path to the image file.\n   * @param {string} [group='global'] - An optional group name for the asset.\n   * @returns {this}\n   */\n  queueImage(name, path, group = 'global') {\n    if (this.cache.has(name) || this.loadQueue.find((asset) => asset.name === name)) {\n      // console.warn(`AssetLoader: Image asset \"${name}\" already cached or queued. Skipping.`);\n      return this\n    }\n    this.loadQueue.push({ type: 'image', name, path, group })\n    return this\n  }\n\n  /**\n   * Queues a JSON file for loading.\n   * @param {string} name - A unique name to identify the asset.\n   * @param {string} path - The path to the JSON file.\n   * @param {string} [group='global'] - An optional group name for the asset.\n   * @returns {this}\n   */\n  queueJSON(name, path, group = 'global') {\n    if (this.cache.has(name) || this.loadQueue.find((asset) => asset.name === name)) {\n      // console.warn(`AssetLoader: JSON asset \"${name}\" already cached or queued. Skipping.`);\n      return this\n    }\n    this.loadQueue.push({ type: 'json', name, path, group })\n    return this\n  }\n\n  /**\n   * Queues an audio file for loading.\n   * @param {string} name - A unique name to identify the asset.\n   * @param {string} path - The path to the audio file.\n   * @param {string} [group='global'] - An optional group name for the asset.\n   * @returns {this}\n   */\n  queueAudio(name, path, group = 'global') {\n    if (this.cache.has(name) || this.loadQueue.find((asset) => asset.name === name)) {\n      // console.warn(`AssetLoader: Audio asset \"${name}\" already cached or queued. Skipping.`);\n      return this\n    }\n    this.loadQueue.push({ type: 'audio', name, path, group })\n    return this\n  }\n\n  /** @private */\n  _loadImage(name, path, group) {\n    return new Promise((resolve, reject) => {\n      if (this.cache.has(name)) {\n        // console.log(`AssetLoader: Image \"${name}\" already in cache.`);\n        this.loadedCount++ // Still count it towards progress if re-queued for a batch\n        const eventManager = this._getEventManager()\n        if (eventManager) {\n          eventManager.emit('asset:progress', {\n            loaded: this.loadedCount,\n            total: this.totalCountInCurrentBatch,\n            assetName: name,\n            assetPath: path,\n            group: group,\n            status: 'cached',\n          })\n        }\n        resolve(this.cache.get(name))\n        return\n      }\n      const image = new Image()\n      image.onload = () => {\n        this.cache.set(name, image)\n        this.loadedCount++\n        // console.log(`AssetLoader: Image \"${name}\" loaded from ${path}`);\n        const eventManager = this._getEventManager()\n        if (eventManager) {\n          eventManager.emit('asset:loaded', { name, path, group, asset: image })\n          eventManager.emit('asset:progress', {\n            loaded: this.loadedCount,\n            total: this.totalCountInCurrentBatch,\n            assetName: name,\n            assetPath: path,\n            group: group,\n            status: 'loaded',\n          })\n        }\n        resolve(image)\n      }\n      image.onerror = (errorEvent) => {\n        const error = new Error(`Failed to load image \"${name}\": ${path}`)\n        console.error(`AssetLoader: ${error.message}`, errorEvent)\n        const eventManager = this._getEventManager()\n        if (eventManager) {\n          eventManager.emit('asset:error', { name, path, group, error })\n          eventManager.emit('asset:progress', {\n            // Still emit progress for error to count it as \"processed\"\n            loaded: this.loadedCount, // loadedCount doesn't increment on error\n            total: this.totalCountInCurrentBatch,\n            assetName: name,\n            assetPath: path,\n            group: group,\n            status: 'error',\n          })\n        }\n        reject(error)\n      }\n      image.src = path\n    })\n  }\n\n  /** @private */\n  _loadJSON(name, path, group) {\n    return new Promise((resolve, reject) => {\n      if (this.cache.has(name)) {\n        // console.log(`AssetLoader: JSON \"${name}\" already in cache.`);\n        this.loadedCount++\n        const eventManager = this._getEventManager()\n        if (eventManager) {\n          eventManager.emit('asset:progress', {\n            loaded: this.loadedCount,\n            total: this.totalCountInCurrentBatch,\n            assetName: name,\n            assetPath: path,\n            group: group,\n            status: 'cached',\n          })\n        }\n        resolve(this.cache.get(name))\n        return\n      }\n      fetch(path)\n        .then((response) => {\n          if (!response.ok) {\n            throw new Error(`HTTP error! status: ${response.status} for ${path}`)\n          }\n          return response.json()\n        })\n        .then((data) => {\n          this.cache.set(name, data)\n          this.loadedCount++\n          // console.log(`AssetLoader: JSON \"${name}\" loaded from ${path}`);\n          const eventManager = this._getEventManager()\n          if (eventManager) {\n            eventManager.emit('asset:loaded', { name, path, group, asset: data })\n            eventManager.emit('asset:progress', {\n              loaded: this.loadedCount,\n              total: this.totalCountInCurrentBatch,\n              assetName: name,\n              assetPath: path,\n              group: group,\n              status: 'loaded',\n            })\n          }\n          resolve(data)\n        })\n        .catch((error) => {\n          console.error(`AssetLoader: Failed to load JSON \"${name}\" from ${path}`, error)\n          const eventManager = this._getEventManager()\n          if (eventManager) {\n            eventManager.emit('asset:error', { name, path, group, error })\n            eventManager.emit('asset:progress', {\n              loaded: this.loadedCount,\n              total: this.totalCountInCurrentBatch,\n              assetName: name,\n              assetPath: path,\n              group: group,\n              status: 'error',\n            })\n          }\n          reject(new Error(`Failed to load JSON \"${name}\": ${path}`))\n        })\n    })\n  }\n\n  _emitProgress(assetName, assetPath, group, status) {\n    const eventManager = this._getEventManager()\n    if (eventManager) {\n      eventManager.emit('asset:progress', {\n        loaded: this.processedInBatchCount, // Use processed count for progress\n        total: this.totalCountInCurrentBatch,\n        assetName,\n        assetPath,\n        group,\n        status,\n      })\n    }\n  }\n\n  /**\n   * @private\n   * Loads an audio asset and decodes it into an AudioBuffer.\n   * @param {string} name - The unique name/key for this asset.\n   * @param {string} path - The URL path to the audio file.\n   * @param {string} group - The asset group.\n   * @returns {Promise<AudioBuffer>} A promise that resolves with the decoded AudioBuffer.\n   */\n  async _loadAudio(name, path, group) {\n    const eventManager = this._getEventManager()\n\n    if (this.cache.has(name)) {\n      const cachedAsset = this.cache.get(name)\n      if (cachedAsset instanceof AudioBuffer) {\n        this.processedInBatchCount++\n        this._emitProgress(name, path, group, 'cached')\n        return cachedAsset\n      } else {\n        console.warn(`AssetLoader: Asset \"${name}\" in cache is not an AudioBuffer. Re-loading.`)\n      }\n    }\n\n    const audioCtx = this._getAudioContext()\n    if (!audioCtx) {\n      this.processedInBatchCount++ // Count as processed (error)\n      const error = new Error('Web Audio API not supported. Cannot load audio.')\n      if (eventManager) eventManager.emit('asset:error', { name, path, group, error })\n      this._emitProgress(name, path, group, 'error')\n      return Promise.reject(error)\n    }\n\n    try {\n      // console.log(`AssetLoader: Fetching audio \"${name}\" from ${path}.`);\n      const response = await fetch(path)\n      if (!response.ok) {\n        throw new Error(`HTTP error! status: ${response.status} for ${path}`)\n      }\n      const arrayBuffer = await response.arrayBuffer()\n      // console.log(`AssetLoader: Decoding audio \"${name}\".`);\n      const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer)\n\n      this.cache.set(name, audioBuffer)\n      this.processedInBatchCount++\n      // console.log(`AssetLoader: Audio \"${name}\" loaded and decoded successfully.`);\n      if (eventManager) {\n        eventManager.emit('asset:loaded', { name, path, group, asset: audioBuffer })\n      }\n      this._emitProgress(name, path, group, 'loaded')\n      return audioBuffer\n    } catch (error) {\n      this.processedInBatchCount++ // Count errors as processed\n      console.error(`AssetLoader: Error loading or decoding audio \"${name}\" from ${path}:`, error)\n      if (eventManager) {\n        eventManager.emit('asset:error', { name, path, group, error })\n      }\n      this._emitProgress(name, path, group, 'error')\n      throw new Error(`Failed to load/decode audio: ${name} at ${path}`)\n    }\n  }\n\n  /**\n   * Starts loading all queued assets.\n   * @param {string} [batchId='defaultBatch'] - An optional identifier for this loading batch.\n   * @returns {Promise<Map<string, any>>} A promise that resolves with a map of successfully loaded assets in this batch,\n   * or rejects if any critical asset fails (though individual errors are also emitted).\n   */\n  loadAll(batchId = `batch_${Date.now()}`) {\n    if (this.loadQueue.length === 0) {\n      const eventManager = this._getEventManager()\n      if (eventManager)\n        eventManager.emit('asset:batchCompleted', {\n          batchId,\n          loadedAssets: new Map(),\n          totalAssetsInBatch: 0,\n          success: true,\n        })\n      return Promise.resolve(new Map())\n    }\n\n    const currentQueue = [...this.loadQueue]\n    this.loadQueue = []\n\n    this.totalCountInCurrentBatch = currentQueue.length\n    this.loadedCount = 0\n    this.currentBatchId = batchId\n\n    // console.log(`AssetLoader: Starting to load batch \"${batchId}\" with ${this.totalCountInCurrentBatch} asset(s)...`);\n    const eventManager = this._getEventManager()\n    if (eventManager) {\n      eventManager.emit('asset:batchStarted', {\n        batchId,\n        totalAssets: this.totalCountInCurrentBatch,\n      })\n    }\n\n    const promises = currentQueue.map((assetToLoad) => {\n      let loadPromise\n      if (assetToLoad.type === 'image') {\n        loadPromise = this._loadImage(assetToLoad.name, assetToLoad.path, assetToLoad.group)\n      } else if (assetToLoad.type === 'json') {\n        loadPromise = this._loadJSON(assetToLoad.name, assetToLoad.path, assetToLoad.group)\n      } else if (assetToLoad.type === 'audio') {\n        // Check for 'audio' type\n        loadPromise = this._loadAudio(assetToLoad.name, assetToLoad.path, assetToLoad.group)\n      } else {\n        console.warn(\n          `AssetLoader: Unknown asset type \"${assetToLoad.type}\" for \"${assetToLoad.name}\". Skipping.`,\n        )\n        const err = new Error(`Unknown asset type: ${assetToLoad.type}`)\n        if (eventManager)\n          eventManager.emit('asset:error', {\n            name: assetToLoad.name,\n            path: assetToLoad.path,\n            group: assetToLoad.group,\n            error: err,\n          })\n        loadPromise = Promise.reject(err)\n      }\n      return loadPromise\n        .then((asset) => ({ name: assetToLoad.name, status: 'fulfilled', value: asset }))\n        .catch((error) => ({ name: assetToLoad.name, status: 'rejected', reason: error }))\n    })\n\n    return Promise.allSettled(promises).then((results) => {\n      const successfullyLoadedAssets = new Map()\n      let allSucceeded = true\n      results.forEach((result) => {\n        if (result.status === 'fulfilled') {\n          successfullyLoadedAssets.set(result.name, result.value)\n        } else {\n          allSucceeded = false\n          // console.warn(`AssetLoader: Asset \"${result.name}\" failed to load in batch \"${batchId}\". Reason:`, result.reason.message);\n        }\n      })\n\n      if (eventManager) {\n        eventManager.emit('asset:batchCompleted', {\n          batchId,\n          loadedAssets: successfullyLoadedAssets,\n          totalAssetsInBatch: this.totalCountInCurrentBatch,\n          success: allSucceeded,\n        })\n      }\n\n      if (allSucceeded) {\n        // console.log(`AssetLoader: Batch \"${batchId}\" (${this.loadedCount}/${this.totalCountInCurrentBatch}) loaded successfully.`);\n        return successfullyLoadedAssets\n      } else {\n        // console.warn(`AssetLoader: Batch \"${batchId}\" completed with some errors.`);\n        // Even with errors, resolve with what did load, but signal overall failure to the caller of loadAll.\n        return Promise.reject(\n          new Error(`Batch \"${batchId}\" had loading errors. Some assets may have loaded.`),\n        )\n      }\n    })\n  }\n\n  /**\n   * Retrieves a pre-loaded asset from the cache.\n   * @param {string} name - The name/key of the asset to retrieve.\n   * @returns {HTMLImageElement | object | AudioBuffer | undefined} The cached asset, or undefined if not found.\n   */\n  get(name) {\n    if (!this.cache.has(name)) {\n      // console.warn(`AssetLoader: Asset \"${name}\" not found in cache. Was it loaded or loading failed?`);\n      return undefined\n    }\n    return this.cache.get(name)\n  }\n\n  getProgress() {\n    /* ... (no changes, but now reflects totalCountInCurrentBatch) ... */\n    if (this.totalCountInCurrentBatch === 0 && this.loadQueue.length === 0) {\n      return { loaded: this.loadedCount, total: this.totalCountInCurrentBatch, progress: 1 }\n    }\n    return {\n      loaded: this.loadedCount,\n      total: this.totalCountInCurrentBatch,\n      progress:\n        this.totalCountInCurrentBatch > 0 ? this.loadedCount / this.totalCountInCurrentBatch : 0,\n    }\n  }\n  /**\n   * Checks if all assets currently being loaded have finished loading.\n   * @returns {boolean} True if loadedCount is 0, false otherwise.\n   */\n  isDoneLoading() {\n    return this.loadedCount === 0\n  }\n\n  /**\n   * Gets the current number of assets being loaded.\n   * @returns {number}\n   */\n  getLoadingCount() {\n    return this.totalCountInCurrentBatch - this.loadedCount\n  } // Number of assets *remaining* in current batch\n\n  clearCache() {\n    /* ... (no changes) ... */\n    this.cache.clear()\n    this.loadedCount = 0\n    this.totalCountInCurrentBatch = 0\n    this.loadQueue = []\n    console.log('AssetLoader: Cache and queue cleared.')\n  }\n  destroy() {\n    /* ... as before, including audioContext.close() if it exists ... */\n    if (this.audioContext && this.audioContext.state !== 'closed') {\n      this.audioContext\n        .close()\n        .then(() => {\n          // console.log(\"AssetLoader: AudioContext closed during destroy.\");\n        })\n        .catch((e) => console.error('AssetLoader: Error closing AudioContext during destroy:', e))\n      this.audioContext = null\n    }\n    this.clearCache()\n  }\n}\n\nexport default AssetLoader\n","// src/engine/core/Keyboard.js\n\n/**\n * @file Keyboard.js\n * @description Manages raw keyboard input state (pressed, just pressed, just released keys).\n */\n\nclass Keyboard {\n  constructor() {\n    /** @private @type {Set<string>} Raw pressed physical keys (event.code) */\n    this.pressedKeys = new Set()\n    /** @private @type {Set<string>} Raw physical keys just pressed this frame */\n    this.justPressedKeys = new Set()\n    /** @private @type {Set<string>} Raw physical keys just released this frame */\n    this.justReleasedKeys = new Set()\n\n    this._onKeyDown = this._onKeyDown.bind(this)\n    this._onKeyUp = this._onKeyUp.bind(this)\n    // console.log(\"Keyboard module: Constructed\");\n  }\n\n  /**\n   * Initializes keyboard event listeners.\n   */\n  initialize() {\n    window.addEventListener('keydown', this._onKeyDown)\n    window.addEventListener('keyup', this._onKeyUp)\n    // console.log(\"Keyboard module: Initialized and listeners attached.\");\n  }\n\n  /**\n   * @private\n   * Handles the keydown event.\n   * @param {KeyboardEvent} event\n   */\n  _onKeyDown(event) {\n    const code = event.code\n    // If the key was not already in pressedKeys, then it's \"just pressed\" for this frame.\n    if (!this.pressedKeys.has(code)) {\n      this.justPressedKeys.add(code)\n    }\n    this.pressedKeys.add(code)\n  }\n\n  /**\n   * @private\n   * Handles the keyup event.\n   * @param {KeyboardEvent} event\n   */\n  _onKeyUp(event) {\n    const code = event.code\n    this.pressedKeys.delete(code)\n    this.justReleasedKeys.add(code) // Mark as just released for this frame\n  }\n\n  /**\n   * Updates internal state for the next frame.\n   * Specifically, clears the \"just pressed\" and \"just released\" sets.\n   * This should be called once per game loop, typically by the main InputManager.\n   */\n  update() {\n    this.justPressedKeys.clear()\n    this.justReleasedKeys.clear()\n  }\n\n  /**\n   * Checks if a specific key is currently held down.\n   * @param {string} keyCode - The event.code of the key.\n   * @returns {boolean}\n   */\n  isKeyPressed(keyCode) {\n    return this.pressedKeys.has(keyCode)\n  }\n\n  /**\n   * Checks if a specific key was just pressed in the current frame.\n   * @param {string} keyCode - The event.code of the key.\n   * @returns {boolean}\n   */\n  isKeyJustPressed(keyCode) {\n    return this.justPressedKeys.has(keyCode)\n  }\n\n  /**\n   * Checks if a specific key was just released in the current frame.\n   * @param {string} keyCode - The event.code of the key.\n   * @returns {boolean}\n   */\n  isKeyJustReleased(keyCode) {\n    return this.justReleasedKeys.has(keyCode)\n  }\n\n  /**\n   * Cleans up event listeners and clears states.\n   */\n  destroy() {\n    window.removeEventListener('keydown', this._onKeyDown)\n    window.removeEventListener('keyup', this._onKeyUp)\n    this.pressedKeys.clear()\n    this.justPressedKeys.clear()\n    this.justReleasedKeys.clear()\n    // console.log(\"Keyboard module: Destroyed and listeners removed.\");\n  }\n}\n\nexport default Keyboard\n","// src/engine/core/Mouse.js\n\n/**\n * @file Mouse.js\n * @description Manages raw mouse input state (position, pressed, just pressed, just released buttons).\n */\n\nclass Mouse {\n  constructor() {\n    /** @private @type {HTMLCanvasElement | null} Reference to the game canvas */\n    this.canvas = null\n\n    /** @private @type {{x: number, y: number}} Mouse position relative to the browser viewport */\n    this.viewportPosition = { x: 0, y: 0 }\n    /** @private @type {{x: number, y: number}} Mouse position relative to the canvas element */\n    this.canvasPosition = { x: 0, y: 0 }\n\n    /** @private @type {Set<number>} Currently pressed mouse buttons (event.button code) */\n    this.pressedButtons = new Set()\n    /** @private @type {Set<number>} Mouse buttons just pressed this frame */\n    this.justPressedButtons = new Set()\n    /** @private @type {Set<number>} Mouse buttons just released this frame */\n    this.justReleasedButtons = new Set()\n\n    this._onMouseMove = this._onMouseMove.bind(this)\n    this._onMouseDown = this._onMouseDown.bind(this)\n    this._onMouseUp = this._onMouseUp.bind(this)\n    this._onContextMenu = this._onContextMenu.bind(this)\n    // console.log(\"Mouse module: Constructed\");\n  }\n\n  // Static constants for mouse button codes (matches event.button)\n  static BUTTON_LEFT = 0\n  static BUTTON_MIDDLE = 1\n  static BUTTON_RIGHT = 2\n  static BUTTON_BROWSER_BACK = 3\n  static BUTTON_BROWSER_FORWARD = 4\n\n  /**\n   * Initializes mouse event listeners on the provided canvas.\n   * @param {HTMLCanvasElement} canvasElement - The game canvas element.\n   */\n  initialize(canvasElement) {\n    if (!canvasElement || !(canvasElement instanceof HTMLCanvasElement)) {\n      console.error('Mouse.initialize: Invalid canvas element provided.')\n      return\n    }\n    this.canvas = canvasElement\n\n    this.canvas.addEventListener('mousemove', this._onMouseMove)\n    this.canvas.addEventListener('mousedown', this._onMouseDown)\n    this.canvas.addEventListener('mouseup', this._onMouseUp)\n    this.canvas.addEventListener('contextmenu', this._onContextMenu)\n\n    // console.log(\"Mouse module: Initialized and listeners attached to canvas.\");\n  }\n\n  _onMouseMove(event) {\n    if (!this.canvas) return\n    const rect = this.canvas.getBoundingClientRect()\n    this.viewportPosition.x = event.clientX\n    this.viewportPosition.y = event.clientY\n    this.canvasPosition.x = event.clientX - rect.left\n    this.canvasPosition.y = event.clientY - rect.top\n    const scaleX = this.canvas.width / rect.width\n    const scaleY = this.canvas.height / rect.height\n    this.canvasPosition.x *= scaleX\n    this.canvasPosition.y *= scaleY\n  }\n\n  _onMouseDown(event) {\n    const buttonCode = event.button\n    if (!this.pressedButtons.has(buttonCode)) {\n      this.justPressedButtons.add(buttonCode)\n    }\n    this.pressedButtons.add(buttonCode)\n\n    // Optional: Prevent default for browser navigation buttons if you want to use them in-game\n    // if (buttonCode === Mouse.BUTTON_BROWSER_BACK || buttonCode === Mouse.BUTTON_BROWSER_FORWARD) {\n    //     event.preventDefault();\n    // }\n  }\n\n  _onMouseUp(event) {\n    const buttonCode = event.button\n    this.pressedButtons.delete(buttonCode)\n    this.justReleasedButtons.add(buttonCode)\n  }\n\n  _onContextMenu(event) {\n    event.preventDefault()\n  }\n\n  update() {\n    this.justPressedButtons.clear()\n    this.justReleasedButtons.clear()\n  }\n\n  getViewportPosition() {\n    return { ...this.viewportPosition }\n  }\n\n  getCanvasPosition() {\n    return { ...this.canvasPosition }\n  }\n\n  isButtonPressed(buttonCode) {\n    return this.pressedButtons.has(buttonCode)\n  }\n\n  isButtonJustPressed(buttonCode) {\n    return this.justPressedButtons.has(buttonCode)\n  }\n\n  isButtonJustReleased(buttonCode) {\n    return this.justReleasedButtons.has(buttonCode)\n  }\n\n  destroy() {\n    if (this.canvas) {\n      this.canvas.removeEventListener('mousemove', this._onMouseMove)\n      this.canvas.removeEventListener('mousedown', this._onMouseDown)\n      this.canvas.removeEventListener('mouseup', this._onMouseUp)\n      this.canvas.removeEventListener('contextmenu', this._onContextMenu)\n    }\n    this.pressedButtons.clear()\n    this.justPressedButtons.clear()\n    this.justReleasedButtons.clear()\n    this.viewportPosition = { x: 0, y: 0 }\n    this.canvasPosition = { x: 0, y: 0 }\n    this.canvas = null\n    // console.log(\"Mouse module: Destroyed and listeners removed.\");\n  }\n}\n\nexport default Mouse\n","// src/engine/core/Gamepad.js\n\n/**\n * @file Gamepad.js\n * @description Manages gamepad input, including connection/disconnection,\n * button states, and axis values for multiple gamepads.\n */\n\nclass GamepadHandler {\n  constructor(inputManager) {\n    /** @private @type {import('./InputManager.js').default} Reference to the main InputManager */\n    this.inputManager = inputManager\n    this.connectedGamepads = new Map()\n    this.gamepadStates = new Map()\n    this.prevGamepadStates = new Map()\n    this.defaultDeadZone = 0.15 // Default dead zone for analog sticks\n    this._onGamepadConnected = this._onGamepadConnected.bind(this)\n    this._onGamepadDisconnected = this._onGamepadDisconnected.bind(this)\n  }\n\n  initialize() {\n    window.addEventListener('gamepadconnected', this._onGamepadConnected)\n    window.addEventListener('gamepaddisconnected', this._onGamepadDisconnected)\n    this.scanForGamepads()\n\n    console.log('GamepadHandler: Initialized, event listeners attached, and initial scan complete.')\n    if (this.connectedGamepads.size > 0) {\n      console.log(\n        `GamepadHandler: Found ${this.connectedGamepads.size} gamepad(s) on initialization.`,\n      )\n      this.connectedGamepads.forEach((gp) => {\n        console.log(\n          `  - Gamepad [${gp.index}]: ${gp.id}, Buttons: ${gp.buttons.length}, Axes: ${gp.axes.length}, Mapping: ${gp.mapping}`,\n        )\n      })\n    } else {\n      console.log('GamepadHandler: No gamepads initially detected. Waiting for connection events.')\n    }\n  }\n\n  _onGamepadConnected(event) {\n    const gamepad = event.gamepad\n    if (!gamepad) return\n    console.log(\n      `%cGamepadHandler: Gamepad CONNECTED at index ${gamepad.index}`,\n      'color: limegreen; font-weight: bold;',\n      `\\n  ID: ${gamepad.id}`,\n      `\\n  Buttons: ${gamepad.buttons.length}`,\n      `\\n  Axes: ${gamepad.axes.length}`,\n      `\\n  Mapping: \"${gamepad.mapping}\"`,\n    )\n    this.connectedGamepads.set(gamepad.index, gamepad)\n    if (!this.gamepadStates.has(gamepad.index)) {\n      this.gamepadStates.set(gamepad.index, this._createEmptyGamepadState(gamepad))\n      this.prevGamepadStates.set(gamepad.index, this._createEmptyGamepadState(gamepad))\n    }\n    if (this.inputManager && this.inputManager.engine && this.inputManager.engine.events) {\n      this.inputManager.engine.events.emit('gamepadConnected', {\n        index: gamepad.index,\n        id: gamepad.id,\n        buttons: gamepad.buttons.length,\n        axes: gamepad.axes.length,\n        mapping: gamepad.mapping,\n      })\n    }\n  }\n\n  _onGamepadDisconnected(event) {\n    const gamepad = event.gamepad\n    if (!gamepad) return\n    console.log(\n      `%cGamepadHandler: Gamepad DISCONNECTED at index ${gamepad.index}: ${gamepad.id}`,\n      'color: orange;',\n    )\n    this.connectedGamepads.delete(gamepad.index)\n    this.gamepadStates.delete(gamepad.index)\n    this.prevGamepadStates.delete(gamepad.index)\n    if (this.inputManager && this.inputManager.engine && this.inputManager.engine.events) {\n      this.inputManager.engine.events.emit('gamepadDisconnected', {\n        index: gamepad.index,\n        id: gamepad.id,\n      })\n    }\n  }\n\n  _createEmptyGamepadState(gamepad) {\n    return {\n      buttons: Array(gamepad.buttons.length)\n        .fill(null)\n        .map(() => ({ pressed: false, value: 0 })),\n      axes: Array(gamepad.axes.length).fill(0),\n    }\n  }\n\n  scanForGamepads() {\n    const rawGamepads = navigator.getGamepads ? navigator.getGamepads() : []\n    for (let i = 0; i < rawGamepads.length; i++) {\n      const gamepad = rawGamepads[i]\n      if (gamepad) {\n        if (!this.connectedGamepads.has(gamepad.index)) {\n          this._onGamepadConnected({ gamepad: gamepad })\n        }\n      } else {\n        if (this.connectedGamepads.has(i)) {\n          this._onGamepadDisconnected({ gamepad: this.connectedGamepads.get(i) })\n        }\n      }\n    }\n  }\n\n  update() {\n    this.scanForGamepads()\n    const rawGamepads = navigator.getGamepads ? navigator.getGamepads() : []\n\n    this.connectedGamepads.forEach((trackedGamepad, index) => {\n      const gamepad = rawGamepads[index]\n      if (gamepad) {\n        let currentState = this.gamepadStates.get(index)\n        let prevState = this.prevGamepadStates.get(index)\n\n        if (!currentState || !prevState) {\n          if (!this.gamepadStates.has(index)) {\n            this.gamepadStates.set(index, this._createEmptyGamepadState(gamepad))\n            this.prevGamepadStates.set(index, this._createEmptyGamepadState(gamepad))\n          }\n          currentState = this.gamepadStates.get(index)\n          prevState = this.prevGamepadStates.get(index)\n          if (!currentState || !prevState) return\n        }\n\n        // Copy current to previous for buttons\n        for (let b = 0; b < gamepad.buttons.length; b++) {\n          if (currentState.buttons[b]) {\n            prevState.buttons[b] = { ...currentState.buttons[b] }\n          } else {\n            prevState.buttons[b] = { pressed: false, value: 0 }\n            currentState.buttons[b] = { pressed: false, value: 0 }\n          }\n        }\n        // Copy current to previous for axes\n        for (let a = 0; a < gamepad.axes.length; a++) {\n          prevState.axes[a] = currentState.axes[a] || 0\n        }\n\n        // Update current state from raw gamepad data for buttons\n        for (let b = 0; b < gamepad.buttons.length; b++) {\n          const rawButton = gamepad.buttons[b]\n          if (currentState.buttons[b]) {\n            currentState.buttons[b].pressed = rawButton.pressed\n            currentState.buttons[b].value = rawButton.value\n\n            // --- ENHANCED LOG FOR ALL BUTTON STATE CHANGES ---\n            if (rawButton.pressed !== prevState.buttons[b].pressed) {\n              console.log(\n                `[GamepadHandler Raw] Gamepad ${index}, Button ${b} state changed. Pressed: ${rawButton.pressed}, Value: ${rawButton.value.toFixed(2)}`,\n              )\n            } else if (\n              rawButton.pressed &&\n              Math.abs(rawButton.value - prevState.buttons[b].value) > 0.01\n            ) {\n              // Log if an analog button's value changes significantly while pressed\n              console.log(\n                `[GamepadHandler Raw] Gamepad ${index}, Button ${b} VALUE CHANGED. Value: ${rawButton.value.toFixed(2)}`,\n              )\n            }\n            // --- END ENHANCED LOG ---\n          }\n        }\n        // Update current state from raw gamepad data for axes\n        for (let a = 0; a < gamepad.axes.length; a++) {\n          let value = gamepad.axes[a]\n          // --- ENHANCED LOG FOR ALL AXIS MOVEMENTS (above deadzone) ---\n          if (Math.abs(value) > this.defaultDeadZone * 0.5) {\n            // Log even slight movements\n            // console.log(`[GamepadHandler Raw] Gamepad ${index}, Axis ${a} RAW VALUE: ${value.toFixed(4)}`);\n          }\n          // --- END ENHANCED LOG ---\n          if (Math.abs(value) < this.defaultDeadZone) value = 0\n          currentState.axes[a] = value\n          if (value !== 0 && Math.abs(value - prevState.axes[a]) > 0.01) {\n            // Log if axis value is active and changed\n            console.log(\n              `[GamepadHandler Raw] Gamepad ${index}, Axis ${a} ACTIVE VALUE: ${value.toFixed(4)}`,\n            )\n          }\n        }\n      } else if (this.connectedGamepads.has(index)) {\n        this._onGamepadDisconnected({ gamepad: this.connectedGamepads.get(index) })\n      }\n    })\n  }\n\n  getConnectedGamepadCount() {\n    return this.connectedGamepads.size\n  }\n  getGamepadApiObject(gamepadIndex) {\n    return this.connectedGamepads.get(gamepadIndex) || null\n  }\n  isButtonPressed(gamepadIndex, buttonIndex) {\n    const state = this.gamepadStates.get(gamepadIndex)\n    return state && state.buttons[buttonIndex] ? state.buttons[buttonIndex].pressed : false\n  }\n  isButtonJustPressed(gamepadIndex, buttonIndex) {\n    const current = this.gamepadStates.get(gamepadIndex)\n    const prev = this.prevGamepadStates.get(gamepadIndex)\n    if (current && prev && current.buttons[buttonIndex] && prev.buttons[buttonIndex]) {\n      return current.buttons[buttonIndex].pressed && !prev.buttons[buttonIndex].pressed\n    }\n    return false\n  }\n  isButtonJustReleased(gamepadIndex, buttonIndex) {\n    const current = this.gamepadStates.get(gamepadIndex)\n    const prev = this.prevGamepadStates.get(gamepadIndex)\n    if (current && prev && current.buttons[buttonIndex] && prev.buttons[buttonIndex]) {\n      return !current.buttons[buttonIndex].pressed && prev.buttons[buttonIndex].pressed\n    }\n    return false\n  }\n  getButtonValue(gamepadIndex, buttonIndex) {\n    const state = this.gamepadStates.get(gamepadIndex)\n    return state && state.buttons[buttonIndex] ? state.buttons[buttonIndex].value : 0\n  }\n  getAxisValue(gamepadIndex, axisIndex) {\n    const state = this.gamepadStates.get(gamepadIndex)\n    return state && state.axes[axisIndex] !== undefined ? state.axes[axisIndex] : 0\n  }\n  destroy() {\n    window.removeEventListener('gamepadconnected', this._onGamepadConnected)\n    window.removeEventListener('gamepaddisconnected', this._onGamepadDisconnected)\n    this.connectedGamepads.clear()\n    this.gamepadStates.clear()\n    this.prevGamepadStates.clear()\n  }\n\n  static BUTTON_A = 0\n  static BUTTON_B = 1\n  static BUTTON_X = 2\n  static BUTTON_Y = 3\n  static BUTTON_L1 = 4\n  static BUTTON_R1 = 5\n  static BUTTON_L2 = 6\n  static BUTTON_R2 = 7\n  static BUTTON_SELECT = 8\n  static BUTTON_START = 9\n  static BUTTON_L3 = 10\n  static BUTTON_R3 = 11\n  static BUTTON_DPAD_UP = 12\n  static BUTTON_DPAD_DOWN = 13\n  static BUTTON_DPAD_LEFT = 14\n  static BUTTON_DPAD_RIGHT = 15\n  static BUTTON_GUIDE = 16\n  static AXIS_LEFT_STICK_X = 0\n  static AXIS_LEFT_STICK_Y = 1\n  static AXIS_RIGHT_STICK_X = 2\n  static AXIS_RIGHT_STICK_Y = 3\n}\nexport default GamepadHandler\n","// src/engine/core/InputManager.js\n\n/**\n * @file InputManager.js\n * @description Orchestrates keyboard and mouse input modules, and provides an action mapping system.\n */\n\nimport Keyboard from './Keyboard.js' // Assuming Keyboard.js is in the same directory\nimport Mouse from './Mouse.js' // Assuming Mouse.js is in the same directory\nimport GamepadHandler from './Gamepad.js'\n\n/**\n * @typedef {object} KeyBinding\n * @property {'key'} type\n * @property {string} code - The event.code for the keyboard key.\n */\n\n/**\n * @typedef {object} MouseBinding\n * @property {'mouse'} type\n * @property {number} button - The mouse button code (e.g., InputManager.MOUSE_BUTTON_LEFT).\n */\n\n/**\n * @typedef {object} GamepadButtonBinding\n * @property {'gamepadButton'} type\n * @property {number} buttonIndex - The index of the gamepad button.\n * @property {number} [padIndex=0] - The index of the gamepad (defaulting to 0).\n */\n\n/**\n * @typedef {object} GamepadAxisBinding\n * @property {'gamepadAxis'} type\n * @property {number} axisIndex - The index of the gamepad axis.\n * @property {number} direction - The direction to check for: 1 for positive, -1 for negative.\n * @property {number} [threshold=0.5] - The value the axis must exceed.\n * @property {number} [padIndex=0] - The index of the gamepad (defaulting to 0).\n */\n\n/**\n * @typedef {KeyBinding | MouseBinding | GamepadButtonBinding | GamepadAxisBinding} InputBinding\n */\n\n/**\n * @class InputManager\n * @description Central hub for input. Manages keyboard and mouse sub-modules,\n * and handles abstract input actions mapped to physical keys.\n * Call `initialize(canvas)` once, then `update()` once per game frame.\n */\n\nlet padIndexBtn\nlet padIndexAxis\nlet axisVal\nlet threshold\nlet direction\nclass InputManager {\n  constructor() {\n    /** @private @type {Keyboard} Handles keyboard input */\n    this.keyboard = new Keyboard()\n    /** @private @type {Mouse} Handles mouse input */\n    this.mouse = new Mouse()\n    /** @private @type {GamepadHandler} Handles gamepad input */ // ADD THIS\n    this.gamepadHandler = new GamepadHandler(this) // ADD THIS (pass 'this' for potential engine access)\n\n    // --- Action Mapping Properties (remain from your original InputManager) ---\n    /** @private @type {Map<string, Array<InputBinding>>} Maps action names to an array of their InputBinding objects. */ // MODIFIED THIS\n    this.actionToBindingsMap = new Map()\n    /** @private @type {Map<string, string[]>} Maps KEYBOARD input codes back to a list of action names */\n    this.codeToActionsMap = new Map()\n    /** @private @type {Map<string, {pressed: boolean, justPressed: boolean, justReleased: boolean}>} Stores action states */\n    this.actionStates = new Map()\n\n    // Bind event handlers FOR ACTION MAPPING to this instance\n    // These will listen to window events directly for simplicity in action processing\n    this._onKeyDownForActions = this._onKeyDownForActions.bind(this)\n    this._onKeyUpForActions = this._onKeyUpForActions.bind(this)\n\n    console.log('InputManager: Constructed. Orchestrates Keyboard and Mouse modules.')\n  }\n\n  // --- Static Constants for Mouse Buttons (exposed via InputManager for convenience) ---\n  static MOUSE_BUTTON_LEFT = Mouse.BUTTON_LEFT\n  static MOUSE_BUTTON_MIDDLE = Mouse.BUTTON_MIDDLE\n  static MOUSE_BUTTON_RIGHT = Mouse.BUTTON_RIGHT\n  static MOUSE_BUTTON_BROWSER_BACK = Mouse.BUTTON_BROWSER_BACK\n  static MOUSE_BUTTON_BROWSER_FORWARD = Mouse.BUTTON_BROWSER_FORWARD\n\n  // --- Static Constants for Gamepad Buttons (exposed via InputManager) ---\n  static GP_BUTTON_A = GamepadHandler.BUTTON_A\n  static GP_BUTTON_B = GamepadHandler.BUTTON_B\n  static GP_BUTTON_X = GamepadHandler.BUTTON_X\n  static GP_BUTTON_Y = GamepadHandler.BUTTON_Y\n  static GP_BUTTON_L1 = GamepadHandler.BUTTON_L1 // Left Bumper\n  static GP_BUTTON_R1 = GamepadHandler.BUTTON_R1 // Right Bumper\n  static GP_BUTTON_L2 = GamepadHandler.BUTTON_L2 // Left Trigger\n  static GP_BUTTON_R2 = GamepadHandler.BUTTON_R2 // Right Trigger\n  static GP_BUTTON_SELECT = GamepadHandler.BUTTON_SELECT // Back/Select\n  static GP_BUTTON_START = GamepadHandler.BUTTON_START // Start/Menu\n  static GP_BUTTON_L3 = GamepadHandler.BUTTON_L3 // Left Stick Press\n  static GP_BUTTON_R3 = GamepadHandler.BUTTON_R3 // Right Stick Press\n  static GP_BUTTON_DPAD_UP = GamepadHandler.BUTTON_DPAD_UP\n  static GP_BUTTON_DPAD_DOWN = GamepadHandler.BUTTON_DPAD_DOWN\n  static GP_BUTTON_DPAD_LEFT = GamepadHandler.BUTTON_DPAD_LEFT\n  static GP_BUTTON_DPAD_RIGHT = GamepadHandler.BUTTON_DPAD_RIGHT\n  static GP_BUTTON_GUIDE = GamepadHandler.BUTTON_GUIDE // Home/Guide\n\n  // --- Static Constants for Gamepad Axes ---\n  static GP_AXIS_LEFT_STICK_X = GamepadHandler.AXIS_LEFT_STICK_X\n  static GP_AXIS_LEFT_STICK_Y = GamepadHandler.AXIS_LEFT_STICK_Y\n  static GP_AXIS_RIGHT_STICK_X = GamepadHandler.AXIS_RIGHT_STICK_X\n  static GP_AXIS_RIGHT_STICK_Y = GamepadHandler.AXIS_RIGHT_STICK_Y\n\n  /**\n   * Initializes the InputManager and its sub-modules (Keyboard, Mouse, Gamepad).\n   * @param {HTMLCanvasElement} canvasElement - The game canvas element (required for Mouse).\n   * @param {import('./IroncladEngine.js').default} [engine] - Optional: engine reference for event emitting by sub-modules.\n   */\n  initialize(canvasElement, engine = null) {\n    this.engine = engine // Store engine reference if provided (GamepadHandler might use it)\n\n    this.keyboard.initialize()\n    this.mouse.initialize(canvasElement) // Mouse module needs the canvas\n    this.gamepadHandler.initialize()\n\n    // Add listeners specifically for the action mapping system\n    // These are separate from the listeners within Keyboard.js for raw state,\n    // though they listen to the same DOM events. This keeps action logic central.\n    window.addEventListener('keydown', this._onKeyDownForActions)\n    window.addEventListener('keyup', this._onKeyUpForActions)\n\n    console.log('InputManager: Initialized. Keyboard and Mouse modules are active.')\n  }\n\n  /**\n   * @private Handles keydown events specifically for updating action states.\n   * Relies on `this.keyboard` for the definitive state of raw keys if needed.\n   */\n  _onKeyDownForActions(event) {\n    const code = event.code\n    const actions = this.codeToActionsMap.get(code)\n\n    if (actions) {\n      actions.forEach((actionName) => {\n        const state = this.actionStates.get(actionName)\n        if (state) {\n          // To be \"justPressed\", the action must not have been pressed,\n          // AND at least one of its keys must NOW be considered pressed.\n          // The Keyboard module has already updated its state from this same event.\n          let isAnyMappedKeyPressed = false\n          const mappedCodes = this.actionToBindingsMap.get(actionName)\n          if (mappedCodes) {\n            for (const mappedCode of mappedCodes) {\n              // Query the Keyboard module for the current state\n              if (this.keyboard.isKeyPressed(mappedCode)) {\n                isAnyMappedKeyPressed = true\n                break\n              }\n            }\n          }\n\n          if (isAnyMappedKeyPressed && !state.pressed) {\n            state.justPressed = true\n          }\n          if (isAnyMappedKeyPressed) {\n            // If any key for the action is down\n            state.pressed = true\n          }\n        }\n      })\n    }\n  }\n\n  /**\n   * @private Handles keyup events specifically for updating action states.\n   * Relies on `this.keyboard` for the definitive state of raw keys.\n   */\n  _onKeyUpForActions(event) {\n    const code = event.code\n    const actions = this.codeToActionsMap.get(code)\n\n    if (actions) {\n      actions.forEach((actionName) => {\n        const state = this.actionStates.get(actionName)\n        if (state && state.pressed) {\n          // Only process if action was considered pressed\n          // Check if any *other* key for this action is still pressed\n          let isStillPressedByOtherKey = false\n          const mappedCodes = this.actionToBindingsMap.get(actionName)\n          if (mappedCodes) {\n            for (const mappedCode of mappedCodes) {\n              // Query the Keyboard module, note: event.code (the released key) will be reported as NOT pressed by keyboard module at this point.\n              if (mappedCode !== code && this.keyboard.isKeyPressed(mappedCode)) {\n                isStillPressedByOtherKey = true\n                break\n              }\n            }\n          }\n\n          if (!isStillPressedByOtherKey) {\n            state.justReleased = true\n            state.pressed = false\n          }\n        }\n      })\n    }\n  }\n\n  /**\n   * Updates all input modules and polls all bindings to update action states.\n   * Should be called once per game loop frame by the engine.\n   */\n  update() {\n    this.keyboard.update() // Clears keyboard's \"just\" states\n    this.mouse.update() // Clears mouse's \"just\" states\n    this.gamepadHandler.update() // Polls gamepads and updates their \"just\" states\n\n    // Update all action states by polling current input states from modules\n    this.actionToBindingsMap.forEach((bindings, actionName) => {\n      const currentActionState = this.actionStates.get(actionName)\n      if (!currentActionState) return\n\n      const oldPressedState = currentActionState.pressed\n      let newPressedState = false\n      let highestActionValue = 0 // For analog actions, take the strongest input\n\n      for (const binding of bindings) {\n        let bindingIsActive = false\n        let bindingValue = 0\n\n        switch (binding.type) {\n          case 'key':\n            bindingIsActive = this.keyboard.isKeyPressed(binding.code)\n            if (bindingIsActive) bindingValue = 1 // Digital input\n            break\n          case 'mouse': // Assuming you might want to map mouse buttons to actions\n            bindingIsActive = this.mouse.isButtonPressed(binding.button)\n            if (bindingIsActive) bindingValue = 1 // Digital input\n            break\n          case 'gamepadButton':\n            padIndexBtn = binding.padIndex || 0\n            bindingIsActive = this.gamepadHandler.isButtonPressed(padIndexBtn, binding.buttonIndex)\n            if (bindingIsActive) {\n              bindingValue = this.gamepadHandler.getButtonValue(padIndexBtn, binding.buttonIndex)\n              // Ensure digital button presses (like D-pad) contribute a value of 1\n              if (bindingValue === 0 && bindingIsActive) bindingValue = 1.0\n\n              if (actionName.startsWith('move')) {\n                // Conditional log\n                console.log(\n                  `[InputManager] Action: \"${actionName}\", GamepadBtn[${padIndexBtn}][${binding.buttonIndex}] Active: ${bindingIsActive}, RawValFromGetBtnVal: ${this.gamepadHandler.getButtonValue(padIndexBtn, binding.buttonIndex).toFixed(2)}, EffectiveBindingVal: ${bindingValue.toFixed(2)}`,\n                )\n              }\n            }\n            break\n          case 'gamepadAxis':\n            padIndexAxis = binding.padIndex || 0\n            axisVal = this.gamepadHandler.getAxisValue(padIndexAxis, binding.axisIndex)\n            threshold = binding.threshold === undefined ? 0.3 : binding.threshold\n            direction = binding.direction // Should be 1 (positive) or -1 (negative)\n\n            if (direction > 0 && axisVal >= threshold) {\n              bindingIsActive = true\n              bindingValue =\n                1 - threshold > 0 ? Math.min(1, (axisVal - threshold) / (1 - threshold)) : 1\n            } else if (direction < 0 && axisVal <= -threshold) {\n              bindingIsActive = true\n              bindingValue =\n                1 - threshold > 0\n                  ? Math.min(1, (Math.abs(axisVal) - threshold) / (1 - threshold))\n                  : 1\n            }\n            // --- DEBUG LOG FOR GAMEPAD AXIS BINDING ---\n            if (bindingIsActive && actionName.startsWith('move')) {\n              // Log only for move actions\n              console.log(\n                `[InputManager] Action: \"${actionName}\", GamepadAxis[${padIndexAxis}][${binding.axisIndex}] Raw: ${axisVal.toFixed(2)}, Dir: ${direction}, Thresh: ${threshold}, Active: ${bindingIsActive}, Value: ${bindingValue.toFixed(2)}`,\n              )\n            }\n            break\n        }\n        if (bindingIsActive) {\n          newPressedState = true\n          highestActionValue = Math.max(highestActionValue, bindingValue)\n          // If one binding is active, the action is considered active.\n          // We could break here if we only care about on/off,\n          // but iterating all allows us to get the max analog value.\n        }\n      }\n\n      currentActionState.pressed = newPressedState\n      currentActionState.value = newPressedState ? highestActionValue : 0\n      currentActionState.justPressed = !oldPressedState && newPressedState\n      currentActionState.justReleased = oldPressedState && !newPressedState\n    })\n  }\n\n  /**\n   * Defines an action and maps it to one or more input bindings.\n   * @param {string} actionName - The unique name for the action (e.g., \"moveUp\", \"fire\").\n   * @param {Array<InputBinding>} bindings - An array of binding objects.\n   */\n  defineAction(actionName, bindings) {\n    if (!Array.isArray(bindings) || bindings.length === 0) {\n      console.warn(`InputManager: No bindings provided for action \"${actionName}\".`)\n      return\n    }\n    this.actionToBindingsMap.set(actionName, bindings)\n    // Initialize action state with value field\n    this.actionStates.set(actionName, {\n      pressed: false,\n      justPressed: false,\n      justReleased: false,\n      value: 0,\n    })\n\n    // The codeToActionsMap is primarily for keyboard event-driven updates.\n    // With polling in update(), its role might change or diminish for actions.\n    // For now, let's keep it for keyboard bindings for the _onKeyDownForActions.\n    bindings.forEach((binding) => {\n      if (binding.type === 'key') {\n        const code = binding.code\n        if (!this.codeToActionsMap.has(code)) {\n          this.codeToActionsMap.set(code, [])\n        }\n        const actionsForCode = this.codeToActionsMap.get(code)\n        if (!actionsForCode.includes(actionName)) {\n          actionsForCode.push(actionName)\n        }\n      }\n    })\n    // console.log(`InputManager: Action \"${actionName}\" defined with ${bindings.length} binding(s).`);\n  }\n\n  removeAction(actionName) {\n    // ... (implementation from your existing InputManager is fine) ...\n    const codes = this.actionToBindingsMap.get(actionName)\n    if (codes) {\n      codes.forEach((code) => {\n        const actionsForCode = this.codeToActionsMap.get(code)\n        if (actionsForCode) {\n          const index = actionsForCode.indexOf(actionName)\n          if (index > -1) actionsForCode.splice(index, 1)\n          if (actionsForCode.length === 0) this.codeToActionsMap.delete(code)\n        }\n      })\n    }\n    this.actionToBindingsMap.delete(actionName)\n    this.actionStates.delete(actionName)\n    // console.log(`InputManager: Action \"${actionName}\" removed.`);\n  }\n\n  isActionPressed(actionName) {\n    const state = this.actionStates.get(actionName)\n    return state ? state.pressed : false\n  }\n\n  isActionJustPressed(actionName) {\n    const state = this.actionStates.get(actionName)\n    return state ? state.justPressed : false\n  }\n\n  isActionJustReleased(actionName) {\n    const state = this.actionStates.get(actionName)\n    return state ? state.justReleased : false\n  }\n\n  // --- Raw Key State API (Delegates to Keyboard module) ---\n  isKeyPressed(keyCode) {\n    return this.keyboard.isKeyPressed(keyCode)\n  }\n  isKeyJustPressed(keyCode) {\n    return this.keyboard.isKeyJustPressed(keyCode)\n  }\n  isKeyJustReleased(keyCode) {\n    return this.keyboard.isKeyJustReleased(keyCode)\n  }\n\n  // --- Raw Mouse State API (Delegates to Mouse module) ---\n  getMouseViewportPosition() {\n    // Renamed to avoid conflict if you had getMousePosition before\n    return this.mouse.getViewportPosition()\n  }\n  getCanvasMousePosition() {\n    return this.mouse.getCanvasPosition()\n  }\n  isMouseButtonPressed(buttonCode) {\n    return this.mouse.isButtonPressed(buttonCode)\n  }\n  isMouseButtonJustPressed(buttonCode) {\n    return this.mouse.isButtonJustPressed(buttonCode)\n  }\n  isMouseButtonJustReleased(buttonCode) {\n    return this.mouse.isButtonJustReleased(buttonCode)\n  }\n  // --- Raw Gamepad State API (Delegates to GamepadHandler module) ---\n  getConnectedGamepadCount() {\n    return this.gamepadHandler.getConnectedGamepadCount()\n  }\n  isGamepadButtonPressed(padIndex, buttonIndex) {\n    return this.gamepadHandler.isButtonPressed(padIndex, buttonIndex)\n  }\n  isGamepadButtonJustPressed(padIndex, buttonIndex) {\n    return this.gamepadHandler.isButtonJustPressed(padIndex, buttonIndex)\n  }\n  isGamepadButtonJustReleased(padIndex, buttonIndex) {\n    return this.gamepadHandler.isButtonJustReleased(padIndex, buttonIndex)\n  }\n  getGamepadButtonValue(padIndex, buttonIndex) {\n    return this.gamepadHandler.getButtonValue(padIndex, buttonIndex)\n  }\n  getGamepadAxisValue(padIndex, axisIndex) {\n    return this.gamepadHandler.getAxisValue(padIndex, axisIndex)\n  }\n\n  /**\n   * Gets the analog value of an action (e.g., from an axis or trigger).\n   * Typically ranges from 0 to 1.\n   * @param {string} actionName - The name of the action.\n   * @returns {number}\n   */\n  getActionValue(actionName) {\n    const state = this.actionStates.get(actionName)\n    return state ? state.value : 0\n  }\n\n  /**\n   * Cleans up event listeners and all input states from sub-modules.\n   */\n  destroy() {\n    // Remove action-specific keyboard listeners\n    window.removeEventListener('keydown', this._onKeyDownForActions)\n    window.removeEventListener('keyup', this._onKeyUpForActions)\n\n    this.keyboard.destroy()\n    this.mouse.destroy()\n    this.gamepadHandler.destroy() // ADD THIS LINE\n\n    this.actionToBindingsMap.clear() // MODIFIED\n    this.codeToActionsMap.clear() // Keep if still used by keyboard action handlers\n    this.actionStates.clear()\n    console.log('InputManager: Destroyed. All input modules destroyed.')\n  }\n}\n\nexport default InputManager\n","// src/engine/core/EventManager.js\n\n/**\n * @file EventManager.js\n * @description A simple publish/subscribe event system for the Ironclad Engine.\n */\n\n/**\n * @class EventManager\n * @description Allows for registering, unregistering, and emitting custom events.\n */\nclass EventManager {\n  constructor() {\n    /**\n     * @private\n     * @type {Map<string, Array<{callback: Function, context: any, once: boolean}>>}\n     * @description Stores event listeners. The key is the eventName,\n     * and the value is an array of listener objects.\n     */\n    this.listeners = new Map()\n    console.log('EventManager: Initialized.')\n  }\n\n  /**\n   * Subscribes a callback function to a specific event.\n   * @param {string} eventName - The name of the event to subscribe to.\n   * @param {Function} callback - The function to call when the event is emitted.\n   * @param {any} [context=null] - The `this` context for the callback function.\n   * @param {boolean} [once=false] - If true, the listener will be removed after the first time it's triggered.\n   */\n  on(eventName, callback, context = null, once = false) {\n    if (typeof callback !== 'function') {\n      console.error(\n        `EventManager.on: Provided callback for event \"${eventName}\" is not a function.`,\n      )\n      return\n    }\n    if (!this.listeners.has(eventName)) {\n      this.listeners.set(eventName, [])\n    }\n    this.listeners.get(eventName).push({ callback, context, once })\n    // console.log(`EventManager: Listener added for event \"${eventName}\".`);\n  }\n\n  /**\n   * Subscribes a callback function to a specific event, to be triggered only once.\n   * @param {string} eventName - The name of the event to subscribe to.\n   * @param {Function} callback - The function to call when the event is emitted.\n   * @param {any} [context=null] - The `this` context for the callback function.\n   */\n  once(eventName, callback, context = null) {\n    this.on(eventName, callback, context, true)\n  }\n\n  /**\n   * Unsubscribes a callback function from a specific event.\n   * For this to work reliably, the same function reference used for `on` must be provided.\n   * If context was provided during `on`, it should ideally be matched too for more specific unsubscription,\n   * but this basic version primarily relies on the callback reference.\n   * @param {string} eventName - The name of the event to unsubscribe from.\n   * @param {Function} callback - The callback function to remove.\n   * @param {any} [context=null] - Optional: The context that was used during subscription.\n   */\n  off(eventName, callback, context = null) {\n    if (typeof callback !== 'function') {\n      console.error(\n        `EventManager.off: Provided callback for event \"${eventName}\" is not a function.`,\n      )\n      return\n    }\n    const eventListeners = this.listeners.get(eventName)\n    if (eventListeners) {\n      // Filter out the listener. If context is also provided, match it.\n      // This creates a new array, which is fine.\n      this.listeners.set(\n        eventName,\n        eventListeners.filter((listener) => {\n          return (\n            listener.callback !== callback || (context !== null && listener.context !== context)\n          )\n        }),\n      )\n      // console.log(`EventManager: Listener removed for event \"${eventName}\".`);\n    }\n  }\n\n  /**\n   * Emits an event, calling all subscribed callback functions.\n   * @param {string} eventName - The name of the event to emit.\n   * @param {...any} args - Arguments to pass to the callback functions.\n   */\n  emit(eventName, ...args) {\n    const eventListeners = this.listeners.get(eventName)\n    if (eventListeners && eventListeners.length > 0) {\n      // console.log(`EventManager: Emitting event \"${eventName}\" with args:`, args);\n      // Iterate over a copy of the listeners array in case a listener modifies the original array (e.g., by calling off or on)\n      const listenersToCall = [...eventListeners]\n\n      listenersToCall.forEach((listener) => {\n        try {\n          listener.callback.apply(listener.context, args)\n        } catch (error) {\n          console.error(`EventManager: Error in listener for event \"${eventName}\":`, error)\n        }\n        if (listener.once) {\n          // If it was a 'once' listener, remove it using the more specific 'off'\n          this.off(eventName, listener.callback, listener.context)\n        }\n      })\n    }\n  }\n\n  /**\n   * Removes all listeners for a specific event.\n   * @param {string} eventName - The name of the event.\n   */\n  removeAllListeners(eventName) {\n    if (this.listeners.has(eventName)) {\n      this.listeners.delete(eventName)\n      console.log(`EventManager: All listeners removed for event \"${eventName}\".`)\n    }\n  }\n\n  /**\n   * Removes all listeners for all events. Use with caution (e.g., for full engine reset).\n   */\n  clearAll() {\n    this.listeners.clear()\n    console.log('EventManager: All event listeners cleared.')\n  }\n}\n\nexport default EventManager\n","// src/engine/ecs/EntityManager.js\n\n/**\n * @file EntityManager.js\n * @description Manages entities and their components in an Entity-Component-System architecture.\n */\n\n/**\n * @typedef {number} EntityId - A unique identifier for an entity.\n * @typedef {string} ComponentTypeName - The string identifier for a component type (e.g., \"Position\", \"Health\").\n * @typedef {object} ComponentData - A plain JavaScript object representing the data for a component.\n */\n\n/**\n * @class EntityManager\n * @description Handles creation, deletion, and querying of entities and their components.\n */\nclass EntityManager {\n  constructor() {\n    /**\n     * @private\n     * @type {number} The next available ID for a new entity.\n     */\n    this.nextEntityId = 0\n\n    /**\n     * @private\n     * @type {Set<EntityId>} Stores all currently active entity IDs.\n     */\n    this.entities = new Set()\n\n    /**\n     * @private\n     * @type {Map<EntityId, Map<ComponentTypeName, ComponentData>>}\n     * @description Stores components for each entity.\n     * Outer Map: entityId -> Inner Map: componentTypeName -> componentInstance\n     */\n    this.componentsByEntity = new Map()\n\n    /**\n     * @private\n     * @type {Map<ComponentTypeName, Set<EntityId>>}\n     * @description Stores entities grouped by their component types for efficient querying.\n     * Outer Map: componentTypeName -> Inner Set: Set of entityIds\n     */\n    this.entitiesByComponentType = new Map()\n\n    console.log('EntityManager: Initialized.')\n  }\n\n  /**\n   * Creates a new entity and returns its ID.\n   * @returns {EntityId} The ID of the newly created entity.\n   */\n  createEntity() {\n    const entityId = this.nextEntityId++\n    this.entities.add(entityId)\n    this.componentsByEntity.set(entityId, new Map())\n    // console.log(`EntityManager: Entity ${entityId} created.`);\n    return entityId\n  }\n\n  /**\n   * Destroys an entity and removes all its associated components.\n   * @param {EntityId} entityId - The ID of the entity to destroy.\n   */\n  destroyEntity(entityId) {\n    if (!this.entities.has(entityId)) {\n      // console.warn(`EntityManager: Entity ${entityId} not found or already destroyed.`);\n      return\n    }\n\n    // Remove entity from component-based lookups\n    const entityComponents = this.componentsByEntity.get(entityId)\n    if (entityComponents) {\n      entityComponents.forEach((component, componentTypeName) => {\n        const entitiesWithThisComponent = this.entitiesByComponentType.get(componentTypeName)\n        if (entitiesWithThisComponent) {\n          entitiesWithThisComponent.delete(entityId)\n        }\n      })\n    }\n\n    this.componentsByEntity.delete(entityId)\n    this.entities.delete(entityId)\n    // console.log(`EntityManager: Entity ${entityId} destroyed.`);\n  }\n\n  /**\n   * Checks if an entity is still alive (exists).\n   * @param {EntityId} entityId - The ID of the entity to check.\n   * @returns {boolean} True if the entity exists, false otherwise.\n   */\n  isEntityAlive(entityId) {\n    return this.entities.has(entityId)\n  }\n\n  /**\n   * Adds a component to an entity. If a component of the same type already exists, it's overwritten.\n   * @param {EntityId} entityId - The ID of the entity.\n   * @param {ComponentTypeName} componentTypeName - The string type/name of the component (e.g., \"Position\").\n   * @param {ComponentData} data - The data for the component.\n   * @returns {ComponentData | undefined} The added component data, or undefined if the entity doesn't exist.\n   */\n  addComponent(entityId, componentTypeName, data) {\n    if (!this.entities.has(entityId)) {\n      console.warn(\n        `EntityManager.addComponent: Entity ${entityId} not found. Cannot add component \"${componentTypeName}\".`,\n      )\n      return undefined\n    }\n    if (typeof componentTypeName !== 'string' || !componentTypeName) {\n      console.error(`EntityManager.addComponent: componentTypeName must be a non-empty string.`)\n      return undefined\n    }\n    if (typeof data !== 'object' || data === null) {\n      console.error(\n        `EntityManager.addComponent: Component data for \"${componentTypeName}\" must be an object.`,\n      )\n      return undefined\n    }\n\n    this.componentsByEntity.get(entityId).set(componentTypeName, data)\n\n    if (!this.entitiesByComponentType.has(componentTypeName)) {\n      this.entitiesByComponentType.set(componentTypeName, new Set())\n    }\n    this.entitiesByComponentType.get(componentTypeName).add(entityId)\n\n    // console.log(`EntityManager: Component \"${componentTypeName}\" added to entity ${entityId}.`, data);\n    return data\n  }\n\n  /**\n   * Removes a component from an entity.\n   * @param {EntityId} entityId - The ID of the entity.\n   * @param {ComponentTypeName} componentTypeName - The type/name of the component to remove.\n   * @returns {boolean} True if the component was removed, false otherwise (e.g., entity or component not found).\n   */\n  removeComponent(entityId, componentTypeName) {\n    if (!this.entities.has(entityId)) {\n      // console.warn(`EntityManager.removeComponent: Entity ${entityId} not found.`);\n      return false\n    }\n    const entityComponents = this.componentsByEntity.get(entityId)\n    if (!entityComponents || !entityComponents.has(componentTypeName)) {\n      // console.warn(`EntityManager.removeComponent: Component \"${componentTypeName}\" not found on entity ${entityId}.`);\n      return false\n    }\n\n    entityComponents.delete(componentTypeName)\n\n    const entitiesWithThisComponent = this.entitiesByComponentType.get(componentTypeName)\n    if (entitiesWithThisComponent) {\n      entitiesWithThisComponent.delete(entityId)\n      if (entitiesWithThisComponent.size === 0) {\n        // Optional: clean up empty sets from entitiesByComponentType\n        // this.entitiesByComponentType.delete(componentTypeName);\n      }\n    }\n    // console.log(`EntityManager: Component \"${componentTypeName}\" removed from entity ${entityId}.`);\n    return true\n  }\n\n  /**\n   * Retrieves a component from an entity.\n   * @param {EntityId} entityId - The ID of the entity.\n   * @param {ComponentTypeName} componentTypeName - The type/name of the component to retrieve.\n   * @returns {ComponentData | undefined} The component data, or undefined if not found.\n   */\n  getComponent(entityId, componentTypeName) {\n    const entityComponents = this.componentsByEntity.get(entityId)\n    return entityComponents ? entityComponents.get(componentTypeName) : undefined\n  }\n\n  /**\n   * Checks if an entity has a specific component.\n   * @param {EntityId} entityId - The ID of the entity.\n   * @param {ComponentTypeName} componentTypeName - The type/name of the component to check for.\n   * @returns {boolean} True if the entity has the component, false otherwise.\n   */\n  hasComponent(entityId, componentTypeName) {\n    const entityComponents = this.componentsByEntity.get(entityId)\n    return entityComponents ? entityComponents.has(componentTypeName) : false\n  }\n\n  /**\n   * Retrieves all components for a given entity.\n   * @param {EntityId} entityId - The ID of the entity.\n   * @returns {Map<ComponentTypeName, ComponentData> | undefined} A map of component type names to component data, or undefined if entity not found.\n   */\n  getAllComponents(entityId) {\n    return this.componentsByEntity.get(entityId)\n  }\n\n  /**\n   * Retrieves all entity IDs that possess all of the specified component types.\n   * @param {ComponentTypeName[]} componentTypeNames - An array of component type names.\n   * @returns {EntityId[]} An array of entity IDs that have all the specified components.\n   * Returns an empty array if no component types are specified or no entities match.\n   */\n  getEntitiesWithComponents(componentTypeNames) {\n    if (!Array.isArray(componentTypeNames) || componentTypeNames.length === 0) {\n      return []\n    }\n\n    // Find the smallest set of entities for the first component type to iterate over\n    let smallestSet = null\n    let smallestSetSize = Infinity\n\n    for (const typeName of componentTypeNames) {\n      const entitiesForType = this.entitiesByComponentType.get(typeName)\n      if (!entitiesForType) {\n        return [] // If any component type has no entities, then no entity can have all of them.\n      }\n      if (entitiesForType.size < smallestSetSize) {\n        smallestSet = entitiesForType\n        smallestSetSize = entitiesForType.size\n      }\n    }\n\n    if (!smallestSet) {\n      // Should only happen if componentTypeNames was empty, already handled.\n      return []\n    }\n\n    const resultEntities = []\n    smallestSet.forEach((entityId) => {\n      // Ensure the entity still exists and has ALL other required components\n      if (this.entities.has(entityId)) {\n        let hasAll = true\n        for (const typeName of componentTypeNames) {\n          if (!this.hasComponent(entityId, typeName)) {\n            hasAll = false\n            break\n          }\n        }\n        if (hasAll) {\n          resultEntities.push(entityId)\n        }\n      }\n    })\n    return resultEntities\n  }\n}\n\nexport default EntityManager\n","// src/engine/ecs/PrefabManager.js\n\n/**\n * @file PrefabManager.js\n * @description Manages entity prefab definitions and facilitates spawning entities from them.\n */\n\n/**\n * @typedef {object} ComponentDefinition\n * @property {import('./EntityManager.js').ComponentTypeName} type - The type name of the component.\n * @property {object} data - The initial data for the component.\n */\n\n/**\n * @typedef {object} PrefabDefinition\n * @property {string} [description] - An optional description of the prefab.\n * @property {Array<ComponentDefinition>} components - An array of component definitions.\n */\n\n/**\n * @class PrefabManager\n * @description Loads, stores, and manages entity prefab definitions from JSON.\n * Provides functionality to spawn entities based on these prefabs.\n */\nclass PrefabManager {\n  /**\n   * Creates an instance of PrefabManager.\n   * @param {import('./EntityManager.js').default} entityManager - Reference to the EntityManager.\n   * @param {import('../core/AssetLoader.js').default} assetLoader - Reference to the AssetLoader.\n   */\n  constructor(entityManager, assetLoader) {\n    if (!entityManager) {\n      throw new Error('PrefabManager: EntityManager instance is required.')\n    }\n    if (!assetLoader) {\n      throw new Error('PrefabManager: AssetLoader instance is required.')\n    }\n    /**\n     * @private\n     * @type {import('./EntityManager.js').default}\n     */\n    this.entityManager = entityManager\n    /**\n     * @private\n     * @type {import('../core/AssetLoader.js').default}\n     */\n    this.assetLoader = assetLoader\n\n    /**\n     * @private\n     * @type {Map<string, PrefabDefinition>}\n     * @description Stores all loaded prefab definitions, keyed by prefab name.\n     */\n    this.prefabs = new Map()\n\n    console.log('PrefabManager: Initialized.')\n  }\n\n  /**\n   * Loads prefab definitions from a previously loaded JSON asset.\n   * @param {string} assetKey - The key used in AssetLoader for the loaded JSON file containing prefab definitions.\n   * @returns {boolean} True if prefabs were successfully parsed and added, false otherwise.\n   */\n  loadPrefabsFromAsset(assetKey) {\n    const prefabData = this.assetLoader.get(assetKey)\n    if (!prefabData || typeof prefabData !== 'object') {\n      console.error(\n        `PrefabManager: Prefab data asset \"${assetKey}\" not found or is not a valid object.`,\n      )\n      return false\n    }\n\n    let count = 0\n    for (const prefabName in prefabData) {\n      if (Object.hasOwnProperty.call(prefabData, prefabName)) {\n        const definition = prefabData[prefabName]\n        if (definition && Array.isArray(definition.components)) {\n          this.prefabs.set(prefabName, definition)\n          count++\n        } else {\n          console.warn(\n            `PrefabManager: Invalid prefab definition for \"${prefabName}\" in asset \"${assetKey}\". Missing \"components\" array.`,\n          )\n        }\n      }\n    }\n    console.log(`PrefabManager: Loaded ${count} prefabs from asset \"${assetKey}\".`)\n    return count > 0\n  }\n\n  /**\n   * Spawns an entity based on a prefab definition.\n   * @param {string} prefabName - The name of the prefab to spawn.\n   * @param {object} [overrideData={}] - An object containing data to override the prefab's defaults.\n   * Keys are component type names, values are objects with properties to override.\n   * Example: { \"Position\": { \"x\": 100, \"y\": 200 }, \"Health\": { \"current\": 50 } }\n   * @returns {import('./EntityManager.js').EntityId | null} The ID of the spawned entity, or null if prefab not found or error.\n   */\n  spawnEntity(prefabName, overrideData = {}) {\n    const prefab = this.prefabs.get(prefabName)\n    if (!prefab) {\n      console.error(`PrefabManager: Prefab \"${prefabName}\" not found.`)\n      return null\n    }\n\n    const entityId = this.entityManager.createEntity()\n    if (entityId === null || entityId === undefined) {\n      // Check if entity creation failed (shouldn't with current EM)\n      console.error(`PrefabManager: Failed to create entity for prefab \"${prefabName}\".`)\n      return null\n    }\n\n    // console.log(`PrefabManager: Spawning entity ${entityId} from prefab \"${prefabName}\" with overrides:`, overrideData);\n\n    prefab.components.forEach((compDef) => {\n      // Deep clone the component data from the prefab to avoid shared references\n      let finalComponentData = JSON.parse(JSON.stringify(compDef.data || {}))\n\n      // Apply overrides if any exist for this component type\n      if (overrideData[compDef.type]) {\n        finalComponentData = { ...finalComponentData, ...overrideData[compDef.type] }\n      }\n\n      this.entityManager.addComponent(entityId, compDef.type, finalComponentData)\n    })\n\n    console.log(\n      `PrefabManager: Entity ${entityId} spawned successfully from prefab \"${prefabName}\".`,\n    )\n    return entityId\n  }\n\n  /**\n   * Gets a prefab definition.\n   * @param {string} prefabName - The name of the prefab.\n   * @returns {PrefabDefinition | undefined}\n   */\n  getPrefabDefinition(prefabName) {\n    return this.prefabs.get(prefabName)\n  }\n}\n\nexport default PrefabManager\n","// src/engine/rendering/Camera.js\n\n/**\n * @file Camera.js\n * @description Defines a Camera class to manage the game's viewport,\n * follow a target, and stay within world boundaries.\n */\n\n/**\n * @class Camera\n * @description Manages the viewport of the game world.\n */\nclass Camera {\n  /**\n   * Creates a new Camera instance.\n   * @param {object} config - Configuration object for the camera.\n   * @param {number} config.viewportWidth - The width of the camera's viewport (typically canvas width).\n   * @param {number} config.viewportHeight - The height of the camera's viewport (typically canvas height).\n   * @param {number} config.worldWidth - The total width of the game world.\n   * @param {number} config.worldHeight - The total height of the game world.\n   */\n  constructor({ viewportWidth, viewportHeight, worldWidth, worldHeight }) {\n    /**\n     * @type {number}\n     * @description The x-coordinate of the camera's top-left corner in world space.\n     * This is effectively the viewportX offset.\n     */\n    this.x = 0\n\n    /**\n     * @type {number}\n     * @description The y-coordinate of the camera's top-left corner in world space.\n     * This is effectively the viewportY offset.\n     */\n    this.y = 0\n\n    if (viewportWidth <= 0 || viewportHeight <= 0) {\n      console.error('Camera: viewportWidth and viewportHeight must be greater than 0.')\n      // Set to default sensible values to prevent further errors down the line\n      this.viewportWidth = 800\n      this.viewportHeight = 600\n    } else {\n      this.viewportWidth = viewportWidth\n      this.viewportHeight = viewportHeight\n    }\n\n    this.worldWidth = worldWidth\n    this.worldHeight = worldHeight\n\n    /**\n     * @private\n     * @type {object | null}\n     * @description The target entity for the camera to follow.\n     * Expected to have `worldX`, `worldY`, `width`, `height` properties.\n     */\n    this.target = null\n\n    /**\n     * @private\n     * @type {number}\n     * @description The desired x-coordinate on the screen where the target's center should be.\n     */\n    this.screenFocusX = this.viewportWidth / 2\n\n    /**\n     * @private\n     * @type {number}\n     * @description The desired y-coordinate on the screen where the target's center should be.\n     */\n    this.screenFocusY = this.viewportHeight / 2\n\n    console.log(\n      `Camera: Initialized. Viewport: ${this.viewportWidth}x${this.viewportHeight}, World: ${this.worldWidth}x${this.worldHeight}`,\n    )\n  }\n\n  /**\n   * Sets the target entity for the camera to follow.\n   * The target should either have direct worldX, worldY, width, height properties,\n   * OR have getPosition() and getDimensions() methods.\n   * @param {object | null} targetEntity - The entity or object to follow.\n   * @param {number} [screenFocusX] - Optional: The x-coordinate on screen where the target should be centered.\n   * @param {number} [screenFocusY] - Optional: The y-coordinate on screen where the target should be centered.\n   */\n  setTarget(targetEntity, screenFocusX, screenFocusY) {\n    if (targetEntity === null) {\n      this.target = null\n      console.log('Camera: Target cleared.')\n      return\n    }\n\n    const hasMethods =\n      typeof targetEntity.getPosition === 'function' &&\n      typeof targetEntity.getDimensions === 'function'\n    const hasDirectProps =\n      typeof targetEntity.worldX === 'number' &&\n      typeof targetEntity.worldY === 'number' &&\n      typeof targetEntity.width === 'number' &&\n      typeof targetEntity.height === 'number'\n\n    if (hasMethods || hasDirectProps) {\n      this.target = targetEntity\n      this.screenFocusX = screenFocusX !== undefined ? screenFocusX : this.viewportWidth / 2\n      this.screenFocusY = screenFocusY !== undefined ? screenFocusY : this.viewportHeight / 2\n      console.log('Camera: Target set.', targetEntity)\n    } else {\n      console.warn(\n        'Camera: Invalid target. Must have worldX/Y/width/height properties OR getPosition/getDimensions methods.',\n        targetEntity,\n      )\n      this.target = null\n    }\n  }\n\n  /**\n   * Updates the camera's position. If a target is set, it follows the target.\n   * Also clamps the camera to the world boundaries.\n   */\n  update() {\n    if (this.target) {\n      let targetWorldX, targetWorldY, targetWidth, targetHeight\n\n      if (\n        typeof this.target.getPosition === 'function' &&\n        typeof this.target.getDimensions === 'function'\n      ) {\n        const pos = this.target.getPosition() // Should return {x, y}\n        const dim = this.target.getDimensions() // Should return {width, height}\n\n        if (\n          pos &&\n          dim &&\n          typeof pos.x === 'number' &&\n          typeof pos.y === 'number' &&\n          typeof dim.width === 'number' &&\n          typeof dim.height === 'number'\n        ) {\n          targetWorldX = pos.x\n          targetWorldY = pos.y\n          targetWidth = dim.width\n          targetHeight = dim.height\n        } else {\n          console.warn(\n            \"Camera: Target's getPosition() or getDimensions() did not return expected data.\",\n            { pos, dim },\n          )\n          return // Cannot update camera without valid target data\n        }\n      } else if (\n        this.target.worldX !== undefined &&\n        this.target.worldY !== undefined &&\n        this.target.width !== undefined &&\n        this.target.height !== undefined\n      ) {\n        // Fallback to direct properties\n        targetWorldX = this.target.worldX\n        targetWorldY = this.target.worldY\n        targetWidth = this.target.width\n        targetHeight = this.target.height\n      } else {\n        // Target is invalid or doesn't conform to expected interfaces\n        console.warn('Camera: Target is missing required properties or methods for tracking.')\n        this.target = null // Clear invalid target\n        return\n      }\n\n      // Ensure we have valid numbers before proceeding\n      if (isNaN(targetWorldX) || isNaN(targetWorldY) || isNaN(targetWidth) || isNaN(targetHeight)) {\n        console.warn('Camera: Target provided NaN values for position or dimensions.', {\n          targetWorldX,\n          targetWorldY,\n          targetWidth,\n          targetHeight,\n        })\n        // Optionally, keep the camera at its current position or reset target\n        // this.target = null; // Or just don't update camera position this frame\n        return\n      }\n\n      const targetCenterX = targetWorldX + targetWidth / 2\n      const targetCenterY = targetWorldY + targetHeight / 2\n\n      this.x = targetCenterX - this.screenFocusX\n      this.y = targetCenterY - this.screenFocusY\n    }\n\n    // Clamp camera to world boundaries (no changes to this part)\n    if (this.worldWidth > 0 && this.worldHeight > 0) {\n      this.x = Math.max(0, this.x)\n      this.y = Math.max(0, this.y)\n      if (this.worldWidth < this.viewportWidth) {\n        this.x = 0\n      } else {\n        this.x = Math.min(this.x, this.worldWidth - this.viewportWidth)\n      }\n      if (this.worldHeight < this.viewportHeight) {\n        this.y = 0\n      } else {\n        this.y = Math.min(this.y, this.worldHeight - this.viewportHeight)\n      }\n    } else {\n      this.x = 0\n      this.y = 0\n    }\n  }\n\n  /**\n   * Gets the current x-coordinate of the viewport (camera's top-left x in world space).\n   * @returns {number}\n   */\n  getViewportX() {\n    return this.x\n  }\n\n  /**\n   * Gets the current y-coordinate of the viewport (camera's top-left y in world space).\n   * @returns {number}\n   */\n  getViewportY() {\n    return this.y\n  }\n\n  /**\n   * Updates the dimensions of the game world.\n   * @param {number} worldWidth - The new total width of the game world.\n   * @param {number} worldHeight - The new total height of the game world.\n   */\n  setWorldDimensions(worldWidth, worldHeight) {\n    this.worldWidth = worldWidth\n    this.worldHeight = worldHeight\n    console.log(`Camera: World dimensions updated to ${this.worldWidth}x${this.worldHeight}`)\n    this.update() // Re-clamp immediately\n  }\n\n  /**\n   * Updates the dimensions of the camera's viewport (e.g., if canvas is resized).\n   * @param {number} viewportWidth - The new width of the viewport.\n   * @param {number} viewportHeight - The new height of the viewport.\n   */\n  setViewportDimensions(viewportWidth, viewportHeight) {\n    if (viewportWidth > 0) this.viewportWidth = viewportWidth\n    if (viewportHeight > 0) this.viewportHeight = viewportHeight\n    // Recalculate screen focus points if they were based on viewport center\n    this.screenFocusX = this.viewportWidth / 2\n    this.screenFocusY = this.viewportHeight / 2\n    console.log(\n      `Camera: Viewport dimensions updated to ${this.viewportWidth}x${this.viewportHeight}`,\n    )\n    this.update() // Re-clamp immediately\n  }\n}\n\nexport default Camera\n","// src/engine/core/SaveLoadManager.js\n\n/**\n * @file SaveLoadManager.js\n * @description Manages saving and loading game state using a provider pattern and localStorage.\n */\n\nconst SAVE_FILE_VERSION = '1.0.0'\nconst LOCAL_STORAGE_KEY_PREFIX = 'IRONCLAD_SAVE_SLOT_'\n\nclass SaveLoadManager {\n  /**\n   * @param {import('./IroncladEngine.js').default} engine - The main engine instance, for event emitting.\n   */\n  constructor(engine) {\n    /** @private @type {import('./IroncladEngine.js').default} */\n    this.engine = engine\n    /**\n     * @private\n     * @type {Map<string, {getSaveData: function(): any, loadSaveData: function(any): void}>}\n     * @description Stores registered data providers. Key is a unique string identifier.\n     * Provider object must have getSaveData() and loadSaveData(data) methods.\n     */\n    this.providers = new Map()\n    // console.log(\"SaveLoadManager: Constructed.\");\n  }\n\n  /**\n   * Registers a data provider with the SaveLoadManager.\n   * @param {string} uniqueKey - A unique key to identify this provider's data in the save file.\n   * @param {{getSaveData: function(): any, loadSaveData: function(any): void}} provider - The provider object.\n   */\n  registerDataProvider(uniqueKey, provider) {\n    if (typeof uniqueKey !== 'string' || !uniqueKey) {\n      console.error('SaveLoadManager.registerDataProvider: uniqueKey must be a non-empty string.')\n      return\n    }\n    if (\n      !provider ||\n      typeof provider.getSaveData !== 'function' ||\n      typeof provider.loadSaveData !== 'function'\n    ) {\n      console.error(\n        `SaveLoadManager.registerDataProvider: Provider for key \"${uniqueKey}\" is invalid or missing required methods (getSaveData, loadSaveData).`,\n      )\n      return\n    }\n    if (this.providers.has(uniqueKey)) {\n      console.warn(\n        `SaveLoadManager.registerDataProvider: Provider for key \"${uniqueKey}\" already exists. Overwriting.`,\n      )\n    }\n    this.providers.set(uniqueKey, provider)\n    // console.log(`SaveLoadManager: Data provider \"${uniqueKey}\" registered.`);\n  }\n\n  /**\n   * Unregisters a data provider.\n   * @param {string} uniqueKey - The key of the provider to unregister.\n   */\n  unregisterDataProvider(uniqueKey) {\n    if (this.providers.delete(uniqueKey)) {\n      // console.log(`SaveLoadManager: Data provider \"${uniqueKey}\" unregistered.`);\n    } else {\n      // console.warn(`SaveLoadManager.unregisterDataProvider: Provider for key \"${uniqueKey}\" not found.`);\n    }\n  }\n\n  /** @private Builds the localStorage key for a given slotId. */\n  _buildSlotKey(slotId) {\n    return `${LOCAL_STORAGE_KEY_PREFIX}${slotId}`\n  }\n\n  /**\n   * Saves the current game state to a specified slot.\n   * @param {string|number} slotId - The identifier for the save slot.\n   * @param {object} [userMetadata={}] - Optional metadata to save (e.g., playerName, level, currentSceneName for display).\n   * @returns {Promise<boolean>} True if saving was successful, false otherwise.\n   */\n  async saveGame(slotId, userMetadata = {}) {\n    // console.log(`SaveLoadManager: Attempting to save game to slot \"${slotId}\"...`);\n    if (slotId === null || slotId === undefined) {\n      console.error('SaveLoadManager.saveGame: slotId cannot be null or undefined.')\n      this.engine.events.emit('saveGameFailed', { slotId, error: 'Invalid slot ID' })\n      return false\n    }\n\n    const rootSaveObject = {\n      saveFileVersion: SAVE_FILE_VERSION,\n      timestamp: Date.now(),\n      metadata: userMetadata,\n      gameData: {},\n    }\n\n    try {\n      for (const [key, provider] of this.providers) {\n        // console.log(`SaveLoadManager: Getting data from provider \"${key}\"...`);\n        const dataPart = await Promise.resolve(provider.getSaveData())\n        rootSaveObject.gameData[key] = dataPart\n      }\n\n      const jsonString = JSON.stringify(rootSaveObject)\n      localStorage.setItem(this._buildSlotKey(slotId), jsonString)\n      // console.log(`SaveLoadManager: Game saved successfully to slot \"${slotId}\". Size: ${jsonString.length} chars`);\n      this.engine.events.emit('saveGameCompleted', { slotId, metadata: userMetadata })\n      return true\n    } catch (error) {\n      console.error(`SaveLoadManager.saveGame: Error saving to slot \"${slotId}\":`, error)\n      let errorMessage = 'Unknown error'\n      if (error instanceof DOMException && error.name === 'QuotaExceededError') {\n        errorMessage = 'Storage quota exceeded. Could not save game.'\n      } else if (error instanceof Error) {\n        errorMessage = error.message\n      }\n      this.engine.events.emit('saveGameFailed', {\n        slotId,\n        error: errorMessage,\n        originalError: error,\n      })\n      return false\n    }\n  }\n\n  /**\n   * Loads game state from a specified slot.\n   * It calls `loadSaveData` on all registered providers. The game will then need to\n   * orchestrate scene transitions and state setup based on the loaded data,\n   * often in response to the 'loadGameCompleted' event or by using the returned data.\n   * @param {string|number} slotId - The identifier for the save slot.\n   * @returns {Promise<object|null>} The full loaded save data object if successful, or null on failure.\n   */\n  async loadGame(slotId) {\n    // console.log(`SaveLoadManager: Attempting to load game from slot \"${slotId}\"...`);\n    if (slotId === null || slotId === undefined) {\n      console.error('SaveLoadManager.loadGame: slotId cannot be null or undefined.')\n      this.engine.events.emit('loadGameFailed', { slotId, error: 'Invalid slot ID' })\n      return null\n    }\n\n    const slotKey = this._buildSlotKey(slotId)\n    const jsonString = localStorage.getItem(slotKey)\n\n    if (!jsonString) {\n      console.warn(`SaveLoadManager.loadGame: No save data found for slot \"${slotId}\".`)\n      this.engine.events.emit('loadGameFailed', { slotId, error: 'No data in slot' })\n      return null\n    }\n\n    try {\n      const loadedRootObject = JSON.parse(jsonString)\n\n      if (loadedRootObject.saveFileVersion !== SAVE_FILE_VERSION) {\n        console.warn(\n          `SaveLoadManager.loadGame: Save file version mismatch for slot \"${slotId}\". Expected ${SAVE_FILE_VERSION}, got ${loadedRootObject.saveFileVersion}. Attempting to load anyway, but migrations may be needed.`,\n        )\n        // Future: Implement migration logic here based on version.\n      }\n\n      // TODO: Consider loading order/dependencies if necessary.\n      // For now, load in registration order.\n      for (const [key, provider] of this.providers) {\n        const dataPart = loadedRootObject.gameData ? loadedRootObject.gameData[key] : undefined\n        // console.log(`SaveLoadManager: Loading data for provider \"${key}\"...`);\n        await Promise.resolve(provider.loadSaveData(dataPart)) // Pass undefined if dataPart for key is missing\n      }\n\n      // console.log(`SaveLoadManager: Game loaded successfully from slot \"${slotId}\".`);\n      this.engine.events.emit('loadGameCompleted', { slotId, loadedData: loadedRootObject })\n      return loadedRootObject // Return the full data for the engine/UI to use for scene transitions etc.\n    } catch (error) {\n      console.error(`SaveLoadManager.loadGame: Error loading from slot \"${slotId}\":`, error)\n      this.engine.events.emit('loadGameFailed', {\n        slotId,\n        error: error.message,\n        originalError: error,\n      })\n      return null\n    }\n  }\n\n  /**\n   * Retrieves metadata for all available save slots.\n   * @returns {Promise<Array<{slotId: string|number, timestamp: number, metadata: object}>>}\n   */\n  async getSaveSlots() {\n    const slots = []\n    for (let i = 0; i < localStorage.length; i++) {\n      const key = localStorage.key(i)\n      if (key && key.startsWith(LOCAL_STORAGE_KEY_PREFIX)) {\n        const slotId = key.substring(LOCAL_STORAGE_KEY_PREFIX.length)\n        try {\n          const jsonString = localStorage.getItem(key)\n          if (jsonString) {\n            const data = JSON.parse(jsonString)\n            // Ensure basic structure exists before adding\n            if (data && typeof data.timestamp === 'number' && typeof data.metadata === 'object') {\n              slots.push({\n                slotId: slotId, // Could be numeric if your slotIds are numbers\n                timestamp: data.timestamp,\n                metadata: data.metadata || {},\n              })\n            } else {\n              console.warn(\n                `SaveLoadManager.getSaveSlots: Invalid data structure in slot \"${slotId}\". Skipping.`,\n              )\n            }\n          }\n        } catch (error) {\n          console.warn(\n            `SaveLoadManager.getSaveSlots: Error parsing data for slot \"${slotId}\". It might be corrupted.`,\n            error,\n          )\n          // Optionally add it as a corrupted slot:\n          // slots.push({ slotId: slotId, timestamp: 0, metadata: { error: \"Corrupted\" } });\n        }\n      }\n    }\n    // Sort by timestamp, most recent first\n    slots.sort((a, b) => b.timestamp - a.timestamp)\n    return slots\n  }\n\n  /**\n   * Deletes a save slot.\n   * @param {string|number} slotId - The identifier for the save slot to delete.\n   * @returns {Promise<void>}\n   */\n  async deleteSlot(slotId) {\n    if (slotId === null || slotId === undefined) {\n      console.error('SaveLoadManager.deleteSlot: slotId cannot be null or undefined.')\n      return\n    }\n    const slotKey = this._buildSlotKey(slotId)\n    localStorage.removeItem(slotKey)\n    // console.log(`SaveLoadManager: Deleted save slot \"${slotId}\".`);\n    this.engine.events.emit('saveSlotDeleted', { slotId })\n  }\n\n  /**\n   * Deletes all game save slots.\n   * @returns {Promise<void>}\n   */\n  async deleteAllSlots() {\n    const keysToRemove = []\n    for (let i = 0; i < localStorage.length; i++) {\n      const key = localStorage.key(i)\n      if (key && key.startsWith(LOCAL_STORAGE_KEY_PREFIX)) {\n        keysToRemove.push(key)\n      }\n    }\n    keysToRemove.forEach((key) => localStorage.removeItem(key))\n    console.log(`SaveLoadManager: Deleted all ${keysToRemove.length} game save slots.`)\n    this.engine.events.emit('allSaveSlotsDeleted')\n  }\n}\n\nexport default SaveLoadManager\n","// src/engine/fx/BaseEffect.js\n\n/**\n * @file BaseEffect.js\n * @description An abstract base class for visual effects.\n * Specific effects (Shake, Flash, etc.) should extend this class.\n */\n// src/engine/fx/BaseEffect.js\n\nclass BaseEffect {\n  constructor(options = {}) {\n    this.engine = options.engine || null\n    this.duration = options.duration !== undefined ? options.duration : 1000 // ms\n    this.timer = 0 // Current time elapsed for this effect in ms\n    this.isActive = options.startsActive !== undefined ? options.startsActive : true\n    this.isFinished = false\n    this.id = options.id || this.constructor.name // For logging\n\n    // console.log(`[${this.id} Constructor] Duration: ${this.duration}ms, Active: ${this.isActive}`);\n  }\n\n  update(deltaTime) {\n    if (!this.isActive || this.isFinished) {\n      return\n    }\n\n    this.timer += deltaTime * 1000\n\n    // --- DETAILED LOGGING FOR TIMER AND DURATION ---\n    // console.log(`[${this.id} Update] Timer: ${this.timer.toFixed(0)}ms / ${this.duration}ms, IsActive: ${this.isActive}, IsFinished: ${this.isFinished}`);\n\n    if (this.duration !== Infinity && this.timer >= this.duration) {\n      this.isFinished = true\n      this.isActive = false\n      console.log(\n        `%c[${this.id} Update] Effect FINISHED. Timer: ${this.timer.toFixed(0)}ms, Duration: ${this.duration}ms`,\n        'color: red; font-weight: bold;',\n      )\n    }\n  }\n\n  apply(mainContext, sceneCanvas, effectPipelineData = {}) {\n    if (!this.isActive || this.isFinished) {\n      return effectPipelineData\n    }\n    console.warn(`BaseEffect (${this.id}): apply() method not implemented in subclass.`)\n    return effectPipelineData\n  }\n\n  start() {\n    this.timer = 0\n    this.isActive = true\n    this.isFinished = false\n    // console.log(`[${this.id} Start] Effect started/restarted.`);\n  }\n\n  stop() {\n    this.isActive = false\n    this.isFinished = true\n    // console.log(`[${this.id} Stop] Effect stopped.`);\n  }\n\n  reset() {\n    this.timer = 0\n    this.isActive = false\n    this.isFinished = false\n    // console.log(`[${this.id} Reset] Effect reset.`);\n  }\n\n  hasFinished() {\n    return this.isFinished\n  }\n\n  setEngine(engine) {\n    this.engine = engine\n  }\n}\n\nexport default BaseEffect\n","// src/engine/fx/FlashEffect.js\nimport BaseEffect from './BaseEffect.js'\n\n/**\n * @class FlashEffect\n * @extends BaseEffect\n * @description Creates a screen flash visual effect with a specified color and opacity.\n */\nclass FlashEffect extends BaseEffect {\n  /**\n   * Creates an instance of FlashEffect.\n   * @param {object} options - Configuration options for the flash effect.\n   * @param {string} [options.color='rgba(255, 255, 255, 0.5)'] - The color of the flash (CSS color string).\n   * @param {number} [options.duration=300] - Duration of the flash in milliseconds.\n   * @param {number} [options.maxOpacity=0.7] - The peak opacity of the flash (0 to 1).\n   * @param {boolean} [options.fadeOut=true] - Whether the flash opacity should fade out over its duration.\n   * @param {boolean} [options.startsActive=true] - Whether the effect starts active.\n   * @param {import('../core/IroncladEngine.js').default} [options.engine=null] - Optional reference to the engine.\n   * @param {string} [options.id] - Optional ID for logging/identification.\n   */\n  constructor(options = {}) {\n    const effectId =\n      options.id || `FlashEffect(C:${options.color || 'white'},D:${options.duration || 300})`\n    super({ ...options, id: effectId }) // Handles duration, startsActive, engine, id\n\n    this.color = options.color || 'rgba(255, 255, 255, 0.5)' // Default to semi-transparent white\n    this.maxOpacity = Math.max(\n      0,\n      Math.min(options.maxOpacity !== undefined ? options.maxOpacity : 0.7, 1),\n    )\n    this.fadeOut = options.fadeOut !== undefined ? options.fadeOut : true\n\n    this.currentOpacity = 0 // Will be set by start() or if startsActive\n\n    if (this.isActive) {\n      // If startsActive was true in BaseEffect\n      this.currentOpacity = this.maxOpacity\n      // Timer and isFinished are handled by BaseEffect's constructor or start()\n    }\n    // console.log(`[${this.id} Constructor] Color: ${this.color}, MaxOpacity: ${this.maxOpacity}, FadeOut: ${this.fadeOut}, Active: ${this.isActive}`);\n  }\n\n  /**\n   * Updates the flash effect's opacity over time if fadeOut is enabled.\n   * @param {number} deltaTime - The time elapsed since the last frame, in seconds.\n   */\n  update(deltaTime) {\n    if (!this.isActive || this.isFinished) {\n      if (this.currentOpacity !== 0) this.currentOpacity = 0\n      return\n    }\n\n    super.update(deltaTime) // Handles timer and sets isFinished, isActive\n\n    if (this.isFinished) {\n      // If super.update() just marked it as finished\n      this.currentOpacity = 0\n    } else if (this.fadeOut && this.duration > 0) {\n      // Linear fade out\n      this.currentOpacity =\n        this.maxOpacity * Math.max(0, (this.duration - this.timer) / this.duration)\n    } else if (!this.fadeOut) {\n      // If no fadeOut, opacity remains maxOpacity until finished, then drops to 0\n      this.currentOpacity = this.isFinished ? 0 : this.maxOpacity\n    }\n    this.currentOpacity = Math.max(0, Math.min(this.currentOpacity, this.maxOpacity)) // Clamp\n\n    // console.log(`[${this.id} Update] Timer: ${this.timer.toFixed(0)}, CurrentOpacity: ${this.currentOpacity.toFixed(2)}, Active: ${this.isActive}, Finished: ${this.isFinished}`);\n  }\n\n  /**\n   * Applies the screen flash by drawing a colored overlay.\n   * This method is called by the EffectsManager during its postRender phase,\n   * typically after the scene and any positional effects have been drawn.\n   * @param {CanvasRenderingContext2D} mainContext - The context of the main (visible) canvas.\n   * @param {HTMLCanvasElement} sceneCanvas - The offscreen canvas (not directly used by flash, but passed by manager).\n   * @param {object} [effectPipelineData={}] - Data from previous effects (not typically used by flash).\n   * @returns {void}\n   */\n  apply(mainContext, sceneCanvas, effectPipelineData = {}) {\n    if (!this.isActive || this.isFinished || this.currentOpacity <= 0) {\n      // console.log(`[${this.id} Apply] Not active or zero opacity. Opacity: ${this.currentOpacity}, Active: ${this.isActive}`);\n      return\n    }\n\n    const originalAlpha = mainContext.globalAlpha\n    mainContext.globalAlpha = this.currentOpacity\n    mainContext.fillStyle = this.color\n    mainContext.fillRect(0, 0, mainContext.canvas.width, mainContext.canvas.height)\n    mainContext.globalAlpha = originalAlpha // Restore globalAlpha\n\n    // console.log(`[${this.id} Apply] Applied flash. Color: ${this.color}, Opacity: ${this.currentOpacity.toFixed(2)}`);\n  }\n\n  /**\n   * Starts or restarts the flash effect.\n   */\n  start() {\n    super.start() // Resets timer, sets isActive=true, isFinished=false\n    this.currentOpacity = this.maxOpacity\n    // console.log(`[${this.id} Start] Flash started/restarted. Opacity: ${this.currentOpacity}`);\n  }\n\n  /**\n   * Resets the flash effect to its initial state.\n   */\n  reset() {\n    super.reset() // Resets timer, isActive, isFinished\n    this.currentOpacity = 0 // Opacity is 0 until started\n    // console.log(`[${this.id} Reset] Flash reset.`);\n  }\n}\n\nexport default FlashEffect\n","// src/engine/fx/ShakeEffect.js\nimport BaseEffect from './BaseEffect.js'\n\n/**\n * @class ShakeEffect\n * @extends BaseEffect\n * @description Creates a screen shake visual effect.\n */\nclass ShakeEffect extends BaseEffect {\n  /**\n   * Creates an instance of ShakeEffect.\n   * @param {object} options - Configuration options for the shake effect.\n   * @param {number} [options.intensity=10] - Maximum pixel offset for the shake.\n   * @param {number} [options.duration=500] - Duration of the shake in milliseconds.\n   * @param {boolean} [options.decay=true] - Whether the shake intensity should decay over its duration.\n   * @param {import('../core/IroncladEngine.js').default} [options.engine=null] - Optional reference to the engine.\n   */\n  constructor(options = {}) {\n    super(options) // Handles duration, startsActive, engine\n\n    this.baseIntensity = Math.abs(options.intensity || 10)\n    this.currentIntensity = this.isActive ? this.baseIntensity : 0\n    this.decay = options.decay !== undefined ? options.decay : true\n\n    // Ensure timer is 0 if effect starts active, as super.start() would do this.\n    // BaseEffect constructor sets isActive based on options.startsActive (defaults to true).\n    // BaseEffect.start() resets timer to 0.\n    if (this.isActive) {\n      this.timer = 0\n      this.isFinished = false\n    }\n    // console.log(`[ShakeEffect Constructor] Initialized. Intensity: ${this.baseIntensity}, Duration: ${this.duration}ms, Decay: ${this.decay}, Active: ${this.isActive}, Timer: ${this.timer}`);\n  }\n\n  /**\n   * Updates the shake effect's intensity over time if decay is enabled.\n   * @param {number} deltaTime - The time elapsed since the last frame, in seconds.\n   */\n  update(deltaTime) {\n    const effectName = this.id || `ShakeEffect(Dur:${this.duration},Decay:${this.decay})`\n    // console.log(`[${effectName}] Pre-Update. Active: ${this.isActive}, Finished: ${this.isFinished}, Timer: ${this.timer.toFixed(0)}, CurrentIntensity: ${this.currentIntensity.toFixed(2)}, BaseIntensity: ${this.baseIntensity}`);\n\n    if (!this.isActive || this.isFinished) {\n      if (this.currentIntensity !== 0) {\n        // console.log(`[${effectName}] Update: Inactive/Finished, ensuring intensity is 0.`);\n        this.currentIntensity = 0\n      }\n      return\n    }\n\n    super.update(deltaTime) // This updates this.timer and potentially sets this.isFinished and this.isActive = false\n\n    // console.log(`[${effectName}] Post super.update. Active: ${this.isActive}, Finished: ${this.isFinished}, Timer: ${this.timer.toFixed(0)}`);\n\n    if (this.decay && this.duration > 0) {\n      // console.log(`[${effectName}] Update: Applying decay. BaseIntensity: ${this.baseIntensity}`);\n      if (this.isFinished) {\n        // If super.update() marked it as finished\n        this.currentIntensity = 0\n      } else {\n        // Linear decay: intensity decreases as timer approaches duration\n        this.currentIntensity =\n          this.baseIntensity * Math.max(0, (this.duration - this.timer) / this.duration)\n      }\n      // console.log(`[${effectName}] Update: Decayed Intensity: ${this.currentIntensity.toFixed(2)}`);\n    } else if (!this.decay) {\n      // No decay\n      // console.log(`[${effectName}] Update: No decay. BaseIntensity: ${this.baseIntensity}`);\n      // Intensity should remain baseIntensity until it's finished\n      this.currentIntensity = this.isFinished ? 0 : this.baseIntensity\n      // console.log(`[${effectName}] Update: Non-decayed Intensity: ${this.currentIntensity.toFixed(2)} (isFinished: ${this.isFinished})`);\n    }\n\n    this.currentIntensity = Math.max(0, this.currentIntensity) // Ensure intensity doesn't go negative\n  }\n\n  /**\n   * Applies the screen shake by modifying the drawing offset.\n   * @param {CanvasRenderingContext2D} mainContext - The context of the main (visible) canvas.\n   * @param {HTMLCanvasElement} sceneCanvas - The offscreen canvas containing the rendered scene content.\n   * @param {object} [effectPipelineData={drawX: 0, drawY: 0}] - Data from previous effects.\n   * @returns {{drawX: number, drawY: number}} The modified draw offsets.\n   */\n  apply(mainContext, sceneCanvas, effectPipelineData = {}) {\n    // isFinished is checked by super.update() which sets isActive = false.\n    // BaseEffect.apply also checks isActive.\n    // We also need to check currentIntensity because it might be 0 even if not \"finished\" by timer (e.g. decay completed)\n    if (!this.isActive || this.currentIntensity <= 0) {\n      // Simplified check\n      // console.log(`[ShakeEffect Apply] Not active or zero intensity. Intensity: ${this.currentIntensity}, Active: ${this.isActive}, Finished: ${this.isFinished}`);\n      return effectPipelineData\n    }\n\n    const currentDrawX = effectPipelineData.drawX || 0\n    const currentDrawY = effectPipelineData.drawY || 0\n\n    const offsetX = (Math.random() - 0.5) * 2 * this.currentIntensity\n    const offsetY = (Math.random() - 0.5) * 2 * this.currentIntensity\n\n    // console.log(`[ShakeEffect Apply] Active. Intensity=${this.currentIntensity.toFixed(2)}, OffsetX=${offsetX.toFixed(2)}, OffsetY=${offsetY.toFixed(2)}`);\n\n    return {\n      drawX: currentDrawX + offsetX,\n      drawY: currentDrawY + offsetY,\n    }\n  }\n\n  start() {\n    super.start() // Resets timer, sets isActive=true, isFinished=false\n    this.currentIntensity = this.baseIntensity\n    // console.log(`[ShakeEffect Start] Started/Restarted. Intensity: ${this.currentIntensity}, Duration: ${this.duration}`);\n  }\n\n  reset() {\n    super.reset() // Resets timer, isActive, isFinished\n    this.currentIntensity = 0\n    // console.log(`[ShakeEffect Reset] Reset.`);\n  }\n}\n\nexport default ShakeEffect\n","// src/engine/fx/TintEffect.js\nimport BaseEffect from './BaseEffect.js'\n\n/**\n * @class TintEffect\n * @extends BaseEffect\n * @description Applies a persistent or timed color tint to the screen.\n */\nclass TintEffect extends BaseEffect {\n  /**\n   * Creates an instance of TintEffect.\n   * @param {object} options - Configuration options for the tint effect.\n   * @param {string} [options.color='rgba(0, 0, 0, 0)'] - The color of the tint (CSS color string).\n   * @param {number} [options.opacity=0.25] - The opacity of the tint (0 to 1).\n   * @param {number} [options.duration=Infinity] - Duration of the tint in milliseconds. Infinity for a persistent tint.\n   * @param {boolean} [options.startsActive=true] - Whether the effect starts active.\n   * @param {import('../core/IroncladEngine.js').default} [options.engine=null] - Optional reference to the engine.\n   * @param {string} [options.id] - Optional ID for logging/identification.\n   */\n  constructor(options = {}) {\n    const effectId =\n      options.id || `TintEffect(C:${options.color || 'transparent'},O:${options.opacity || 0})`\n    // If duration is not explicitly set for a tint, make it Infinity by default\n    const duration = options.duration !== undefined ? options.duration : Infinity\n    super({ ...options, id: effectId, duration: duration }) // Handles startsActive, engine, id\n\n    this.color = options.color || 'rgba(0, 0, 0, 0)' // Default to fully transparent\n    this.opacity = Math.max(0, Math.min(options.opacity !== undefined ? options.opacity : 0.25, 1))\n\n    // currentOpacity will be this.opacity when active, or 0 if not.\n    // Unlike FlashEffect, a persistent tint doesn't usually fade internally unless duration is set.\n    this.currentOpacity = this.isActive ? this.opacity : 0\n\n    // console.log(`[${this.id} Constructor] Color: ${this.color}, Opacity: ${this.opacity}, Duration: ${this.duration}ms, Active: ${this.isActive}`);\n  }\n\n  /**\n   * Updates the tint effect. Mainly handles timed tints.\n   * For persistent tints (duration=Infinity), this mostly relies on BaseEffect.\n   * @param {number} deltaTime - The time elapsed since the last frame, in seconds.\n   */\n  update(deltaTime) {\n    if (!this.isActive || this.isFinished) {\n      // Ensure opacity is 0 if not active or finished,\n      // especially if it was a timed tint that just ended.\n      if (this.currentOpacity !== 0) this.currentOpacity = 0\n      return\n    }\n\n    super.update(deltaTime) // Handles timer and sets isFinished, isActive for timed tints\n\n    if (this.isFinished) {\n      this.currentOpacity = 0\n    } else {\n      // For a tint, opacity is generally constant while active, unless it's a timed tint that fades.\n      // For now, we assume a timed tint just disappears when finished.\n      // If a fade-out for timed tints is desired, that logic would go here.\n      this.currentOpacity = this.opacity\n    }\n    // console.log(`[${this.id} Update] Timer: ${this.timer.toFixed(0)}, CurrentOpacity: ${this.currentOpacity.toFixed(2)}, Active: ${this.isActive}, Finished: ${this.isFinished}`);\n  }\n\n  /**\n   * Applies the screen tint by drawing a colored overlay.\n   * @param {CanvasRenderingContext2D} mainContext - The context of the main (visible) canvas.\n   * @param {HTMLCanvasElement} sceneCanvas - The offscreen canvas.\n   * @param {object} [effectPipelineData={}] - Data from previous effects.\n   * @returns {void}\n   */\n  apply(mainContext, sceneCanvas, effectPipelineData = {}) {\n    if (\n      !this.isActive ||\n      this.isFinished ||\n      this.currentOpacity <= 0 ||\n      this.color === 'rgba(0, 0, 0, 0)'\n    ) {\n      // console.log(`[${this.id} Apply] Not active, zero opacity, or transparent color. Opacity: ${this.currentOpacity}, Active: ${this.isActive}`);\n      return\n    }\n\n    const originalAlpha = mainContext.globalAlpha\n    mainContext.globalAlpha = this.currentOpacity // Use the tint's current opacity\n    mainContext.fillStyle = this.color\n    mainContext.fillRect(0, 0, mainContext.canvas.width, mainContext.canvas.height)\n    mainContext.globalAlpha = originalAlpha\n\n    // console.log(`[${this.id} Apply] Applied tint. Color: ${this.color}, Opacity: ${this.currentOpacity.toFixed(2)}`);\n  }\n\n  /**\n   * Starts or restarts the tint effect.\n   */\n  start() {\n    super.start()\n    this.currentOpacity = this.opacity // Set to defined opacity when started\n    // console.log(`[${this.id} Start] Tint started/restarted. Opacity: ${this.currentOpacity}`);\n  }\n\n  /**\n   * Stops the tint effect. For persistent tints, this effectively removes them.\n   */\n  stop() {\n    super.stop()\n    this.currentOpacity = 0 // Ensure visually gone\n    // console.log(`[${this.id} Stop] Tint stopped. Opacity set to 0.`);\n  }\n\n  /**\n   * Resets the tint effect.\n   */\n  reset() {\n    super.reset()\n    this.currentOpacity = 0 // Not active, so no opacity\n    // console.log(`[${this.id} Reset] Tint reset.`);\n  }\n\n  /**\n   * Sets the color of the tint.\n   * @param {string} newColor\n   */\n  setColor(newColor) {\n    this.color = newColor\n  }\n\n  /**\n   * Sets the opacity of the tint.\n   * @param {number} newOpacity - Value between 0 and 1.\n   */\n  setOpacity(newOpacity) {\n    this.opacity = Math.max(0, Math.min(newOpacity, 1))\n    if (this.isActive && !this.isFinished) {\n      // Only update currentOpacity if active\n      this.currentOpacity = this.opacity\n    }\n  }\n}\n\nexport default TintEffect\n","// src/engine/core/EffectsManager.js\n\n/**\n * @file EffectsManager.js\n * @description Manages a list of active visual effects and applies them.\n */\n\n// Import specific effect classes as they are created\nimport FlashEffect from '../fx/FlashEffect.js'\nimport ShakeEffect from '../fx/ShakeEffect.js' // Make sure path is correct\nimport TintEffect from '../fx/TintEffect.js'\n// import FlashEffect from '../fx/FlashEffect.js'; // Example for future\n// import TintEffect from '../fx/TintEffect.js';   // Example for future\n\nclass EffectsManager {\n  /**\n   * @param {import('./IroncladEngine.js').default} engine - The main engine instance.\n   */\n  constructor(engine) {\n    /** @private @type {import('./IroncladEngine.js').default} */\n    this.engine = engine\n\n    /**\n     * @private\n     * @type {import('../fx/BaseEffect.js').default[]}\n     * @description Array holding currently active effect instances.\n     */\n    this.activeEffects = []\n\n    /**\n     * @private\n     * @type {TintEffect | null}\n     * @description Holds the current persistent tint effect, if any.\n     */\n    this.persistentTintEffect = null // ADD THIS LINE\n\n    // console.log(\"EffectsManager: Constructed (Modular Effects).\");\n  }\n\n  /**\n   * Adds a new effect instance to the manager.\n   * @param {import('../fx/BaseEffect.js').default} effectInstance - An instance of a class extending BaseEffect.\n   */\n  addEffect(effectInstance) {\n    if (\n      effectInstance &&\n      typeof effectInstance.update === 'function' &&\n      typeof effectInstance.apply === 'function'\n    ) {\n      if (typeof effectInstance.setEngine === 'function') {\n        effectInstance.setEngine(this.engine)\n      }\n      if (typeof effectInstance.start === 'function' && effectInstance.isActive !== false) {\n        // Check isActive in case startsActive was false\n        effectInstance.start() // Ensure effect's internal timer is reset and it's active\n      }\n      this.activeEffects.push(effectInstance)\n      // console.log(`EffectsManager: Added effect - ${effectInstance.constructor.name}`);\n    } else {\n      console.error('EffectsManager: Attempted to add an invalid effect instance.', effectInstance)\n    }\n  }\n\n  /**\n   * Triggers a screen shake effect by creating and adding a ShakeEffect instance.\n   * @param {number} [intensity=10] - Maximum pixel offset for the shake.\n   * @param {number} [duration=500] - Duration of the shake in milliseconds.\n   * @param {boolean} [decay=true] - Whether the shake intensity should decay.\n   */\n  shake(intensity = 10, duration = 500, decay = true) {\n    const effectId = `Shake-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`\n\n    const shakeEffect = new ShakeEffect({\n      id: effectId,\n      engine: this.engine,\n      intensity: intensity,\n      duration: duration,\n      decay: decay,\n    })\n    this.addEffect(shakeEffect)\n    // console.log(`EffectsManager: Shake effect initiated.`);\n  }\n\n  /**\n   * Triggers a screen flash effect by creating and adding a FlashEffect instance.\n   * @param {string} [color='rgba(255, 255, 255, 0.5)'] - The color of the flash.\n   * @param {number} [duration=300] - Duration of the flash in milliseconds.\n   * @param {number} [maxOpacity=0.7] - The peak opacity of the flash (0 to 1).\n   * @param {boolean} [fadeOut=true] - Whether the flash should fade out.\n   */\n  flash(color = 'rgba(255, 255, 255, 0.5)', duration = 300, maxOpacity = 0.7, fadeOut = true) {\n    const effectId = `Flash-<span class=\"math-inline\">{Date.now()}-</span>{Math.random().toString(36).substr(2, 5)}`\n    const flashEffect = new FlashEffect({\n      id: effectId,\n      engine: this.engine,\n      color: color,\n      duration: duration,\n      maxOpacity: maxOpacity,\n      fadeOut: fadeOut,\n    })\n    this.addEffect(flashEffect)\n    // console.log(`[EffectsManager flash()] Flash effect initiated with ID: ${effectId}. Color: ${color}, Duration: ${duration}, Opacity: ${maxOpacity}`);\n  }\n\n  /**\n   * Applies or updates a persistent screen tint.\n   * If a tint is already active, its color and opacity are updated.\n   * If opacity is 0, the tint is effectively cleared.\n   * @param {string} [color='rgba(0,0,0,0)'] - The color of the tint.\n   * @param {number} [opacity=0] - The opacity of the tint (0 to 1).\n   */\n  tint(color = 'rgba(0,0,0,0)', opacity = 0) {\n    const clampedOpacity = Math.max(0, Math.min(opacity, 1))\n\n    if (clampedOpacity === 0 && !this.persistentTintEffect) {\n      // No tint to apply and no existing tint, do nothing.\n      return\n    }\n\n    if (!this.persistentTintEffect) {\n      // Create a new persistent tint effect\n      const effectId = `PersistentTint-${Date.now()}`\n      this.persistentTintEffect = new TintEffect({\n        id: effectId,\n        engine: this.engine,\n        color: color,\n        opacity: clampedOpacity,\n        duration: Infinity, // Persistent\n        startsActive: clampedOpacity > 0, // Only start if opacity is > 0\n      })\n      this.addEffect(this.persistentTintEffect) // Add to active effects to be processed\n      // console.log(`[EffectsManager tint()] New persistent tint applied: ${color} @ ${clampedOpacity}`);\n    } else {\n      // Update existing persistent tint\n      this.persistentTintEffect.setColor(color)\n      this.persistentTintEffect.setOpacity(clampedOpacity)\n\n      if (clampedOpacity > 0 && !this.persistentTintEffect.isActive) {\n        this.persistentTintEffect.start() // Re-activate if it was stopped but now has opacity\n        // Ensure it's in activeEffects if it was somehow removed but not nullified\n        if (!this.activeEffects.includes(this.persistentTintEffect)) {\n          this.addEffect(this.persistentTintEffect)\n        }\n      } else if (clampedOpacity === 0 && this.persistentTintEffect.isActive) {\n        this.persistentTintEffect.stop() // Stop it if opacity is now 0\n      }\n      // console.log(`[EffectsManager tint()] Persistent tint updated: ${color} @ ${clampedOpacity}`);\n    }\n  }\n\n  /**\n   * Clears any persistent screen tint.\n   */\n  clearTint() {\n    if (this.persistentTintEffect) {\n      this.persistentTintEffect.stop() // This will set its currentOpacity to 0\n      // The effect will be removed from activeEffects by the update loop if it's marked finished.\n      // Or, we can explicitly remove it if we are sure it's the only way it's managed.\n      // For now, stop() is enough as its apply() will do nothing.\n      // To fully remove and allow a new one to be created cleanly:\n      // const index = this.activeEffects.indexOf(this.persistentTintEffect);\n      // if (index > -1) {\n      //     this.activeEffects.splice(index, 1);\n      // }\n      // this.persistentTintEffect = null;\n      // console.log(\"EffectsManager: Persistent screen tint cleared.\");\n    }\n    // Alternatively, just set to transparent:\n    this.tint('rgba(0,0,0,0)', 0)\n  }\n\n  /**\n   * Updates all active effects and removes finished ones.\n   * @param {number} deltaTime - Time elapsed since the last frame, in seconds.\n   */\n  update(deltaTime) {\n    // --- LOG TO CONFIRM EffectsManager.update IS CALLED ---\n    console.log(\n      `%c[EffectsManager Update] Called. Active effects count: ${this.activeEffects.length}`,\n      'color: purple;',\n    )\n\n    if (this.activeEffects.length === 0) return\n\n    for (let i = this.activeEffects.length - 1; i >= 0; i--) {\n      const effect = this.activeEffects[i]\n\n      // --- LOG TO CONFIRM WE ARE PROCESSING AN EFFECT ---\n      console.log(\n        `[EffectsManager Update] Processing effect: ${effect.id || effect.constructor.name} (Active: ${effect.isActive}, Finished: ${effect.isFinished})`,\n      )\n\n      if (effect.isActive && !effect.hasFinished()) {\n        // Double check conditions here\n        effect.update(deltaTime) // This should call ShakeEffect.update -> BaseEffect.update\n      }\n\n      if (effect.hasFinished()) {\n        this.activeEffects.splice(i, 1)\n        console.log(\n          `%c[EffectsManager Update] Removed finished effect: ${effect.id || effect.constructor.name}. Remaining: ${this.activeEffects.length}`,\n          'color: orange;',\n        )\n      }\n    }\n  }\n\n  /**\n   * Called after the main scene rendering (to an offscreen canvas).\n   * This method applies all active effects and draws the result to the visible main canvas.\n   * @param {CanvasRenderingContext2D} mainContext - The context of the visible main canvas.\n   * @param {HTMLCanvasElement} sceneCanvas - The offscreen canvas containing the rendered scene content.\n   */\n  postRender(mainContext, sceneCanvas) {\n    if (!mainContext || !sceneCanvas) {\n      console.error('EffectsManager.postRender: Missing mainContext or sceneCanvas.')\n      return\n    }\n\n    mainContext.save()\n    mainContext.clearRect(0, 0, mainContext.canvas.width, mainContext.canvas.height)\n\n    // Initial draw position for the scene canvas\n    let effectPipelineData = { drawX: 0, drawY: 0 }\n\n    // Apply effects that modify draw position (like shake)\n    this.activeEffects.forEach((effect) => {\n      if (effect.isActive && !effect.hasFinished()) {\n        // Check if the effect's apply method is expected to return positional data\n        // For ShakeEffect, it does.\n        if (effect instanceof ShakeEffect) {\n          effectPipelineData = effect.apply(mainContext, sceneCanvas, effectPipelineData)\n        }\n      }\n    })\n\n    // Draw the sceneCanvas with accumulated positional effects\n    mainContext.drawImage(\n      sceneCanvas,\n      Math.round(effectPipelineData.drawX),\n      Math.round(effectPipelineData.drawY),\n    )\n\n    // Apply overlay effects (like flash, tint) after the scene is drawn\n    this.activeEffects.forEach((effect) => {\n      if (effect.isActive && !effect.hasFinished()) {\n        // These effects draw directly onto the mainContext\n        // Example: if (effect instanceof FlashEffect || effect instanceof TintEffect)\n        if (!(effect instanceof ShakeEffect)) {\n          // Apply non-shake effects here\n          effect.apply(mainContext, sceneCanvas, effectPipelineData) // sceneCanvas might be needed for context by some effects\n        }\n      }\n    })\n\n    mainContext.restore()\n  }\n\n  /**\n   * Clears all active effects.\n   */\n  clearAllEffects() {\n    this.activeEffects.forEach((effect) => {\n      if (typeof effect.stop === 'function') {\n        effect.stop()\n      }\n    })\n    this.activeEffects = []\n    // console.log(\"EffectsManager: All active effects cleared.\");\n  }\n\n  destroy() {\n    this.clearAllEffects()\n    // console.log(\"EffectsManager: Destroyed.\");\n  }\n}\n\nexport default EffectsManager\n","// src/engine/core/AudioManager.js\n\n/**\n * @file AudioManager.js\n * @description Manages loading, playback, and control of audio assets (sound effects and music).\n */\nclass AudioManager {\n  /**\n   * @param {import('./AssetLoader.js').default} assetLoader - Instance of the AssetLoader.\n   * @param {import('./EventManager.js').default} [eventManager=null] - Optional EventManager for audio events.\n   */\n  constructor(assetLoader, eventManager = null) {\n    if (!assetLoader) {\n      throw new Error('AudioManager: AssetLoader instance is required.')\n    }\n    /** @private @type {import('./AssetLoader.js').default} */\n    this.assetLoader = assetLoader\n    /** @private @type {import('./EventManager.js').default | null} */\n    this.eventManager = eventManager\n\n    /** @private @type {AudioContext | null} Reference from AssetLoader or created here. */\n    this.audioContext = this.assetLoader.audioContext || this._getAudioContext()\n\n    /** @private @type {GainNode | null} Master gain node for overall volume control. */\n    this.masterGainNode = null\n    /** @private @type {GainNode | null} Gain node for sound effects. */\n    this.sfxGainNode = null\n    /** @private @type {GainNode | null} Gain node for background music. */\n    this.musicGainNode = null\n\n    /** @private @type {AudioBufferSourceNode | null} Current background music source. */\n    this.currentMusicSource = null\n    /** @private @type {string | null} Name of the currently playing music track. */\n    this.currentMusicName = null\n    /** @private @type {boolean} Whether the current music is set to loop. */\n    this.isMusicLooping = false\n    /** @private @type {number} Volume for music (0-1). */\n    this.musicVolume = 0.7 // Default music volume\n    /** @private @type {number} Volume for sound effects (0-1). */\n    this.sfxVolume = 0.9 // Default SFX volume\n    /** @private @type {number} Master volume (0-1). */\n    this.masterVolume = 1.0\n\n    /** @private @type {boolean} Global mute state. */\n    this.isMuted = false\n\n    if (this.audioContext) {\n      this.masterGainNode = this.audioContext.createGain()\n      this.masterGainNode.connect(this.audioContext.destination)\n      this.masterGainNode.gain.setValueAtTime(this.masterVolume, this.audioContext.currentTime)\n\n      this.sfxGainNode = this.audioContext.createGain()\n      this.sfxGainNode.connect(this.masterGainNode)\n      this.sfxGainNode.gain.setValueAtTime(this.sfxVolume, this.audioContext.currentTime)\n\n      this.musicGainNode = this.audioContext.createGain()\n      this.musicGainNode.connect(this.masterGainNode)\n      this.musicGainNode.gain.setValueAtTime(this.musicVolume, this.audioContext.currentTime)\n      // console.log(\"AudioManager: Initialized with AudioContext and GainNodes.\");\n    } else {\n      console.warn('AudioManager: AudioContext not available. Audio playback will be disabled.')\n    }\n  }\n\n  /**\n   * @private\n   * Gets or creates an AudioContext.\n   * @returns {AudioContext | null}\n   */\n  _getAudioContext() {\n    if (this.audioContext) return this.audioContext\n    try {\n      this.audioContext = new (window.AudioContext || window.webkitAudioContext)()\n      // console.log(\"AudioManager: AudioContext created internally.\");\n      return this.audioContext\n    } catch (e) {\n      console.error('AudioManager: Web Audio API is not supported.', e)\n      return null\n    }\n  }\n\n  /**\n   * Plays a sound effect.\n   * @param {string} assetName - The name of the pre-loaded audio asset (AudioBuffer).\n   * @param {number} [volume=1.0] - Volume for this specific sound (0-1), relative to sfxVolume.\n   * @param {number} [playbackRate=1.0] - Speed of playback.\n   * @param {number} [startTime=0] - Offset in seconds to start playing from.\n   * @returns {AudioBufferSourceNode | null} The created source node, or null if sound cannot be played.\n   */\n  playSoundEffect(assetName, volume = 1.0, playbackRate = 1.0, startTime = 0) {\n    if (!this.audioContext || !this.sfxGainNode || this.isMuted) return null\n\n    const audioBuffer = this.assetLoader.get(assetName)\n    if (!(audioBuffer instanceof AudioBuffer)) {\n      console.warn(\n        `AudioManager.playSoundEffect: Audio asset \"${assetName}\" not found or not an AudioBuffer.`,\n      )\n      return null\n    }\n\n    const source = this.audioContext.createBufferSource()\n    source.buffer = audioBuffer\n    source.playbackRate.value = playbackRate\n\n    // Create a dedicated gain node for this specific sound effect instance for individual volume control\n    const individualSfxGain = this.audioContext.createGain()\n    individualSfxGain.gain.setValueAtTime(\n      Math.max(0, Math.min(volume, 1)),\n      this.audioContext.currentTime,\n    )\n\n    source.connect(individualSfxGain)\n    individualSfxGain.connect(this.sfxGainNode) // Connect to the main SFX gain node\n\n    source.start(0, startTime) // Play immediately, from startTime\n    // console.log(`AudioManager: Playing SFX \"${assetName}\"`);\n    return source\n  }\n\n  /**\n   * Plays background music. Stops any currently playing music.\n   * @param {string} assetName - The name of the pre-loaded audio asset (AudioBuffer).\n   * @param {boolean} [loop=true] - Whether the music should loop.\n   * @param {number} [volume=1.0] - Volume for this music track (0-1), relative to musicVolume.\n   * @param {number} [fadeInDuration=0] - Duration in seconds for a fade-in effect.\n   */\n  playMusic(assetName, loop = true, volume = 1.0, fadeInDuration = 0) {\n    if (!this.audioContext || !this.musicGainNode) return\n\n    this.stopMusic() // Stop any current music\n\n    const audioBuffer = this.assetLoader.get(assetName)\n    if (!(audioBuffer instanceof AudioBuffer)) {\n      console.warn(\n        `AudioManager.playMusic: Audio asset \"${assetName}\" not found or not an AudioBuffer.`,\n      )\n      return\n    }\n\n    this.currentMusicSource = this.audioContext.createBufferSource()\n    this.currentMusicSource.buffer = audioBuffer\n    this.currentMusicSource.loop = loop\n    this.isMusicLooping = loop\n    this.currentMusicName = assetName\n\n    // Individual gain for this track, allowing fade-ins and specific volume adjustments\n    const trackGainNode = this.audioContext.createGain()\n    trackGainNode.connect(this.musicGainNode)\n    this.currentMusicSource.connect(trackGainNode)\n\n    const targetVolume = Math.max(0, Math.min(volume, 1))\n\n    if (fadeInDuration > 0 && !this.isMuted) {\n      trackGainNode.gain.setValueAtTime(0, this.audioContext.currentTime)\n      trackGainNode.gain.linearRampToValueAtTime(\n        targetVolume,\n        this.audioContext.currentTime + fadeInDuration,\n      )\n    } else if (!this.isMuted) {\n      trackGainNode.gain.setValueAtTime(targetVolume, this.audioContext.currentTime)\n    } else {\n      trackGainNode.gain.setValueAtTime(0, this.audioContext.currentTime) // Start muted if globally muted\n    }\n\n    this.currentMusicSource.start(0)\n    // console.log(`AudioManager: Playing music \"${assetName}\". Loop: ${loop}, Volume: ${targetVolume}`);\n    if (this.eventManager)\n      this.eventManager.emit('audio:musicStarted', { name: assetName, loop: loop })\n  }\n\n  /**\n   * Stops the currently playing background music.\n   * @param {number} [fadeOutDuration=0] - Duration in seconds for a fade-out effect.\n   */\n  stopMusic(fadeOutDuration = 0) {\n    if (this.currentMusicSource && this.audioContext && this.musicGainNode) {\n      const sourceToStop = this.currentMusicSource\n      const nameOfMusicStopped = this.currentMusicName\n      // console.log(`AudioManager: Stopping music \"${nameOfMusicStopped}\". Fade: ${fadeOutDuration}s`);\n\n      if (fadeOutDuration > 0 && sourceToStop.context.state === 'running') {\n        // Assuming the music source is connected to a gain node which is then connected to musicGainNode\n        // We need to find that specific gain node to fade it out.\n        // For simplicity, if we fade, we fade the main musicGainNode, then restore it.\n        // A better approach: each playing music track has its own gain node.\n        // Our current playMusic creates a trackGainNode, but we don't store it on this.\n        // For now, simple stop or fade main musicGainNode then reset.\n\n        const currentMusicVolume = this.musicGainNode.gain.value\n        this.musicGainNode.gain.setValueAtTime(currentMusicVolume, this.audioContext.currentTime)\n        this.musicGainNode.gain.linearRampToValueAtTime(\n          0,\n          this.audioContext.currentTime + fadeOutDuration,\n        )\n\n        sourceToStop.stop(this.audioContext.currentTime + fadeOutDuration)\n        // Restore musicGainNode volume after fade for next track\n        setTimeout(\n          () => {\n            if (this.musicGainNode && !this.isMuted) {\n              this.musicGainNode.gain.setValueAtTime(\n                this.musicVolume,\n                this.audioContext.currentTime,\n              )\n            }\n          },\n          fadeOutDuration * 1000 + 50,\n        )\n      } else {\n        sourceToStop.stop(0)\n      }\n\n      this.currentMusicSource.onended = () => {\n        if (this.eventManager && nameOfMusicStopped === this.currentMusicName) {\n          // Ensure it's the same track that ended\n          this.eventManager.emit('audio:musicEnded', { name: nameOfMusicStopped })\n        }\n      }\n\n      this.currentMusicSource = null\n      this.currentMusicName = null\n    }\n  }\n\n  /**\n   * Pauses the AudioContext (suspends all audio).\n   */\n  pauseAll() {\n    if (this.audioContext && this.audioContext.state === 'running') {\n      this.audioContext.suspend().then(() => {\n        /* console.log(\"AudioManager: AudioContext suspended.\"); */\n      })\n    }\n  }\n\n  /**\n   * Resumes the AudioContext (resumes all audio).\n   * Needs user interaction to start/resume AudioContext if it was suspended by browser policy.\n   */\n  resumeAll() {\n    if (this.audioContext && this.audioContext.state === 'suspended') {\n      this.audioContext.resume().then(() => {\n        /* console.log(\"AudioManager: AudioContext resumed.\"); */\n      })\n    } else if (!this.audioContext) {\n      // Attempt to create it if it wasn't created (e.g. due to no user interaction yet)\n      this._getAudioContext()\n    }\n  }\n\n  // --- Volume Controls ---\n  _updateGains() {\n    if (!this.audioContext) return\n    const masterVol = this.isMuted ? 0 : this.masterVolume\n    if (this.masterGainNode) {\n      this.masterGainNode.gain.linearRampToValueAtTime(\n        masterVol,\n        this.audioContext.currentTime + 0.05,\n      )\n    }\n    if (this.sfxGainNode) {\n      this.sfxGainNode.gain.linearRampToValueAtTime(\n        this.sfxVolume,\n        this.audioContext.currentTime + 0.05,\n      )\n    }\n    if (this.musicGainNode) {\n      this.musicGainNode.gain.linearRampToValueAtTime(\n        this.musicVolume,\n        this.audioContext.currentTime + 0.05,\n      )\n    }\n  }\n\n  setMasterVolume(volume) {\n    this.masterVolume = Math.max(0, Math.min(volume, 1))\n    this._updateGains()\n    // console.log(`AudioManager: Master volume set to ${this.masterVolume}`);\n  }\n  setSfxVolume(volume) {\n    this.sfxVolume = Math.max(0, Math.min(volume, 1))\n    this._updateGains()\n    // console.log(`AudioManager: SFX volume set to ${this.sfxVolume}`);\n  }\n  setMusicVolume(volume) {\n    this.musicVolume = Math.max(0, Math.min(volume, 1))\n    this._updateGains()\n    // console.log(`AudioManager: Music volume set to ${this.musicVolume}`);\n  }\n\n  getSfxVolume() {\n    return this.sfxVolume\n  }\n  getMusicVolume() {\n    return this.musicVolume\n  }\n  getMasterVolume() {\n    return this.masterVolume\n  }\n\n  setMuted(muted) {\n    this.isMuted = !!muted\n    this._updateGains()\n    // console.log(`AudioManager: Mute state set to ${this.isMuted}`);\n    if (this.eventManager) this.eventManager.emit('audio:muteChanged', { muted: this.isMuted })\n  }\n  toggleMute() {\n    this.setMuted(!this.isMuted)\n  }\n  getMuted() {\n    return this.isMuted\n  }\n\n  /**\n   * Call this when the engine is shutting down.\n   */\n  destroy() {\n    this.stopMusic()\n    if (this.masterGainNode) this.masterGainNode.disconnect()\n    if (this.sfxGainNode) this.sfxGainNode.disconnect()\n    if (this.musicGainNode) this.musicGainNode.disconnect()\n    // AudioContext is managed by AssetLoader's destroy method or shared.\n    // If AudioManager created it internally, it should close it.\n    // For now, assume AssetLoader handles its AudioContext.\n    // console.log(\"AudioManager: Destroyed.\");\n  }\n}\n\nexport default AudioManager\n","// src/engine/dialogue/DialogueManager.js\n// import DialogueBox from '../ui/DialogueBox.js'; // Will be passed in\n\n/**\n * @file DialogueManager.js\n * @description Manages the flow and state of dialogues in the game.\n */\nclass DialogueManager {\n  /**\n   * @param {import('../core/IroncladEngine.js').default} engine\n   */\n  constructor(engine) {\n    if (!engine) {\n      throw new Error('DialogueManager: Engine instance is required.')\n    }\n    /** @private @type {import('../core/IroncladEngine.js').default} */\n    this.engine = engine\n    /** @private @type {import('../core/AssetLoader.js').default} */\n    this.assetLoader = engine.getAssetLoader()\n    /** @private @type {import('../core/InputManager.js').default} */\n    this.inputManager = engine.getInputManager()\n    /** @private @type {import('../core/EventManager.js').default} */\n    this.eventManager = engine.getEventManager()\n\n    /** @private @type {import('../ui/DialogueBox.js').default | null} The UI element for displaying dialogue. */\n    this.dialogueBoxInstance = null\n\n    /** @private @type {object | null} The entire loaded collection of dialogues. */\n    this.dialogueData = null\n    /** @private @type {string | null} Asset key for the main dialogue JSON file. */\n    this.dialogueDataAssetKey = 'gameDialogues' // Example key, configure as needed\n\n    /** @private @type {Array<object> | null} The current dialogue set (array of nodes) being processed. */\n    this.currentDialogueSet = null\n    /** @private @type {string | null} The ID of the current dialogue set. */\n    this.currentDialogueId = null\n    /** @private @type {number} Index of the current node within the currentDialogueSet. */\n    this.currentNodeIndex = -1\n    /** @private @type {object | null} The current dialogue node object. */\n    this.currentNode = null\n\n    /** @type {boolean} Whether a dialogue is currently active. */\n    this.isActive = false\n\n    // console.log(\"DialogueManager: Constructed.\");\n  }\n\n  /**\n   * Sets the DialogueBox UI instance that this manager will control.\n   * @param {import('../ui/DialogueBox.js').default} dialogueBox\n   */\n  setDialogueBox(dialogueBox) {\n    this.dialogueBoxInstance = dialogueBox\n    if (this.dialogueBoxInstance) {\n      this.dialogueBoxInstance.onTextAdvance = this._handleTextAdvanceRequest.bind(this)\n      this.dialogueBoxInstance.onChoiceSelect = this._handleChoiceSelection.bind(this)\n      // console.log(\"DialogueManager: DialogueBox instance set and callbacks configured.\");\n    } else {\n      console.warn('DialogueManager: A null DialogueBox instance was set.')\n    }\n  }\n\n  /**\n   * Loads the main dialogue data file.\n   * Call this after assets are loaded, e.g., in a scene's initialize.\n   * @param {string} [assetKey] - The asset key for the dialogue JSON file.\n   * @returns {boolean} True if data was loaded, false otherwise.\n   */\n  loadDialogueData(assetKey) {\n    this.dialogueDataAssetKey = assetKey || this.dialogueDataAssetKey\n    if (!this.assetLoader) {\n      console.error('DialogueManager: AssetLoader not available to load dialogue data.')\n      return false\n    }\n    this.dialogueData = this.assetLoader.get(this.dialogueDataAssetKey)\n    if (this.dialogueData) {\n      // console.log(`DialogueManager: Dialogue data \"${this.dialogueDataAssetKey}\" loaded successfully.`);\n      return true\n    } else {\n      console.error(\n        `DialogueManager: Failed to load dialogue data from asset key \"${this.dialogueDataAssetKey}\". Ensure it's preloaded.`,\n      )\n      return false\n    }\n  }\n\n  /**\n   * Starts a dialogue sequence by its ID.\n   * @param {string} dialogueId - The ID of the dialogue set to start (e.g., \"npc_hermit_greeting\").\n   * @returns {boolean} True if dialogue started successfully, false otherwise.\n   */\n  startDialogue(dialogueId) {\n    if (!this.dialogueBoxInstance) {\n      console.error('DialogueManager: Cannot start dialogue, DialogueBox instance not set.')\n      return false\n    }\n    if (!this.dialogueData) {\n      // console.warn(\"DialogueManager: Dialogue data not loaded. Attempting to load now.\");\n      if (!this.loadDialogueData()) {\n        // Try loading default key\n        console.error(\n          `DialogueManager: Cannot start dialogue \"${dialogueId}\", dialogue data still not available.`,\n        )\n        return false\n      }\n    }\n    if (!this.dialogueData[dialogueId]) {\n      console.error(`DialogueManager: Dialogue ID \"${dialogueId}\" not found in loaded data.`)\n      return false\n    }\n\n    this.currentDialogueId = dialogueId\n    this.currentDialogueSet = this.dialogueData[dialogueId]\n    this.currentNodeIndex = 0\n    this.isActive = true\n\n    this.dialogueBoxInstance.show() // Make sure the dialogue box is visible\n\n    this._displayCurrentNode()\n    if (this.eventManager) this.eventManager.emit('dialogue:started', { dialogueId })\n    // console.log(`DialogueManager: Started dialogue \"${dialogueId}\".`);\n    return true\n  }\n\n  /**\n   * Displays the current dialogue node in the DialogueBox.\n   * @private\n   */\n  _displayCurrentNode() {\n    if (\n      !this.currentDialogueSet ||\n      this.currentNodeIndex < 0 ||\n      this.currentNodeIndex >= this.currentDialogueSet.length\n    ) {\n      // console.log(\"DialogueManager: No current node to display or index out of bounds. Ending dialogue.\");\n      this.endDialogue()\n      return\n    }\n    this.currentNode = this.currentDialogueSet[this.currentNodeIndex]\n    // console.log(`DialogueManager: Displaying node ${this.currentNodeIndex}:`, this.currentNode);\n\n    this.dialogueBoxInstance.displayMessage({\n      speaker: this.currentNode.speaker,\n      text: this.currentNode.text,\n      portraitKey: this.currentNode.portrait,\n      choices: this.currentNode.choices, // Pass choices data to DialogueBox\n    })\n    if (this.eventManager)\n      this.eventManager.emit('dialogue:nodeDisplayed', {\n        dialogueId: this.currentDialogueId,\n        node: this.currentNode,\n      })\n  }\n\n  /**\n   * Called by DialogueBox when player advances text (and no choices).\n   * @private\n   */\n  _handleTextAdvanceRequest() {\n    if (!this.isActive || !this.currentNode) return\n    // console.log(\"DialogueManager: Text advance request received.\");\n    this.advanceToNextNode(this.currentNode.next)\n  }\n\n  /**\n   * Called by DialogueBox when player selects a choice.\n   * @private\n   * @param {string} choiceId - The 'id' of the choice object (not the 'leadsTo' value yet).\n   */\n  _handleChoiceSelection(choiceId) {\n    if (!this.isActive || !this.currentNode || !this.currentNode.choices) return\n\n    const selectedChoice = this.currentNode.choices.find((c) => c.id === choiceId) // Assuming choices have unique 'id's\n    if (selectedChoice && selectedChoice.leadsTo) {\n      // console.log(`DialogueManager: Choice \"${selectedChoice.text}\" selected, leading to node/dialogue ID: \"${selectedChoice.leadsTo}\".`);\n      if (this.eventManager)\n        this.eventManager.emit('dialogue:choiceMade', {\n          dialogueId: this.currentDialogueId,\n          choice: selectedChoice,\n        })\n      // TODO: Check if leadsTo is an internal node ID or a new dialogue ID\n      this.advanceToNextNode(selectedChoice.leadsTo)\n    } else {\n      console.warn('DialogueManager: Selected choice or its leadsTo path not found.', choiceId)\n      this.endDialogue() // Or handle differently\n    }\n  }\n\n  /**\n   * Advances to a specified next node ID or ends dialogue.\n   * @param {string | null | undefined} nextNodeId - The ID of the next node in the current set.\n   */\n  advanceToNextNode(nextNodeId) {\n    if (!this.isActive) return\n\n    if (nextNodeId) {\n      const nextIndex = this.currentDialogueSet.findIndex((node) => node.id === nextNodeId)\n      if (nextIndex !== -1) {\n        this.currentNodeIndex = nextIndex\n        this._displayCurrentNode()\n      } else {\n        console.warn(\n          `DialogueManager: Next node ID \"${nextNodeId}\" not found in current dialogue set \"${this.currentDialogueId}\". Ending dialogue.`,\n        )\n        this.endDialogue()\n      }\n    } else {\n      // No 'next' specified, end of this branch/dialogue\n      // console.log(`DialogueManager: No next node specified for node ID \"${this.currentNode?.id}\". Ending dialogue.`);\n      this.endDialogue()\n    }\n  }\n\n  /**\n   * Ends the current dialogue.\n   */\n  endDialogue() {\n    if (!this.isActive) return\n    // console.log(`DialogueManager: Ending dialogue \"${this.currentDialogueId}\".`);\n    const endedDialogueId = this.currentDialogueId\n\n    this.isActive = false\n    this.currentDialogueId = null\n    this.currentDialogueSet = null\n    this.currentNodeIndex = -1\n    this.currentNode = null\n\n    if (this.dialogueBoxInstance) {\n      this.dialogueBoxInstance.hide() // Hide the dialogue box\n    }\n    if (this.eventManager) this.eventManager.emit('dialogue:ended', { dialogueId: endedDialogueId })\n  }\n\n  /**\n   * Update method, called by the game loop (e.g., from a scene's update).\n   * Primarily listens for player input to advance dialogue if it's waiting.\n   * @param {number} deltaTime\n   */\n  update(deltaTime) {\n    if (!this.isActive || !this.dialogueBoxInstance || !this.inputManager) return\n\n    // If text is fully displayed, no choices are active, and player gives advance input\n    if (\n      this.dialogueBoxInstance.isWaitingForAdvance &&\n      !this.dialogueBoxInstance.isWaitingForChoice\n    ) {\n      // Using a generic 'confirm' or 'interact' action\n      if (\n        this.inputManager.isActionJustPressed('menuConfirm') ||\n        this.inputManager.isActionJustPressed('interact')\n      ) {\n        // console.log(\"DialogueManager: Advance action detected.\");\n        this.dialogueBoxInstance.requestAdvance() // This will trigger onTextAdvance -> _handleTextAdvanceRequest\n      }\n    }\n    // Choice selection is handled by Button clicks which directly call _handleChoiceSelection via DialogueBox.onChoiceSelect\n  }\n\n  /**\n   * Cleans up resources, though most are managed by other systems.\n   */\n  destroy() {\n    this.endDialogue() // Ensure current dialogue is properly ended\n    this.dialogueBoxInstance = null // Release reference\n    this.dialogueData = null\n    // console.log(\"DialogueManager: Destroyed.\");\n  }\n}\n\nexport default DialogueManager\n","// src/engine/core/IroncladEngine.js\n\n/**\n * @file IroncladEngine.js\n * @description Main class for the Ironclad Engine.\n */\n\nimport GameLoop from './GameLoop.js'\nimport SceneManager from './SceneManager.js'\nimport AssetLoader from './AssetLoader.js'\nimport InputManager from './InputManager.js'\nimport EventManager from './EventManager.js'\nimport EntityManager from '../ecs/EntityManager.js'\nimport PrefabManager from '../ecs/PrefabManager.js'\nimport Camera from '../rendering/Camera.js' // 1. Import Camera\nimport SaveLoadManager from './SaveLoadManager.js'\nimport EffectsManager from './EffectsManager.js'\nimport AudioManager from './AudioManager.js'\nimport DialogueManager from '../dialogue/DialogueManager.js'\n// import System from '../ecs/System.js'; // For type hint in getSystem if using TS/JSDoc extensively\n\nclass IroncladEngine {\n  static instance = null\n\n  /**\n   * @type {HTMLCanvasElement | null}\n   */\n  canvas = null\n  /**\n   * @type {CanvasRenderingContext2D | null}\n   */\n  context = null\n  /**\n   * @type {AssetLoader | null}\n   */\n  assetLoader = null\n  /**\n   * @type {InputManager | null}\n   */\n  inputManager = null\n  /**\n   * @type {SceneManager | null}\n   */\n  sceneManager = null\n  /**\n   * @type {EventManager | null}\n   */\n  events = null\n  /**\n   * @type {EntityManager | null}\n   */\n  entityManager = null\n  /**\n   * @type {PrefabManager | null}\n   */\n  prefabManager = null\n  /**\n   * @type {Camera | null} // 2. Add Camera property\n   */\n  camera = null\n\n  /**\n   * @private\n   * @type {Array<{system: import('../ecs/System.js').default, priority: number}>}\n   */\n  systems = []\n  /** @private @type {string | null} */\n  assetManifestPath = null\n  /** @private @type {object | null} */\n  sceneRegistry = null\n  /** @private @type {GameLoop | null} */\n  gameLoop = null\n\n  constructor({\n    canvas,\n    width = 800,\n    height = 600,\n    assetManifestPath,\n    sceneRegistry,\n    defaultWorldWidth, // Optional: for initial camera world bounds\n    defaultWorldHeight, // Optional: for initial camera world bounds\n  } = {}) {\n    if (IroncladEngine.instance) {\n      /* ... singleton check ... */ return IroncladEngine.instance\n    }\n    if (!canvas) throw new Error('IroncladEngine: Canvas element or ID must be provided.')\n\n    if (!assetManifestPath)\n      throw new Error('IroncladEngine: assetManifestPath must be provided in config.')\n    if (!sceneRegistry || Object.keys(sceneRegistry).length === 0) {\n      throw new Error('IroncladEngine: sceneRegistry must be provided and be non-empty in config.')\n    }\n\n    if (typeof canvas === 'string') {\n      this.canvas = document.getElementById(canvas)\n      if (!this.canvas)\n        throw new Error(`IroncladEngine: Canvas element with ID \"${canvas}\" not found.`)\n    } else if (canvas instanceof HTMLCanvasElement) {\n      this.canvas = canvas\n    } else {\n      throw new Error('IroncladEngine: Invalid canvas parameter.')\n    }\n\n    this.canvas.width = width\n    this.canvas.height = height\n    this.context = this.canvas.getContext('2d')\n    if (!this.context) throw new Error('IroncladEngine: Failed to get 2D rendering context.')\n\n    this.offscreenCanvas = document.createElement('canvas')\n    this.offscreenCanvas.width = width\n    this.offscreenCanvas.height = height\n    this.offscreenContext = this.offscreenCanvas.getContext('2d')\n    if (!this.offscreenContext)\n      throw new Error('IroncladEngine: Failed to get 2D rendering context for offscreen canvas.')\n    console.log('IroncladEngine: Offscreen canvas created.')\n\n    // Instantiate core managers\n    this.assetLoader = new AssetLoader()\n    this.inputManager = new InputManager()\n    this.inputManager.initialize(this.canvas, this)\n    this.saveLoadManager = new SaveLoadManager(this)\n    this.events = new EventManager()\n    this.entityManager = new EntityManager()\n    this.prefabManager = new PrefabManager(this.entityManager, this.assetLoader)\n    this.effectsManager = new EffectsManager(this)\n    this.audioManager = new AudioManager(this.assetLoader, this.events)\n    this.dialogueManager = new DialogueManager(this)\n\n    this.sceneManager = new SceneManager()\n    this.sceneManager.setContextAndEngine(this.offscreenContext, this)\n    // 2. Instantiate Camera\n    this.camera = new Camera({\n      viewportWidth: this.canvas.width,\n      viewportHeight: this.canvas.height,\n      worldWidth: defaultWorldWidth || this.canvas.width, // Initial world size\n      worldHeight: defaultWorldHeight || this.canvas.height, // Can be updated by scenes\n    })\n    console.log('IroncladEngine: Main Camera instantiated.')\n\n    this.systems = []\n    this.assetManifestPath = assetManifestPath\n    this.sceneRegistry = sceneRegistry\n    this.gameLoop = new GameLoop(\n      (deltaTime) => this._update(deltaTime),\n      () => this._render(),\n    )\n\n    IroncladEngine.instance = this\n    if (window) {\n      window.gameEngine = this\n      console.log('IroncladEngine: Instance created and assigned to window.gameEngine.')\n    }\n\n    this._registerScenes()\n  }\n\n  // ... _registerScenes, _update, _render, start, stop methods remain the same ...\n  // (Make sure _update calls system updates, _render calls sceneManager.render)\n  /** @private */\n  _registerScenes() {\n    console.log('IroncladEngine: Registering scenes...')\n    for (const sceneName in this.sceneRegistry) {\n      if (Object.hasOwnProperty.call(this.sceneRegistry, sceneName)) {\n        const SceneClass = this.sceneRegistry[sceneName]\n        if (typeof SceneClass === 'function') {\n          try {\n            this.sceneManager.add(sceneName, new SceneClass())\n          } catch (e) {\n            console.error(`IroncladEngine: Error instantiating scene \"${sceneName}\":`, e)\n          }\n        } else {\n          console.warn(\n            `IroncladEngine: Item in sceneRegistry for \"${sceneName}\" is not a class/constructor.`,\n          )\n        }\n      }\n    }\n  }\n\n  /** @private */\n  _update(deltaTime) {\n    this.sceneManager.update(deltaTime) // Scene logic first\n    for (const systemWrapper of this.systems) {\n      // Then ECS systems\n      const system = systemWrapper.system\n      const requiredComponents = system.constructor.requiredComponents || []\n      let relevantEntities = []\n      if (requiredComponents.length > 0) {\n        relevantEntities = this.entityManager.getEntitiesWithComponents(requiredComponents)\n      }\n      try {\n        system.update(deltaTime, relevantEntities, this)\n      } catch (error) {\n        console.error(`Error in system ${system.constructor.name}.update():`, error, system)\n      }\n    }\n    if (this.effectsManager) {\n      // console.log(\"[IroncladEngine._update] About to call effectsManager.update(). Active effects before:\", this.effectsManager.activeEffects.length);\n      this.effectsManager.update(deltaTime)\n      // console.log(\"[IroncladEngine._update] Called effectsManager.update(). Active effects after:\", this.effectsManager.activeEffects.length);\n    } else {\n      console.error('[IroncladEngine._update] effectsManager is MISSING!')\n    }\n    this.inputManager.update() // Input manager state clear at the end\n    if (this.events) {\n      this.events.emit('engine:update:frameEnd', deltaTime)\n    } else {\n      console.error('[IroncladEngine._update] events (EventManager) is missing!')\n    }\n  }\n\n  /** @private */\n  _render() {\n    if (!this.offscreenContext || !this.context || !this.sceneManager || !this.effectsManager)\n      return\n\n    // 1. Clear the offscreen canvas (main drawing target for scenes)\n    // Scenes themselves might also clear, but good to ensure it here.\n    this.offscreenContext.clearRect(0, 0, this.offscreenCanvas.width, this.offscreenCanvas.height)\n    // Or fill with a default background color if your scenes don't always draw a full background\n    // this.offscreenContext.fillStyle = 'black';\n    // this.offscreenContext.fillRect(0, 0, this.offscreenCanvas.width, this.offscreenCanvas.height);\n\n    // 2. Have the SceneManager render the current scene(s) TO THE OFFSCREEN CANVAS\n    // Ensure SceneManager's render method uses the context it was given (offscreenContext)\n    this.sceneManager.render() // SceneManager's render method uses this.context, which should be offscreenContext\n\n    // 3. Apply post-processing effects and draw to the main (visible) canvas\n    this.effectsManager.postRender(this.context, this.offscreenCanvas)\n\n    // 4. Render any top-level UI that should NOT be affected by screen effects (e.g., debug console)\n    // Example: if (this.debugOverlay) this.debugOverlay.render(this.context);\n  }\n\n  start(initialSceneName, initialSceneData = {}) {\n    if (!this.sceneManager.scenes[initialSceneName]) {\n      const errorMsg = `IroncladEngine: Cannot start. Initial scene \"${initialSceneName}\" not found. Available: ${Object.keys(this.sceneManager.scenes).join(', ')}`\n      console.error(errorMsg)\n      throw new Error(errorMsg)\n    }\n    this.sceneManager.switchTo(initialSceneName, { ...initialSceneData, engine: this }) // Ensure engine is passed to initial scene's initialize\n    this.gameLoop.start()\n    console.log(`IroncladEngine: Started with initial scene \"${initialSceneName}\".`)\n  }\n\n  stop() {\n    this.gameLoop.stop()\n    console.log('IroncladEngine: Stopped.')\n  }\n\n  // --- Public API Getters ---\n  getOffscreenCanvas() {\n    return this.offscreenCanvas\n  } // Optional getter\n  getOffscreenContext() {\n    return this.offscreenContext\n  } // Optional getter\n  getEffectsManager() {\n    return this.effectsManager\n  } // Optional getter\n  getAssetLoader() {\n    return this.assetLoader\n  }\n  getInputManager() {\n    return this.inputManager\n  }\n  getSceneManager() {\n    return this.sceneManager\n  }\n  getEventManager() {\n    return this.events\n  }\n  getEntityManager() {\n    return this.entityManager\n  }\n  getPrefabManager() {\n    return this.prefabManager\n  }\n  getSaveLoadManager() {\n    return this.saveLoadManager\n  }\n  getCanvas() {\n    return this.canvas\n  }\n  getContext() {\n    return this.context\n  }\n  getGameLoop() {\n    return this.gameLoop\n  }\n  getAssetManifestPath() {\n    return this.assetManifestPath\n  }\n  getCamera() {\n    return this.camera\n  } // 3. Add getter for Camera\n\n  // --- System Management API ---\n  registerSystem(systemInstance, priority = 0) {\n    /* ... as before ... */\n    if (!systemInstance || typeof systemInstance.update !== 'function') {\n      console.error('IroncladEngine.registerSystem: Invalid system instance.', systemInstance)\n      return\n    }\n    if (this.systems.some((sWrapper) => sWrapper.system === systemInstance)) {\n      console.warn(\n        `IroncladEngine: System \"${systemInstance.constructor.name}\" is already registered.`,\n      )\n      return\n    }\n    if (typeof systemInstance.initialize === 'function') {\n      systemInstance.initialize(this)\n    }\n    this.systems.push({ system: systemInstance, priority: priority })\n    this.systems.sort((a, b) => a.priority - b.priority)\n    console.log(\n      `IroncladEngine: System \"${systemInstance.constructor.name}\" registered with priority ${priority}.`,\n    )\n  }\n\n  unregisterSystem(systemInstance) {\n    /* ... as before ... */\n    const initialLength = this.systems.length\n    this.systems = this.systems.filter((sWrapper) => sWrapper.system !== systemInstance)\n    if (this.systems.length < initialLength) {\n      if (typeof systemInstance.shutdown === 'function') {\n        systemInstance.shutdown()\n      }\n      console.log(`IroncladEngine: System \"${systemInstance.constructor.name}\" unregistered.`)\n    } else {\n      console.warn(\n        `IroncladEngine.unregisterSystem: System \"${systemInstance.constructor.name}\" not found.`,\n      )\n    }\n  }\n\n  /**\n   * Retrieves a registered system instance by its class.\n   * @param {Function} SystemClass - The class of the system to retrieve.\n   * @returns {System | null} The system instance, or null if not found.\n   */\n  getSystem(SystemClass) {\n    // 4. Add getSystem method\n    const wrapper = this.systems.find((sw) => sw.system instanceof SystemClass)\n    return wrapper ? wrapper.system : null\n  }\n}\n\nexport default IroncladEngine\n","// src/engine/core/BaseScene.js\n\n/**\n * @file BaseScene.js\n * @description A base class for all game scenes, providing common lifecycle methods,\n * properties, and UI element management helpers.\n */\n\n/**\n * @class BaseScene\n * @abstract\n * @description An abstract base class that game scenes can extend.\n * It provides a common structure for initialization, updates, rendering,\n * and managing a list of UI elements.\n */\nclass BaseScene {\n  constructor() {\n    /** * Reference to the game engine instance.\n     * This is typically set by the SceneManager when the scene is initialized.\n     * @type {import('./IroncladEngine.js').default | null}\n     */\n    this.engine = null\n\n    /** * UI context data passed to this scene or managed by it.\n     * Can be used for sharing state with UI elements or other scenes.\n     * @type {object | null}\n     */\n    this.uiContext = null\n\n    /** * Array to hold UI elements managed by this scene.\n     * Use `addUIElement()` to add elements to this list.\n     * @type {import('../ui/BaseUIElement.js').default[]}\n     */\n    this.uiElements = []\n\n    /**\n     * If true, this scene blocks updates to scenes below it in the SceneManager stack.\n     * If false (e.g., for a HUD), scenes below it will also update.\n     * @type {boolean}\n     */\n    this.isModal = true\n\n    /**\n     * Tracks if the scene has been successfully initialized.\n     * @type {boolean}\n     */\n    this.isInitialized = false\n\n    // console.log(`${this.constructor.name}: Constructed.`);\n  }\n\n  /**\n   * Called by the SceneManager when the scene is first activated (pushed or switched to).\n   * Override this for one-time setup, loading scene-specific assets, and initializing UI.\n   * @param {import('./IroncladEngine.js').default} engine - The engine instance.\n   * @param {object} [data={}] - Optional data passed when the scene is activated (e.g., from `pushScene` or `switchTo`).\n   * @returns {Promise<void> | void}\n   */\n  async initialize(engine, data = {}) {\n    this.engine = engine\n    // If data contains uiContext, assign it\n    if (data && data.uiContext !== undefined) {\n      this.uiContext = data.uiContext\n    }\n    this.isInitialized = true\n    // console.log(`${this.constructor.name}: Initialized with data:`, data);\n  }\n\n  /**\n   * Called every frame by the SceneManager if this scene is active.\n   * Override for scene-specific game logic, input handling.\n   * This base implementation updates managed UI elements.\n   * @param {number} deltaTime - The time elapsed since the last frame, in seconds.\n   * @param {import('./IroncladEngine.js').default} engine - The engine instance.\n   * @returns {Promise<void> | void}\n   */\n  async update(deltaTime, engine) {\n    if (this.uiElements && this.uiElements.length > 0 && this.isInitialized) {\n      this.updateUIElements(deltaTime, engine)\n    }\n  }\n\n  /**\n   * Called every frame by the SceneManager for visible scenes.\n   * Override for scene-specific drawing operations (background, game world elements).\n   * This base implementation renders managed UI elements.\n   * @param {CanvasRenderingContext2D} context - The rendering context.\n   * @param {import('./IroncladEngine.js').default} engine - The engine instance.\n   * @returns {Promise<void> | void}\n   */\n  async render(context, engine) {\n    if (this.uiElements && this.uiElements.length > 0 && this.isInitialized) {\n      this.renderUIElements(context, engine)\n    }\n  }\n\n  /**\n   * Called by SceneManager when another modal scene is pushed on top of this one.\n   * @param {import('./IroncladEngine.js').default} engine - The engine instance.\n   * @returns {Promise<void> | void}\n   */\n  async pause(engine) {\n    // console.log(`${this.constructor.name}: Paused.`);\n  }\n\n  /**\n   * Called by SceneManager when this scene becomes active again after a scene above it was popped.\n   * @param {import('./IroncladEngine.js').default} engine - The engine instance.\n   * @param {any} [data=null] - Optional data passed from the popped scene.\n   * @returns {Promise<void> | void}\n   */\n  async resume(engine, data = null) {\n    // console.log(`${this.constructor.name}: Resumed with data:`, data);\n  }\n\n  /**\n   * Called by SceneManager when the scene is about to be permanently removed.\n   * Override for final cleanup. Base implementation clears UI elements.\n   * @param {import('./IroncladEngine.js').default} engine - The engine instance.\n   * @returns {Promise<void> | void}\n   */\n  async unload(engine) {\n    // console.log(`${this.constructor.name}: Unloading.`);\n    this.uiElements.forEach((element) => {\n      if (typeof element.destroy === 'function') {\n        element.destroy()\n      }\n    })\n    this.uiElements = []\n    this.isInitialized = false\n    this.engine = null // Nullify engine reference\n    this.uiContext = null\n  }\n\n  /**\n   * Adds a UI element to this scene's managed list and sets its engine reference.\n   * @param {import('../ui/BaseUIElement.js').default} element - The UI element to add.\n   */\n  addUIElement(element) {\n    if (element) {\n      if (typeof element.setEngine === 'function' && this.engine) {\n        element.setEngine(this.engine)\n      }\n      this.uiElements.push(element)\n    } else {\n      console.warn(`${this.constructor.name}: Attempted to add a null or undefined UI element.`)\n    }\n  }\n\n  /**\n   * Updates all enabled and visible UI elements managed by this scene.\n   * Typically called by the scene's `update` method or `BaseScene.update`.\n   * @param {number} deltaTime - The time elapsed since the last frame, in seconds.\n   * @param {import('./IroncladEngine.js').default} engine - The engine instance.\n   */\n  updateUIElements(deltaTime, engine) {\n    if (!engine || !engine.inputManager) {\n      // console.warn(`${this.constructor.name}: updateUIElements called without engine or inputManager.`);\n      return\n    }\n    const mousePos = engine.inputManager.getCanvasMousePosition()\n    for (const element of this.uiElements) {\n      if (element.visible && element.enabled) {\n        element.update(deltaTime, engine, mousePos)\n      }\n    }\n  }\n\n  /**\n   * Renders all visible UI elements managed by this scene.\n   * Typically called by the scene's `render` method or `BaseScene.render`.\n   * @param {CanvasRenderingContext2D} context - The rendering context.\n   * @param {import('./IroncladEngine.js').default} engine - The engine instance.\n   */\n  renderUIElements(context, engine) {\n    for (const element of this.uiElements) {\n      if (element.visible) {\n        element.render(context, engine)\n      }\n    }\n  }\n}\nexport default BaseScene\n","// src/engine/ecs/BaseEntity.js (New File)\n\n/**\n * @file BaseEntity.js\n * @description A base class for game entities that interact with the EntityManager.\n */\nclass BaseEntity {\n  /** @type {number} Unique ID for this entity within the EntityManager. */\n  id\n  /** @type {import('./EntityManager.js').default} Reference to the EntityManager. */\n  entityManager\n  /** @type {import('../core/IroncladEngine.js').default | null} Optional reference to the engine. */\n  engine\n\n  /**\n   * Creates a BaseEntity instance.\n   * @param {object} options\n   * @param {import('./EntityManager.js').default} options.entityManager - The EntityManager instance.\n   * @param {import('../core/IroncladEngine.js').default} [options.engine] - Optional engine instance for global access.\n   * @param {number} [options.id] - Optional existing entity ID to wrap. If not provided, a new one is created.\n   */\n  constructor(options = {}) {\n    if (!options.entityManager) {\n      throw new Error('BaseEntity: EntityManager instance is required in options.')\n    }\n    this.entityManager = options.entityManager\n    this.engine = options.engine || null\n\n    if (options.id !== undefined && this.entityManager.isEntityAlive(options.id)) {\n      this.id = options.id\n      // console.log(`BaseEntity: Wrapping existing entity ID ${this.id}`);\n    } else {\n      this.id = this.entityManager.createEntity()\n      // console.log(`BaseEntity: Created new entity ID ${this.id}`);\n    }\n  }\n\n  /**\n   * Adds a component to this entity.\n   * @param {import('./EntityManager.js').ComponentTypeName} componentTypeName\n   * @param {import('./EntityManager.js').ComponentData} data\n   * @returns {import('./EntityManager.js').ComponentData | undefined}\n   */\n  addComponent(componentTypeName, data) {\n    return this.entityManager.addComponent(this.id, componentTypeName, data)\n  }\n\n  /**\n   * Retrieves a component from this entity.\n   * @param {import('./EntityManager.js').ComponentTypeName} componentTypeName\n   * @returns {import('./EntityManager.js').ComponentData | undefined}\n   */\n  getComponent(componentTypeName) {\n    return this.entityManager.getComponent(this.id, componentTypeName)\n  }\n\n  /**\n   * Checks if this entity has a specific component.\n   * @param {import('./EntityManager.js').ComponentTypeName} componentTypeName\n   * @returns {boolean}\n   */\n  hasComponent(componentTypeName) {\n    return this.entityManager.hasComponent(this.id, componentTypeName)\n  }\n\n  /**\n   * Removes a component from this entity.\n   * @param {import('./EntityManager.js').ComponentTypeName} componentTypeName\n   * @returns {boolean}\n   */\n  removeComponent(componentTypeName) {\n    return this.entityManager.removeComponent(this.id, componentTypeName)\n  }\n\n  /**\n   * Retrieves all components for this entity.\n   * @returns {Map<import('./EntityManager.js').ComponentTypeName, import('./EntityManager.js').ComponentData> | undefined}\n   */\n  getAllComponents() {\n    return this.entityManager.getAllComponents(this.id)\n  }\n\n  /**\n   * Destroys this entity and all its components from the EntityManager.\n   */\n  destroy() {\n    if (this.entityManager.isEntityAlive(this.id)) {\n      this.entityManager.destroyEntity(this.id)\n    }\n    // Further cleanup of this instance's references could go here if needed\n    // e.g., this.entityManager = null; this.engine = null;\n  }\n\n  /**\n   * Emits an event through the engine's EventManager, if available.\n   * @param {string} eventName - The name of the event.\n   * @param {object} [eventData={}] - Additional data for the event.\n   */\n  emitEvent(eventName, eventData = {}) {\n    if (this.engine && this.engine.events && typeof this.engine.events.emit === 'function') {\n      this.engine.events.emit(eventName, { entityId: this.id, ...eventData })\n    } else {\n      // console.warn(`BaseEntity (ID: ${this.id}): Cannot emit event \"${eventName}\", engine or EventManager not available/functional.`);\n    }\n  }\n}\n\nexport default BaseEntity\n","// src/engine/ecs/System.js\n\n/**\n * @file System.js\n * @description Base class for all systems in the Entity-Component-System architecture.\n */\n\n/**\n * @class System\n * @description Game-specific systems should extend this class.\n * They typically define a static `requiredComponents` array specifying which\n * components an entity must have for the system to process it, and an `update` method\n * containing the system's logic.\n *\n * @property {import('../core/IroncladEngine.js').default | null} engine - A reference to the main engine instance.\n * This is typically set by the IroncladEngine when the system is registered.\n */\nclass System {\n  /**\n   * An array of component type names (strings) that an entity must possess\n   * for this system to be interested in processing it.\n   * Derived systems should override this static property.\n   * Example: `static requiredComponents = [\"Position\", \"Velocity\"];`\n   * @type {Array<import('./EntityManager.js').ComponentTypeName>}\n   * @static\n   */\n  static requiredComponents = []\n\n  /**\n   * Creates an instance of a System.\n   * The engine instance can be injected later via the `initialize` method\n   * or directly if the system is created by game code that has an engine reference.\n   */\n  constructor() {\n    /**\n     * @type {import('../core/IroncladEngine.js').default | null}\n     */\n    this.engine = null\n    // console.log(`${this.constructor.name} system constructor called.`);\n  }\n\n  /**\n   * Called when the system is registered with the IroncladEngine.\n   * This is a good place for one-time setup or to store the engine reference.\n   * @param {import('../core/IroncladEngine.js').default} engine - The IroncladEngine instance.\n   */\n  initialize(engine) {\n    this.engine = engine\n    // console.log(`System \"${this.constructor.name}\": Initialized.`);\n  }\n\n  /**\n   * The main update method for the system, called every frame by the IroncladEngine.\n   * Derived systems MUST override this method with their specific logic.\n   *\n   * @param {number} deltaTime - The time elapsed since the last frame, in seconds.\n   * @param {Array<import('./EntityManager.js').EntityId>} entities - An array of entity IDs that match\n   * this system's `requiredComponents`. The engine pre-filters these.\n   * @param {import('../core/IroncladEngine.js').default} engine - The IroncladEngine instance, providing access\n   * to services like EntityManager, InputManager, EventManager, etc.\n   * (This is passed for convenience, same as `this.engine` if `initialize` was called).\n   */\n  update(deltaTime, entities, engine) {\n    // Default implementation does nothing. Derived systems should override this.\n    // console.warn(`System \"${this.constructor.name}\" must implement the update() method.`);\n  }\n\n  /**\n   * Called when the system is unregistered from the IroncladEngine or when the engine is shutting down.\n   * Useful for any cleanup tasks the system might need to perform.\n   */\n  shutdown() {\n    // console.log(`System \"${this.constructor.name}\": Shutdown.`);\n  }\n}\n\nexport default System\n","// src/engine/rendering/Sprite.js\n\n/**\n * @file Sprite.js\n * @description Defines a Sprite class for rendering images or portions of images (sprite sheets) on the canvas.\n */\n\n/**\n * @class Sprite\n * @description Represents a visual object that can be drawn on the canvas.\n * It can render an entire image or a specific rectangular portion of an image (a frame from a sprite sheet).\n */\nclass Sprite {\n  /**\n   * Creates an instance of Sprite.\n   * @param {HTMLImageElement} image - The HTMLImageElement to be rendered. Should be pre-loaded.\n   * @param {number} [x=0] - The x-coordinate for the sprite's top-left corner on the canvas.\n   * @param {number} [y=0] - The y-coordinate for the sprite's top-left corner on the canvas.\n   * @param {number} [width] - The width to draw the sprite on the canvas. Defaults to source frame width or image width.\n   * @param {number} [height] - The height to draw the sprite on the canvas. Defaults to source frame height or image height.\n   * @param {number} [sx=0] - The x-coordinate of the top-left corner of the sub-rectangle (frame) in the source image.\n   * @param {number} [sy=0] - The y-coordinate of the top-left corner of the sub-rectangle (frame) in the source image.\n   * @param {number} [sWidth] - The width of the sub-rectangle (frame) in the source image. Defaults to image width.\n   * @param {number} [sHeight] - The height of the sub-rectangle (frame) in the source image. Defaults to image height.\n   */\n  constructor(image, x = 0, y = 0, width, height, sx = 0, sy = 0, sWidth, sHeight) {\n    if (!(image instanceof HTMLImageElement)) {\n      console.error('Sprite: Invalid image provided. Must be an HTMLImageElement.', image)\n      // You might throw an error here or set a placeholder/error state\n      this.image = null // Or a placeholder error image\n    } else {\n      this.image = image\n    }\n\n    this.x = x\n    this.y = y\n\n    // Source image frame dimensions\n    this.sx = sx\n    this.sy = sy\n    this.sWidth = sWidth === undefined ? (this.image ? this.image.naturalWidth - sx : 0) : sWidth\n    this.sHeight =\n      sHeight === undefined ? (this.image ? this.image.naturalHeight - sy : 0) : sHeight\n\n    // Destination canvas dimensions\n    this.width = width === undefined ? this.sWidth : width\n    this.height = height === undefined ? this.sHeight : height\n\n    /** @type {boolean} - Whether the sprite should be rendered. */\n    this.visible = true\n\n    /** @type {number} - Opacity of the sprite (0 = transparent, 1 = opaque). */\n    this.opacity = 1.0\n\n    /** @type {number} - Rotation angle in radians. Rotation is around the sprite's anchor point. */\n    this.rotation = 0 // Radians\n\n    /** @type {{x: number, y: number}} - Anchor point for rotation and scaling (0-1 scale, e.g., {x:0.5, y:0.5} is center). */\n    this.anchor = { x: 0.5, y: 0.5 }\n\n    /** @type {number} - Horizontal scale factor. */\n    this.scaleX = 1.0\n    /** @type {number} - Vertical scale factor. */\n    this.scaleY = 1.0\n\n    // Ensure sWidth and sHeight are valid if image is null initially due to error\n    if (!this.image) {\n      this.sWidth = sWidth === undefined ? 0 : sWidth\n      this.sHeight = sHeight === undefined ? 0 : sHeight\n      this.width = width === undefined ? this.sWidth : width\n      this.height = height === undefined ? this.sHeight : height\n    }\n  }\n\n  /**\n   * Sets the source frame for the sprite, useful for sprite sheet animations.\n   * @param {number} sx - The x-coordinate of the top-left corner of the frame in the source image.\n   * @param {number} sy - The y-coordinate of the top-left corner of the frame in the source image.\n   * @param {number} sWidth - The width of the frame in the source image.\n   * @param {number} sHeight - The height of the frame in the source image.\n   */\n  setFrame(sx, sy, sWidth, sHeight) {\n    this.sx = sx\n    this.sy = sy\n    this.sWidth = sWidth\n    this.sHeight = sHeight\n    // Optionally update destination width/height if they are meant to match the frame\n    // this.width = sWidth;\n    // this.height = sHeight;\n  }\n\n  /**\n   * Renders the sprite on the canvas context.\n   * Takes into account visibility, opacity, rotation, and scaling.\n   * @param {CanvasRenderingContext2D} context - The 2D rendering context of the canvas.\n   */\n  render(context) {\n    if (!this.visible || !this.image || this.opacity <= 0) {\n      return\n    }\n\n    context.save() // Save current context state\n\n    // Apply opacity\n    context.globalAlpha = this.opacity\n\n    // Calculate translation point for rotation and scaling\n    const anchorX = this.x + this.width * this.anchor.x\n    const anchorY = this.y + this.height * this.anchor.y\n\n    // Translate to anchor point, apply rotation and scale, then translate back\n    if (this.rotation !== 0 || this.scaleX !== 1.0 || this.scaleY !== 1.0) {\n      context.translate(anchorX, anchorY)\n      if (this.rotation !== 0) context.rotate(this.rotation)\n      if (this.scaleX !== 1.0 || this.scaleY !== 1.0) context.scale(this.scaleX, this.scaleY)\n      context.translate(-anchorX, -anchorY)\n    }\n\n    // Draw the image\n    // The sx, sy, sWidth, sHeight define the portion of the image to draw.\n    // The x, y, width, height define where on the canvas and how large to draw it.\n    try {\n      context.drawImage(\n        this.image,\n        this.sx,\n        this.sy,\n        this.sWidth,\n        this.sHeight,\n        this.x,\n        this.y,\n        this.width,\n        this.height,\n      )\n    } catch (e) {\n      console.error('Error drawing sprite:', e, this)\n      // This can happen if sWidth/sHeight are 0 or image is not fully loaded/corrupted\n    }\n\n    context.restore() // Restore context state to prevent affecting other drawings\n  }\n\n  /**\n   * Updates the sprite's state. Placeholder for now, could be used for animations internal to the sprite.\n   * @param {number} deltaTime - The time elapsed since the last frame, in seconds.\n   */\n  update(deltaTime) {\n    // Currently, Sprite is mostly a rendering component.\n    // Animation logic (changing sx, sy) would typically be handled by an AnimationController\n    // or an Entity that owns this Sprite.\n  }\n}\n\nexport default Sprite\n","// src/engine/ecs/systems/RenderSystem.js\n\nimport System from '../System.js'\nimport Sprite from '../../rendering/Sprite.js' // Ensure this path is correct\n\nclass RenderSystem extends System {\n  static requiredComponents = ['Position', 'RenderableSprite']\n\n  assetLoader = null\n\n  constructor() {\n    super()\n    // console.log(\"RenderSystem: Constructor called\");\n  }\n\n  initialize(engine) {\n    super.initialize(engine)\n    if (this.engine) {\n      this.assetLoader = this.engine.getAssetLoader()\n    }\n    if (!this.assetLoader) {\n      console.error('RenderSystem: AssetLoader not available from engine!')\n    }\n    // console.log('RenderSystem: Initialized.');\n  }\n\n  update(deltaTime, entities, engine) {\n    // Not typically used for drawing if executeRenderPass is primary drawing method.\n  }\n\n  executeRenderPass(context, camera) {\n    if (!this.engine || !this.assetLoader || !context || !camera) {\n      console.error(\n        'RenderSystem.executeRenderPass: Missing critical dependencies (engine, assetLoader, context, or camera).',\n      )\n      return\n    }\n\n    const entityManager = this.engine.getEntityManager()\n    if (!entityManager) {\n      console.error('RenderSystem.executeRenderPass: EntityManager not available.')\n      return\n    }\n\n    const entitiesToRender = entityManager.getEntitiesWithComponents(\n      RenderSystem.requiredComponents,\n    )\n\n    // console.log(`[RenderSystem] Found ${entitiesToRender.length} entities to render.`); // DEBUG\n\n    const viewportX = camera.getViewportX()\n    const viewportY = camera.getViewportY()\n\n    for (const entityId of entitiesToRender) {\n      const position = entityManager.getComponent(entityId, 'Position')\n      const renderable = entityManager.getComponent(entityId, 'RenderableSprite')\n\n      // This check should be redundant if getEntitiesWithComponents works correctly\n      if (!position || !renderable) {\n        // console.warn(`[RenderSystem] Entity ${entityId} missing Position or RenderableSprite component during render pass.`);\n        continue\n      }\n\n      if (renderable.visible === false) {\n        // Check visibility flag from component\n        // console.log(`[RenderSystem] Entity ${entityId} (${renderable.assetName}) is not visible.`);\n        continue\n      }\n\n      // Use 'assetName' from the RenderableSprite component\n      const assetKey = renderable.assetName // <<<< CORRECTED PROPERTY NAME\n      if (!assetKey) {\n        // console.warn(`[RenderSystem] Entity ${entityId} RenderableSprite missing assetName.`);\n        continue\n      }\n\n      // Lazy instantiate or update Sprite object on the component\n      // This allows AnimationSystem to modify sx, sy, sWidth, sHeight on RenderableSprite\n      // and RenderSystem picks them up.\n      if (\n        !renderable.spriteInstance ||\n        renderable.spriteInstance.image?.src.includes(assetKey) === false\n      ) {\n        // Re-create if image source changed\n        const image = this.assetLoader.get(assetKey)\n        if (image instanceof HTMLImageElement) {\n          // console.log(`[RenderSystem] Creating/Updating spriteInstance for entity ${entityId} with asset \"${assetKey}\"`);\n          renderable.spriteInstance = new Sprite(\n            image,\n            0,\n            0, // Initial x, y for Sprite object (will be updated below)\n            renderable.width || image.naturalWidth, // Destination width\n            renderable.height || image.naturalHeight, // Destination height\n            renderable.sx || 0, // Source X\n            renderable.sy || 0, // Source Y\n            renderable.sWidth || image.naturalWidth, // Source Width\n            renderable.sHeight || image.naturalHeight, // Source Height\n          )\n        } else {\n          console.warn(\n            `[RenderSystem] Image asset \"${assetKey}\" not found or not an HTMLImageElement for entity ${entityId}. Cannot render.`,\n          )\n          renderable.spriteInstance = null // Ensure it's null if image fails\n          continue\n        }\n      }\n\n      if (renderable.spriteInstance) {\n        const sprite = renderable.spriteInstance\n\n        sprite.x = Math.floor(position.x - viewportX + (renderable.offsetX || 0))\n        sprite.y = Math.floor(position.y - viewportY + (renderable.offsetY || 0))\n        // Position.z could be used for layering/sorting here or for y-sorting\n        // For now, let's assume RenderableSprite.layer or Position.z can be used by a sort function if needed\n\n        // Ensure sprite's display dimensions are set from the component, fallback to frame/image size\n        sprite.width =\n          renderable.width !== undefined\n            ? renderable.width\n            : renderable.sWidth || sprite.image.naturalWidth\n        sprite.height =\n          renderable.height !== undefined\n            ? renderable.height\n            : renderable.sHeight || sprite.image.naturalHeight\n\n        // Update sprite from RenderableSprite component (animation system might change these)\n        if (renderable.sx !== undefined) sprite.sx = renderable.sx\n        if (renderable.sy !== undefined) sprite.sy = renderable.sy\n        if (renderable.sWidth !== undefined) sprite.sWidth = renderable.sWidth\n        if (renderable.sHeight !== undefined) sprite.sHeight = renderable.sHeight\n\n        sprite.opacity = renderable.opacity !== undefined ? renderable.opacity : 1.0\n        sprite.rotation = renderable.rotation !== undefined ? renderable.rotation : 0\n        sprite.scaleX = renderable.scaleX !== undefined ? renderable.scaleX : 1.0\n        sprite.scaleY = renderable.scaleY !== undefined ? renderable.scaleY : 1.0\n        sprite.visible = renderable.visible !== undefined ? renderable.visible : true // Already checked above but good for sprite state\n\n        // console.log(`[RenderSystem] Rendering Entity ${entityId} (${assetKey}) at screen (x:${sprite.x}, y:${sprite.y})`);\n        sprite.render(context)\n      }\n    }\n  }\n}\n\nexport default RenderSystem\n","// src/engine/ecs/systems/AnimationSystem.js\n\nimport System from '../System.js'\n\n// ... (JSDoc typedefs remain the same) ...\n\nclass AnimationSystem extends System {\n  static requiredComponents = ['RenderableSprite', 'Animation']\n\n  constructor() {\n    super()\n    // console.log(\"[AnimationSystem] Constructor called\");\n  }\n\n  initialize(engine) {\n    super.initialize(engine)\n    console.log('[AnimationSystem] Initialized.')\n  }\n\n  update(deltaTime, entities, engine) {\n    if (!engine || !engine.entityManager) {\n      console.error('[AnimationSystem] EntityManager not available via engine instance!')\n      return\n    }\n\n    if (entities.length > 0) {\n      // console.log(`[AnimationSystem] Updating ${entities.length} entities.`);\n    }\n\n    for (const entityId of entities) {\n      const anim = engine.entityManager.getComponent(entityId, 'Animation')\n      const renderable = engine.entityManager.getComponent(entityId, 'RenderableSprite')\n\n      if (!anim || !renderable) {\n        // This should not happen if requiredComponents filter works\n        console.warn(\n          `[AnimationSystem] Entity ${entityId} missing Animation or RenderableSprite component.`,\n        )\n        continue\n      }\n\n      // console.log(`[AnimationSystem] Processing Entity ${entityId}:`, { anim, renderable });\n\n      if (!anim.isPlaying || !anim.currentAnimation || !anim.animations) {\n        // console.log(`[AnimationSystem] Entity ${entityId}: Not playing or no current animation/definitions. isPlaying: ${anim.isPlaying}, currentAnim: ${anim.currentAnimation}`);\n        continue\n      }\n\n      const currentAnimDef = anim.animations[anim.currentAnimation]\n      if (\n        !currentAnimDef ||\n        !currentAnimDef.frames ||\n        currentAnimDef.frames.length === 0 ||\n        !currentAnimDef.columns ||\n        currentAnimDef.columns <= 0 ||\n        !currentAnimDef.tileWidth || // Added check for tileWidth\n        !currentAnimDef.tileHeight // Added check for tileHeight\n      ) {\n        console.warn(\n          `[AnimationSystem] Entity ${entityId}: Animation definition for \"${anim.currentAnimation}\" is invalid (missing frames, columns, tileWidth, or tileHeight). Def:`,\n          currentAnimDef,\n        )\n        continue\n      }\n\n      // console.log(`[AnimationSystem] Entity ${entityId}, Anim: \"${anim.currentAnimation}\", FrameTimer (before): ${anim.frameTimer.toFixed(3)}, CurrentFrameIdx: ${anim.currentFrameIndex}`);\n\n      const effectiveDeltaTime = deltaTime * (anim.playbackSpeed || 1.0)\n      anim.frameTimer += effectiveDeltaTime\n\n      if (anim.frameTimer >= currentAnimDef.frameDuration) {\n        anim.frameTimer -= currentAnimDef.frameDuration\n        anim.currentFrameIndex++\n        // console.log(`[AnimationSystem] Entity ${entityId}: Advanced to frame index ${anim.currentFrameIndex}`);\n\n        if (anim.currentFrameIndex >= currentAnimDef.frames.length) {\n          if (currentAnimDef.loop) {\n            anim.currentFrameIndex = 0\n            // console.log(`[AnimationSystem] Entity ${entityId}: Looped animation \"${anim.currentAnimation}\" to frame 0.`);\n          } else {\n            anim.currentFrameIndex = currentAnimDef.frames.length - 1\n            anim.isPlaying = false\n            console.log(\n              `[AnimationSystem] Entity ${entityId}: Animation \"${anim.currentAnimation}\" completed and stopped.`,\n            )\n            if (engine.events) {\n              engine.events.emit('animation:completed', {\n                entityId,\n                animationName: anim.currentAnimation,\n              })\n            }\n          }\n        }\n      }\n\n      const frameValueFromArray = currentAnimDef.frames[anim.currentFrameIndex]\n      const tileWidth = currentAnimDef.tileWidth\n      const tileHeight = currentAnimDef.tileHeight\n      const columns = currentAnimDef.columns\n\n      if (tileWidth > 0 && tileHeight > 0 && columns > 0 && frameValueFromArray !== undefined) {\n        renderable.sx = (frameValueFromArray % columns) * tileWidth\n        renderable.sy = Math.floor(frameValueFromArray / columns) * tileHeight\n        renderable.sWidth = tileWidth\n        renderable.sHeight = tileHeight\n\n        /* CRITICAL LOG: What values are being set on RenderableSprite?\n        console.log(\n          `[AnimationSystem] Entity ${entityId}, Anim: \"${anim.currentAnimation}\", FrameValue: ${frameValueFromArray}, CurrentFrameIdx: ${anim.currentFrameIndex} => Set Renderable: sx=${renderable.sx}, sy=${renderable.sy}, sW=${renderable.sWidth}, sH=${renderable.sHeight}`,\n        )\n          */\n      } else {\n        console.warn(\n          `[AnimationSystem] Entity ${entityId}: Animation \"${anim.currentAnimation}\" has invalid tileWidth, tileHeight, columns, or frameValueFromArray is undefined. FrameValue: ${frameValueFromArray}, tileW: ${tileWidth}, tileH: ${tileHeight}, cols: ${columns}`,\n        )\n      }\n    }\n  }\n}\n\nexport default AnimationSystem\n","// src/engine/physics/PhysicsConstants.js\r\n\r\n/**\r\n * @file PhysicsConstants.js\r\n * @description Defines global constants for the physics system.\r\n */\r\n\r\n/**\r\n * Gravitational acceleration on the Y-axis in pixels per second squared.\r\n * Positive value means downward acceleration.\r\n * @type {number}\r\n */\r\nexport const GRAVITY_Y = 980 // A common value, adjust for game feel\r\n\r\n/**\r\n * Gravitational acceleration on the X-axis (usually 0 for most 2D/2.5D games).\r\n * @type {number}\r\n */\r\nexport const GRAVITY_X = 0\r\n\r\n/**\r\n * Gravitational acceleration on the Z-axis (usually 0 if Z is depth not height).\r\n * If Z was your vertical jump axis, this would be non-zero and GRAVITY_Y might be 0.\r\n * For our X (horizontal), Y (vertical screen, jump/fall), Z (depth) convention, Z gravity is 0.\r\n * @type {number}\r\n */\r\nexport const GRAVITY_Z = 0\r\n\r\n/**\r\n * A small value to prevent division by zero or floating point inaccuracies.\r\n * @type {number}\r\n */\r\nexport const EPSILON = 0.0001\r\n\r\n// Add other constants as needed, e.g.:\r\n// export const DEFAULT_AIR_DENSITY = 1.2;\r\n// export const DEFAULT_STATIC_FRICTION = 0.6;\r\n// export const DEFAULT_DYNAMIC_FRICTION = 0.4;\r\n","/* eslint-disable no-prototype-builtins */\n// src/engine/physics/PhysicsSystem.js\nimport System from '../ecs/System.js'\nimport { GRAVITY_Y, EPSILON } from './PhysicsConstants.js' // Assuming GRAVITY_X and GRAVITY_Z are 0\n\n// --- Component Structure Definitions (JSDoc Typedefs) ---\n/** @typedef {object} PositionComponent {x,y,z} */\n/** @typedef {object} VelocityComponent {vx,vy,vz} */\n/** @typedef {object} AccelerationComponent {ax,ay,az} */\n/** @typedef {object} PhysicsBodyComponent {entityType, mass, useGravity, gravityScale, friction, restitution, isOnGround} */\n/** @typedef {object} ColliderComponent {shape, width, height, depth, offsetX,Y,Z, isTrigger, collisionLayer, collisionMask, collidesWithTiles} */\n\nclass PhysicsSystem extends System {\n  static requiredComponents = ['Position', 'Velocity', 'PhysicsBody', 'Collider']\n\n  constructor() {\n    super()\n  }\n  initialize(engine) {\n    super.initialize(engine)\n  }\n\n  _getAABB(position, collider) {\n    // Assuming position x,y,z is the CENTER of the entity\n    const centerX = position.x + (collider.offsetX || 0)\n    const centerY = position.y + (collider.offsetY || 0)\n    const centerZ = position.z + (collider.offsetZ || 0) // Keep Z for 3D AABB definition\n    const halfWidth = collider.width / 2\n    const halfHeight = collider.height / 2\n    const halfDepth = (collider.depth || 1) / 2 // Default depth if not specified for Z\n\n    return {\n      minX: centerX - halfWidth,\n      maxX: centerX + halfWidth,\n      minY: centerY - halfHeight,\n      maxY: centerY + halfHeight,\n      minZ: centerZ - halfDepth,\n      maxZ: centerZ + halfDepth, // Full 3D AABB\n      centerX,\n      centerY,\n      centerZ,\n    }\n  }\n\n  _checkAABBOverlap(aabb1, aabb2) {\n    // Full 3D AABB Check\n    return (\n      aabb1.minX < aabb2.maxX &&\n      aabb1.maxX > aabb2.minX &&\n      aabb1.minY < aabb2.maxY &&\n      aabb1.maxY > aabb2.minY &&\n      aabb1.minZ < aabb2.maxZ &&\n      aabb1.maxZ > aabb2.minZ\n    )\n  }\n\n  _checkEntityTileCollision(entityId, position, collider, scene) {\n    /* ... as previously refined for 8-point check ... */\n  }\n\n  update(deltaTime, entities, engineRef) {\n    if (!this.entityManager || !this.engine) return\n\n    const activeScene = this.engine.sceneManager\n      ? this.engine.sceneManager.getActiveSceneInstance()\n      : null\n    const canCheckTileCollisions =\n      activeScene &&\n      typeof activeScene.isTileSolidAtWorldXY === 'function' &&\n      activeScene.tileWidth > 0 &&\n      activeScene.tileHeight > 0\n\n    // --- I. Update Positions based on Velocity and Forces (Integration) ---\n    for (const entityId of entities) {\n      // ... (Integration logic for position, velocity, acceleration, gravity as before) ...\n      // This calculates the \"desired\" or \"potential\" new position for this frame.\n      const position = this.entityManager.getComponent(entityId, 'Position')\n      const velocity = this.entityManager.getComponent(entityId, 'Velocity')\n      const physicsBody = this.entityManager.getComponent(entityId, 'PhysicsBody')\n      let acceleration = this.entityManager.getComponent(entityId, 'Acceleration')\n      if (!acceleration) acceleration = { ax: 0, ay: 0, az: 0 }\n\n      if (physicsBody.entityType === 'static') {\n        velocity.vx = 0\n        velocity.vy = 0\n        velocity.vz = 0\n        acceleration.ax = 0\n        acceleration.ay = 0\n        acceleration.az = 0\n        continue\n      }\n\n      let frameAx = acceleration.ax\n      let frameAy = acceleration.ay\n      let frameAz = acceleration.az\n      if (physicsBody.useGravity) {\n        frameAy += GRAVITY_Y * (physicsBody.gravityScale || 1.0)\n      }\n      velocity.vx += frameAx * deltaTime\n      velocity.vy += frameAy * deltaTime\n      velocity.vz += frameAz * deltaTime\n      position.x += velocity.vx * deltaTime\n      position.y += velocity.vy * deltaTime\n      position.z += velocity.vz * deltaTime\n      acceleration.ax = 0\n      acceleration.ay = physicsBody.useGravity\n        ? frameAy - GRAVITY_Y * (physicsBody.gravityScale || 1.0)\n        : 0\n      acceleration.az = 0 // Reset non-persistent parts\n    }\n\n    // --- II. Collision Detection & Response ---\n    // Iterate multiple times for better stability (e.g., 2-5 iterations)\n    const collisionIterations = 3 // Configurable\n    for (let iter = 0; iter < collisionIterations; iter++) {\n      for (let i = 0; i < entities.length; i++) {\n        const entityIdA = entities[i]\n        const positionA = this.entityManager.getComponent(entityIdA, 'Position')\n        const velocityA = this.entityManager.getComponent(entityIdA, 'Velocity')\n        const physicsBodyA = this.entityManager.getComponent(entityIdA, 'PhysicsBody')\n        const colliderA = this.entityManager.getComponent(entityIdA, 'Collider')\n\n        if (\n          !positionA ||\n          !velocityA ||\n          !physicsBodyA ||\n          !colliderA ||\n          physicsBodyA.entityType === 'static'\n        ) {\n          continue\n        }\n        if (physicsBodyA.hasOwnProperty('isOnGround')) physicsBodyA.isOnGround = false // Reset for this iteration pass\n\n        // A. Tile Collision Response (Axis-separated, simplified for now)\n        // This needs careful reintegration of precise snapping logic.\n        // For Octopath-style, this would be the primary collision for environment.\n        if (canCheckTileCollisions && colliderA.collidesWithTiles) {\n          const prevX = positionA.x - velocityA.vx * deltaTime // Approximate position before this iteration's X move\n          const prevY = positionA.y - velocityA.vy * deltaTime // Approximate position before this iteration's Y move\n\n          // Check X\n          if (\n            velocityA.vx !== 0 &&\n            this._checkEntityTileCollision(entityIdA, positionA, colliderA, activeScene)\n          ) {\n            positionA.x = prevX // Simple revert for X\n            velocityA.vx = 0\n          }\n          // Check Y (using current, potentially X-reverted positionA.x)\n          if (\n            velocityA.vy !== 0 &&\n            this._checkEntityTileCollision(entityIdA, positionA, colliderA, activeScene)\n          ) {\n            if (velocityA.vy > 0) {\n              // Moving Down (landing)\n              if (physicsBodyA.hasOwnProperty('isOnGround')) physicsBodyA.isOnGround = true\n            }\n            positionA.y = prevY // Simple revert for Y\n            velocityA.vy = 0\n          }\n        }\n\n        // B. Entity-to-Entity AABB Collision\n        const aabbA = this._getAABB(positionA, colliderA)\n\n        for (let j = i + 1; j < entities.length; j++) {\n          const entityIdB = entities[j]\n          const positionB = this.entityManager.getComponent(entityIdB, 'Position')\n          const velocityB = this.entityManager.getComponent(entityIdB, 'Velocity')\n          const physicsBodyB = this.entityManager.getComponent(entityIdB, 'PhysicsBody')\n          const colliderB = this.entityManager.getComponent(entityIdB, 'Collider')\n\n          if (!positionB || !colliderB || !physicsBodyB) continue\n\n          const canACollideB = (colliderA.collisionLayer & colliderB.collisionMask) !== 0\n          const canBCollideA = (colliderB.collisionLayer & colliderA.collisionMask) !== 0\n          if (!(canACollideB && canBCollideA)) continue\n\n          const aabbB = this._getAABB(positionB, colliderB)\n\n          if (this._checkAABBOverlap(aabbA, aabbB)) {\n            if (colliderA.isTrigger || colliderB.isTrigger) {\n              if (this.engine.events)\n                this.engine.events.emit('triggerEnter', { entityA: entityIdA, entityB: entityIdB })\n            } else {\n              // --- SOLID AABB COLLISION RESPONSE ---\n              // console.log(`%c[PhysicsSystem] SOLID AABB Collision: ${entityIdA} vs ${entityIdB}`, \"color: #FF8C00;\");\n\n              // Calculate penetration depths on X and Y (focus on 2D plane for now)\n              const overlapX = Math.min(aabbA.maxX, aabbB.maxX) - Math.max(aabbA.minX, aabbB.minX)\n              const overlapY = Math.min(aabbA.maxY, aabbB.maxY) - Math.max(aabbA.minY, aabbB.minY)\n\n              // Determine minimum penetration axis\n              if (overlapX < overlapY) {\n                // Resolve X collision\n                const penetration = overlapX\n                const direction = Math.sign(aabbA.centerX - aabbB.centerX) // Direction to push A\n\n                if (physicsBodyA.entityType === 'dynamic' && physicsBodyB.entityType === 'static') {\n                  positionA.x += direction * (penetration + EPSILON)\n                  velocityA.vx = 0\n                } else if (\n                  physicsBodyA.entityType === 'dynamic' &&\n                  physicsBodyB.entityType === 'dynamic'\n                ) {\n                  positionA.x += direction * (penetration / 2 + EPSILON)\n                  positionB.x -= direction * (penetration / 2 + EPSILON)\n                  // Simple velocity response: dampening or reflect based on mass/restitution later\n                  const vxa = velocityA.vx\n                  const vxb = velocityB.vx // Store before zeroing for potential reflection\n                  velocityA.vx = 0 // Simplistic stop\n                  velocityB.vx = 0 // Simplistic stop\n                }\n                // Update aabbA if further checks needed in this iteration for entity A\n                // aabbA = this._getAABB(positionA, colliderA);\n              } else {\n                // Resolve Y collision\n                const penetration = overlapY\n                const direction = Math.sign(aabbA.centerY - aabbB.centerY)\n\n                if (physicsBodyA.entityType === 'dynamic' && physicsBodyB.entityType === 'static') {\n                  positionA.y += direction * (penetration + EPSILON)\n                  if (velocityA.vy > 0 && direction < 0) {\n                    // A was moving down, hit static B from top\n                    if (physicsBodyA.hasOwnProperty('isOnGround')) physicsBodyA.isOnGround = true\n                  }\n                  velocityA.vy = 0\n                } else if (\n                  physicsBodyA.entityType === 'dynamic' &&\n                  physicsBodyB.entityType === 'dynamic'\n                ) {\n                  positionA.y += direction * (penetration / 2 + EPSILON)\n                  positionB.y -= direction * (penetration / 2 + EPSILON)\n                  const vya = velocityA.vy\n                  const vyb = velocityB.vy\n                  velocityA.vy = 0\n                  velocityB.vy = 0\n                  if (physicsBodyA.hasOwnProperty('isOnGround') && vya > 0 && direction < 0)\n                    physicsBodyA.isOnGround = true\n                  if (physicsBodyB.hasOwnProperty('isOnGround') && vyb > 0 && direction > 0)\n                    physicsBodyB.isOnGround = true\n                }\n                // aabbA = this._getAABB(positionA, colliderA);\n              }\n            } // end solid AABB collision\n          } // end overlap check\n        } // end inner entity loop (B)\n      } // end outer entity loop (A) for Collision Phase\n    } // end collisionIterations loop\n  } // End update method\n}\n\nexport default PhysicsSystem\n","// src/engine/rendering/TileLayerRenderer.js\n\n/**\n * @file TileLayerRenderer.js\n * @description Utility class to render individual tile layers from Tiled JSON map data,\n * supporting multiple tilesets and tile flipping/rotation.\n */\n\n/**\n * @typedef {object} ProcessedTileset\n * @property {number} firstgid\n * @property {HTMLImageElement} image\n * @property {string} name\n * @property {number} tileWidth\n * @property {number} tileHeight\n * @property {number} columns\n * @property {number} imageWidth\n * @property {number} imageHeight\n * @property {number} spacing\n * @property {number} margin\n */\n\nclass TileLayerRenderer {\n  /**\n   * Creates an instance of TileLayerRenderer.\n   * @param {object} mapData - The full parsed Tiled JSON map data.\n   * @param {Map<string, HTMLImageElement>} loadedTilesetImages - A Map where keys are the\n   * image paths (matching `mapData.tilesets[i].image`) and values are the\n   * pre-loaded HTMLImageElement objects.\n   */\n  constructor(mapData, loadedTilesetImages) {\n    this.mapData = mapData\n    this.mapDefaultTileWidth = mapData.tilewidth\n    this.mapDefaultTileHeight = mapData.tileheight\n\n    /**\n     * @private\n     * @type {Array<ProcessedTileset>}\n     */\n    this.processedTilesets = []\n\n    if (!mapData.tilesets || mapData.tilesets.length === 0) {\n      console.warn('TileLayerRenderer: No tilesets array found in mapData.')\n    } else {\n      mapData.tilesets.forEach((tsDef) => {\n        if (!tsDef.image) {\n          console.warn(\n            `TileLayerRenderer: Tileset definition (name: ${tsDef.name}, firstgid: ${tsDef.firstgid}) is missing 'image' property. Source: ${tsDef.source || 'N/A'}. This tileset will be skipped.`,\n          )\n          return\n        }\n        const imageElement = loadedTilesetImages.get(tsDef.image) // Key is the path from JSON\n\n        if (imageElement) {\n          if (\n            !tsDef.tilewidth ||\n            !tsDef.tileheight ||\n            tsDef.tilewidth <= 0 ||\n            tsDef.tileheight <= 0\n          ) {\n            console.error(\n              `TileLayerRenderer: Tileset \"${tsDef.name || tsDef.image}\" has invalid 'tilewidth' or 'tileheight'. Skipping.`,\n            )\n            return\n          }\n          this.processedTilesets.push({\n            firstgid: parseInt(tsDef.firstgid, 10),\n            image: imageElement,\n            name: tsDef.name || tsDef.image,\n            tileWidth: parseInt(tsDef.tilewidth, 10),\n            tileHeight: parseInt(tsDef.tileheight, 10),\n            columns:\n              parseInt(tsDef.columns, 10) ||\n              Math.floor(imageElement.naturalWidth / parseInt(tsDef.tilewidth, 10)),\n            imageWidth: parseInt(tsDef.imagewidth, 10) || imageElement.naturalWidth,\n            imageHeight: parseInt(tsDef.imageheight, 10) || imageElement.naturalHeight,\n            spacing: parseInt(tsDef.spacing, 10) || 0,\n            margin: parseInt(tsDef.margin, 10) || 0,\n          })\n        } else {\n          console.warn(\n            `TileLayerRenderer: Tileset image for \"${tsDef.image}\" (first GID: ${tsDef.firstgid}) not found in pre-loaded images map. This tileset's tiles cannot be rendered.`,\n          )\n        }\n      })\n      this.processedTilesets.sort((a, b) => b.firstgid - a.firstgid) // Sort for efficient GID lookup\n    }\n    console.log(\n      `TileLayerRenderer: Initialized. Processed ${this.processedTilesets.length} tilesets.`,\n    )\n  }\n\n  /**\n   * Finds the correct processed tileset and source rect for a given GID.\n   * @private\n   * @param {number} cleanGid - The Global ID of the tile, without flip flags.\n   * @returns {ProcessedTileset & {sx: number, sy: number} | null}\n   */\n  _getTileDetails(cleanGid) {\n    if (cleanGid === 0) return null\n    for (const tileset of this.processedTilesets) {\n      if (cleanGid >= tileset.firstgid) {\n        const localId = cleanGid - tileset.firstgid\n        const tileX = localId % tileset.columns\n        const tileY = Math.floor(localId / tileset.columns)\n\n        const sx = tileset.margin + tileX * (tileset.tileWidth + tileset.spacing)\n        const sy = tileset.margin + tileY * (tileset.tileHeight + tileset.spacing)\n\n        if (\n          sx < 0 ||\n          sy < 0 ||\n          sx + tileset.tileWidth > tileset.imageWidth ||\n          sy + tileset.tileHeight > tileset.imageHeight\n        ) {\n          // console.warn(`TileLayerRenderer: GID ${cleanGid} (local ${localId}) for tileset \"${tileset.name}\" maps out of image bounds.`);\n          return null\n        }\n        return { ...tileset, sx, sy } // Return the full tileset info + sx, sy\n      }\n    }\n    // console.warn(`TileLayerRenderer: GID ${cleanGid} not found in any processed tileset.`);\n    return null\n  }\n\n  /**\n   * Renders a specific named tile layer.\n   * @param {CanvasRenderingContext2D} context - The drawing context.\n   * @param {import('./Camera.js').default} camera - The game camera.\n   * @param {string | number} layerIdentifier - The name (string) or index (number) of the layer to render.\n   */\n  renderLayer(context, camera, layerIdentifier) {\n    if (!this.mapData || !this.mapData.layers || this.processedTilesets.length === 0) return\n    if (!context || !camera) {\n      console.error('TileLayerRenderer.renderLayer: Missing context or camera.')\n      return\n    }\n\n    const layer =\n      typeof layerIdentifier === 'string'\n        ? this.mapData.layers.find((l) => l.name === layerIdentifier && l.type === 'tilelayer')\n        : this.mapData.layers[layerIdentifier]\n\n    if (!layer || layer.type !== 'tilelayer' || layer.visible === false || layer.opacity === 0) {\n      // console.warn(`TileLayerRenderer: Layer \"${layerIdentifier}\" not found, not a tilelayer, not visible, or fully transparent.`);\n      return\n    }\n\n    const viewportX = camera.getViewportX()\n    const viewportY = camera.getViewportY()\n    const canvasWidth = context.canvas.width\n    const canvasHeight = context.canvas.height\n\n    // Use map's default tile dimensions for grid placement and culling\n    const destTileWidth = this.mapDefaultTileWidth\n    const destTileHeight = this.mapDefaultTileHeight\n\n    const startCol = Math.max(0, Math.floor(viewportX / destTileWidth))\n    const endCol = Math.min(layer.width, Math.ceil((viewportX + canvasWidth) / destTileWidth))\n    const startRow = Math.max(0, Math.floor(viewportY / destTileHeight))\n    const endRow = Math.min(layer.height, Math.ceil((viewportY + canvasHeight) / destTileHeight))\n\n    const originalAlpha = context.globalAlpha\n    if (layer.opacity !== undefined && layer.opacity < 1.0) {\n      context.globalAlpha = originalAlpha * layer.opacity\n    }\n\n    for (let r = startRow; r < endRow; r++) {\n      for (let c = startCol; c < endCol; c++) {\n        const tileIndex = r * layer.width + c\n        const gidWithFlags = layer.data[tileIndex]\n        if (gidWithFlags === 0) continue\n\n        const FLIPPED_HORIZONTALLY_FLAG = 0x80000000\n        const FLIPPED_VERTICALLY_FLAG = 0x40000000\n        const FLIPPED_DIAGONALLY_FLAG = 0x20000000\n        const cleanGid =\n          gidWithFlags &\n          ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG)\n\n        const tileDetails = this._getTileDetails(cleanGid)\n        if (!tileDetails) continue\n\n        const dx = Math.floor(c * destTileWidth - viewportX)\n        const dy = Math.floor(r * destTileHeight - viewportY)\n\n        if (\n          dx + destTileWidth < 0 ||\n          dx > canvasWidth ||\n          dy + destTileHeight < 0 ||\n          dy > canvasHeight\n        ) {\n          continue\n        }\n\n        context.save()\n        const centerX = dx + destTileWidth / 2\n        const centerY = dy + destTileHeight / 2\n        context.translate(centerX, centerY)\n\n        let scaleX = 1,\n          scaleY = 1,\n          rotation = 0\n        const flipH = gidWithFlags & FLIPPED_HORIZONTALLY_FLAG\n        const flipV = gidWithFlags & FLIPPED_VERTICALLY_FLAG\n        const flipD = gidWithFlags & FLIPPED_DIAGONALLY_FLAG\n\n        if (flipD) {\n          rotation = Math.PI / 2\n          if (flipH && flipV) {\n            scaleX = 1\n            scaleY = -1\n          } else if (flipH) {\n            scaleX = -1\n            scaleY = -1\n          } else if (flipV) {\n            scaleX = 1\n            scaleY = 1\n          } else {\n            scaleX = -1\n          }\n        } else {\n          if (flipH) scaleX = -1\n          if (flipV) scaleY = -1\n        }\n\n        if (scaleX !== 1 || scaleY !== 1) context.scale(scaleX, scaleY)\n        if (rotation !== 0) context.rotate(rotation)\n\n        context.translate(-centerX, -centerY)\n\n        try {\n          context.drawImage(\n            tileDetails.image,\n            tileDetails.sx,\n            tileDetails.sy,\n            tileDetails.tileWidth,\n            tileDetails.tileHeight, // Use tileWidth/Height from specific tileset\n            dx,\n            dy,\n            destTileWidth,\n            destTileHeight, // Draw to map's default grid cell size\n          )\n        } catch (e) {\n          console.error('Error drawing tile in layer:', layer.name, e)\n        }\n        context.restore()\n      }\n    }\n    if (layer.opacity !== undefined && layer.opacity < 1.0) {\n      context.globalAlpha = originalAlpha\n    }\n  }\n}\n\nexport default TileLayerRenderer\n","// src/engine/ui/BaseUIElement.js\n\nclass BaseUIElement {\n  constructor(options = {}) {\n    this.x = options.x || 0\n    this.y = options.y || 0\n    this.width = options.width || 0\n    this.height = options.height || 0\n    this.visible = options.visible !== undefined ? options.visible : true\n    this.enabled = options.enabled !== undefined ? options.enabled : true\n    this.id = options.id || null\n    this.text = options.text || '' // Store text for logging if available\n\n    this.engine = null\n\n    this._clickCallback = null\n    if (typeof options.onClick === 'function') {\n      this._clickCallback = options.onClick\n    }\n  }\n\n  setEngine(engine) {\n    this.engine = engine\n  }\n\n  containsPoint(px, py) {\n    return px >= this.x && px <= this.x + this.width && py >= this.y && py <= this.y + this.height\n  }\n\n  update(deltaTime, engine, mousePos) {\n    // Base implementation\n  }\n\n  render(context, engine) {\n    if (!this.visible) {\n      return\n    }\n    if (!context) {\n      console.error(\n        `UIElement (${this.id || this.constructor.name}): Render called without a valid context.`,\n      )\n      return\n    }\n    this._drawSelf(context, engine)\n  }\n\n  _drawSelf(context, engine) {\n    // console.warn(`UIElement (${this.id || this.constructor.name}): _drawSelf method not implemented.`);\n  }\n\n  onClick(callback) {\n    if (typeof callback === 'function') {\n      this._clickCallback = callback\n    }\n  }\n\n  _triggerClick() {\n    const elementName = this.id || this.text || this.constructor.name // Get a descriptive name\n    if (this.enabled && this.visible && this._clickCallback) {\n      console.log(\n        `%c[BaseUIElement] TRIGGERING CLICK for: ${elementName}`,\n        'color: limegreen; font-weight: bold;',\n      )\n      this._clickCallback()\n    } else {\n      console.warn(\n        `%c[BaseUIElement] Click NOT triggered for: ${elementName}. Enabled: ${this.enabled}, Visible: ${this.visible}, CallbackPresent: ${!!this._clickCallback}`,\n        'color: orange;',\n      )\n    }\n  }\n\n  setPosition(x, y) {\n    this.x = x\n    this.y = y\n  }\n  setSize(width, height) {\n    this.width = width\n    this.height = height\n  }\n  show() {\n    this.visible = true\n  }\n  hide() {\n    this.visible = false\n  }\n  enable() {\n    this.enabled = true\n  }\n  disable() {\n    this.enabled = false\n  }\n}\n\nexport default BaseUIElement\n","// src/engine/ui/Label.js\nimport BaseUIElement from './BaseUIElement.js'\n\n/**\n * @class Label\n * @extends BaseUIElement\n * @description A simple UI element for displaying text.\n */\nclass Label extends BaseUIElement {\n  /**\n   * Creates an instance of Label.\n   * @param {object} options - Configuration options for the label.\n   * @param {number} [options.x=0] - The x-coordinate of the label.\n   * @param {number} [options.y=0] - The y-coordinate of the label.\n   * @param {string} options.text - The text content of the label.\n   * @param {string} [options.font='16px sans-serif'] - The font style (e.g., '20px Arial', 'bold 16px Verdana').\n   * @param {string} [options.color='white'] - The color of the text.\n   * @param {string} [options.textAlign='left'] - Horizontal alignment of the text ('left', 'center', 'right', 'start', 'end').\n   * @param {string} [options.textBaseline='top'] - Vertical alignment of the text ('top', 'middle', 'bottom', 'alphabetic', 'hanging', 'ideographic').\n   * @param {boolean} [options.visible=true] - Whether the label is visible.\n   * @param {boolean} [options.enabled=true] - Whether the label is enabled (usually doesn't affect Labels directly unless for color change).\n   * @param {string} [options.id] - Optional ID for the label.\n   * @param {string} [options.disabledColor='gray'] - Optional color for text when element is disabled.\n   * @param {number} [options.width] - Optional: Explicit width.\n   * @param {number} [options.height] - Optional: Explicit height.\n   */\n  constructor(options = {}) {\n    super(options) // Pass all options to BaseUIElement constructor\n\n    this.text = options.text || ''\n    this.font = options.font || '16px sans-serif'\n    this.color = options.color || 'white'\n    this.textAlign = options.textAlign || 'left'\n    this.textBaseline = options.textBaseline || 'top'\n    this.disabledColor = options.disabledColor || 'gray' // Color when this.enabled is false\n  }\n\n  /**\n   * Sets the text content of the label.\n   * @param {string} newText - The new text to display.\n   */\n  setText(newText) {\n    this.text = newText\n  }\n\n  /**\n   * Specific drawing logic for the Label.\n   * Called by BaseUIElement.render after visibility and context checks.\n   * @param {CanvasRenderingContext2D} context - The rendering context.\n   * @param {import('../core/IroncladEngine.js').default} [engine] - The engine instance.\n   * @protected\n   */\n  _drawSelf(context, engine) {\n    // No need to check this.visible or context here, BaseUIElement's render method did it.\n    if (!this.text) {\n      return // Don't render if no text\n    }\n\n    context.font = this.font\n    context.fillStyle = this.enabled ? this.color : this.disabledColor\n    context.textAlign = this.textAlign\n    context.textBaseline = this.textBaseline\n\n    // The x, y from BaseUIElement are used as the anchor point based on textAlign and textBaseline.\n    context.fillText(this.text, this.x, this.y)\n  }\n\n  /**\n   * Update logic for the Label (typically none for a static label).\n   * @param {number} deltaTime - The time elapsed since the last frame.\n   * @param {import('../core/IroncladEngine.js').default} engine - The engine instance.\n   * @param {{x: number, y: number}} [mousePos] - Current canvas-relative mouse position.\n   */\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos) // Call base update\n    // Labels are typically static and don't require per-frame updates beyond visibility/enable checks handled by base.\n  }\n}\n\nexport default Label\n","// src/engine/ui/Button.js\nimport BaseUIElement from './BaseUIElement.js'\n\nclass Button extends BaseUIElement {\n  constructor(options = {}) {\n    super(options)\n\n    // this.text is inherited from BaseUIElement if options.text is passed\n    this.font = options.font || '16px sans-serif'\n    this.textColor = options.textColor || 'white'\n    this.disabledTextColor = options.disabledTextColor || 'darkgray'\n\n    this.backgroundColor = options.backgroundColor || 'gray'\n    this.hoverBackgroundColor = options.hoverBackgroundColor || 'darkgray'\n    this.pressedBackgroundColor = options.pressedBackgroundColor || 'dimgray'\n    this.disabledBackgroundColor = options.disabledBackgroundColor || '#555555'\n\n    this.borderColor = options.borderColor || 'black'\n    this.borderWidth = options.borderWidth !== undefined ? options.borderWidth : 1\n\n    this.isHovered = false\n    this.isPressed = false\n  }\n\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos)\n    const elementName = this.id || this.text || this.constructor.name\n\n    console.log(\n      `[Button: ${elementName}] update called. Visible: ${this.visible}, Enabled: ${this.enabled}`,\n    )\n\n    if (!this.visible) {\n      console.log(`[Button: ${elementName}] Not visible. Skipping update.`)\n      this.isHovered = false\n      this.isPressed = false\n      return\n    }\n    if (!this.enabled) {\n      console.log(`[Button: ${elementName}] Not enabled. Skipping interaction.`)\n      this.isHovered = false\n      this.isPressed = false\n      return\n    }\n\n    // Log current state for this button\n    // console.log(`[Button: ${elementName}] Updating. Pos:(${this.x},${this.y}) W:${this.width} H:${this.height}. Mouse:(${mousePos.x},${mousePos.y})`);\n\n    this.isHovered = this.containsPoint(mousePos.x, mousePos.y)\n\n    const inputManager = engine.inputManager\n    const leftButton = inputManager.constructor.MOUSE_BUTTON_LEFT\n\n    const lmbPressed = inputManager.isMouseButtonPressed(leftButton)\n    const lmbJustReleased = inputManager.isMouseButtonJustReleased(leftButton)\n\n    // if (this.text === 'Back') { // Focus debug on one button\n    //     console.log(`[Button: ${elementName}] Hover: ${this.isHovered}, PressedState: ${this.isPressed}, LMB_Down: ${lmbPressed}, LMB_JustUp: ${lmbJustReleased}`);\n    // }\n\n    if (this.isHovered) {\n      if (lmbPressed) {\n        // Mouse button is currently down\n        this.isPressed = true\n      } else {\n        // Mouse button is up\n        if (this.isPressed && lmbJustReleased) {\n          // Was pressed, and just released now WHILE HOVERED\n          console.log(\n            `%c[Button: ${elementName}] Click condition met! Hovered, was pressed, just released.`,\n            'color: cyan;',\n          )\n          this._triggerClick()\n        }\n        this.isPressed = false\n      }\n    } else {\n      // If mouse is not over the button\n      if (!lmbPressed) {\n        // If mouse button is also up, definitely not pressed\n        this.isPressed = false\n      }\n      // If mouse was pressed on button then dragged off, isPressed remains true until button is released\n    }\n  }\n\n  _drawSelf(context, engine) {\n    const elementName = this.id || this.text || this.constructor.name\n    let currentBgColor = this.backgroundColor\n    let currentTxtColor = this.textColor\n\n    if (!this.enabled) {\n      currentBgColor = this.disabledBackgroundColor\n      currentTxtColor = this.disabledTextColor\n    } else if (this.isPressed && this.isHovered) {\n      currentBgColor = this.pressedBackgroundColor\n    } else if (this.isHovered) {\n      currentBgColor = this.hoverBackgroundColor\n    }\n\n    context.fillStyle = currentBgColor\n    context.fillRect(this.x, this.y, this.width, this.height)\n\n    if (this.borderWidth > 0) {\n      context.strokeStyle = this.borderColor\n      context.lineWidth = this.borderWidth\n      context.strokeRect(this.x, this.y, this.width, this.height)\n    }\n\n    context.font = this.font\n    context.fillStyle = currentTxtColor\n    context.textAlign = 'center'\n    context.textBaseline = 'middle'\n    context.fillText(this.text, this.x + this.width / 2, this.y + this.height / 2)\n  }\n}\n\nexport default Button\n","// src/engine/ui/Checkbox.js\nimport BaseUIElement from './BaseUIElement.js'\n\n/**\n * @class Checkbox\n * @extends BaseUIElement\n * @description An interactive UI element that represents a boolean state (checked/unchecked).\n */\nclass Checkbox extends BaseUIElement {\n  /**\n   * Creates an instance of Checkbox.\n   * @param {object} options - Configuration options for the checkbox.\n   * @param {number} [options.x=0] - The x-coordinate of the checkbox (top-left of the box).\n   * @param {number} [options.y=0] - The y-coordinate of the checkbox (top-left of the box).\n   * @param {string} [options.label=''] - Text label to display next to the checkbox.\n   * @param {boolean} [options.isChecked=false] - Initial checked state.\n   * @param {number} [options.boxSize=16] - The size (width and height) of the checkbox square.\n   * @param {number} [options.labelOffset=5] - Space between the checkbox and the label text.\n   * @param {string} [options.font='16px sans-serif'] - Font for the label.\n   * @param {string} [options.textColor='white'] - Color for the label text.\n   * @param {string} [options.boxColor='white'] - Color of the checkbox border/box.\n   * @param {string} [options.checkColor='dodgerblue'] - Color of the checkmark or fill when checked.\n   * @param {string} [options.hoverBoxColor=null] - Optional: Color of the box when hovered (if you implement hover visual state).\n   * @param {string} [options.disabledColor='gray'] - Color for box and text when disabled.\n   * @param {string} [options.disabledCheckColor='darkgray'] - Color for the checkmark when disabled and checked.\n   * @param {function} [options.onClick] - Callback function when the checkbox is clicked (state toggled).\n   * @param {boolean} [options.visible=true] - Whether the checkbox is visible.\n   * @param {boolean} [options.enabled=true] - Whether the checkbox is enabled.\n   * @param {string} [options.id] - Optional ID.\n   * @param {number} [options.width] - Optional: Explicit width for the entire clickable area (box + label).\n   * @param {number} [options.height] - Optional: Explicit height for the entire clickable area.\n   */\n  constructor(options = {}) {\n    const boxSize = options.boxSize || 16\n    const labelOffset = options.labelOffset || 5\n    let estimatedWidth = boxSize\n    if (options.label) {\n      // A more accurate width estimation would require measuring text with canvas context.\n      // For simplicity, this is a rough estimate.\n      // Consider setting explicit width if precise hit area for label text is needed.\n      estimatedWidth += labelOffset + options.label.length * (boxSize * 0.6) // Rough estimate\n    }\n\n    super({\n      ...options,\n      width: options.width !== undefined ? options.width : estimatedWidth,\n      height: options.height !== undefined ? options.height : boxSize,\n    })\n\n    this.label = options.label || ''\n    this.isChecked = !!options.isChecked // Ensure boolean\n    this.boxSize = boxSize\n    this.labelOffset = labelOffset\n\n    this.font = options.font || '16px sans-serif'\n    this.textColor = options.textColor || 'white'\n    this.boxColor = options.boxColor || 'white'\n    this.checkColor = options.checkColor || 'dodgerblue'\n    this.hoverBoxColor = options.hoverBoxColor || null\n    this.disabledColor = options.disabledColor || 'gray'\n    this.disabledCheckColor = options.disabledCheckColor || 'darkgray'\n\n    this.isHovered = false // For visual feedback on hover, if desired\n  }\n\n  /**\n   * Toggles the checked state of the checkbox.\n   * Also triggers the onClick callback.\n   */\n  toggle() {\n    if (!this.enabled || !this.visible) return\n    this.isChecked = !this.isChecked\n    this._triggerClick() // Call the inherited click handler from BaseUIElement\n  }\n\n  /**\n   * Sets the checked state of the checkbox.\n   * @param {boolean} checked - The new checked state.\n   * @param {boolean} [triggerCallback=false] - Whether to trigger the onClick callback.\n   */\n  setChecked(checked, triggerCallback = false) {\n    if (!this.enabled || !this.visible) return\n    const newState = !!checked\n    if (this.isChecked !== newState) {\n      this.isChecked = newState\n      if (triggerCallback) {\n        this._triggerClick()\n      }\n    }\n  }\n\n  /**\n   * Updates the checkbox's state based on mouse input.\n   * @param {number} deltaTime - The time elapsed since the last frame.\n   * @param {import('../core/IroncladEngine.js').default} engine - The engine instance.\n   * @param {{x: number, y: number}} mousePos - Current canvas-relative mouse position.\n   */\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos) // Call base update\n\n    if (!this.visible || !this.enabled) {\n      this.isHovered = false\n      return\n    }\n\n    // The clickable area is defined by this.x, this.y, this.width, this.height from BaseUIElement\n    this.isHovered = this.containsPoint(mousePos.x, mousePos.y)\n\n    const inputManager = engine.inputManager\n    if (\n      this.isHovered &&\n      inputManager.isMouseButtonJustPressed(inputManager.constructor.MOUSE_BUTTON_LEFT)\n    ) {\n      this.toggle()\n    }\n  }\n\n  /**\n   * Specific drawing logic for the Checkbox.\n   * Called by BaseUIElement.render after visibility and context checks.\n   * @param {CanvasRenderingContext2D} context - The rendering context.\n   * @param {import('../core/IroncladEngine.js').default} engine - The engine instance.\n   * @protected\n   */\n  _drawSelf(context, engine) {\n    // Determine colors based on enabled state\n    const currentBoxColor = !this.enabled\n      ? this.disabledColor\n      : this.isHovered && this.hoverBoxColor\n        ? this.hoverBoxColor\n        : this.boxColor\n    const currentTextColor = !this.enabled ? this.disabledColor : this.textColor\n    const currentCheckColor = !this.enabled ? this.disabledCheckColor : this.checkColor\n\n    // Draw the checkbox square\n    context.strokeStyle = currentBoxColor\n    context.lineWidth = 2\n    context.strokeRect(this.x, this.y, this.boxSize, this.boxSize)\n\n    // Draw the checkmark if checked\n    if (this.isChecked) {\n      context.fillStyle = currentCheckColor\n      // Simple filled square as checkmark\n      const padding = Math.max(2, this.boxSize / 5) // Ensure padding is reasonable\n      context.fillRect(\n        this.x + padding,\n        this.y + padding,\n        this.boxSize - padding * 2,\n        this.boxSize - padding * 2,\n      )\n    }\n\n    // Draw the label text\n    if (this.label) {\n      context.font = this.font\n      context.fillStyle = currentTextColor\n      context.textAlign = 'left'\n      context.textBaseline = 'middle' // Align text vertically with the middle of the box\n      context.fillText(\n        this.label,\n        this.x + this.boxSize + this.labelOffset,\n        this.y + this.boxSize / 2,\n      )\n    }\n  }\n}\n\nexport default Checkbox\n","// src/engine/ui/Panel.js\nimport BaseUIElement from './BaseUIElement.js'\n\n/**\n * @class Panel\n * @extends BaseUIElement\n * @description A UI element that acts as a container for other UI elements,\n * providing a background and border.\n */\nclass Panel extends BaseUIElement {\n  /**\n   * Creates an instance of Panel.\n   * @param {object} options - Configuration options for the panel.\n   * @param {number} [options.x=0] - The x-coordinate.\n   * @param {number} [options.y=0] - The y-coordinate.\n   * @param {number} [options.width=100] - The width of the panel.\n   * @param {number} [options.height=100] - The height of the panel.\n   * @param {string} [options.backgroundColor=null] - Background color (e.g., 'rgba(0,0,0,0.5)'). If null, no background.\n   * @param {string} [options.borderColor='gray'] - Border color.\n   * @param {number} [options.borderWidth=0] - Width of the border. If 0, no border.\n   * @param {BaseUIElement[]} [options.children=[]] - Initial child elements.\n   * @param {boolean} [options.visible=true] - Whether the panel is visible.\n   * @param {boolean} [options.enabled=true] - Whether the panel is enabled for updates.\n   * @param {string} [options.id] - Optional ID.\n   */\n  constructor(options = {}) {\n    super(options) // BaseUIElement handles x, y, width, height, visible, enabled, id\n\n    this.backgroundColor = options.backgroundColor || null\n    this.borderColor = options.borderColor || 'gray'\n    this.borderWidth = options.borderWidth !== undefined ? options.borderWidth : 0\n\n    /** @type {BaseUIElement[]} */\n    this.children = []\n    if (options.children && Array.isArray(options.children)) {\n      options.children.forEach((child) => this.addChild(child))\n    }\n  }\n\n  addChild(element) {\n    if (element instanceof BaseUIElement) {\n      this.children.push(element)\n      if (this.engine) {\n        element.setEngine(this.engine)\n      }\n    } else {\n      console.warn('Panel.addChild: Attempted to add a non-UIElement.', element)\n    }\n  }\n\n  removeChild(element) {\n    const index = this.children.indexOf(element)\n    if (index > -1) {\n      this.children.splice(index, 1)\n      return true\n    }\n    return false\n  }\n\n  clearChildren() {\n    this.children = []\n  }\n\n  setEngine(engine) {\n    super.setEngine(engine)\n    this.children.forEach((child) => child.setEngine(engine))\n  }\n\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos) // Call base update\n\n    if (!this.visible || !this.enabled) {\n      // If panel is not active, its children aren't either for input processing purposes.\n      // Individual children will still respect their own visible/enabled flags for rendering/updating.\n      return\n    }\n\n    for (const child of this.children) {\n      // Children's update methods will check their own visible/enabled flags.\n      child.update(deltaTime, engine, mousePos)\n    }\n  }\n\n  /**\n   * Specific drawing logic for the Panel and its children.\n   * Called by BaseUIElement.render after visibility and context checks.\n   * @param {CanvasRenderingContext2D} context - The rendering context.\n   * @param {import('../core/IroncladEngine.js').default} engine - The engine instance.\n   * @protected\n   */\n  _drawSelf(context, engine) {\n    // No need for this.visible check, BaseUIElement.render handles it.\n\n    // Render panel background\n    if (this.backgroundColor) {\n      context.fillStyle = this.backgroundColor\n      context.fillRect(this.x, this.y, this.width, this.height)\n    }\n\n    // Render panel border\n    if (this.borderWidth > 0) {\n      context.strokeStyle = this.borderColor\n      context.lineWidth = this.borderWidth\n      context.strokeRect(this.x, this.y, this.width, this.height)\n    }\n\n    // Render children\n    // Children will perform their own visibility checks via their public render method.\n    // context.save(); // Optional: if applying clipping or transforms for children\n    // If children's x,y were relative to the panel, you'd use:\n    // context.translate(this.x, this.y);\n    // Then children would draw at (child.x, child.y) within this translated space.\n    // For now, children use absolute canvas coordinates.\n\n    for (const child of this.children) {\n      // IMPORTANT: Call the child's public `render` method, not `_drawSelf` directly.\n      // This allows each child to do its own visibility/context checks.\n      child.render(context, engine)\n    }\n\n    // if (context.translate was used) context.restore();\n  }\n}\n\nexport default Panel\n","// src/engine/ui/ValueBar.js\nimport BaseUIElement from './BaseUIElement.js'\n\n/**\n * @class ValueBar\n * @extends BaseUIElement\n * @description A UI element to visually represent a quantifiable value as a proportion of a maximum,\n * such as health, mana, progress, etc.\n */\nclass ValueBar extends BaseUIElement {\n  /**\n   * Creates an instance of ValueBar.\n   * @param {object} options - Configuration options for the value bar.\n   * @param {number} [options.x=0] - The x-coordinate.\n   * @param {number} [options.y=0] - The y-coordinate.\n   * @param {number} [options.width=100] - The width of the bar.\n   * @param {number} [options.height=10] - The height of the bar.\n   * @param {number} [options.maxValue=100] - The maximum value the bar can represent.\n   * @param {number} [options.currentValue=options.maxValue] - The current value to display.\n   * @param {string} [options.backgroundColor='#444444'] - Color of the bar's background (empty part).\n   * @param {string} [options.fillColor='skyblue'] - Color of the bar's fill (current value part).\n   * @param {string} [options.borderColor='black'] - Color of the bar's border.\n   * @param {number} [options.borderWidth=1] - Width of the border.\n   * @param {boolean} [options.showText=false] - Whether to display text value on the bar.\n   * @param {string} [options.font] - Font for the text (if shown). Defaults to a calculated size.\n   * @param {string} [options.textColor='white'] - Color for the text (if shown).\n   * @param {function(number, number): string} [options.textFormatFunction] - Function to format the text. Receives (current, max). Defaults to `current / max`.\n   * @param {boolean} [options.visible=true] - Whether the bar is visible.\n   * @param {string} [options.id] - Optional ID.\n   */\n  constructor(options = {}) {\n    super(options) // Handles x, y, width, height, visible, enabled, id\n\n    this.maxValue = options.maxValue !== undefined ? options.maxValue : 100\n    let initialCurrentValue =\n      options.currentValue !== undefined ? options.currentValue : this.maxValue\n    this.currentValue = Math.max(0, Math.min(initialCurrentValue, this.maxValue))\n\n    this.backgroundColor = options.backgroundColor || '#444444'\n    this.fillColor = options.fillColor || 'skyblue' // Changed default from 'green'\n    this.borderColor = options.borderColor || 'black'\n    this.borderWidth = options.borderWidth !== undefined ? options.borderWidth : 1\n\n    this.showText = options.showText || false\n    this.font = options.font || `${Math.max(8, Math.floor(this.height * 0.7))}px sans-serif`\n    this.textColor = options.textColor || 'white'\n    this.textFormatFunction =\n      options.textFormatFunction ||\n      ((current, max) => `${Math.round(current)} / ${Math.round(max)}`)\n  }\n\n  /**\n   * Sets the current and maximum values of the bar.\n   * @param {number} current - The new current value.\n   * @param {number} [max] - The new maximum value. If undefined, maxValue remains unchanged.\n   */\n  setValue(current, max) {\n    if (max !== undefined) {\n      this.maxValue = Math.max(1, max)\n    }\n    this.currentValue = Math.max(0, Math.min(current, this.maxValue))\n  }\n\n  /**\n   * Sets the fill level of the bar based on a percentage.\n   * @param {number} percentage - The percentage (0 to 100) to fill the bar.\n   */\n  setPercentage(percentage) {\n    const clampedPercentage = Math.max(0, Math.min(percentage, 100))\n    this.currentValue = (clampedPercentage / 100) * this.maxValue\n  }\n\n  /**\n   * Update logic for the ValueBar.\n   * @param {number} deltaTime - The time elapsed since the last frame.\n   * @param {import('../core/IroncladEngine.js').default} engine - The engine instance.\n   * @param {{x: number, y: number}} [mousePos] - Current canvas-relative mouse position.\n   */\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos)\n    // Value bars are typically updated via setValue/setPercentage.\n  }\n\n  /**\n   * Specific drawing logic for the ValueBar.\n   * Called by BaseUIElement.render after visibility and context checks.\n   * @param {CanvasRenderingContext2D} context - The rendering context.\n   * @param {import('../core/IroncladEngine.js').default} [engine] - The engine instance.\n   * @protected\n   */\n  _drawSelf(context, engine) {\n    // Draw background\n    context.fillStyle = this.backgroundColor\n    context.fillRect(this.x, this.y, this.width, this.height)\n\n    // Calculate fill width\n    const fillPercentage = this.maxValue > 0 ? this.currentValue / this.maxValue : 0\n    const fillWidth = Math.max(0, this.width * fillPercentage)\n\n    // Draw fill\n    if (fillWidth > 0) {\n      context.fillStyle = this.fillColor\n      context.fillRect(this.x, this.y, fillWidth, this.height)\n    }\n\n    // Draw border\n    if (this.borderWidth > 0) {\n      context.strokeStyle = this.borderColor\n      context.lineWidth = this.borderWidth\n      context.strokeRect(this.x, this.y, this.width, this.height)\n    }\n\n    // Draw text\n    if (this.showText) {\n      context.font = this.font\n      context.fillStyle = this.textColor\n      context.textAlign = 'center'\n      context.textBaseline = 'middle'\n      const textToDisplay = this.textFormatFunction(this.currentValue, this.maxValue)\n      context.fillText(textToDisplay, this.x + this.width / 2, this.y + this.height / 2 + 1)\n    }\n  }\n}\n\nexport default ValueBar\n","// src/engine/ui/Slider.js\nimport BaseUIElement from './BaseUIElement.js'\nimport Label from './Label.js' // For displaying the value\n\n/**\n * @class Slider\n * @extends BaseUIElement\n * @description An interactive UI element for selecting a value within a range by dragging a thumb.\n */\nclass Slider extends BaseUIElement {\n  /**\n   * Creates an instance of Slider.\n   * @param {object} options - Configuration options for the slider.\n   * @param {number} [options.x=0] - The x-coordinate.\n   * @param {number} [options.y=0] - The y-coordinate.\n   * @param {number} [options.width=200] - The length of the slider track for a horizontal slider, or width if vertical.\n   * @param {number} [options.height=20] - The thickness of the slider track for a horizontal slider, or height if vertical. (This is for the main clickable area)\n   * @param {number} [options.minValue=0] - The minimum value of the slider.\n   * @param {number} [options.maxValue=100] - The maximum value of the slider.\n   * @param {number} [options.currentValue=options.minValue] - The initial value of the slider.\n   * @param {number} [options.step=1] - The increment step for the slider's value. Use 0 for continuous.\n   * @param {string} [options.orientation='horizontal'] - 'horizontal' or 'vertical'.\n   * @param {string} [options.trackColor='#555555'] - Color of the slider track.\n   * @param {string} [options.thumbColor='dodgerblue'] - Color of the slider thumb.\n   * @param {string} [options.hoverThumbColor='deepskyblue'] - Color of the thumb when hovered.\n   * @param {string} [options.pressedThumbColor='royalblue'] - Color of the thumb when pressed/dragged.\n   * @param {number} [options.thumbWidth=10] - Width of the thumb for a horizontal slider.\n   * @param {number} [options.thumbHeight=20] - Height of the thumb for a horizontal slider. (Should be >= track height)\n   * @param {boolean} [options.showValueText=false] - Whether to display the current value as text.\n   * @param {string} [options.valueTextFont='12px sans-serif'] - Font for the value text.\n   * @param {string} [options.valueTextColor='white'] - Color for the value text.\n   * @param {function(number): string} [options.valueTextFormatFunction] - Formats the value text. Receives (currentValue).\n   * @param {function(number): void} [options.onValueChanged] - Callback when the value changes. Receives (newValue).\n   * @param {boolean} [options.visible=true]\n   * @param {boolean} [options.enabled=true]\n   * @param {string} [options.id]\n   */\n  constructor(options = {}) {\n    // BaseUIElement's width/height will represent the main track's clickable area\n    super(options)\n\n    this.minValue = options.minValue || 0\n    this.maxValue = options.maxValue !== undefined ? options.maxValue : 100\n    this.step = options.step !== undefined ? options.step : 1 // 0 or null for continuous\n    this.currentValue = options.currentValue !== undefined ? options.currentValue : this.minValue\n    this.currentValue = this._snapToStep(this._clampValue(this.currentValue))\n\n    this.orientation = options.orientation || 'horizontal'\n    this.trackColor = options.trackColor || '#555555'\n    this.thumbColor = options.thumbColor || 'dodgerblue'\n    this.hoverThumbColor = options.hoverThumbColor || 'deepskyblue'\n    this.pressedThumbColor = options.pressedThumbColor || 'royalblue'\n    this.disabledColor = options.disabledColor || '#333333'\n    this.disabledThumbColor = options.disabledThumbColor || '#444444'\n\n    // Thumb dimensions\n    // For horizontal: width is its thickness across track, height is its visual height\n    // For vertical: width is its visual width, height is its thickness across track\n    if (this.orientation === 'horizontal') {\n      this.thumbWidth = options.thumbWidth || 10\n      this.thumbHeight = options.thumbHeight || this.height // Default to track height\n    } else {\n      // vertical\n      this.thumbWidth = options.thumbWidth || this.width // Default to track width\n      this.thumbHeight = options.thumbHeight || 10\n    }\n\n    this.showValueText = options.showValueText || false\n    this.valueTextFont = options.valueTextFont || '12px sans-serif'\n    this.valueTextColor = options.valueTextColor || 'white'\n    this.valueTextFormatFunction =\n      options.valueTextFormatFunction || ((value) => `${Math.round(value)}`)\n\n    this.onValueChanged = options.onValueChanged || null\n\n    this.isDragging = false\n    this.isHoveredOnThumb = false\n\n    this._thumbRect = { x: 0, y: 0, width: 0, height: 0 } // Internal, for hit detection\n    this._updateThumbRect()\n  }\n\n  _clampValue(value) {\n    return Math.max(this.minValue, Math.min(value, this.maxValue))\n  }\n\n  _snapToStep(value) {\n    if (this.step > 0) {\n      return Math.round((value - this.minValue) / this.step) * this.step + this.minValue\n    }\n    return value\n  }\n\n  /**\n   * Calculates the thumb's current rectangle based on currentValue.\n   * @private\n   */\n  _updateThumbRect() {\n    const valueRatio = (this.currentValue - this.minValue) / (this.maxValue - this.minValue || 1)\n    if (this.orientation === 'horizontal') {\n      const trackInnerWidth = this.width - this.thumbWidth // Available travel distance for thumb's center\n      const thumbCenterX = this.x + this.thumbWidth / 2 + trackInnerWidth * valueRatio\n      this._thumbRect.x = thumbCenterX - this.thumbWidth / 2\n      this._thumbRect.y = this.y + this.height / 2 - this.thumbHeight / 2 // Center thumb on track\n      this._thumbRect.width = this.thumbWidth\n      this._thumbRect.height = this.thumbHeight\n    } else {\n      // Vertical\n      const trackInnerHeight = this.height - this.thumbHeight // Available travel distance\n      const thumbCenterY = this.y + this.thumbHeight / 2 + trackInnerHeight * valueRatio\n      this._thumbRect.x = this.x + this.width / 2 - this.thumbWidth / 2 // Center thumb on track\n      this._thumbRect.y = thumbCenterY - this.thumbHeight / 2\n      this._thumbRect.width = this.thumbWidth\n      this._thumbRect.height = this.thumbHeight\n    }\n  }\n\n  _pixelToValue(pixelPos) {\n    let ratio = 0\n    if (this.orientation === 'horizontal') {\n      const trackInnerWidth = this.width - this.thumbWidth\n      // Convert mouseX to position of thumb's center relative to track start for thumb's center\n      const thumbCenterRelativeX = Math.max(\n        0,\n        Math.min(pixelPos.x - (this.x + this.thumbWidth / 2), trackInnerWidth),\n      )\n      ratio = trackInnerWidth > 0 ? thumbCenterRelativeX / trackInnerWidth : 0\n    } else {\n      // Vertical\n      const trackInnerHeight = this.height - this.thumbHeight\n      const thumbCenterRelativeY = Math.max(\n        0,\n        Math.min(pixelPos.y - (this.y + this.thumbHeight / 2), trackInnerHeight),\n      )\n      ratio = trackInnerHeight > 0 ? thumbCenterRelativeY / trackInnerHeight : 0\n    }\n    let value = this.minValue + (this.maxValue - this.minValue) * ratio\n    return this._snapToStep(this._clampValue(value))\n  }\n\n  setValue(value, triggerCallback = true) {\n    const clampedValue = this._clampValue(value)\n    const snappedValue = this._snapToStep(clampedValue)\n    if (this.currentValue !== snappedValue) {\n      this.currentValue = snappedValue\n      this._updateThumbRect()\n      if (this.onValueChanged && triggerCallback) {\n        this.onValueChanged(this.currentValue)\n      }\n    }\n  }\n\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos)\n    if (!this.visible || !this.enabled) {\n      this.isDragging = false\n      this.isHoveredOnThumb = false\n      return\n    }\n\n    const inputManager = engine.inputManager\n    const leftButton = inputManager.constructor.MOUSE_BUTTON_LEFT\n\n    this.isHoveredOnThumb = this.containsPoint(mousePos.x, mousePos.y, this._thumbRect)\n    const isHoveredOnTrack = this.containsPoint(mousePos.x, mousePos.y) // Check hover on whole element\n\n    if (inputManager.isMouseButtonJustPressed(leftButton)) {\n      if (this.isHoveredOnThumb) {\n        this.isDragging = true\n      } else if (isHoveredOnTrack) {\n        // Clicked on track, not thumb\n        const newValue = this._pixelToValue(mousePos)\n        this.setValue(newValue)\n        this.isDragging = true // Allow dragging immediately from new position\n      }\n    }\n\n    if (this.isDragging) {\n      if (inputManager.isMouseButtonPressed(leftButton)) {\n        const newValue = this._pixelToValue(mousePos)\n        this.setValue(newValue) // setValue calls _updateThumbRect and callback\n      } else {\n        // Mouse button released\n        this.isDragging = false\n      }\n    }\n  }\n\n  /**\n   * Overloaded containsPoint to check against a specific rect (for thumb).\n   * @param {number} px\n   * @param {number} py\n   * @param {object} [rect=this] - The rectangle to check against (defaults to the element itself).\n   * @returns {boolean}\n   */\n  containsPoint(px, py, rect = this) {\n    return px >= rect.x && px <= rect.x + rect.width && py >= rect.y && py <= rect.y + rect.height\n  }\n\n  _drawSelf(context, engine) {\n    const currentTrackColor = !this.enabled ? this.disabledColor : this.trackColor\n    let currentThumbColor = !this.enabled ? this.disabledThumbColor : this.thumbColor\n\n    if (this.enabled) {\n      if (this.isDragging) {\n        currentThumbColor = this.pressedThumbColor\n      } else if (this.isHoveredOnThumb && this.hoverThumbColor) {\n        currentThumbColor = this.hoverThumbColor\n      }\n    }\n\n    // Draw Track\n    context.fillStyle = currentTrackColor\n    if (this.orientation === 'horizontal') {\n      const trackVisualY = this.y + this.height / 2 - (this.options.trackVisualThickness || 4) / 2\n      const trackVisualThickness = this.options.trackVisualThickness || Math.min(this.height / 2, 4)\n      context.fillRect(this.x, trackVisualY, this.width, trackVisualThickness)\n    } else {\n      // Vertical\n      const trackVisualX = this.x + this.width / 2 - (this.options.trackVisualThickness || 4) / 2\n      const trackVisualThickness = this.options.trackVisualThickness || Math.min(this.width / 2, 4)\n      context.fillRect(trackVisualX, this.y, trackVisualThickness, this.height)\n    }\n\n    // Draw Thumb\n    this._updateThumbRect() // Ensure thumb rect is up-to-date before drawing\n    context.fillStyle = currentThumbColor\n    context.fillRect(\n      this._thumbRect.x,\n      this._thumbRect.y,\n      this._thumbRect.width,\n      this._thumbRect.height,\n    )\n    // Optional: Draw border around thumb\n    // context.strokeStyle = 'black';\n    // context.strokeRect(this._thumbRect.x, this._thumbRect.y, this._thumbRect.width, this._thumbRect.height);\n\n    // Draw Value Text\n    if (this.showValueText) {\n      context.font = this.valueTextFont\n      context.fillStyle = !this.enabled ? this.disabledColor : this.valueTextColor\n      context.textAlign = 'center'\n      context.textBaseline = 'middle'\n      const textToDisplay = this.valueTextFormatFunction(this.currentValue)\n      if (this.orientation === 'horizontal') {\n        context.fillText(\n          textToDisplay,\n          this.x + this.width + 10 + context.measureText(textToDisplay).width / 2,\n          this.y + this.height / 2,\n        )\n      } else {\n        // Vertical\n        context.fillText(\n          textToDisplay,\n          this.x + this.width / 2,\n          this.y + this.height + 10 + parseFloat(this.valueTextFont) / 2,\n        )\n      }\n    }\n  }\n}\n\nexport default Slider\n","// src/engine/ui/ScrollablePanel.js\nimport BaseUIElement from './BaseUIElement.js'\n\n/**\n * @class ScrollablePanel\n * @extends BaseUIElement\n * @description A panel that can contain child elements and allows vertical scrolling\n * if the content height exceeds the panel height.\n */\nclass ScrollablePanel extends BaseUIElement {\n  /**\n   * Creates an instance of ScrollablePanel.\n   * @param {object} options - Configuration options.\n   * @param {number} [options.x=0] - The x-coordinate.\n   * @param {number} [options.y=0] - The y-coordinate.\n   * @param {number} [options.width=200] - The width of the panel.\n   * @param {number} [options.height=150] - The height of the panel (viewport height).\n   * @param {string} [options.backgroundColor=null] - Background color.\n   * @param {string} [options.borderColor='gray'] - Border color.\n   * @param {number} [options.borderWidth=1] - Width of the border.\n   * @param {number} [options.padding=5] - Inner padding for content.\n   * @param {number} [options.scrollbarWidth=10] - Width of the scrollbar.\n   * @param {string} [options.scrollbarTrackColor='#333'] - Color of the scrollbar track.\n   * @param {string} [options.scrollbarThumbColor='#888'] - Color of the scrollbar thumb.\n   * @param {string} [options.scrollbarThumbHoverColor='#AAA'] - Color of the scrollbar thumb on hover.\n   * @param {number} [options.mouseWheelSensitivity=20] - Pixels to scroll per mouse wheel event.\n   */\n  constructor(options = {}) {\n    super(options)\n\n    this.backgroundColor = options.backgroundColor || null\n    this.borderColor = options.borderColor || 'gray'\n    this.borderWidth = options.borderWidth !== undefined ? options.borderWidth : 1\n    this.padding = options.padding !== undefined ? options.padding : 5\n\n    /** @type {BaseUIElement[]} */\n    this.children = []\n    this.contentHeight = 0 // Total height of all children\n    this.scrollTop = 0 // Current vertical scroll offset\n\n    this.scrollbarWidth = options.scrollbarWidth || 10\n    this.scrollbarTrackColor = options.scrollbarTrackColor || '#333'\n    this.scrollbarThumbColor = options.scrollbarThumbColor || '#888'\n    this.scrollbarThumbHoverColor = options.scrollbarThumbHoverColor || '#AAA'\n    this.mouseWheelSensitivity = options.mouseWheelSensitivity || 20\n\n    this._scrollbarTrackRect = { x: 0, y: 0, width: 0, height: 0 }\n    this._scrollbarThumbRect = { x: 0, y: 0, width: 0, height: 0 }\n    this._isDraggingThumb = false\n    this._isMouseOverScrollbarThumb = false\n    this._dragStartY = 0\n    this._dragStartScrollTop = 0\n\n    this._boundHandleMouseWheel = this._handleMouseWheel.bind(this)\n    this._isMouseOverPanel = false // To manage wheel listener\n  }\n\n  setEngine(engine) {\n    super.setEngine(engine)\n    this.children.forEach((child) => child.setEngine(engine))\n  }\n\n  addChild(element) {\n    if (element instanceof BaseUIElement) {\n      this.children.push(element)\n      if (this.engine) {\n        element.setEngine(this.engine)\n      }\n      this._calculateContentHeightAndArrange() // Recalculate and potentially update scrollbar\n    } else {\n      console.warn('ScrollablePanel.addChild: Attempted to add a non-UIElement.', element)\n    }\n  }\n\n  removeChild(element) {\n    const index = this.children.indexOf(element)\n    if (index > -1) {\n      this.children.splice(index, 1)\n      this._calculateContentHeightAndArrange()\n      return true\n    }\n    return false\n  }\n\n  clearChildren() {\n    this.children = []\n    this.contentHeight = 0\n    this.scrollTop = 0\n    this._updateScrollbar()\n  }\n\n  _calculateContentHeightAndArrange() {\n    // For this version, assume children y positions are relative to content area top (0)\n    // And children are stacked vertically. A more complex layout manager could be used.\n    // This method also updates the scrollbar after calculating content height.\n    let currentY = this.padding\n    this.children.forEach((child) => {\n      child.x = this.padding // Simple horizontal positioning within padding\n      child.y = currentY // Stack vertically\n      currentY += child.height + this.padding // Add padding between elements\n    })\n    this.contentHeight = Math.max(0, currentY - this.padding) // Total height used by children\n    this.scrollTo(this.scrollTop) // Re-clamp scrollTop and update scrollbar\n  }\n\n  _updateScrollbar() {\n    if (this.contentHeight <= this.height - 2 * this.padding) {\n      // No scrollbar needed\n      this._scrollbarTrackRect.height = 0\n      this._scrollbarThumbRect.height = 0\n      return\n    }\n\n    const viewportInnerHeight = this.height - 2 * this.padding\n    this._scrollbarTrackRect = {\n      x: this.x + this.width - this.scrollbarWidth - this.padding,\n      y: this.y + this.padding,\n      width: this.scrollbarWidth,\n      height: viewportInnerHeight,\n    }\n\n    const thumbHeightRatio = Math.min(1, viewportInnerHeight / this.contentHeight)\n    this._scrollbarThumbRect.height = Math.max(10, viewportInnerHeight * thumbHeightRatio) // Min thumb height\n    this._scrollbarThumbRect.width = this.scrollbarWidth\n    this._scrollbarThumbRect.x = this._scrollbarTrackRect.x\n\n    const scrollableRange = this.contentHeight - viewportInnerHeight\n    const scrollRatio = scrollableRange > 0 ? this.scrollTop / scrollableRange : 0\n    const availableThumbTravel = this._scrollbarTrackRect.height - this._scrollbarThumbRect.height\n\n    this._scrollbarThumbRect.y = this._scrollbarTrackRect.y + availableThumbTravel * scrollRatio\n  }\n\n  scrollTo(newScrollTop, fromScrollbar = false) {\n    const viewportInnerHeight = this.height - 2 * this.padding\n    const maxScrollTop = Math.max(0, this.contentHeight - viewportInnerHeight)\n    this.scrollTop = Math.max(0, Math.min(newScrollTop, maxScrollTop))\n    this._updateScrollbar()\n  }\n\n  _handleMouseWheel(event) {\n    if (!this.visible || !this.enabled || !this._isMouseOverPanel) return\n    event.preventDefault()\n    const newScrollTop = this.scrollTop + (event.deltaY > 0 ? 1 : -1) * this.mouseWheelSensitivity\n    this.scrollTo(newScrollTop)\n  }\n\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos)\n    if (!this.visible) {\n      if (this._isMouseOverPanel) {\n        // Remove listener if panel becomes invisible while mouse was over\n        engine.canvas.removeEventListener('wheel', this._boundHandleMouseWheel)\n        this._isMouseOverPanel = false\n      }\n      return\n    }\n\n    const wasMouseOverPanel = this._isMouseOverPanel\n    this._isMouseOverPanel = this.containsPoint(mousePos.x, mousePos.y)\n\n    if (this._isMouseOverPanel && !wasMouseOverPanel && this.enabled) {\n      engine.canvas.addEventListener('wheel', this._boundHandleMouseWheel, { passive: false })\n    } else if (!this._isMouseOverPanel && wasMouseOverPanel) {\n      engine.canvas.removeEventListener('wheel', this._boundHandleMouseWheel)\n    }\n\n    if (!this.enabled) {\n      if (wasMouseOverPanel) engine.canvas.removeEventListener('wheel', this._boundHandleMouseWheel)\n      this._isMouseOverPanel = false\n      this.isDraggingThumb = false\n      return\n    }\n\n    // Scrollbar interaction\n    this._isMouseOverScrollbarThumb =\n      this.containsPoint(mousePos.x, mousePos.y, this._scrollbarThumbRect) &&\n      this._scrollbarTrackRect.height > 0 // Only if scrollbar active\n\n    const inputManager = engine.inputManager\n    const leftButton = inputManager.constructor.MOUSE_BUTTON_LEFT\n\n    if (inputManager.isMouseButtonJustPressed(leftButton)) {\n      if (this._isMouseOverScrollbarThumb) {\n        this._isDraggingThumb = true\n        this._dragStartY = mousePos.y\n        this._dragStartScrollTop = this.scrollTop\n      } else if (\n        this.containsPoint(mousePos.x, mousePos.y, this._scrollbarTrackRect) &&\n        this._scrollbarTrackRect.height > 0\n      ) {\n        // Click on track\n        const clickRatio =\n          (mousePos.y - this._scrollbarTrackRect.y) / this._scrollbarTrackRect.height\n        const viewportInnerHeight = this.height - 2 * this.padding\n        const newScrollTop = clickRatio * this.contentHeight - viewportInnerHeight * clickRatio\n        this.scrollTo(newScrollTop)\n        this._isDraggingThumb = true // Allow immediate drag from new position\n        this._dragStartY = mousePos.y\n        this._dragStartScrollTop = this.scrollTop\n      }\n    }\n\n    if (this._isDraggingThumb) {\n      if (inputManager.isMouseButtonPressed(leftButton)) {\n        const dy = mousePos.y - this._dragStartY\n        const viewportInnerHeight = this.height - 2 * this.padding\n        const scrollableRange = this.contentHeight - viewportInnerHeight\n        const trackScrollableRange =\n          this._scrollbarTrackRect.height - this._scrollbarThumbRect.height\n\n        if (trackScrollableRange > 0 && scrollableRange > 0) {\n          const scrollTopDelta = (dy / trackScrollableRange) * scrollableRange\n          this.scrollTo(this._dragStartScrollTop + scrollTopDelta)\n        }\n      } else {\n        this._isDraggingThumb = false\n      }\n    }\n\n    // Update children: transform mouse coordinates for children\n    // Children are positioned relative to the panel's content area (0,0 after padding)\n    // Their y is effectively (child.y - this.scrollTop)\n    const contentOriginX = this.x + this.padding\n    const contentOriginY = this.y + this.padding\n\n    for (const child of this.children) {\n      if (child.visible && child.enabled) {\n        // Child's actual screen Y for hit testing (considering scroll)\n        const childScreenY = contentOriginY + child.y - this.scrollTop\n        const childScreenX = contentOriginX + child.x // Assuming child.x is relative to panel padding\n\n        // Check if child is visible within the clipped viewport\n        const childIsVisibleInViewport =\n          childScreenY < this.y + this.height - this.padding &&\n          childScreenY + child.height > this.y + this.padding\n\n        if (childIsVisibleInViewport) {\n          // Create mousePos relative to the child's coordinate system IF child expects relative mouse\n          // OR pass global mousePos and let child use its absolute screen coords for containsPoint\n          // For now, assuming child.update takes global mousePos and child uses its screen coords for hit detection\n          // We need to ensure child.x and child.y are correctly set for rendering within the scrolled view.\n          // The rendering part handles this by translating context.\n          // For update, the child needs to know its current screen position.\n          // Let's make a temporary modification to child's x/y for its update logic.\n          const originalChildX = child.x\n          const originalChildY = child.y\n          child.x = childScreenX // Temporarily set to screen coordinates for hit detection\n          child.y = childScreenY\n\n          child.update(deltaTime, engine, mousePos)\n\n          child.x = originalChildX // Restore original relative x\n          child.y = originalChildY // Restore original relative y\n        }\n      }\n    }\n  }\n\n  _drawSelf(context, engine) {\n    // Draw panel background\n    if (this.backgroundColor) {\n      context.fillStyle = this.backgroundColor\n      context.fillRect(this.x, this.y, this.width, this.height)\n    }\n\n    // --- Setup Clipping for Content ---\n    context.save()\n    context.beginPath()\n    context.rect(\n      this.x + this.padding,\n      this.y + this.padding,\n      this.width -\n        2 * this.padding -\n        (this._scrollbarTrackRect.height > 0 ? this.scrollbarWidth : 0), // Adjust width for scrollbar\n      this.height - 2 * this.padding,\n    )\n    context.clip()\n\n    // Translate context for scrolled content\n    context.translate(this.x + this.padding, this.y + this.padding - this.scrollTop)\n\n    // Render children (they will use their y relative to content top)\n    for (const child of this.children) {\n      // Child's render method will check its own visibility\n      child.render(context, engine)\n    }\n\n    context.restore() // Remove clipping and translation\n\n    // --- Draw Scrollbar ---\n    this._updateScrollbar() // Ensure positions are correct\n    if (this._scrollbarTrackRect.height > 0) {\n      // Only draw if scrollbar is active\n      // Draw track\n      context.fillStyle = this.scrollbarTrackColor\n      context.fillRect(\n        this._scrollbarTrackRect.x,\n        this._scrollbarTrackRect.y,\n        this._scrollbarTrackRect.width,\n        this._scrollbarTrackRect.height,\n      )\n\n      // Draw thumb\n      let currentThumbColor = this.scrollbarThumbColor\n      if (this.enabled) {\n        if (this._isDraggingThumb) {\n          currentThumbColor = this.scrollbarThumbHoverColor // Or a dedicated dragging color\n        } else if (this._isMouseOverScrollbarThumb) {\n          currentThumbColor = this.scrollbarThumbHoverColor\n        }\n      }\n      context.fillStyle = currentThumbColor\n      context.fillRect(\n        this._scrollbarThumbRect.x,\n        this._scrollbarThumbRect.y,\n        this._scrollbarThumbRect.width,\n        this._scrollbarThumbRect.height,\n      )\n    }\n\n    // Draw panel border (drawn last to be on top of content edges and scrollbar)\n    if (this.borderWidth > 0) {\n      context.strokeStyle = this.borderColor\n      context.lineWidth = this.borderWidth\n      context.strokeRect(this.x, this.y, this.width, this.height)\n    }\n  }\n\n  destroy() {\n    super.destroy()\n    if (this.engine && this.engine.canvas && this._isMouseOverPanel) {\n      this.engine.canvas.removeEventListener('wheel', this._boundHandleMouseWheel)\n    }\n    this.children.forEach((child) => {\n      if (typeof child.destroy === 'function') child.destroy()\n    })\n    this.children = []\n  }\n}\n\nexport default ScrollablePanel\n","// src/engine/ui/TextInputField.js\nimport BaseUIElement from './BaseUIElement.js'\n\n/**\n * @class TextInputField\n * @extends BaseUIElement\n * @description An interactive UI element for text input from the keyboard.\n */\nclass TextInputField extends BaseUIElement {\n  /**\n   * Creates an instance of TextInputField.\n   * @param {object} options - Configuration options.\n   * @param {number} [options.x=0] - The x-coordinate.\n   * @param {number} [options.y=0] - The y-coordinate.\n   * @param {number} [options.width=200] - The width of the input field.\n   * @param {number} [options.height=30] - The height of the input field.\n   * @param {string} [options.initialText=''] - Initial text content.\n   * @param {string} [options.placeholderText=''] - Text to display when the field is empty and not focused.\n   * @param {number} [options.maxLength=null] - Maximum number of characters. Null for no limit.\n   * @param {string} [options.font='16px sans-serif'] - Font for the text.\n   * @param {string} [options.textColor='black'] - Color of the text.\n   * @param {string} [options.placeholderColor='gray'] - Color of the placeholder text.\n   * @param {string} [options.backgroundColor='white'] - Background color of the field.\n   * @param {string} [options.borderColor='gray'] - Border color.\n   * @param {string} [options.focusBorderColor='dodgerblue'] - Border color when focused.\n   * @param {number} [options.borderWidth=1] - Width of the border.\n   * @param {number} [options.padding=5] - Inner padding for text.\n   * @param {function(string):void} [options.onEnterPressed] - Callback when Enter is pressed. Receives current text.\n   * @param {function(string):void} [options.onTextChanged] - Callback when text changes. Receives new text.\n   * @param {function():void} [options.onFocus] - Callback when the field gains focus.\n   * @param {function():void} [options.onBlur] - Callback when the field loses focus.\n   */\n  constructor(options = {}) {\n    super(options) // Handles x, y, width, height, visible, enabled, id\n\n    this.text = options.initialText || ''\n    this.placeholderText = options.placeholderText || ''\n    this.maxLength = options.maxLength || null\n\n    this.font = options.font || '16px sans-serif'\n    this.textColor = options.textColor || 'black'\n    this.placeholderColor = options.placeholderColor || 'gray'\n    this.backgroundColor = options.backgroundColor || 'white'\n    this.borderColor = options.borderColor || 'gray'\n    this.focusBorderColor = options.focusBorderColor || 'dodgerblue'\n    this.borderWidth = options.borderWidth !== undefined ? options.borderWidth : 1\n    this.padding = options.padding !== undefined ? options.padding : 5\n\n    this.isFocused = false\n    this.cursorVisible = false\n    this.cursorBlinkRate = 500 // milliseconds\n    this._cursorBlinkTimer = 0\n    this._cursorPosition = this.text.length // For now, cursor is always at the end\n\n    this.onEnterPressed = options.onEnterPressed || null\n    this.onTextChanged = options.onTextChanged || null\n    this.onFocusCallback = options.onFocus || null\n    this.onBlurCallback = options.onBlur || null\n\n    // Bind methods that will be used as event handlers\n    this._boundHandleKeyDown = this._handleKeyDown.bind(this)\n    this._boundHandleGlobalClick = this._handleGlobalClick.bind(this)\n  }\n\n  focus() {\n    if (!this.enabled || this.isFocused) return\n    this.isFocused = true\n    this.cursorVisible = true\n    this._cursorBlinkTimer = 0\n    window.addEventListener('keydown', this._boundHandleKeyDown)\n    document.addEventListener('mousedown', this._boundHandleGlobalClick, true) // Capture phase for global click\n    if (this.onFocusCallback) this.onFocusCallback()\n    // console.log(`TextInput (${this.id || 'ID_not_set'}): Focused`);\n  }\n\n  blur() {\n    if (!this.isFocused) return\n    this.isFocused = false\n    this.cursorVisible = false\n    window.removeEventListener('keydown', this._boundHandleKeyDown)\n    document.removeEventListener('mousedown', this._boundHandleGlobalClick, true)\n    if (this.onBlurCallback) this.onBlurCallback()\n    // console.log(`TextInput (${this.id || 'ID_not_set'}): Blurred`);\n  }\n\n  setText(newText, triggerCallback = true) {\n    const oldText = this.text\n    if (this.maxLength !== null) {\n      this.text = newText.substring(0, this.maxLength)\n    } else {\n      this.text = newText\n    }\n    this._cursorPosition = this.text.length // Keep cursor at end for simplicity\n\n    if (this.onTextChanged && triggerCallback && oldText !== this.text) {\n      this.onTextChanged(this.text)\n    }\n  }\n\n  _handleGlobalClick(event) {\n    // If click is outside this element, blur it.\n    if (this.isFocused) {\n      const canvas = this.engine ? this.engine.canvas : null\n      if (canvas && event.target !== canvas) {\n        // Click was outside canvas entirely\n        this.blur()\n        return\n      }\n      // If click was on canvas, check if it's outside this element's bounds\n      if (canvas) {\n        const rect = canvas.getBoundingClientRect()\n        const mouseX = event.clientX - rect.left\n        const mouseY = event.clientY - rect.top\n        // TODO: Adjust mouseX, mouseY for canvas scaling if necessary\n        if (!this.containsPoint(mouseX, mouseY)) {\n          this.blur()\n        }\n      }\n    }\n  }\n\n  _handleKeyDown(event) {\n    if (!this.isFocused || !this.enabled) return\n\n    // Prevent default browser action for keys we handle,\n    // to avoid page scroll on space, or form submission on enter etc.\n    if (\n      event.key.length === 1 ||\n      event.key === 'Backspace' ||\n      event.key === 'Enter' ||\n      event.key === 'Delete'\n    ) {\n      event.preventDefault()\n    }\n\n    let newText = this.text\n    if (event.key.length === 1) {\n      // Printable character\n      if (this.maxLength === null || this.text.length < this.maxLength) {\n        newText += event.key\n      }\n    } else if (event.key === 'Backspace') {\n      newText = this.text.substring(0, this.text.length - 1)\n    } else if (event.key === 'Enter') {\n      if (this.onEnterPressed) {\n        this.onEnterPressed(this.text)\n      }\n      // Optionally blur on enter: this.blur();\n      return // Don't update text or call onTextChanged for Enter itself\n    } else if (event.key === 'Delete') {\n      // Basic delete: if cursor was at end, same as backspace.\n      // Advanced: would delete char at cursor position.\n      newText = this.text.substring(0, this.text.length - 1)\n    } else {\n      return // Ignore other special keys for now (arrows, home, end, etc.)\n    }\n\n    if (newText !== this.text) {\n      this.setText(newText) // This will call onTextChanged\n    }\n    this.cursorVisible = true // Make cursor visible on key press\n    this._cursorBlinkTimer = 0 // Reset blink timer\n  }\n\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos)\n    if (!this.visible) return // Enabled check is handled in focus/blur/keydown\n\n    // Handle click to focus\n    const inputManager = engine.inputManager\n    if (\n      this.enabled &&\n      inputManager.isMouseButtonJustPressed(inputManager.constructor.MOUSE_BUTTON_LEFT)\n    ) {\n      if (this.containsPoint(mousePos.x, mousePos.y)) {\n        if (!this.isFocused) {\n          this.focus()\n        }\n      }\n      // Blurring by clicking outside is handled by _handleGlobalClick\n    }\n\n    // Cursor blinking\n    if (this.isFocused) {\n      this._cursorBlinkTimer += deltaTime * 1000 // deltaTime is in seconds\n      if (this._cursorBlinkTimer >= this.cursorBlinkRate) {\n        this.cursorVisible = !this.cursorVisible\n        this._cursorBlinkTimer = 0\n      }\n    } else {\n      this.cursorVisible = false\n    }\n  }\n\n  _drawSelf(context, engine) {\n    // Background\n    context.fillStyle = this.backgroundColor\n    context.fillRect(this.x, this.y, this.width, this.height)\n\n    // Border\n    if (this.borderWidth > 0) {\n      context.strokeStyle =\n        this.isFocused && this.enabled ? this.focusBorderColor : this.borderColor\n      context.lineWidth = this.borderWidth\n      context.strokeRect(this.x, this.y, this.width, this.height)\n    }\n\n    // Text or Placeholder\n    context.font = this.font\n    context.textAlign = 'left'\n    context.textBaseline = 'middle' // Vertically center text\n\n    const textX = this.x + this.padding\n    const textY = this.y + this.height / 2\n\n    if (this.text.length > 0) {\n      context.fillStyle = this.enabled ? this.textColor : 'darkgray'\n      context.fillText(this.text, textX, textY)\n    } else if (!this.isFocused && this.placeholderText) {\n      context.fillStyle = this.enabled ? this.placeholderColor : 'darkgray'\n      context.fillText(this.placeholderText, textX, textY)\n    }\n\n    // Cursor\n    if (this.isFocused && this.cursorVisible && this.enabled) {\n      // Measure text to position cursor at the end\n      // For simplicity, cursor is always at the end.\n      // A more complex implementation would track cursor position within the text.\n      const textWidth = context.measureText(this.text).width\n      const cursorX = textX + textWidth\n      const cursorYStart = this.y + this.padding\n      const cursorYEnd = this.y + this.height - this.padding\n\n      context.strokeStyle = this.enabled ? this.textColor : 'darkgray'\n      context.lineWidth = 1\n      context.beginPath()\n      context.moveTo(cursorX, cursorYStart)\n      context.lineTo(cursorX, cursorYEnd)\n      context.stroke()\n    }\n  }\n\n  destroy() {\n    super.destroy() // Call if BaseUIElement has a destroy method\n    this.blur() // Ensure listeners are removed\n    // console.log(`TextInput (${this.id || 'ID_not_set'}): Destroyed`);\n  }\n}\n\nexport default TextInputField\n","// src/engine/ui/DialogueBox.js\nimport BaseUIElement from './BaseUIElement.js'\nimport Button from './Button.js'\n// import Label from './Label.js'; // Not strictly needed if Button handles its own text\n\n/**\n * @class DialogueBox\n * @extends BaseUIElement\n * @description A UI element for displaying dialogue text, speaker name, character portraits, and interactive choices.\n *\n * @param {object} options - Configuration options for the dialogue box.\n * @param {number} [options.x=0] - The x-coordinate of the top-left corner.\n * @param {number} [options.y=0] - The y-coordinate of the top-left corner.\n * @param {number} [options.width=600] - The width of the dialogue box.\n * @param {number} [options.height=150] - The height of the dialogue box.\n * @param {string} [options.backgroundColor='rgba(0,0,0,0.75)'] - Background color of the box.\n * @param {string} [options.borderColor='white'] - Border color of the box.\n * @param {number} [options.borderWidth=2] - Width of the border.\n * @param {number} [options.padding=15] - Inner padding for text and other elements.\n * @param {string} [options.textFont='18px sans-serif'] - Font for the main dialogue text.\n * @param {string} [options.textColor='white'] - Color for the main dialogue text.\n * @param {string} [options.speakerFont='bold 20px sans-serif'] - Font for the speaker's name.\n * @param {string} [options.speakerColor='lightblue'] - Color for the speaker's name.\n * @param {number} [options.typewriterSpeed=50] - Characters per second for the typewriter effect (0 for instant text).\n * @param {string} [options.advanceIndicatorText='▼'] - Text/symbol for the advance indicator.\n * @param {string} [options.advanceIndicatorFont='16px sans-serif'] - Font for the advance indicator.\n * @param {string} [options.advanceIndicatorColor='lightgray'] - Color for the advance indicator.\n * @param {object} [options.portraitConfig] - Configuration for character portraits.\n * @param {number} [options.portraitConfig.width=80] - Width of the portrait area.\n * @param {number} [options.portraitConfig.height=80] - Height of the portrait area.\n * @param {number} [options.portraitConfig.xOffset=10] - X offset for the portrait from the box edge.\n * @param {number} [options.portraitConfig.yOffset=10] - Y offset for the portrait.\n * @param {number} [options.choiceButtonHeight=30] - Height of each choice button.\n * @param {number} [options.choiceButtonWidth=0] - Width of choice buttons. If 0, fits to panel width minus padding/portrait.\n * @param {number} [options.choiceSpacing=5] - Vertical spacing between choice buttons.\n * @param {string} [options.choiceFont='16px sans-serif'] - Font for choice button text.\n * @param {string} [options.choiceButtonBgColor='rgba(50,50,50,0.8)'] - Background color for choice buttons.\n * @param {string} [options.choiceButtonHoverBgColor='rgba(80,80,80,0.9)'] - Hover background color for choice buttons.\n * @param {string} [options.choiceButtonTextColor='white'] - Text color for choice buttons.\n * @param {function(string):void} [options.onChoiceSelect] - Callback when a player choice is selected (passes the choice ID).\n * @param {function():void} [options.onTextAdvance] - Callback when player requests to advance fully displayed text (and no choices are pending).\n */\nclass DialogueBox extends BaseUIElement {\n  constructor(options = {}) {\n    super(options)\n\n    this.backgroundColor = options.backgroundColor || 'rgba(0,0,0,0.75)'\n    this.borderColor = options.borderColor || 'white'\n    this.borderWidth = options.borderWidth !== undefined ? options.borderWidth : 2\n    this.padding = options.padding !== undefined ? options.padding : 15\n\n    this.textFont = options.textFont || '18px sans-serif'\n    this.textColor = options.textColor || 'white'\n    this.speakerFont = options.speakerFont || 'bold 20px sans-serif'\n    this.speakerColor = options.speakerColor || 'lightblue'\n\n    this.typewriterSpeed = options.typewriterSpeed !== undefined ? options.typewriterSpeed : 50\n\n    this.advanceIndicatorText = options.advanceIndicatorText || '▼'\n    this.advanceIndicatorFont = options.advanceIndicatorFont || '16px sans-serif'\n    this.advanceIndicatorColor = options.advanceIndicatorColor || 'lightgray'\n\n    this.portraitConfig = {\n      width: options.portraitConfig?.width || 80,\n      height: options.portraitConfig?.height || 80,\n      xOffset: options.portraitConfig?.xOffset || this.padding,\n      yOffset: options.portraitConfig?.yOffset || this.padding,\n    }\n\n    this.choiceButtonHeight = options.choiceButtonHeight || 30\n    this.choiceButtonWidth = options.choiceButtonWidth || 0\n    this.choiceSpacing = options.choiceSpacing || 5\n    this.choiceFont = options.choiceFont || '16px sans-serif'\n    this.choiceButtonBgColor = options.choiceButtonBgColor || 'rgba(50,50,50,0.8)'\n    this.choiceButtonHoverBgColor = options.choiceButtonHoverBgColor || 'rgba(80,80,80,0.9)'\n    this.choiceButtonTextColor = options.choiceButtonTextColor || 'white'\n\n    /** @type {string} The name of the current speaker. */\n    this.speakerName = ''\n    /** @type {string} The full dialogue text to be displayed for the current line. */\n    this.fullText = ''\n    /** @type {string} The currently visible portion of the fullText (for typewriter effect). */\n    this.currentDisplayedText = ''\n    /** @type {HTMLImageElement | null} The current character portrait image. */\n    this.portraitImage = null\n\n    /** @type {Array<{id: string, text: string}>} Raw choice data. */\n    this.currentChoicesData = []\n    /** @private @type {Button[]} Interactive Button elements for choices. */\n    this._choiceButtons = []\n\n    /** @private @type {number} Character index for the typewriter effect. */\n    this._typewriterIndex = 0\n    /** @private @type {number} Timer for controlling typewriter speed interval. */\n    this._typewriterTimer = 0\n    /** @private @type {boolean} True if the fullText has been displayed by the typewriter. */\n    this._isTextFullyDisplayed = true\n    /** @type {boolean} True if the dialogue box is currently waiting for player input to advance (and no choices are shown). */\n    this.isWaitingForAdvance = false\n    /** @type {boolean} True if player choices are currently displayed and awaiting selection. */\n    this.isWaitingForChoice = false\n\n    /** @type {function(string):void | null} Callback when a choice is selected. */\n    this.onChoiceSelect = options.onChoiceSelect || null\n    /** @type {function():void | null} Callback when text advance is requested. */\n    this.onTextAdvance = options.onTextAdvance || null\n  }\n\n  /**\n   * Displays a new dialogue message, including speaker, text, optional portrait, and choices.\n   * Resets typewriter and choice states.\n   * @param {object} dialogueData - Data for the dialogue line.\n   * @param {string} [dialogueData.speaker] - Name of the speaker.\n   * @param {string} dialogueData.text - The dialogue text.\n   * @param {string} [dialogueData.portraitKey] - Asset key for the portrait image (loaded via AssetLoader).\n   * @param {Array<{id: string, text: string}>} [dialogueData.choices] - Array of player choices.\n   */\n  displayMessage(dialogueData = {}) {\n    this.speakerName = dialogueData.speaker || ''\n    this.fullText = dialogueData.text || ''\n    this.currentDisplayedText = ''\n    this._typewriterIndex = 0\n    this._typewriterTimer = 0\n    this._isTextFullyDisplayed = this.typewriterSpeed <= 0 || !this.fullText\n    this.isWaitingForAdvance = false\n    this.isWaitingForChoice = false\n\n    this.currentChoicesData = dialogueData.choices || []\n    this._destroyChoiceButtons()\n\n    if (this._isTextFullyDisplayed) {\n      this.currentDisplayedText = this.fullText\n      if (this.currentChoicesData.length > 0) {\n        this._createChoiceButtons()\n        this.isWaitingForChoice = true\n      } else {\n        this.isWaitingForAdvance = true\n      }\n    }\n\n    this.portraitImage = null\n    if (dialogueData.portraitKey && this.engine && this.engine.assetLoader) {\n      const imgAsset = this.engine.assetLoader.get(dialogueData.portraitKey)\n      if (imgAsset instanceof HTMLImageElement) {\n        this.portraitImage = imgAsset\n      } else {\n        // console.warn(`DialogueBox: Portrait asset \"${dialogueData.portraitKey}\" not found or not an image.`);\n      }\n    }\n  }\n\n  /**\n   * Destroys existing choice buttons.\n   * @private\n   */\n  _destroyChoiceButtons() {\n    this._choiceButtons.forEach((button) => {\n      if (typeof button.destroy === 'function') button.destroy()\n    })\n    this._choiceButtons = []\n  }\n\n  /**\n   * Creates Button instances for the current choices.\n   * @private\n   */\n  _createChoiceButtons() {\n    this._destroyChoiceButtons()\n    if (!this.engine || this.currentChoicesData.length === 0) return\n\n    // Calculate available width for choice buttons, considering portrait and padding\n    let choiceAreaX = this.x + this.padding\n    let choiceAreaWidth = this.width - 2 * this.padding\n\n    if (this.portraitImage && this.portraitConfig.xOffset < this.width / 2) {\n      // Assuming portrait on left\n      choiceAreaX = this.x + this.portraitConfig.xOffset + this.portraitConfig.width + this.padding\n      choiceAreaWidth = this.width - (choiceAreaX - this.x) - this.padding\n    }\n\n    const buttonWidth = this.choiceButtonWidth > 0 ? this.choiceButtonWidth : choiceAreaWidth\n    // If buttonWidth is less than choiceAreaWidth, center the buttons\n    const buttonX = choiceAreaX + (choiceAreaWidth - buttonWidth) / 2\n\n    // Position choices from the bottom of the dialogue box, stacking upwards\n    let startChoiceY = this.y + this.height - this.padding - this.choiceButtonHeight\n\n    ;[...this.currentChoicesData].reverse().forEach((choiceData, indexFromBottom) => {\n      const button = new Button({\n        engine: this.engine,\n        x: buttonX,\n        y: startChoiceY - indexFromBottom * (this.choiceButtonHeight + this.choiceSpacing),\n        width: buttonWidth,\n        height: this.choiceButtonHeight,\n        text: choiceData.text,\n        font: this.choiceFont,\n        textColor: this.choiceButtonTextColor,\n        backgroundColor: this.choiceButtonBgColor,\n        hoverBackgroundColor: this.choiceButtonHoverBgColor,\n        pressedBackgroundColor: this.choiceButtonHoverBgColor, // Can be same as hover or different\n        onClick: () => this._handleChoiceSelected(choiceData.id),\n      })\n      this._choiceButtons.unshift(button) // Add to beginning to keep original order for iteration\n    })\n  }\n\n  /**\n   * Processes a player's request to advance the dialogue.\n   * If text is currently typing via typewriter effect, it completes the text instantly.\n   * If text is fully displayed and no choices are pending, it invokes the onTextAdvance callback.\n   */\n  requestAdvance() {\n    if (!this.enabled || !this.visible) return\n\n    if (!this._isTextFullyDisplayed) {\n      this._isTextFullyDisplayed = true\n      this.currentDisplayedText = this.fullText\n      this._typewriterIndex = this.fullText.length\n      this._typewriterTimer = 0\n      if (this.currentChoicesData.length > 0 && this._choiceButtons.length === 0) {\n        this._createChoiceButtons()\n      }\n      this.isWaitingForChoice = this.currentChoicesData.length > 0\n      this.isWaitingForAdvance = !this.isWaitingForChoice\n    } else if (this.isWaitingForAdvance && this.currentChoicesData.length === 0) {\n      if (this.onTextAdvance) {\n        this.onTextAdvance()\n      }\n    }\n  }\n\n  /**\n   * Internally handles a choice selection.\n   * @private\n   * @param {string} choiceId - The ID of the selected choice.\n   */\n  _handleChoiceSelected(choiceId) {\n    if (!this.enabled || !this.visible || !this.isWaitingForChoice) return\n    if (this.onChoiceSelect) {\n      this.onChoiceSelect(choiceId)\n    }\n  }\n\n  /**\n   * Updates the dialogue box state, primarily for the typewriter effect and choice button updates.\n   * @param {number} deltaTime - Time since the last frame in seconds.\n   * @param {import('../../engine/core/IroncladEngine.js').default} engine - The engine instance.\n   * @param {{x: number, y: number}} mousePos - Current mouse position.\n   */\n  update(deltaTime, engine, mousePos) {\n    super.update(deltaTime, engine, mousePos)\n    if (!this.visible || !this.enabled) return\n\n    if (!this._isTextFullyDisplayed && this.typewriterSpeed > 0 && this.fullText.length > 0) {\n      this._typewriterTimer += deltaTime * 1000\n      const interval = 1000 / this.typewriterSpeed\n\n      while (this._typewriterTimer >= interval && this._typewriterIndex < this.fullText.length) {\n        this._typewriterIndex++\n        this.currentDisplayedText = this.fullText.substring(0, this._typewriterIndex)\n        this._typewriterTimer -= interval\n      }\n\n      if (this._typewriterIndex >= this.fullText.length) {\n        this._isTextFullyDisplayed = true\n        this.currentDisplayedText = this.fullText\n        if (this.currentChoicesData.length > 0 && this._choiceButtons.length === 0) {\n          this._createChoiceButtons()\n        }\n        this.isWaitingForChoice = this.currentChoicesData.length > 0\n        this.isWaitingForAdvance = !this.isWaitingForChoice\n      }\n    }\n\n    if (this.isWaitingForChoice) {\n      this._choiceButtons.forEach((button) => {\n        if (button.visible && button.enabled) {\n          button.update(deltaTime, engine, mousePos)\n        }\n      })\n    }\n  }\n\n  /**\n   * Renders the dialogue box, including background, border, portrait, speaker name, text, choices, and advance indicator.\n   * @param {CanvasRenderingContext2D} context - The rendering context.\n   * @param {import('../../engine/core/IroncladEngine.js').default} engine - The engine instance.\n   * @protected\n   */\n  _drawSelf(context, engine) {\n    // Draw background box\n    if (this.backgroundColor) {\n      context.fillStyle = this.backgroundColor\n      context.fillRect(this.x, this.y, this.width, this.height)\n    }\n    if (this.borderWidth > 0 && this.borderColor) {\n      context.strokeStyle = this.borderColor\n      context.lineWidth = this.borderWidth\n      context.strokeRect(this.x, this.y, this.width, this.height)\n    }\n\n    let textContentX = this.x + this.padding\n    let currentContentY = this.y + this.padding\n    let dialogueTextDisplayWidth = this.width - 2 * this.padding\n\n    if (this.portraitImage instanceof HTMLImageElement) {\n      const pConfig = this.portraitConfig\n      try {\n        context.drawImage(\n          this.portraitImage,\n          this.x + pConfig.xOffset,\n          this.y + pConfig.yOffset,\n          pConfig.width,\n          pConfig.height,\n        )\n        if (this.x + pConfig.xOffset + pConfig.width < this.x + this.width / 2) {\n          // If portrait is on the left side\n          textContentX = this.x + pConfig.xOffset + pConfig.width + this.padding\n          dialogueTextDisplayWidth = this.width - (textContentX - this.x) - this.padding\n        }\n      } catch (e) {\n        console.error('DialogueBox: Error drawing portrait.', e)\n      }\n    }\n\n    if (this.speakerName) {\n      context.fillStyle = this.speakerColor\n      context.font = this.speakerFont\n      context.textAlign = 'left'\n      context.textBaseline = 'top'\n      context.fillText(this.speakerName, textContentX, currentContentY)\n      currentContentY += parseFloat(this.speakerFont.match(/\\d+/)?.[0] || '20') * 1.2\n    }\n\n    context.fillStyle = this.textColor\n    context.font = this.textFont\n    context.textAlign = 'left'\n    context.textBaseline = 'top'\n    this.wrapAndDrawText(\n      context,\n      this.currentDisplayedText,\n      textContentX,\n      currentContentY,\n      dialogueTextDisplayWidth,\n      parseFloat(this.textFont.match(/\\d+/)?.[0] || '18') * 1.2,\n    )\n\n    if (this._isTextFullyDisplayed && this.isWaitingForChoice) {\n      this._choiceButtons.forEach((button) => {\n        if (button.visible) button.render(context, engine)\n      })\n    }\n\n    if (\n      this._isTextFullyDisplayed &&\n      this.isWaitingForAdvance &&\n      this.currentChoicesData.length === 0\n    ) {\n      context.fillStyle = this.advanceIndicatorColor\n      context.font = this.advanceIndicatorFont\n      context.textAlign = 'right'\n      context.textBaseline = 'alphabetic'\n      context.fillText(\n        this.advanceIndicatorText,\n        this.x + this.width - this.padding,\n        this.y + this.height - this.padding,\n      )\n    }\n  }\n\n  /**\n   * Basic text wrapping and drawing function.\n   * @param {CanvasRenderingContext2D} context\n   * @param {string} text\n   * @param {number} x\n   * @param {number} y\n   * @param {number} maxWidth\n   * @param {number} lineHeight\n   */\n  wrapAndDrawText(context, text, x, y, maxWidth, lineHeight) {\n    if (!text) return\n    const words = text.split(' ')\n    let line = ''\n    let currentY = y\n    for (let n = 0; n < words.length; n++) {\n      const testLine = line + words[n] + ' '\n      const metrics = context.measureText(testLine)\n      const testWidth = metrics.width\n      if (testWidth > maxWidth && n > 0) {\n        context.fillText(line.trim(), x, currentY)\n        line = words[n] + ' '\n        currentY += lineHeight\n      } else {\n        line = testLine\n      }\n    }\n    context.fillText(line.trim(), x, currentY)\n  }\n\n  /**\n   * Destroys the DialogueBox and its internal UI elements (choice buttons).\n   */\n  destroy() {\n    super.destroy() // If BaseUIElement has its own destroy logic\n    this._destroyChoiceButtons()\n  }\n}\nexport default DialogueBox\n"],"names":["GameLoop","update","render","deltaTime","currentTime","newUpdateFunction","newRenderFunction","SceneManager","context","engineInstance","name","sceneInstance","sceneName","data","newSceneInstance","sceneWrapper","currentTopSceneInstance","dataToPassDown","poppedSceneWrapper","poppedSceneName","newTopSceneInstance","i","sceneEntry","AssetLoader","e","path","group","asset","resolve","reject","eventManager","image","errorEvent","error","response","assetName","assetPath","status","cachedAsset","audioCtx","arrayBuffer","audioBuffer","batchId","currentQueue","promises","assetToLoad","loadPromise","err","results","successfullyLoadedAssets","allSucceeded","result","Keyboard","event","code","keyCode","Mouse","canvasElement","rect","scaleX","scaleY","buttonCode","__publicField","GamepadHandler","inputManager","gp","gamepad","rawGamepads","trackedGamepad","index","currentState","prevState","b","rawButton","value","gamepadIndex","buttonIndex","state","current","prev","axisIndex","padIndexBtn","padIndexAxis","axisVal","threshold","direction","InputManager","engine","actions","actionName","isAnyMappedKeyPressed","mappedCodes","mappedCode","isStillPressedByOtherKey","bindings","currentActionState","oldPressedState","newPressedState","highestActionValue","binding","bindingIsActive","bindingValue","actionsForCode","codes","padIndex","EventManager","eventName","callback","once","eventListeners","listener","args","EntityManager","entityId","entityComponents","component","componentTypeName","entitiesWithThisComponent","componentTypeNames","smallestSet","smallestSetSize","typeName","entitiesForType","resultEntities","hasAll","PrefabManager","entityManager","assetLoader","assetKey","prefabData","count","prefabName","definition","overrideData","prefab","compDef","finalComponentData","Camera","viewportWidth","viewportHeight","worldWidth","worldHeight","targetEntity","screenFocusX","screenFocusY","hasMethods","hasDirectProps","targetWorldX","targetWorldY","targetWidth","targetHeight","pos","dim","targetCenterX","targetCenterY","SAVE_FILE_VERSION","LOCAL_STORAGE_KEY_PREFIX","SaveLoadManager","uniqueKey","provider","slotId","userMetadata","rootSaveObject","key","dataPart","jsonString","errorMessage","slotKey","loadedRootObject","slots","a","keysToRemove","BaseEffect","options","mainContext","sceneCanvas","effectPipelineData","FlashEffect","effectId","originalAlpha","ShakeEffect","currentDrawX","currentDrawY","offsetX","offsetY","TintEffect","duration","newColor","newOpacity","EffectsManager","effectInstance","intensity","decay","shakeEffect","color","maxOpacity","fadeOut","flashEffect","opacity","clampedOpacity","effect","AudioManager","volume","playbackRate","startTime","source","individualSfxGain","loop","fadeInDuration","trackGainNode","targetVolume","fadeOutDuration","sourceToStop","nameOfMusicStopped","currentMusicVolume","masterVol","muted","DialogueManager","dialogueBox","dialogueId","choiceId","selectedChoice","c","nextNodeId","nextIndex","node","endedDialogueId","_IroncladEngine","canvas","width","height","assetManifestPath","sceneRegistry","defaultWorldWidth","defaultWorldHeight","SceneClass","systemWrapper","system","requiredComponents","relevantEntities","initialSceneName","initialSceneData","errorMsg","systemInstance","priority","sWrapper","initialLength","SystemClass","wrapper","sw","IroncladEngine","BaseScene","element","mousePos","BaseEntity","eventData","System","entities","Sprite","x","y","sx","sy","sWidth","sHeight","anchorX","anchorY","_RenderSystem","camera","entitiesToRender","viewportX","viewportY","position","renderable","_a","sprite","RenderSystem","AnimationSystem","anim","currentAnimDef","effectiveDeltaTime","frameValueFromArray","tileWidth","tileHeight","columns","GRAVITY_Y","EPSILON","PhysicsSystem","collider","centerX","centerY","centerZ","halfWidth","halfHeight","halfDepth","aabb1","aabb2","scene","engineRef","activeScene","canCheckTileCollisions","velocity","physicsBody","acceleration","frameAx","frameAy","frameAz","collisionIterations","iter","entityIdA","positionA","velocityA","physicsBodyA","colliderA","prevX","prevY","aabbA","j","entityIdB","positionB","velocityB","physicsBodyB","colliderB","canACollideB","canBCollideA","aabbB","overlapX","overlapY","penetration","vya","vyb","TileLayerRenderer","mapData","loadedTilesetImages","tsDef","imageElement","cleanGid","tileset","localId","tileX","tileY","layerIdentifier","layer","l","canvasWidth","canvasHeight","destTileWidth","destTileHeight","startCol","endCol","startRow","endRow","r","tileIndex","gidWithFlags","FLIPPED_HORIZONTALLY_FLAG","FLIPPED_VERTICALLY_FLAG","FLIPPED_DIAGONALLY_FLAG","tileDetails","dx","dy","rotation","flipH","flipV","BaseUIElement","px","py","elementName","Label","newText","Button","leftButton","lmbPressed","lmbJustReleased","currentBgColor","currentTxtColor","Checkbox","boxSize","labelOffset","estimatedWidth","checked","triggerCallback","newState","currentBoxColor","currentTextColor","currentCheckColor","padding","Panel","child","ValueBar","initialCurrentValue","max","percentage","clampedPercentage","fillPercentage","fillWidth","textToDisplay","Slider","valueRatio","trackInnerWidth","thumbCenterX","trackInnerHeight","thumbCenterY","pixelPos","ratio","thumbCenterRelativeX","thumbCenterRelativeY","clampedValue","snappedValue","isHoveredOnTrack","newValue","currentTrackColor","currentThumbColor","trackVisualY","trackVisualThickness","trackVisualX","ScrollablePanel","currentY","viewportInnerHeight","thumbHeightRatio","scrollableRange","scrollRatio","availableThumbTravel","newScrollTop","fromScrollbar","maxScrollTop","wasMouseOverPanel","clickRatio","trackScrollableRange","scrollTopDelta","contentOriginX","contentOriginY","childScreenY","childScreenX","originalChildX","originalChildY","TextInputField","oldText","mouseX","mouseY","textX","textY","textWidth","cursorX","cursorYStart","cursorYEnd","DialogueBox","_b","_c","_d","dialogueData","imgAsset","button","choiceAreaX","choiceAreaWidth","buttonWidth","buttonX","startChoiceY","choiceData","indexFromBottom","interval","textContentX","currentContentY","dialogueTextDisplayWidth","pConfig","text","maxWidth","lineHeight","words","line","n","testLine"],"mappings":"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"}