@skewedaspect/sage 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/classes/gameLevel.d.ts +6 -7
- package/dist/interfaces/level.d.ts +9 -0
- package/dist/sage.d.ts +1 -0
- package/dist/sage.es.js +249 -226
- package/dist/sage.es.js.map +1 -1
- package/dist/sage.umd.js +1 -1
- package/dist/sage.umd.js.map +1 -1
- package/dist/utils/vectors.d.ts +18 -0
- package/package.json +1 -1
package/dist/sage.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`@babylonjs/core`),require(`@babylonjs/loaders/dynamic`),require(`@babylonjs/core/Cameras/geospatialCamera`),require(`@babylonjs/core/Lights/Clustered/clusteredLightContainer`),require(`@babylonjs/havok`)):typeof define==`function`&&define.amd?define([`exports`,`@babylonjs/core`,`@babylonjs/loaders/dynamic`,`@babylonjs/core/Cameras/geospatialCamera`,`@babylonjs/core/Lights/Clustered/clusteredLightContainer`,`@babylonjs/havok`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.SAGameEngine={},e.BABYLON,e.BABYLON,e.BABYLON,e.BABYLON,e.HavokPhysics))})(this,function(e,t,n,r,i,a){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var o=Object.create,s=Object.defineProperty,c=Object.getOwnPropertyDescriptor,l=Object.getOwnPropertyNames,u=Object.getPrototypeOf,d=Object.prototype.hasOwnProperty,f=(e,t)=>()=>(e&&(t=e(e=0)),t),p=(e,t)=>{let n={};for(var r in e)s(n,r,{get:e[r],enumerable:!0});return t||s(n,Symbol.toStringTag,{value:`Module`}),n},m=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=l(t),a=0,o=i.length,u;a<o;a++)u=i[a],!d.call(e,u)&&u!==n&&s(e,u,{get:(e=>t[e]).bind(null,u),enumerable:!(r=c(t,u))||r.enumerable});return e};a=((e,t,n)=>(n=e==null?{}:o(u(e)),m(t||!e||!e.__esModule?s(n,`default`,{value:e,enumerable:!0}):n,e)))(a);var h=`0.9.0`,g=class{canvas;renderEngine;physics;managers;engines;eventBus;logger;raycast;timer;largeWorldRendering;started=!1;_log;_beforeStartHook=null;_onStartHook=null;_onTeardownHook=null;constructor(e,t,n,r,i,a,o,s,c,l=!1){this.canvas=e,this.renderEngine=t,this.physics=n,this.eventBus=r,this.logger=i,this.raycast=a,this.timer=o,this.engines=s,this.managers=c,this.largeWorldRendering=l,this._log=i.getLogger(`GameEngine`)}onBeforeStart(e){if(this._beforeStartHook!==null)throw Error(`A beforeStart hook is already registered. Only one hook is allowed per lifecycle event.`);this._beforeStartHook=e}onStart(e){if(this._onStartHook!==null)throw Error(`An onStart hook is already registered. Only one hook is allowed per lifecycle event.`);this._onStartHook=e}onTeardown(e){if(this._onTeardownHook!==null)throw Error(`An onTeardown hook is already registered. Only one hook is allowed per lifecycle event.`);this._onTeardownHook=e}subscribe(e,t){return this.eventBus.subscribe(e,t)}subscribeAction(e,t){return this.eventBus.subscribe(`action:${e}`,t)}async start(){this.started?this._log.warn(`Game engine is already started. Skipping start.`):(this._log.info(`Starting SkewedAspect Game Engine (Version: ${h})...`),this._beforeStartHook&&(this._log.debug(`Executing beforeStart hook...`),await this._beforeStartHook(this)),await this.managers.gameManager.start(),this._log.info(`Game engine started successfully`),this._onStartHook&&(this._log.debug(`Executing onStart hook...`),await this._onStartHook(this)),this.started=!0)}async stop(){if(this.started){this._log.info(`Stopping SkewedAspect Game Engine (Version: ${h})...`);let e=null;if(this._onTeardownHook)try{this._log.debug(`Executing onTeardown hook...`),await this._onTeardownHook(this)}catch(t){this._log.error(`Error in onTeardown hook: ${t}`),e||=t}for(let t of Object.keys(this.managers)){let n=this.managers[t];if(n)try{this._log.debug(`Tearing down manager: ${t}`),await n.$teardown()}catch(n){this._log.error(`Error tearing down manager ${t}: ${n}`),e||=n}}for(let t of Object.keys(this.engines)){let n=this.engines[t];if(n)try{this._log.debug(`Tearing down engine: ${t}`),await n.$teardown()}catch(n){this._log.error(`Error tearing down engine ${t}: ${n}`),e||=n}}if(this.started=!1,this._log.info(`Game engine stopped successfully`),e)throw e}else this._log.warn(`Game engine is not started. Skipping stop.`)}},_=class{timers;constructor(){this.timers=new Map}getStyleForLevel(e){let t={trace:`color: #999999`,debug:`color: #00AAAA`,info:`color: #00AA00`,warn:`color: #AAAA00`,error:`color: #AA0000`,timer:`color: #AA00AA`,none:`color: inherit`};return t[e]||t.none}formatMessage(e,t){let n=new Date;return[`${`[${n.getHours()%12||12}:${n.getMinutes().toString().padStart(2,`0`)}:${n.getSeconds().toString().padStart(2,`0`)} ${n.getHours()>=12?`PM`:`AM`}]`} %c${t.toUpperCase()}%c (${e}): `,this.getStyleForLevel(t),``,``]}trace(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`trace`);console.debug(r,i,a,o,t,...n)}debug(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`debug`);console.debug(r,i,a,o,t,...n)}info(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`info`);console.info(r,i,a,o,t,...n)}warn(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`warn`);console.warn(r,i,a,o,t,...n)}error(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`error`);console.error(r,i,a,o,t,...n)}time(e,t){let n=`${e}:${t}`;this.timers.set(n,performance.now())}timeEnd(e,t){let n=`${e}:${t}`,r=this.timers.get(n);if(r!==void 0){let i=performance.now()-r;this.timers.delete(n);let[a,o,s,c]=this.formatMessage(e,`timer`);console.info(`${a} Timer '${t}' completed in ${i.toFixed(2)}ms`,o,s,c)}else console.warn(`[${e}]: Timer '${t}' does not exist`)}},v=class{trace(e,t,...n){}debug(e,t,...n){}info(e,t,...n){}warn(e,t,...n){}error(e,t,...n){}time(e,t){}timeEnd(e,t){}},y=class{category;backend;minLevel;constructor(e,t=`none`,n=new v){this.category=e,this.backend=n,this.minLevel=t}updateSettings(e,t){this.backend=e,this.minLevel=t}trace(e,...t){this.shouldLog(`trace`)&&this.backend.trace(this.category,e,...t)}debug(e,...t){this.shouldLog(`debug`)&&this.backend.debug(this.category,e,...t)}info(e,...t){this.shouldLog(`info`)&&this.backend.info(this.category,e,...t)}warn(e,...t){this.shouldLog(`warn`)&&this.backend.warn(this.category,e,...t)}error(e,...t){this.shouldLog(`error`)&&this.backend.error(this.category,e,...t)}time(e){this.minLevel!==`none`&&this.backend.time(this.category,e)}timeEnd(e){this.minLevel!==`none`&&this.backend.timeEnd(this.category,e)}shouldLog(e){if(this.minLevel===`none`)return!1;switch(this.minLevel){case`trace`:return!0;case`debug`:return e!==`trace`;case`info`:return e===`info`||e===`warn`||e===`error`;case`warn`:return e===`warn`||e===`error`;case`error`:return e===`error`;default:return!1}}},b=class{backend;level;loggers;constructor(e=`debug`,t=new _){this.backend=t,this.level=e,this.loggers=new Map}setBackend(e){this.backend=e,this.loggers.forEach(e=>{e instanceof y&&e.updateSettings(this.backend,this.level)})}setLevel(e){this.level=e,this.loggers.forEach(e=>{e instanceof y&&e.updateSettings(this.backend,this.level)})}getLevel(){return this.level}getLogger(e){let t=this.loggers.get(e);return t||(t=new y(e,this.level,this.backend),this.loggers.set(e,t)),t}},x=class{directMap=new Map;patternSubs=new Set;_log;constructor(e){this._log=e?.getLogger(`EventBus`)||new y(`EventBus`)}subscribe(e,t){return this._log.trace(`Subscribe request for: ${e.toString()}`),e instanceof RegExp||typeof e==`string`&&e.includes(`*`)?this.subscribePattern(e,t):this.subscribeExact(e,t)}subscribeExact(e,t){this._log.debug(`Adding exact subscription for: ${e}`);let n=this.directMap.get(e);n||(n=new Set,this.directMap.set(e,n));let r={callback:e=>t(e)};return n.add(r),()=>{this._log.debug(`Removing exact subscription for: ${e}`),n.delete(r),n.size===0&&this.directMap.delete(e)}}subscribePattern(e,t){let n;if(typeof e==`string`){let t=e.replace(/[-/\\^$+?.()|[\]{}]/g,`\\$&`).replace(/\*/g,`.*`);n=RegExp(`^${t}$`),this._log.debug(`Adding pattern subscription for string: ${e}, regex: ${n.toString()}`)}else n=e,this._log.debug(`Adding pattern subscription for regex: ${n.toString()}`);let r={pattern:n,callback:e=>t(e)};return this.patternSubs.add(r),()=>{this._log.debug(`Removing pattern subscription: ${n.toString()}`),this.patternSubs.delete(r)}}publish(e){this._log.trace(`Publishing event: ${e.type}`,e);let t=0,n=this.directMap.get(e.type);if(n){t+=n.size;for(let t of n)Promise.resolve().then(()=>t.callback(e)).catch(t=>{this._log.error(`Error in event handler for '${e.type}':`,t)})}for(let n of this.patternSubs)n.pattern&&n.pattern.test(e.type)&&(t++,Promise.resolve().then(()=>n.callback(e)).catch(t=>{this._log.error(`Error in pattern event handler for '${e.type}':`,t)}));t===0?this._log.debug(`No subscribers found for event: ${e.type}`):this._log.trace(`Event ${e.type} dispatched to ${t} subscribers`)}async $teardown(){this._log.debug(`Tearing down EventBus`),this.directMap.clear(),this.patternSubs.clear()}},S=class{_engine=null;_mainBus=null;_buses=new Map;_log;constructor(e){this._log=e.getLogger(`AudioEngine`)}async initialize(){this._log.info(`Initializing audio engine...`),this._engine=await(0,t.CreateAudioEngineAsync)(),this._mainBus=await this._engine.createMainBusAsync(`main`),this._log.info(`Audio engine initialized.`)}async $teardown(){this._log.info(`Tearing down audio engine...`),this._buses.clear(),this._mainBus=null,this._engine&&=(this._engine.dispose(),null),this._log.info(`Audio engine torn down.`)}async createBus(e){if(!this._engine||!this._mainBus)throw Error(`AudioEngine not initialized`);this._log.debug(`Creating audio bus: ${e}`);let t=await this._engine.createBusAsync(e,{outBus:this._mainBus});return this._buses.set(e,t),t}getBus(e){return this._buses.get(e)}async createSound(e,t,n,r){if(!this._engine)throw Error(`AudioEngine not initialized`);let i=n??this._mainBus??void 0,a={...r,...i?{outBus:i}:{}};return this._engine.createSoundAsync(e,t,a)}setMasterVolume(e){if(!this._engine)throw Error(`AudioEngine not initialized`);this._engine.volume=e}getMasterVolume(){return this._engine?this._engine.volume:1}setBusVolume(e,t){e.volume=t}};(0,n.registerBuiltInLoaders)();var C=class{_canvas;_engine;_havok;_log;constructor(e,t,n,r){this._canvas=e,this._engine=t,this._havok=n,this._log=r.getLogger(`SceneEngine`)}createScene(){return new t.Scene(this._engine)}enablePhysics(e,n=new t.Vector3(0,-9.8,0),r){this._log.debug(`Enabling physics with gravity (${n.x}, ${n.y}, ${n.z})...`);let i=r===void 0?void 0:{floatingOriginWorldRadius:r},a=new t.HavokPlugin(!0,this._havok,i);e.enablePhysics(n,a)}createFreeCamera(e,n,r,i){let a=new t.FreeCamera(e,n,r);return a.setTarget(t.Vector3.Zero()),i??=this._canvas,i&&a.attachControl(i,!0),a}createHemisphericLight(e,n,r,i=.7){let a=new t.HemisphericLight(e,n,r);return a.intensity=i,a}createDirectionalLight(e,n,r,i=1){let a=new t.DirectionalLight(e,n,r);return a.intensity=i,a}createPointLight(e,n,r,i=1){let a=new t.PointLight(e,n,r);return a.intensity=i,a}createSpotLight(e,n,r,i,a,o,s=1){let c=new t.SpotLight(e,n,r,i,a,o);return c.intensity=s,c}createRectAreaLight(e,n,r,i,a,o=1){let s=new t.RectAreaLight(e,n,r,i,a);return s.intensity=o,s}createSphere(e,n={},r){return t.MeshBuilder.CreateSphere(e,{diameter:1,segments:32,...n},r)}createBox(e,n={},r){return t.MeshBuilder.CreateBox(e,{size:1,...n},r)}createGround(e,n={},r){return t.MeshBuilder.CreateGround(e,{width:6,height:6,subdivisions:2,...n},r)}createCylinder(e,n={},r){return t.MeshBuilder.CreateCylinder(e,{height:2,diameterTop:1,diameterBottom:1,...n},r)}addPhysics(e,n,r={},i){return new t.PhysicsAggregate(e,n,{mass:1,restitution:.75,friction:.5,...r},i)}async loadModel(e,n){this._log.debug(`Loading model: ${e}`);try{let r=await(0,t.LoadAssetContainerAsync)(`${e}`,n);return this._log.debug(`Model loaded successfully: ${e}`),r}catch(t){throw this._log.error(`Failed to load model: ${e}`,t),t}}async importMeshes(e,n,r){this._log.debug(`Importing meshes from: ${n}`);try{let i=e.length>0?{meshNames:e}:void 0,a=await(0,t.ImportMeshAsync)(`${n}`,r,i);return this._log.debug(`Meshes imported successfully: ${e.length>0?e.join(`, `):`all`}`),a}catch(e){throw this._log.error(`Failed to import meshes from: ${n}`,e),e}}async $teardown(){this._log.info(`Tearing down SceneEngine`),this._log.info(`SceneEngine torn down successfully`)}},w=class{_eventBus;_sceneEngine;_log;_containers=new Map;_sourceMeshes=new Map;constructor(e,t,n){this._eventBus=e,this._sceneEngine=t,this._log=n?.getLogger(`AssetManager`)??new y(`AssetManager`)}_parsePath(e){let t=e.indexOf(`#`);return t===-1?{basePath:e}:{basePath:e.substring(0,t),fragment:e.substring(t+1)}}async _loadContainer(e){let t=this._containers.get(e);if(t)return t.refCount++,t.container;this._log.debug(`Loading asset container: ${e}`);let n=this._sceneEngine.createScene();try{let t=await this._sceneEngine.loadModel(e,n);return this._containers.set(e,{container:t,refCount:1}),this._log.debug(`Cached asset container: ${e}`),t}finally{n.dispose()}}_findMeshInContainer(e,t,n){let r=e.meshes.find(e=>e.name===t);if(!r){let r=e.meshes.map(e=>e.name).join(`, `);throw Error(`Mesh '${t}' not found in container for '${n}'. Available meshes: ${r}`)}return r}async load(e){let{basePath:t,fragment:n}=this._parsePath(e);if(n){let r=this._sourceMeshes.get(e);if(r){let e=this._containers.get(t);return e&&e.refCount++,r}let i=await this._loadContainer(t),a=this._findMeshInContainer(i,n,e);return this._sourceMeshes.set(e,a),a}return this._loadContainer(t)}instance(e){let t=this._sourceMeshes.get(e);if(!t)throw Error(`Asset '${e}' is not loaded. Call load() first.`);return t.createInstance(`${t.name}-instance`)}clone(e){let t=this._sourceMeshes.get(e);if(!t)throw Error(`Asset '${e}' is not loaded. Call load() first.`);return t.clone(`${t.name}-clone`)}async preload(e){let t=e.length;for(let n=0;n<e.length;n++)await this.load(e[n]),this._eventBus.publish({type:`asset:progress`,payload:{path:e[n],loaded:n+1,total:t}});this._eventBus.publish({type:`asset:complete`,payload:{paths:e}})}dispose(e){let{basePath:t}=this._parsePath(e),n=this._containers.get(t);if(n&&(n.refCount--,n.refCount<=0)){this._log.debug(`Disposing asset container: ${t} (refCount reached 0)`);for(let[e,n]of this._sourceMeshes)e.startsWith(`${t}#`)&&(n.dispose(),this._sourceMeshes.delete(e));n.container.dispose(),this._containers.delete(t)}}disposeAll(){for(let e of this._containers.values())e.container.dispose();for(let e of this._sourceMeshes.values())e.dispose();this._containers.clear(),this._sourceMeshes.clear()}async $teardown(){this.disposeAll()}},T=[`trigger`,`toggle`,`value`],E=[`keyboard`,`mouse`,`gamepad`];function D(e){return e.type===`keyboard`}function O(e){return e.type===`mouse`}function k(e){return e.type===`gamepad`}var ee=class{type=`trigger`;action;context;deviceID;deviceType;reader;_edgeMode;_threshold;_lastDigitalState=!1;get options(){return{edgeMode:this._edgeMode,threshold:this._threshold}}constructor(e,t,n,r,i={}){this.action=e,this.deviceID=t,this.deviceType=n,this.reader=r,this.context=i.context,this._edgeMode=i.edgeMode??`rising`,this._threshold=i.threshold??.5}process(e,t){let n=this.reader.getValue(e)??!1,r=typeof n==`boolean`?n:n>=this._threshold,i=!1;switch(this._edgeMode){case`rising`:i=r&&!this._lastDigitalState;break;case`falling`:i=!r&&this._lastDigitalState;break;case`both`:i=r!==this._lastDigitalState;break}if(this._lastDigitalState=r,i){let e;if(this.action.type===`analog`){let t=typeof n==`number`?n:n?1:0;if(this.action.minValue!==void 0||this.action.maxValue!==void 0){let e=this.action.minValue??0,n=this.action.maxValue??1;t=e+t*(n-e)}e=t}else e=!0;let r={type:`action:${this.action.name}`,payload:{value:e,deviceId:this.deviceID,context:this.context}};t.publish(r)}}resetEdgeState(e){if(e){let t=this.reader.getValue(e)??!1;this._lastDigitalState=typeof t==`boolean`?t:t>=this._threshold}else this._lastDigitalState=!1}toJSON(){return{type:this.type,action:this.action.name,input:{deviceID:this.deviceID,...this.reader.toJSON()},context:this.context,options:this.options}}},te=class{type=`toggle`;action;context;deviceID;deviceType;reader;_invert;_threshold;_initialState;_onValue;_offValue;_lastDigitalState=!1;_toggleState;get state(){return this._toggleState}set state(e){this._toggleState=e}get onValue(){return this._onValue}get offValue(){return this._offValue}get value(){return this.action.type===`analog`?this._toggleState?this.action.maxValue??1:this.action.minValue??0:this._toggleState?this._onValue:this._offValue}get options(){return{invert:this._invert,threshold:this._threshold,initialState:this._initialState,onValue:this._onValue,offValue:this._offValue}}constructor(e,t,n,r,i={}){this.action=e,this.deviceID=t,this.deviceType=n,this.reader=r,this.context=i.context,this._invert=i.invert??!1,this._threshold=i.threshold??.5,this._initialState=i.initialState??!1,this._toggleState=this._initialState,this._onValue=i.onValue??!0,this._offValue=i.offValue??!1}process(e,t){let n=this.reader.getValue(e)??!1,r=typeof n==`boolean`?n:n>=this._threshold,i=this._invert?!r&&this._lastDigitalState:r&&!this._lastDigitalState;this._lastDigitalState=r,i&&(this._toggleState=!this._toggleState,t.publish({type:`action:${this.action.name}`,payload:{value:this.value,deviceId:this.deviceID,context:this.context}}))}resetEdgeState(e){if(e){let t=this.reader.getValue(e)??!1;this._lastDigitalState=typeof t==`boolean`?t:t>=this._threshold}else this._lastDigitalState=!1}reset(){this._toggleState=this._initialState}toJSON(){return{type:this.type,action:this.action.name,input:{deviceID:this.deviceID,...this.reader.toJSON()},state:this._toggleState,context:this.context,options:this.options}}},ne=class{type=`value`;action;context;deviceID;deviceType;reader;_scale;_offset;_invert;_emitOnChange;_deadzone;_onValue;_offValue;_min;_max;_lastValue;get options(){return{scale:this._scale,offset:this._offset,invert:this._invert,emitOnChange:this._emitOnChange,deadzone:this._deadzone,onValue:this._onValue,offValue:this._offValue,min:this._min,max:this._max}}constructor(e,t,n,r,i={}){this.action=e,this.deviceID=t,this.deviceType=n,this.reader=r,this.context=i.context,this._scale=i.scale??1,this._offset=i.offset??0,this._invert=i.invert??!1,this._emitOnChange=i.emitOnChange??!0,this._deadzone=i.deadzone??0,this._onValue=i.onValue??!0,this._offValue=i.offValue??!1;let a=this.action.type===`analog`?this.action.minValue??-1/0:-1/0;this._min=i.min??a;let o=this.action.type===`analog`?this.action.maxValue??1/0:1/0;this._max=i.max??o}process(e,t){let n=this.reader.getValue(e);if(n===void 0)return;let r=typeof n==`boolean`?n?1:0:n,i=this._deadzone>0&&Math.abs(r)<this._deadzone?0:r;i=(this._invert?-i:i)*this._scale+this._offset,i=Math.max(this._min,Math.min(this._max,i)),!(this._emitOnChange&&this._lastValue===i)&&(this._lastValue=i,t.publish({type:`action:${this.action.name}`,payload:{value:i,deviceId:this.deviceID,context:this.context}}))}resetEdgeState(e){}toJSON(){return{type:this.type,action:this.action.name,input:{deviceID:this.deviceID,...this.reader.toJSON()},context:this.context,options:this.options}}},re=class e{sourceType=`key`;sourceKey;useDelta;constructor(e,t={}){this.sourceKey=e,this.useDelta=t.useDelta||!1}getValue(e){if(e.type!==`keyboard`)return;let t=e;return this.useDelta?t.delta[this.sourceKey]:t.keys[this.sourceKey]}static fromString(t,n={}){return new e(t,n)}toJSON(){return{type:`keyboard`,sourceType:this.sourceType,sourceKey:this.sourceKey,options:{useDelta:this.useDelta}}}},ie=class e{sourceType;sourceKey;constructor(e,t){this.sourceType=e,this.sourceKey=t}getValue(e){if(e.type===`mouse`)switch(this.sourceType){case`button`:{let t=e.buttons[this.sourceKey];return t?t.pressed:void 0}case`position`:{let[t,n]=this.sourceKey.split(`:`);return!t||!n||t!==`absolute`&&t!==`relative`||n!==`x`&&n!==`y`?void 0:e.position[t][n]}case`wheel`:{let t=this.sourceKey===`deltaX`||this.sourceKey===`deltaY`||this.sourceKey===`deltaZ`;return e.wheel&&t?e.wheel[this.sourceKey]:void 0}default:return}}static fromString(t){let[n,...r]=t.split(`:`),i=r.join(`:`);if(!n||!i)throw Error(`Invalid mouse source format: ${t}`);return new e(n,i)}toJSON(){return{type:`mouse`,sourceType:this.sourceType,sourceKey:this.sourceKey}}},ae=class e{sourceType;sourceKey;useAnalogValue;deadzone;invert;constructor(e,t,n={}){this.sourceType=e,this.sourceKey=t,this.useAnalogValue=n.useAnalogValue||!1,this.deadzone=n.deadzone||.1,this.invert=n.invert||!1}getValue(e){if(e.type===`gamepad`)switch(this.sourceType){case`button`:{let t=e.buttons[this.sourceKey];return t?this.useAnalogValue?t.value:t.pressed:void 0}case`axis`:{let t=e.axes[this.sourceKey];return t===void 0?void 0:(Math.abs(t)<this.deadzone&&(t=0),this.invert?-t:t)}default:return}}static fromString(t,n={}){let[r,i]=t.split(`:`);if(!r||!i)throw Error(`Invalid gamepad source format: ${t}`);return new e(r,i,n)}toJSON(){return{type:`gamepad`,sourceType:this.sourceType,sourceKey:this.sourceKey,options:{useAnalogValue:this.useAnalogValue,deadzone:this.deadzone,invert:this.invert}}}},oe=.05,se=.5,ce=class{_bindings=new Map;_actions=new Map;_contexts=new Map;_activeContexts=new Set;_lastActiveDeviceType=null;_lastInputState=new Map;_captureState=null;_eventBus;_inputUnsubscribe=null;_log;constructor(e,t){this._eventBus=e,this._inputUnsubscribe=this._eventBus.subscribe(`input:changed`,e=>{let t=e.payload;t&&this.$handleInput(t.device,t.state)}),this._log=t?.getLogger(`BindingManager`)||new y(`BindingManager`),this._log.debug(`BindingManager initialized`)}_isBindingContextActive(e){return e.context?this._activeContexts.has(e.context):!0}_getOrCreateContext(e,t=!0){let n=this._contexts.get(e);return n||(n={name:e,exclusive:t},this._contexts.set(e,n),this._log.debug(`Auto-created context "${e}" (exclusive: ${t})`)),n}_deactivateExclusiveContexts(e){let t=[];for(let n of this._activeContexts)n!==e&&this._contexts.get(n)?.exclusive&&(this._activeContexts.delete(n),t.push(n));return t}_shouldUpdateActiveDevice(e){return O(e)?Object.values(e.buttons).some(e=>e.pressed)||e.wheel!==void 0:k(e)?Object.values(e.buttons).some(e=>e.pressed)||Object.values(e.axes).some(e=>Math.abs(e)>oe):!0}_createBindingFromDefinition(e){let t=this._actions.get(e.action);if(!t)return this._log.warn(`Cannot create binding: Action "${e.action}" not found.`),null;let{deviceID:n,...r}=e.input;switch(e.type){case`trigger`:return new ee(t,n,e.input.type,this._createInputSourceFromDefinition(r),{...e.options||{},context:e.context});case`toggle`:return new te(t,n,e.input.type,this._createInputSourceFromDefinition(r),{...e.options||{},context:e.context});case`value`:return new ne(t,n,e.input.type,this._createInputSourceFromDefinition(r),{...e.options||{},context:e.context});default:return this._log.error(`Binding type not implemented: ${e.type}`),null}}_createInputSourceFromDefinition(e){switch(e.type){case`keyboard`:return new re(e.sourceKey,e.options);case`mouse`:{let t=e.sourceType;if(!(t===`button`||t===`position`||t===`wheel`))throw Error(`Invalid mouse source type: ${t}`);return new ie(t,e.sourceKey)}case`gamepad`:{let t=e.sourceType;if(!(t===`button`||t===`axis`))throw Error(`Invalid gamepad source type: ${t}`);return new ae(t,e.sourceKey,e.options)}default:throw Error(`Unsupported input source type: ${e.type}`)}}_processCaptureInput(e,t){let n=this._captureState;if(!n)return;let{options:r}=n;if(!r.deviceTypes.includes(e.type))return;let i=null;if(D(t)){if(t.event?.repeat===!0)return;for(let n of Object.keys(t.delta))if(t.delta[n]===!0){i={type:`keyboard`,sourceType:`key`,sourceKey:n,deviceID:e.id};break}}else if(O(t)){for(let n of Object.keys(t.buttons))if(t.buttons[n].pressed){i={type:`mouse`,sourceType:`button`,sourceKey:n,deviceID:e.id};break}}else if(k(t)){for(let n of Object.keys(t.buttons))if(t.buttons[n].pressed){i={type:`gamepad`,sourceType:`button`,sourceKey:n,deviceID:e.id};break}if(!i){for(let n of Object.keys(t.axes))if(Math.abs(t.axes[n])>se){i={type:`gamepad`,sourceType:`axis`,sourceKey:n,deviceID:e.id};break}}}if(!i||r.sourceTypes&&!r.sourceTypes.includes(i.sourceType))return;let{resolve:a}=n;this._cleanupCapture(),a(i)}_cleanupCapture(){let e=this._captureState;if(!e)return;e.abortCleanup&&e.abortCleanup();let{snapshot:t}=e;this._captureState=null,this.deactivateContext(`__sage_capture__`),this._contexts.delete(`__sage_capture__`);for(let e of t)this.activateContext(e)}$handleInput(e,t){if(this._lastInputState.set(e.id,t),this._shouldUpdateActiveDevice(t)&&e.type!==this._lastActiveDeviceType){let t=this._lastActiveDeviceType;this._lastActiveDeviceType=e.type,this._eventBus.publish({type:`input:device-type:changed`,payload:{deviceType:e.type,previousDeviceType:t}})}if(this._captureState){this._processCaptureInput(e,t);return}let n=this._bindings.get(e.id);if(!(!n||n.length===0)&&!(this._activeContexts.size===0&&n.some(e=>e.context)))for(let e of n)this._isBindingContextActive(e)&&e.process(t,this._eventBus)}registerAction(e){if(this._log.debug(`Registering action "${e.name}"`),this._actions.has(e.name)){let t=`Action "${e.name}" already registered.`;throw this._log.error(t),Error(t)}this._actions.set(e.name,e),this._log.debug(`Action "${e.name}" registered successfully`)}getAction(e){this._log.trace(`Getting action "${e}"`);let t=this._actions.get(e)??null;return t||this._log.debug(`Action "${e}" not found`),t}get lastActiveDeviceType(){return this._lastActiveDeviceType}captureInput(e){if(this._captureState)throw Error(`captureInput() already in progress`);let t=[...this._activeContexts];return this.registerContext(`__sage_capture__`,!0),this.activateContext(`__sage_capture__`),new Promise((n,r)=>{let i={resolve:n,reject:r,options:e,snapshot:t};if(this._captureState=i,e.signal){if(e.signal.aborted){this._cleanupCapture(),r(e.signal.reason??Error(`captureInput() aborted`));return}let t=()=>{let t=e.signal?.reason??Error(`captureInput() aborted`);this._cleanupCapture(),r(t)};e.signal.addEventListener(`abort`,t,{once:!0}),i.abortCleanup=()=>{e.signal?.removeEventListener(`abort`,t)}}})}registerContext(e,t=!0){let n=this._getOrCreateContext(e,t);return n.exclusive!==t&&(n.exclusive=t,this._log.info(`Updated context "${e}" exclusivity: ${t}`)),n}activateContext(e){let t=this._getOrCreateContext(e).exclusive;this._log.debug(`Activating context "${e}" (exclusive: ${t})`);let n=this._activeContexts.has(e);if(t){let t=this._deactivateExclusiveContexts(e);t.length>0&&this._log.info(`Deactivated exclusive contexts: ${t.join(`, `)}`)}if(!n){this._activeContexts.add(e);for(let[t,n]of this._bindings.entries()){let r=this._lastInputState.get(t);for(let t of n)t.context===e&&t.resetEdgeState(r)}this._log.info(`Context "${e}" activated${t?` as exclusive`:``}`)}}deactivateContext(e){this._log.debug(`Deactivating context "${e}"`),this._activeContexts.has(e)?(this._activeContexts.delete(e),this._log.info(`Context "${e}" deactivated`)):this._log.debug(`Context "${e}" was not active`)}getActiveContexts(){return[...this._activeContexts]}isContextActive(e){return this._activeContexts.has(e)}getContext(e){return this._contexts.get(e)||null}$registerBinding(e){if(!T.includes(e.type))throw Error(`Invalid binding type: ${e.type}`);e.context&&!this._contexts.has(e.context)&&this.registerContext(e.context),this._bindings.has(e.deviceID)||this._bindings.set(e.deviceID,[]),this._bindings.get(e.deviceID)?.push(e),this._log.debug(`Registered ${e.type} binding for "${e.action.name}" in context "${e.context||null}"`)}registerBinding(e){let t=this._createBindingFromDefinition(e);t?this.$registerBinding(t):this._log.error(`Failed to create binding for action "${e.action}" with type "${e.type}"`)}unregisterBindings(e,t=null){this._log.debug(`Unregistering all bindings for action "${e}" in context "${t}"`);let n=0;for(let[r,i]of this._bindings.entries()){let a=i.filter(r=>{let i=r.context||null,a=r.action.name!==e||i!==t;return a||n++,a});a.length!==i.length&&this._bindings.set(r,a)}this._log.info(`Removed ${n} bindings for action "${e}" in context "${t}"`)}getBindingsForAction(e,t,n){let r=[];for(let i of this._bindings.values())for(let a of i){if(a.action.name!==e||t!==void 0&&a.deviceType!==t)continue;let i=a.context||null;n!==void 0&&i!==n||r.push(a)}return r}exportConfiguration(){this._log.debug(`Exporting binding configuration`);let e=[...this._actions.values()].map(e=>e.type===`analog`?{name:e.name,type:e.type,label:e.label,minValue:e.minValue??0,maxValue:e.maxValue??1}:e),t=[];for(let e of this._bindings.values())for(let n of e)t.push(n.toJSON());let n=[...this._contexts.values()].map(e=>({name:e.name,exclusive:e.exclusive,active:this._activeContexts.has(e.name)}));return this._log.info(`Configuration exported: ${e.length} actions, ${t.length} bindings, ${n.length} contexts`),{actions:e,bindings:t,contexts:n}}importConfiguration(e){this._log.debug(`Importing binding configuration`),this._bindings.clear(),this._actions.clear(),this._contexts.clear(),this._activeContexts.clear();for(let t of e.actions)this.registerAction(t);for(let t of e.contexts)this.registerContext(t.name,t.exclusive),t.active&&this.activateContext(t.name);for(let t of e.bindings)try{this.registerBinding(t)}catch(e){e instanceof Error&&this._log.error(`Failed to import binding for action "${t.action}": ${e.message}`)}this._log.info(`Configuration imported: ${e.actions.length} actions, ${e.bindings.length} bindings, ${e.contexts.length} contexts`)}async $teardown(){if(this._log.info(`Tearing down BindingManager`),this._captureState){let{reject:e}=this._captureState;this._cleanupCapture(),e(Error(`BindingManager torn down during captureInput()`))}this._inputUnsubscribe&&=(this._inputUnsubscribe(),null),this._bindings.clear(),this._lastInputState.clear(),this._actions.clear(),this._contexts.clear(),this._activeContexts.clear(),this._lastActiveDeviceType=null,this._log.info(`BindingManager torn down successfully`)}};function A(){return typeof window<`u`&&window.document!==void 0}function j(){return A()&&!!window.navigator.gpu}for(var le=class{_engine;_eventBus;_entityManager;_inputManager;_levelManager;_log;_boundRenderLoop;_boundResizeHandler;_frameCallbacks=[];_nextCallbackId=0;_paused=!1;started=!1;constructor(e,t,n,r,i,a){this._engine=e,this._eventBus=t,this._entityManager=n,this._inputManager=r,this._levelManager=i,this._log=a?.getLogger(`GameManager`)||new y(`GameManager`),this._boundRenderLoop=this._renderLoop.bind(this),this._boundResizeHandler=this._resizeHandler.bind(this),A()&&window.addEventListener(`resize`,this._boundResizeHandler)}get currentScene(){return this._levelManager.currentLevel?.scene??null}get isPaused(){return this._paused}registerFrameCallback(e){let t=this._nextCallbackId++;return this._frameCallbacks.push({id:t,callback:e}),this._log.debug(`Registered frame callback (id: ${t})`),()=>{let e=this._frameCallbacks.findIndex(e=>e.id===t);e!==-1&&(this._frameCallbacks.splice(e,1),this._log.debug(`Unregistered frame callback (id: ${t})`))}}_renderLoop(){let e=this._engine.getDeltaTime()/1e3;if(this._inputManager.pollGamepads(),!this._paused){this._entityManager.$frameUpdate(e);for(let{callback:t}of this._frameCallbacks)try{t(e)}catch(e){this._log.error(`Error in frame callback:`,e)}}this.currentScene&&this.currentScene.render()}_resizeHandler(){this.started&&this._engine.resize()}async start(){this._engine.runRenderLoop(this._boundRenderLoop),this.started=!0,this._log.info(`SkewedAspect Game Engine started successfully`)}async stop(){this.started=!1,this._engine.stopRenderLoop(),this._log.info(`SkewedAspect Game Engine stopped successfully`)}pause(e){if(this._paused){this._log.debug(`Game is already paused`);return}this._paused=!0,this._log.info(`Game paused`,e?`(reason: ${e})`:``),this._eventBus.publish({type:`game:paused`,payload:{reason:e}})}resume(e){if(!this._paused){this._log.debug(`Game is not paused`);return}this._paused=!1,this._log.info(`Game resumed`,e?`(reason: ${e})`:``),this._eventBus.publish({type:`game:resumed`,payload:{reason:e}})}async $teardown(){this._log.info(`Tearing down GameManager`),this.started&&await this.stop(),A()&&window.removeEventListener(`resize`,this._boundResizeHandler),this._frameCallbacks=[],this._log.info(`GameManager torn down successfully`)}},M=256,N=[];M--;)N[M]=(M+256).toString(16).substring(1);function ue(e){e||=16;var t=``,n=0;return function(){if(!t||n===256){for(t=``,n=(1+e)/2|0;n--;)t+=N[256*Math.random()|0];t=t.substring(n=0,e-2)}return t+N[n++]}}var P=ue(16),F=class{entity=null;$emit(e){if(!this.entity)throw Error(`Entity is not set for this behavior.`);e.senderID=this.entity.id,this.entity.eventBus.publish(e)}$emitStateChanged(e,t){if(!this.entity)throw Error(`Entity is not set for this behavior.`);this.$emit({type:`entity:state-changed`,payload:{entityId:this.entity.id,entityType:this.entity.type,state:e??this.entity.state,changes:t}})}$setEntity(e){this.entity=e}},I=class{id;type;name;node;children=[];parent=null;state;behaviors=[];eventBus;_gameEngine=null;_entityManager=null;_tags=new Set;subscriptions=new Map;constructor(e,t,n,r,i){this.id=P(),this.type=e,this.name=i,this.state=n,this.eventBus=t;for(let e of r)this.attachBehavior(new e)}get tags(){return this._tags}hasTag(e){return this._tags.has(e)}$addTag(e){this._tags.add(e)}$removeTag(e){return this._tags.delete(e)}getChildNode(e){if(this.node)return this.node.getChildren(t=>t.name===e,!1)[0]}findNode(e){if(!this.node)return;let t=this.node;for(let n of e.split(`/`))t=t?.getChildren(e=>e.name===n,!1)[0];return t}$attachToNode(e,t){this.node=e,this._gameEngine=t;for(let n of this.behaviors)n.onNodeAttached?.(e,t);for(let t of this.children)t.node&&(t.node.parent=e)}$detachFromNode(){this.unhighlight();for(let e of this.children)e.node&&(e.node.parent=null);for(let e of this.behaviors)e.onNodeDetached?.();this.node=void 0,this._gameEngine=null}highlight(e){let t=this._gameEngine?.managers?.levelManager?.currentLevel?.outlines;t&&t.highlightEntity(e,this)}unhighlight(e){let t=this._gameEngine?.managers?.levelManager?.currentLevel?.outlines;t&&(e?t.unhighlightEntity(e,this):t.unhighlightEntityAll(this))}$addChild(e){this.children.push(e),e.parent=this,this.node&&e.node&&(e.node.parent=this.node)}$removeChild(e){let t=this.children.indexOf(e);t!==-1&&(this.children.splice(t,1),e.parent=null,e.node&&(e.node.parent=null))}$setEntityManager(e){this._entityManager=e}async send(e,t,n){if(e===this.id)return this.$send(t,n,this.id);if(!this._entityManager)throw Error(`Entity is not registered with an EntityManager.`);return this._entityManager.send(e,t,n,this.id)}async request(e,t,n){if(e===this.id)return this.$request(t,n,this.id);if(!this._entityManager)throw Error(`Entity is not registered with an EntityManager.`);return this._entityManager.request(e,t,n,this.id)}async $send(e,t,n){await this.$processEvent({type:e,targetID:this.id,senderID:n,payload:t})}async $request(e,t,n){let r={type:e,targetID:this.id,senderID:n,payload:t},i;for(let e of this.behaviors)try{let t=await e.processRequest?.(r,this.state);if(t!==void 0)return{success:!0,value:t}}catch(e){i=e instanceof Error?e.message:String(e)}return{success:!1,error:i??`No handler for request "${e}" on entity "${this.name??this.id}"`}}$reset(e,t){this.state={...e},this._tags.clear();for(let e of t)this._tags.add(e);this.node&&this.$detachFromNode();for(let e of this.behaviors)e.onReset?.(this.state);for(let e of this.children)e.$reset({},[]);this.parent=null}async $processEvent(e){for(let t of this.behaviors)if(await t.processEvent(e,this.state))break}$update(e){for(let t of this.behaviors)t.update?.(e,this.state);for(let t of this.children)t.$update(e)}async $destroy(){for(let e of[...this.children])await e.$destroy();this.children.length=0;for(let e of this.behaviors)await e.destroy?.();for(let e of this.subscriptions.values())e.unsubscribe();this.behaviors=[],this.subscriptions.clear()}findBehaviorIndex(e){return this.behaviors.findIndex(t=>t.constructor===e)}hasBehavior(e){return this.findBehaviorIndex(e)!==-1}getBehavior(e){return this.behaviors.find(t=>t.constructor===e)}attachBehavior(e,t){if(this.hasBehavior(e.constructor))throw Error(`Behavior ${e.constructor.name} is already attached to this entity.`);for(let t of e.eventSubscriptions){let e=this.subscriptions.get(t);if(e)e.count++;else{let e=this.eventBus.subscribe(t,this.$processEvent.bind(this));this.subscriptions.set(t,{count:1,unsubscribe:e})}}if(!t)this.behaviors.push(e);else{if([t.before,t.after,t.at].filter(e=>e!==void 0).length>1)throw Error(`attachBehavior: specify only one of before, after, or at`);if(t.before){let n=this.findBehaviorIndex(t.before);if(n===-1)throw Error(`attachBehavior: no behavior of type ${t.before.name} found`);this.behaviors.splice(n,0,e)}else if(t.after){let n=this.findBehaviorIndex(t.after);if(n===-1)throw Error(`attachBehavior: no behavior of type ${t.after.name} found`);this.behaviors.splice(n+1,0,e)}else if(t.at!==void 0){let n=Math.max(0,Math.min(t.at,this.behaviors.length));this.behaviors.splice(n,0,e)}else this.behaviors.push(e)}e.$setEntity(this),this.node&&this._gameEngine&&e.onNodeAttached?.(this.node,this._gameEngine)}detachBehavior(e){let t=this.findBehaviorIndex(e);if(t===-1)return!1;let n=this.behaviors[t];this.node&&n.onNodeDetached?.();for(let e of n.eventSubscriptions){let t=this.subscriptions.get(e);t&&(t.count--,t.count<=0&&(t.unsubscribe(),this.subscriptions.delete(e)))}return this.behaviors.splice(t,1),n.$setEntity(null),!0}},de=class{eventBus;entities=new Map;entityDefinitions=new Map;_gameEngine=null;bindingManager;_log;_entitiesByName=new Map;_entitiesByType=new Map;_entitiesByTag=new Map;_entitiesByNode=new Map;_pools=new Map;constructor(e,t,n){this.eventBus=e,this.bindingManager=n,this._log=t?.getLogger(`EntityManager`)||new y(`EntityManager`),this._log.info(`EntityManager initialized`)}$setGameEngine(e){this._gameEngine=e}get _engine(){if(!this._gameEngine)throw Error(`EntityManager: gameEngine not set. Call $setGameEngine() first.`);return this._gameEngine}_getOrCreateSet(e,t){let n=e.get(t);return n||(n=new Set,e.set(t,n)),n}_indexEntity(e){this._getOrCreateSet(this._entitiesByType,e.type).add(e),e.name&&this._getOrCreateSet(this._entitiesByName,e.name).add(e);for(let t of e.tags)this._getOrCreateSet(this._entitiesByTag,t).add(e);e.node&&this._entitiesByNode.set(e.node,e)}_unindexEntity(e){let t=this._entitiesByType.get(e.type);if(t&&(t.delete(e),t.size===0&&this._entitiesByType.delete(e.type)),e.name){let t=this._entitiesByName.get(e.name);t&&(t.delete(e),t.size===0&&this._entitiesByName.delete(e.name))}for(let t of e.tags){let n=this._entitiesByTag.get(t);n&&(n.delete(e),n.size===0&&this._entitiesByTag.delete(t))}e.node&&this._entitiesByNode.delete(e.node)}_resolveInheritance(e){let t=e.extends;if(!t)return e;let n=this.entityDefinitions.get(t);if(!n){let e=`Cannot extend "${t}": base entity definition is not registered.`;throw this._log.error(e),Error(e)}let r;return(n.tags||e.tags)&&(r=[...new Set([...n.tags??[],...e.tags??[]])]),{type:e.type,name:e.name??n.name,mesh:e.mesh??n.mesh,defaultState:{...n.defaultState,...e.defaultState},behaviors:e.behaviors??n.behaviors,actions:e.actions??n.actions,tags:r,poolable:e.poolable??n.poolable,poolSize:e.poolSize??n.poolSize,children:e.children??n.children,onBeforeCreate:e.onBeforeCreate??n.onBeforeCreate,onCreate:e.onCreate??n.onCreate,onBeforeDestroy:e.onBeforeDestroy??n.onBeforeDestroy,onDestroy:e.onDestroy??n.onDestroy}}_areActionsCompatible(e,t){return!(e.type!==t.type||e.type===`analog`&&t.type===`analog`&&(t.minValue!==void 0&&e.minValue!==t.minValue||t.maxValue!==void 0&&e.maxValue!==t.maxValue))}_registerEntityActions(e){if(e.actions)for(let t of e.actions)try{let n=this.bindingManager.getAction(t.name);n?this._areActionsCompatible(n,t)?this._log.trace(`Action "${t.name}" already registered with compatible options, skipping registration`):this._log.warn(`Action "${t.name}" already registered with different options. Entity "${e.type}" requires: ${JSON.stringify(t)}, but found: ${JSON.stringify(n)}`):(this._log.debug(`Registering action "${t.name}" from entity type "${e.type}"`),this.bindingManager.registerAction(t))}catch(e){this._log.debug(`Failed to register action "${t.name}": ${e instanceof Error?e.message:String(e)}`)}}$frameUpdate(e){this._log.trace(`Updating ${this.entities.size} entities with dt=${e}`);for(let t of this.entities.values())t.parent||t.$update(e)}registerEntityDefinition(e){this._log.debug(`Registering entity definition: ${e.type}`);let t=e.extends?this._resolveInheritance(e):e;this._registerEntityActions(t),this.entityDefinitions.set(t.type,t),t.poolSize&&t.poolSize>0&&this.prewarm(t.type,t.poolSize)}getDefinition(e){return this.entityDefinitions.get(e)}async createEntity(e,t={}){this._log.debug(`Creating entity of type: ${e}`);let n=this.entityDefinitions.get(e);if(!n){let t=`Entity type ${e} is not registered.`;throw this._log.error(t),Error(t)}if(n.poolable){let r=this._pools.get(e);if(r&&r.length>0){this._log.trace(`Recycling entity from pool for type: ${e}`);let i=r.pop();if(i.$reset(n.defaultState,n.tags??[]),i.$setEntityManager(this),t.initialState&&(i.state={...i.state,...t.initialState}),t.tags)for(let e of t.tags)i.$addTag(e);return t.node&&i.$attachToNode(t.node,this._engine),this.entities.set(i.id,i),this._indexEntity(i),this._log.debug(`Recycled entity ${i.id} from pool`),i}}this._log.trace(`Using entity definition with ${n.behaviors?.length||0} behaviors`);let r={...n.defaultState,...t.initialState};if(n.onBeforeCreate){this._log.trace(`Calling onBeforeCreate hook for entity type: ${e}`);let t=await n.onBeforeCreate(r);t!==void 0&&(r=t)}let i=t.name??n.name,a=new I(n.type,this.eventBus,r,n.behaviors,i);if(a.$setEntityManager(this),t.node&&a.$attachToNode(t.node,this._engine),n.tags)for(let e of n.tags)a.$addTag(e);if(t.tags)for(let e of t.tags)a.$addTag(e);if(this.entities.set(a.id,a),this._indexEntity(a),this._log.debug(`Entity created with ID: ${a.id}${i?` (name: ${i})`:``}`),n.onCreate){this._log.trace(`Calling onCreate hook for entity type: ${e}`);let t=await n.onCreate(a.state);t!==void 0&&(a.state=t)}if(n.children)for(let e of n.children){let t={...e.initialState};e.position&&(t._position=e.position),e.rotation&&(t._rotation=e.rotation);let n=await this.createEntity(e.type,{name:e.name,initialState:t});a.$addChild(n)}return a}async destroyEntity(e){this._log.debug(`Destroying entity: ${e}`);let t=this.entities.get(e);if(t){let n=this.entityDefinitions.get(t.type);if(n?.poolable){this._log.trace(`Pooling entity ${e} instead of destroying`);for(let e of[...t.children])await this.destroyEntity(e.id);t.parent&&t.parent.$removeChild(t),this._unindexEntity(t),t.$reset(n.defaultState,n.tags??[]);let r=this._pools.get(t.type);r||(r=[],this._pools.set(t.type,r)),r.push(t),this.entities.delete(e),this._log.debug(`Entity ${e} pooled`);return}for(let e of[...t.children])await this.destroyEntity(e.id);if(t.parent&&t.parent.$removeChild(t),n?.onBeforeDestroy){this._log.trace(`Calling onBeforeDestroy hook for entity: ${e}`);let r=await n.onBeforeDestroy(t.state);r!==void 0&&(t.state=r)}this._unindexEntity(t),await t.$destroy(),n?.onDestroy&&(this._log.trace(`Calling onDestroy hook for entity: ${e}`),await n.onDestroy(t.state)),this.entities.delete(e),this._log.debug(`Entity ${e} destroyed`)}else this._log.warn(`Attempted to destroy non-existent entity: ${e}`)}getEntity(e){return this._log.trace(`Getting entity: ${e}`),this.entities.get(e)??null}addEntity(e){this._log.debug(`Adding existing entity: ${e.id} (type: ${e.type})`),e.$setEntityManager(this),this.entities.set(e.id,e),this._indexEntity(e)}removeEntity(e){this._log.debug(`Removing entity: ${e}`);let t=this.entities.get(e);t?(this._unindexEntity(t),this.entities.delete(e),this._log.debug(`Entity ${e} removed`)):this._log.warn(`Attempted to remove non-existent entity: ${e}`)}addChild(e,t){let n=this.entities.get(e),r=this.entities.get(t);return n?r?(n.$addChild(r),this._log.trace(`Added child ${t} to parent ${e}`),!0):(this._log.warn(`addChild: child entity "${t}" not found`),!1):(this._log.warn(`addChild: parent entity "${e}" not found`),!1)}removeChild(e,t){let n=this.entities.get(e),r=this.entities.get(t);return n?r?(n.$removeChild(r),this._log.trace(`Removed child ${t} from parent ${e}`),!0):(this._log.warn(`removeChild: child entity "${t}" not found`),!1):(this._log.warn(`removeChild: parent entity "${e}" not found`),!1)}getByName(e){let t=this._entitiesByName.get(e);if(t&&t.size>0)return t.values().next().value}getEntitiesByName(e){let t=this._entitiesByName.get(e);return t?Array.from(t):[]}getByType(e){let t=this._entitiesByType.get(e);return t?Array.from(t):[]}getByTag(e){let t=this._entitiesByTag.get(e);return t?Array.from(t):[]}getByTags(e,t=`all`){if(e.length===0)return[];if(t===`any`){let t=new Set;for(let n of e){let e=this._entitiesByTag.get(n);if(e)for(let n of e)t.add(n)}return Array.from(t)}else{let t=e.map(e=>this._entitiesByTag.get(e)).filter(e=>e!==void 0);if(t.length!==e.length)return[];t.sort((e,t)=>e.size-t.size);let[n,...r]=t;return Array.from(n).filter(e=>r.every(t=>t.has(e)))}}getAllEntities(){return this.entities.values()}get entityCount(){return this.entities.size}async send(e,t,n,r){let i=this.entities.get(e)??this.getByName(e);if(!i){this._log.warn(`send: entity "${e}" not found`);return}await i.$send(t,n,r)}async request(e,t,n,r){let i=this.entities.get(e)??this.getByName(e);return i?i.$request(t,n,r):{success:!1,error:`Entity "${e}" not found`}}addTag(e,t){let n=typeof e==`string`?this.entities.get(e):e;return n?n.hasTag(t)?!1:(n.$addTag(t),this._getOrCreateSet(this._entitiesByTag,t).add(n),this._log.trace(`Added tag "${t}" to entity ${n.id}`),!0):(this._log.warn(`addTag: entity not found`),!1)}removeTag(e,t){let n=typeof e==`string`?this.entities.get(e):e;if(!n)return this._log.warn(`removeTag: entity not found`),!1;if(!n.$removeTag(t))return!1;let r=this._entitiesByTag.get(t);return r&&(r.delete(n),r.size===0&&this._entitiesByTag.delete(t)),this._log.trace(`Removed tag "${t}" from entity ${n.id}`),!0}getByNode(e){return this._entitiesByNode.get(e)}attachToNode(e,t){let n=typeof e==`string`?this.entities.get(e):e;return n?this._entitiesByNode.has(t)?(this._log.warn(`attachToNode: node already has an attached entity`),!1):(n.node&&this._entitiesByNode.delete(n.node),n.$attachToNode(t,this._engine),this._entitiesByNode.set(t,n),this._log.trace(`Attached entity ${n.id} to node "${t.name}"`),!0):(this._log.warn(`attachToNode: entity not found`),!1)}detachFromNode(e){let t=typeof e==`string`?this.entities.get(e):e;if(!t)return this._log.warn(`detachFromNode: entity not found`),!1;if(!t.node)return!1;this._entitiesByNode.delete(t.node);let n=t.node.name;return t.$detachFromNode(),this._log.trace(`Detached entity ${t.id} from node "${n}"`),!0}async prewarm(e,t){this._log.debug(`Prewarming pool for "${e}" with ${t} entities`);for(let n=0;n<t;n++){let t=await this.createEntity(e);await this.destroyEntity(t.id)}}async drainPool(e){let t=this._pools.get(e);if(t){this._log.debug(`Draining pool for "${e}" (${t.length} entities)`);for(let e of t)await e.$destroy();t.length=0,this._pools.delete(e)}}async $teardown(){this._log.info(`Tearing down EntityManager with ${this.entities.size} entities`);let e=[...this.entities.keys()];for(let t of e)try{await this.destroyEntity(t)}catch(e){this._log.error(`Error destroying entity ${t}: ${e}`)}for(let e of[...this._pools.keys()])try{await this.drainPool(e)}catch(t){this._log.error(`Error draining pool for "${e}": ${t}`)}this.entityDefinitions.clear(),this._entitiesByName.clear(),this._entitiesByType.clear(),this._entitiesByTag.clear(),this._entitiesByNode.clear(),this._log.info(`EntityManager torn down successfully`)}},fe={hable:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,reinhard:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,hejidawson:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,photographic:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,aces:t.ImageProcessingConfiguration.TONEMAPPING_ACES};function pe(e,n){if(!e.activeCamera)return;n.volumetric&&console.warn(`[SAGE] Volumetric lighting requires renderer: "frameGraph". Ignored in pipeline mode.`);let r=new t.DefaultRenderingPipeline(`sage-default`,!0,e,e.cameras);if(n.bloom&&(r.bloomEnabled=!0,n.bloom.weight!==void 0&&(r.bloomWeight=n.bloom.weight),n.bloom.threshold!==void 0&&(r.bloomThreshold=n.bloom.threshold),n.bloom.scale!==void 0&&(r.bloomScale=n.bloom.scale),n.bloom.kernel!==void 0&&(r.bloomKernel=n.bloom.kernel)),n.tonemap&&(r.imageProcessingEnabled=!0,r.imageProcessing.toneMappingEnabled=!0,n.tonemap.operator&&(r.imageProcessing.toneMappingType=fe[n.tonemap.operator]??t.ImageProcessingConfiguration.TONEMAPPING_STANDARD)),n.grain&&(r.grainEnabled=!0,n.grain.intensity!==void 0&&(r.grain.intensity=n.grain.intensity),n.grain.animated!==void 0&&(r.grain.animated=n.grain.animated)),n.vignette&&(r.imageProcessingEnabled=!0,r.imageProcessing.vignetteEnabled=!0,n.vignette.weight!==void 0&&(r.imageProcessing.vignetteWeight=n.vignette.weight),n.vignette.stretch!==void 0&&(r.imageProcessing.vignetteStretch=n.vignette.stretch),n.vignette.color&&(r.imageProcessing.vignetteColor=new t.Color4(n.vignette.color.r,n.vignette.color.g,n.vignette.color.b,1))),n.sharpen&&(r.sharpenEnabled=!0,n.sharpen.edge!==void 0&&(r.sharpen.edgeAmount=n.sharpen.edge),n.sharpen.color!==void 0&&(r.sharpen.colorAmount=n.sharpen.color)),n.chromaticAberration&&(r.chromaticAberrationEnabled=!0,n.chromaticAberration.amount!==void 0&&(r.chromaticAberration.aberrationAmount=n.chromaticAberration.amount)),n.ssao){let r=new t.SSAO2RenderingPipeline(`sage-ssao`,e,{ssaoRatio:.5,blurRatio:1});n.ssao.radius!==void 0&&(r.radius=n.ssao.radius),n.ssao.samples!==void 0&&(r.samples=n.ssao.samples),n.ssao.totalStrength!==void 0&&(r.totalStrength=n.ssao.totalStrength)}}var me=p({applyFrameGraphPostProcessing:()=>he});async function he(e,n){let r=e.activeCamera;if(!r)return;let i=e.getEngine(),a=new t.FrameGraph(e),o=a.textureManager.createRenderTargetTexture(`sage-fg-color`,{size:{width:100,height:100},sizeIsPercentage:!0,options:{formats:[t.Constants.TEXTUREFORMAT_RGBA],samples:4}}),s=new t.FrameGraphClearTextureTask(`sage-clear`,a);s.targetTexture=o,a.addTask(s);let c=new t.FrameGraphObjectRendererTask(`sage-render`,a,e);c.targetTexture=s.outputTexture,c.depthTexture=s.outputDepthTexture,c.camera=r,c.objectList={meshes:e.meshes,particleSystems:e.particleSystems},c.isMainObjectRenderer=!0,a.addTask(c);let l=c.outputTexture;if(n.ssao){let i=new t.FrameGraphGeometryRendererTask(`sage-geometry`,a,e);a.addTask(i);let o=new t.FrameGraphSSAO2RenderingPipelineTask(`sage-ssao`,a,.5,1);o.sourceTexture=l,o.depthTexture=i.geometryViewDepthTexture,o.normalTexture=i.geometryViewNormalTexture,o.camera=r,a.addTask(o),n.ssao.radius!==void 0&&(o.ssao.radius=n.ssao.radius),n.ssao.samples!==void 0&&(o.ssao.samples=n.ssao.samples),n.ssao.totalStrength!==void 0&&(o.ssao.totalStrength=n.ssao.totalStrength),l=o.outputTexture}if(n.bloom){let e=new t.FrameGraphBloomTask(`sage-bloom`,a,n.bloom.weight??.5,n.bloom.kernel??128,n.bloom.threshold??.1,!0,n.bloom.scale??.5);e.sourceTexture=l,a.addTask(e),l=e.outputTexture}if(n.grain){let e=new t.FrameGraphGrainTask(`sage-grain`,a);e.sourceTexture=l,a.addTask(e),n.grain.intensity!==void 0&&(e.postProcess.intensity=n.grain.intensity),n.grain.animated!==void 0&&(e.postProcess.animated=n.grain.animated),l=e.outputTexture}if(n.sharpen){let e=new t.FrameGraphSharpenTask(`sage-sharpen`,a);e.sourceTexture=l,a.addTask(e),n.sharpen.edge!==void 0&&(e.postProcess.edgeAmount=n.sharpen.edge),n.sharpen.color!==void 0&&(e.postProcess.colorAmount=n.sharpen.color),l=e.outputTexture}if(n.chromaticAberration){let e=new t.FrameGraphChromaticAberrationTask(`sage-chromab`,a);e.sourceTexture=l,a.addTask(e),n.chromaticAberration.amount!==void 0&&(e.postProcess.aberrationAmount=n.chromaticAberration.amount),l=e.outputTexture}if(n.tonemap||n.vignette){let e=new t.FrameGraphImageProcessingTask(`sage-imgproc`,a);if(e.sourceTexture=l,a.addTask(e),n.tonemap&&(e.postProcess.toneMappingEnabled=!0,n.tonemap.operator&&(e.postProcess.toneMappingType=L[n.tonemap.operator]??t.ImageProcessingConfiguration.TONEMAPPING_STANDARD)),n.vignette&&(e.postProcess.vignetteEnabled=!0,n.vignette.weight!==void 0&&(e.postProcess.vignetteWeight=n.vignette.weight),n.vignette.stretch!==void 0&&(e.postProcess.vignetteStretch=n.vignette.stretch),n.vignette.color)){let t=n.vignette.color;e.postProcess.vignetteColor.set(t.r,t.g,t.b,1)}l=e.outputTexture}n.volumetric&&console.warn(`[SAGE] Volumetric lighting config is recognized but automatic light detection is not yet supported. Use scene.frameGraph to add FrameGraphVolumetricLightingTask manually.`);let u=new t.FrameGraphCopyToBackbufferColorTask(`sage-output`,a);u.sourceTexture=l,a.addTask(u),e.cameraToUseForPointers=r,e.frameGraph=a,i.onResizeObservable.add(async()=>{await a.buildAsync()}),await a.buildAsync()}var L,ge=f((()=>{L={hable:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,reinhard:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,hejidawson:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,photographic:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,aces:t.ImageProcessingConfiguration.TONEMAPPING_ACES}}));async function R(e,t){if(t.renderer===`frameGraph`){let{applyFrameGraphPostProcessing:n}=await Promise.resolve().then(()=>(ge(),me));await n(e,t)}else pe(e,t)}var z=class{name;_log;_context;_scene=null;clusteredLights=null;outlines=null;constructor(e,t){this.name=e.name,this._context=t,this._log=t.logger?.getLogger(`Level:${e.name}`)??new y(`Level:${e.name}`,`info`)}get scene(){return this._scene}get isLoaded(){return this._scene!==null}get gameEngine(){return this._context.gameEngine}get propertyHandlers(){return this._context.propertyHandlers}$emitProgress(e,t){this.gameEngine.eventBus.publish({type:`level:progress`,payload:{levelName:this.name,progress:e,message:t}})}async load(){if(this._scene)return this._log.warn(`Level ${this.name} is already loaded`),this._scene;this._log.info(`Loading level: ${this.name}`),this._log.time(`level-${this.name}-load`);try{return this.$emitProgress(0,`Starting level load...`),this._scene=await this.buildScene(),this.$emitProgress(100,`Level loaded successfully`),this.gameEngine.eventBus.publish({type:`level:complete`,payload:{levelName:this.name,message:`Level loaded successfully`,level:this}}),this._log.timeEnd(`level-${this.name}-load`),this._log.info(`Level ${this.name} loaded successfully`),this._scene}catch(e){throw this._log.error(`Failed to load level ${this.name}:`,e),this.gameEngine.eventBus.publish({type:`level:error`,payload:{levelName:this.name,message:`Failed to load level`,error:e}}),e}}async $dispose(){if(this._scene){this._log.info(`Disposing level: ${this.name}`);let e=this._context.gameEngine?.managers?.entityManager;if(e?.getAllEntities)for(let t of e.getAllEntities())t.node&&t.node.getScene()===this._scene&&e.detachFromNode(t);this.outlines&&=(this.outlines.dispose(),null),this.clusteredLights&&=(this.clusteredLights.dispose(),null),this._scene.dispose(),this._scene=null}}},B=class{_scene;_layers=new Map;constructor(e,t){if(this._scene=e,t)for(let[e,n]of Object.entries(t))this.create(e,n)}get(e){return this._layers.get(e)?.layer}create(e,n){let r=this._layers.get(e);if(r)return r.layer;let i=new t.SelectionOutlineLayer(`sage-outline-${e}`,this._scene);return n?.color&&(i.outlineColor=new t.Color3(n.color.r,n.color.g,n.color.b)),n?.thickness!==void 0&&(i.outlineThickness=n.thickness),n?.occlusionStrength!==void 0&&(i.occlusionStrength=n.occlusionStrength),this._layers.set(e,{layer:i,entities:new Set}),i}highlightEntity(e,t){let n=this._layers.get(e);n&&(n.entities.add(t),this._rebuildSelection(n))}unhighlightEntity(e,t){let n=this._layers.get(e);n&&(n.entities.delete(t),this._rebuildSelection(n))}unhighlightEntityAll(e){for(let t of this._layers.values())t.entities.delete(e)&&this._rebuildSelection(t)}clear(e){let t=this._layers.get(e);t&&(t.entities.clear(),t.layer.clearSelection())}dispose(){for(let e of this._layers.values())e.entities.clear(),e.layer.dispose();this._layers.clear()}_rebuildSelection(e){e.layer.clearSelection();for(let t of e.entities)if(t.node){let n=t.node.getChildMeshes();n.length>0&&e.layer.addSelection(n)}}},V=class extends z{_config;_spawnPoints=[];_entityNodes=[];_spawnedEntities=[];_levelSounds=new Map;_playingSoundsBeforeDeactivate=new Set;constructor(e,t){super(e,t),this._config=e}get config(){return this._config}get spawnedEntities(){return this._spawnedEntities}async buildScene(){let{sceneEngine:e}=this.gameEngine.engines,{assetManager:t}=this.gameEngine.managers;this._config.preload&&this._config.preload.length>0&&(this.$emitProgress(2,`Preloading assets...`),await t.preload(this._config.preload));let n=e.createScene();if(this._scene=n,this._config.physics&&(this.$emitProgress(5,`Initializing physics...`),await this._enablePhysics(n)),this._config.scene&&(this.$emitProgress(10,`Loading scene file...`),await this._loadSceneFile(n)),this._config.environment&&(this.$emitProgress(15,`Setting up environment...`),this._processEnvironment(n)),this._processCameras(n),this._processLights(n),this._setupClustering(n),this._config.postProcessing){let e=this._config.postProcessing.renderer??`pipeline`;this._log.info(`Applying post-processing with ${e} renderer`),await R(n,this._config.postProcessing)}return this.outlines=new B(n,this._config.outlines),this.$emitProgress(50,`Processing scene properties...`),await this._processNodeProperties(n),this.$emitProgress(70,`Spawning entities...`),await this._processSpawnPoints(),this.$emitProgress(85,`Configuring entities...`),await this._processEntityNodes(),this._config.sounds&&(this.$emitProgress(95,`Loading sounds...`),await this._processLevelSounds()),n}async _loadSceneFile(e){let t=this._getScenePath();if(t){this._log.debug(`Loading scene file: ${t}`),this._isRightHanded()&&(e.useRightHandedSystem=!0,this._log.debug(`Scene set to right-handed coordinate system`));try{await this.gameEngine.engines.sceneEngine.importMeshes([],t,e),this._log.debug(`Scene file loaded successfully`)}catch(e){throw this._log.error(`Failed to load scene file: ${t}`,e),e}}}_getScenePath(){if(this._config.scene)return typeof this._config.scene==`string`?this._config.scene:this._config.scene.path}_isRightHanded(){return!this._config.scene||typeof this._config.scene==`string`?!1:this._config.scene.rightHanded===!0}_processEnvironment(e){let n=this._config.environment;if(n){if(n.ibl&&(e.environmentTexture=this._createEnvironmentTexture(n.ibl,n.iblResolution??256,e),this._log.debug(`IBL set from: ${n.ibl}`)),n.skybox){let r=this._getFileExtension(n.skybox);if(r===`.hdr`||r===`.env`){if(!n.ibl){let t=n.iblResolution??256;e.environmentTexture=this._createEnvironmentTexture(n.skybox,t,e)}e.environmentTexture&&e.createDefaultSkybox(e.environmentTexture,!0,n.skyboxSize??1e3,0,!1)}else{let r=n.skyboxSize??1e3,i=t.MeshBuilder.CreateSphere(`skybox`,{diameter:r,segments:32},e),a=new t.StandardMaterial(`skybox-mat`,e);a.backFaceCulling=!1,a.disableLighting=!0,a.emissiveTexture=new t.Texture(n.skybox,e),i.material=a,i.infiniteDistance=!0}this._log.debug(`Skybox set from: ${n.skybox}`)}n.rotation!==void 0&&e.environmentTexture&&(e.environmentTexture.rotationY=n.rotation)}}_createEnvironmentTexture(e,n,r){return this._getFileExtension(e)===`.env`?t.CubeTexture.CreateFromPrefilteredData(e,r):new t.HDRCubeTexture(e,r,n)}_getFileExtension(e){let t=e.lastIndexOf(`.`);return t>=0?e.slice(t).toLowerCase():``}_processCameras(e){let t=this._config.cameras,n=this.gameEngine.canvas;if(!t){e.cameras.length>0&&(e.activeCamera=e.cameras[0],n&&e.activeCamera.attachControl(n,!0),this._log.debug(`Activated imported camera: "${e.activeCamera.name}"`));return}let r=null;for(let[n,i]of Object.entries(t)){let t=e.getCameraByName(n);if(t)this._applyCameraConfig(t,i),this._log.debug(`Applied overrides to camera: "${n}"`),(!r||i.active)&&(r=t);else if(i.type){let t=this._createCamera(n,i,e);this._log.debug(`Created ${i.type} camera: "${n}"`),(!r||i.active)&&(r=t)}else this._log.warn(`Camera "${n}" not found in scene and no type specified — skipping`)}if(r){e.activeCamera=r;let i=t[r.name];n&&i?.attachControl!==!1&&r.attachControl(n,!0)}}_createCamera(e,n,i){let a=n.position?this._toVector3(n.position):t.Vector3.Zero(),o;switch(n.type){case`arcRotate`:{let r=n.target?this._toVector3(n.target):t.Vector3.Zero();o=new t.ArcRotateCamera(e,n.alpha??Math.PI/2,n.beta??Math.PI/3,n.radius??10,r,i);break}case`universal`:o=new t.UniversalCamera(e,a,i);break;case`geospatial`:{if(!n.planetRadius)return this._log.warn(`Geospatial camera "${e}" requires planetRadius -- skipping`),new t.FreeCamera(e,t.Vector3.Zero(),i);let a=new r.GeospatialCamera(e,i,{planetRadius:n.planetRadius});n.center&&(a.center=this._toVector3(n.center)),n.yaw!==void 0&&(a.yaw=n.yaw),n.pitch!==void 0&&(a.pitch=n.pitch),n.radius!==void 0&&(a.radius=n.radius),n.checkCollisions!==void 0&&(a.checkCollisions=n.checkCollisions),n.radiusMin!==void 0&&(a.limits.radiusMin=n.radiusMin),n.radiusMax!==void 0&&(a.limits.radiusMax=n.radiusMax),n.pitchMin!==void 0&&(a.limits.pitchMin=n.pitchMin),n.pitchMax!==void 0&&(a.limits.pitchMax=n.pitchMax),n.yawMin!==void 0&&(a.limits.yawMin=n.yawMin),n.yawMax!==void 0&&(a.limits.yawMax=n.yawMax),o=a;break}default:o=new t.FreeCamera(e,a,i);break}return this._applyCameraConfig(o,n),o}_applyCameraConfig(e,n){n.fov!==void 0&&(e.fov=n.fov),n.position&&(e.position=this._toVector3(n.position)),n.minZ!==void 0&&(e.minZ=n.minZ),n.maxZ!==void 0&&(e.maxZ=n.maxZ),e instanceof t.FreeCamera&&(n.speed!==void 0&&(e.speed=n.speed),n.rotation&&(e.rotation=this._toVector3(n.rotation))),e instanceof t.ArcRotateCamera&&(n.target&&e.setTarget(this._toVector3(n.target)),n.lowerRadiusLimit!==void 0&&(e.lowerRadiusLimit=n.lowerRadiusLimit),n.upperRadiusLimit!==void 0&&(e.upperRadiusLimit=n.upperRadiusLimit),n.lowerBetaLimit!==void 0&&(e.lowerBetaLimit=n.lowerBetaLimit),n.upperBetaLimit!==void 0&&(e.upperBetaLimit=n.upperBetaLimit),n.wheelPrecision!==void 0&&(e.wheelPrecision=n.wheelPrecision)),e instanceof r.GeospatialCamera&&(n.yaw!==void 0&&(e.yaw=n.yaw),n.pitch!==void 0&&(e.pitch=n.pitch),n.radius!==void 0&&(e.radius=n.radius),n.center&&(e.center=this._toVector3(n.center)),n.checkCollisions!==void 0&&(e.checkCollisions=n.checkCollisions),n.radiusMin!==void 0&&(e.limits.radiusMin=n.radiusMin),n.radiusMax!==void 0&&(e.limits.radiusMax=n.radiusMax),n.pitchMin!==void 0&&(e.limits.pitchMin=n.pitchMin),n.pitchMax!==void 0&&(e.limits.pitchMax=n.pitchMax))}_processLights(e){let t=this._config.lights;if(t)for(let[n,r]of Object.entries(t)){let t=e.getLightByName(n);t?(this._applyLightConfig(t,r),this._log.debug(`Applied overrides to light: "${n}"`)):r.type?(this._createLight(n,r,e),this._log.debug(`Created ${r.type} light: "${n}"`)):this._log.warn(`Light "${n}" not found in scene and no type specified — skipping`)}}_createLight(e,n,r){let i=n.direction?this._toVector3(n.direction):new t.Vector3(0,-1,0),a=n.position?this._toVector3(n.position):t.Vector3.Zero();switch(n.type){case`hemispheric`:{let a=new t.HemisphericLight(e,i,r);n.groundColor&&(a.groundColor=this._toColor3(n.groundColor)),this._applyLightConfig(a,n);break}case`directional`:{let a=new t.DirectionalLight(e,i,r);this._applyLightConfig(a,n);break}case`point`:{let i=new t.PointLight(e,a,r);this._applyLightConfig(i,n);break}case`spot`:{let o=new t.SpotLight(e,a,i,n.angle??Math.PI/3,n.exponent??2,r);this._applyLightConfig(o,n);break}case`rectarea`:{let o=new t.RectAreaLight(e,a,n.width??1,n.height??1,r);if(this._applyLightConfig(o,n),n.direction){let n=new t.TransformNode(`${e}_pivot`,r);n.position=o.position.clone(),o.position=t.Vector3.Zero(),o.parent=n,n.lookAt(n.position.subtract(i))}break}}}_applyLightConfig(e,t){t.intensity!==void 0&&(e.intensity=t.intensity),t.diffuse&&(e.diffuse=this._toColor3(t.diffuse)),t.specular&&(e.specular=this._toColor3(t.specular))}_setupClustering(e){let n=this._config.clustering;if(!n?.enabled)return;let r=e.lights.filter(e=>i.ClusteredLightContainer.IsLightSupported(e));if(r.length===0){this._log.debug(`Clustering enabled but no eligible lights found`);return}for(let t of r)e.removeLight(t);let a=new i.ClusteredLightContainer(`sage-clustered`,r,e);n.horizontalTiles!==void 0&&(a.horizontalTiles=n.horizontalTiles),n.verticalTiles!==void 0&&(a.verticalTiles=n.verticalTiles),n.depthSlices!==void 0&&(a.depthSlices=n.depthSlices),n.maxRange!==void 0&&(a.maxRange=n.maxRange),this.clusteredLights=a,this._log.debug(`Clustered lighting enabled: ${r.length} lights, ${a.horizontalTiles}x${a.verticalTiles} tiles, ${a.depthSlices} depth slices`);for(let n of e.materials)n instanceof t.PBRMaterial&&(n.useGLTFLightFalloff=!0)}_toVector3(e){return new t.Vector3(e.x,e.y,e.z)}_toColor3(e){return new t.Color3(e.r,e.g,e.b)}async _enablePhysics(e){let n=this._config.physics,r=new t.Vector3(0,-9.81,0);typeof n==`object`&&n.gravity&&(r=new t.Vector3(n.gravity.x,n.gravity.y,n.gravity.z));let i=this.gameEngine.largeWorldRendering?1e5:void 0;this._log.debug(`Enabling physics with gravity: ${r.toString()}`),await this.gameEngine.engines.sceneEngine.enablePhysics(e,r,i)}async _processNodeProperties(e){let t=e.transformNodes.concat(e.meshes);for(let e of t){let t=this._getNormalizedMetadata(e);t&&Object.keys(t).length>0&&(e.metadata={...e.metadata,...t},await this._processNodeMetadata(e))}this._log.debug(`Processed ${t.length} nodes, found ${this._spawnPoints.length} spawn points and ${this._entityNodes.length} entity nodes`)}_getNormalizedMetadata(e){if(!e.metadata)return null;let t=e.metadata?.gltf?.extras;return t?{...t}:e.metadata}async _processNodeMetadata(e){`spawn`in e.metadata&&this._spawnPoints.push({name:e.metadata.spawn,position:e.position.clone(),rotation:e.rotation.clone(),scaling:e.scaling.clone(),node:e}),`entity`in e.metadata&&this._entityNodes.push({type:e.metadata.entity,node:e});for(let[t,n]of this.propertyHandlers)if(t in e.metadata)try{await n(e,e.metadata[t],this,this.gameEngine)}catch(n){this._log.error(`Error in property handler '${t}' for node '${e.name}':`,n)}}async _processSpawnPoints(){for(let e of this._spawnPoints){let t=this._config.spawns?.[e.name];t?await this._processSpawnPoint(e,t):this._log.warn(`No spawn definition found for '${e.name}', skipping`)}}async _processSpawnPoint(e,t){try{let n=await this._spawnEntity(t,e);this._spawnedEntities.push(n),e.node.dispose(),this._log.debug(`Spawned entity '${t.entity}' at spawn point '${e.name}'`)}catch(t){this._log.error(`Failed to spawn entity at spawn point '${e.name}':`,t)}}async _spawnEntity(e,t){let n={...e.config,position:this._vectorToObject(t.position),rotation:this._vectorToObject(t.rotation),scaling:this._vectorToObject(t.scaling)},r=await this.gameEngine.managers.entityManager.createEntity(e.entity,{name:e.name,tags:e.tags,initialState:n});return await this._createEntityMesh(r,t),r}_vectorToObject(e){return{x:e.x,y:e.y,z:e.z}}async _createEntityMesh(e,n){if(!this._scene)return;let r=this.gameEngine.managers.entityManager.getDefinition(e.type);if(!r?.mesh)return;let i=r.mesh,a=this._scene,o=new t.TransformNode(`entity-${e.id}`,a);o.position.copyFrom(n.position),o.rotation.copyFrom(n.rotation);let s;switch(i.source){case`box`:s=[t.MeshBuilder.CreateBox(`${e.type}-mesh`,{size:i.params?.size??1,width:i.params?.width,height:i.params?.height,depth:i.params?.depth},a)];break;case`sphere`:s=[t.MeshBuilder.CreateSphere(`${e.type}-mesh`,{diameter:i.params?.diameter??1,segments:i.params?.segments??16},a)];break;case`capsule`:s=[t.MeshBuilder.CreateCapsule(`${e.type}-mesh`,{height:i.params?.height??1.8,radius:i.params?.radius??.4},a)];break;case`cylinder`:s=[t.MeshBuilder.CreateCylinder(`${e.type}-mesh`,{height:i.params?.height??1,diameter:i.params?.diameter??1},a)];break;default:{let e=await this.gameEngine.engines.sceneEngine.importMeshes([],i.source,a);if(e.meshes.length===0){this._log.warn(`No meshes loaded from: ${i.source}`),o.dispose();return}s=e.meshes;for(let t of e.transformNodes)t.parent||=o;break}}for(let e of s)e.parent||=o;i.scale&&(typeof i.scale==`number`?o.scaling.setAll(i.scale):o.scaling.set(i.scale.x,i.scale.y,i.scale.z));let c=i.material;if(c?.color){let n=c.color;if(c.type===`pbr`){let r=new t.PBRMaterial(`${e.type}-material`,a);r.albedoColor=this._toColor3(n),c.emissive&&(r.emissiveColor=this._toColor3(c.emissive)),r.metallic=c.metallic??0,r.roughness=c.roughness??1;for(let e of s)e.material=r}else{let r=new t.StandardMaterial(`${e.type}-material`,a);r.diffuseColor=this._toColor3(n),c.emissive&&(r.emissiveColor=this._toColor3(c.emissive));for(let e of s)e.material=r}}this.gameEngine.managers.entityManager.attachToNode(e,o)}async _processEntityNodes(){for(let e of this._entityNodes)await this._processEntityNode(e)}async _processEntityNode(e){let t=this._config.entities?.[e.type];try{let n={...t?.config,position:this._vectorToObject(e.node.position)},r=await this.gameEngine.managers.entityManager.createEntity(e.type,{name:t?.name??e.node.name,tags:t?.tags,initialState:n,node:e.node});this._spawnedEntities.push(r),this._log.debug(`Created entity '${e.type}' for node '${e.node.name}'`)}catch(t){this._log.error(`Failed to create entity for node '${e.node.name}':`,t)}}async _processLevelSounds(){let e=this._config.sounds;if(!e)return;let{audioManager:t}=this.gameEngine.managers;if(!t){this._log.warn(`No AudioManager configured. Level sounds will not be created.`);return}for(let[n,r]of Object.entries(e))try{let e=await t.createSound(n,r.url,r.channel,{loop:r.loop??!1,volume:r.volume??1});this._levelSounds.set(n,e),r.autoplay&&e.play(),this._log.debug(`Created level sound: "${n}"`)}catch(e){this._log.error(`Failed to create level sound "${n}":`,e)}}async onDeactivate(){for(let[e,n]of this._levelSounds)n.state===t.SoundState.Started&&(this._playingSoundsBeforeDeactivate.add(e),n.pause())}async onActivate(){for(let e of this._playingSoundsBeforeDeactivate){let t=this._levelSounds.get(e);t&&t.play()}this._playingSoundsBeforeDeactivate.clear()}async $dispose(){let{entityManager:e}=this.gameEngine.managers,t=this._spawnedEntities.map(async t=>{try{await e.destroyEntity(t.id)}catch(e){this._log.error(`Failed to destroy entity ${t.id}:`,e)}});await Promise.all(t),this._spawnedEntities=[],this._spawnPoints=[],this._entityNodes=[];for(let e of this._levelSounds.values())e.dispose();this._levelSounds.clear(),this._playingSoundsBeforeDeactivate.clear(),await super.$dispose()}},_e=class{_eventBus;_gameEngine=null;_log;_logger;_levelConfigs=new Map;_levelClasses=new Map;_loadedLevels=new Map;_propertyHandlers=new Map;_currentLevel=null;_eventUnsubscribers=[];get currentLevel(){return this._currentLevel}get propertyHandlers(){return this._propertyHandlers}constructor(e,t){this._eventBus=e,this._logger=t,this._log=t?.getLogger(`LevelManager`)||new y(`LevelManager`),this._eventUnsubscribers.push(this._eventBus.subscribe(`level:progress`,e=>{this._handleProgress(e.payload)})),this._eventUnsubscribers.push(this._eventBus.subscribe(`level:complete`,e=>{this._handleComplete(e.payload)})),this._eventUnsubscribers.push(this._eventBus.subscribe(`level:error`,e=>{this._handleError(e.payload)})),this._log.info(`LevelManager initialized`)}$setGameEngine(e){this._gameEngine=e}_handleProgress(e){this._log.debug(`Level ${e.levelName} progress: ${e.progress}%`)}_handleComplete(e){this._log.info(`Level ${e.levelName} loaded successfully`)}_handleError(e){this._log.error(`Level ${e.levelName} error: ${e.message}`)}_createContext(){if(!this._gameEngine)throw Error(`LevelManager: gameEngine not set. Call $setGameEngine() first.`);return{gameEngine:this._gameEngine,propertyHandlers:this._propertyHandlers,logger:this._logger}}_createLevelInstance(e){let t=this._createContext();if(e.class){let n=this._levelClasses.get(e.class);if(!n)throw Error(`Level class '${e.class}' is not registered.`);return this._log.debug(`Creating level '${e.name}' using class '${e.class}'`),new n(e,t)}return this._log.debug(`Creating level '${e.name}' using GameLevel`),new V(e,t)}registerLevelConfig(e){this._levelConfigs.has(e.name)&&this._log.warn(`Level config '${e.name}' is already registered. Overwriting.`),this._levelConfigs.set(e.name,e),this._log.info(`Registered level config: ${e.name}`)}registerLevelClass(e,t){this._levelClasses.has(e)&&this._log.warn(`Level class '${e}' is already registered. Overwriting.`),this._levelClasses.set(e,t),this._log.info(`Registered level class: ${e}`)}registerPropertyHandler(e,t){this._propertyHandlers.has(e)&&this._log.warn(`Property handler '${e}' is already registered. Overwriting.`),this._propertyHandlers.set(e,t),this._log.debug(`Registered property handler: ${e}`)}unregisterPropertyHandler(e){this._propertyHandlers.delete(e)}clearAllPropertyHandlers(){this._propertyHandlers.clear()}getLevel(e){return this._loadedLevels.get(e)||null}getLevelConfig(e){return this._levelConfigs.get(e)||null}async loadLevel(e){let t;if(typeof e==`string`){let n=this._levelConfigs.get(e);if(!n)throw Error(`Level config '${e}' is not registered.`);t=n}else t=e,this.registerLevelConfig(t);let n=this._loadedLevels.get(t.name);if(n)return this._log.warn(`Level '${t.name}' is already loaded`),n;this._log.debug(`Loading level: ${t.name}`);let r=this._createLevelInstance(t);return await r.load(),this._loadedLevels.set(t.name,r),r}async activateLevel(e){let t=await this.loadLevel(e);return this._currentLevel=t,this._log.info(`Activated level: ${t.name}`),t}async unloadLevel(e){let t=this._loadedLevels.get(e);t&&(this._currentLevel===t&&(this._currentLevel=null),await t.$dispose(),this._loadedLevels.delete(e),this._log.info(`Unloaded level: ${e}`))}async unloadCurrentLevel(){this._currentLevel?await this.unloadLevel(this._currentLevel.name):this._log.warn(`No current level to unload`)}async transition(e,t){let n=this._currentLevel;try{this._eventBus.publish({type:`level:transition:start`,payload:{from:n?.name,to:e}}),n?.onDeactivate&&await n.onDeactivate(),this._eventBus.publish({type:`level:transition:progress`,payload:{stage:`loading`,levelName:e}});let r=await this.loadLevel(e);if(t?.preloadOnly)return;n&&!t?.keepAlive&&await this.unloadLevel(n.name),this._currentLevel=r,r.onActivate&&await r.onActivate(),this._eventBus.publish({type:`level:transition:complete`,payload:{levelName:e}})}catch(t){throw this._eventBus.publish({type:`level:transition:error`,payload:{from:n?.name,to:e,error:t}}),t}}async $teardown(){for(let e of this._eventUnsubscribers)e();this._eventUnsubscribers=[];for(let e of this._loadedLevels.values())await e.$dispose();this._loadedLevels.clear(),this._levelConfigs.clear(),this._currentLevel=null}},H=class{_audioEngine;_channels=new Map;_masterMuted=!1;_masterVolumeBeforeMute=1;_log;constructor(e,t){this._audioEngine=e,this._log=t.getLogger(`AudioManager`)}async initialize(e){this._log.info(`Initializing audio channels: ${e.join(`, `)}`);for(let t of e){let e=await this._audioEngine.createBus(t);this._channels.set(t,{bus:e,volume:1,muted:!1})}this._log.info(`Audio channels initialized.`)}async $teardown(){this._log.info(`Tearing down audio manager...`);for(let e of this._channels.values())e.bus.dispose();this._channels.clear(),this._log.info(`Audio manager torn down.`)}async createSound(e,t,n,r){let i;if(n){let t=this._channels.get(n);t?i=t.bus:this._log.warn(`Unknown audio channel "${n}", sound "${e}" will use default bus.`)}return this._audioEngine.createSound(e,t,i,r)}setMasterVolume(e){this._masterMuted?this._masterVolumeBeforeMute=e:this._audioEngine.setMasterVolume(e)}getMasterVolume(){return this._masterMuted?this._masterVolumeBeforeMute:this._audioEngine.getMasterVolume()}setMasterMuted(e){e!==this._masterMuted&&(this._masterMuted=e,e?(this._masterVolumeBeforeMute=this._audioEngine.getMasterVolume(),this._audioEngine.setMasterVolume(0)):this._audioEngine.setMasterVolume(this._masterVolumeBeforeMute))}isMasterMuted(){return this._masterMuted}setChannelVolume(e,t){let n=this._channels.get(e);if(!n){this._log.warn(`Unknown audio channel: ${e}`);return}n.volume=t,n.muted||this._audioEngine.setBusVolume(n.bus,t)}getChannelVolume(e){return this._channels.get(e)?.volume??1}setChannelMuted(e,t){let n=this._channels.get(e);if(!n){this._log.warn(`Unknown audio channel: ${e}`);return}t!==n.muted&&(n.muted=t,this._audioEngine.setBusVolume(n.bus,t?0:n.volume))}isChannelMuted(e){return this._channels.get(e)?.muted??!1}getChannels(){return Array.from(this._channels.keys())}},U=class{_entityManager;_levelManager;_log;_beforeSerializeHooks=[];_afterDeserializeHooks=[];constructor(e,t,n){this._entityManager=e,this._levelManager=t,this._log=n?.getLogger(`SaveManager`)||new y(`SaveManager`),this._log.info(`SaveManager initialized`)}onBeforeSerialize(e){this._beforeSerializeHooks.push(e)}onAfterDeserialize(e){this._afterDeserializeHooks.push(e)}serialize(){this._log.debug(`Serializing game state...`);let e=this._levelManager.currentLevel?.name??``,t=[];for(let e of this._entityManager.getAllEntities()){let n={id:e.id,type:e.type,tags:Array.from(e.tags),state:structuredClone(e.state)};if(e.name&&(n.name=e.name),e.parent&&(n.parentId=e.parent.id),e.node){let{position:t,rotation:r,scaling:i}=e.node;n.transform={position:{x:t.x,y:t.y,z:t.z},rotation:{x:r.x,y:r.y,z:r.z},scaling:{x:i.x,y:i.y,z:i.z}}}t.push(n)}let n={};for(let e of this._beforeSerializeHooks){let t=e();n={...n,...t}}let r={version:1,levelName:e,entities:t,custom:n};return this._log.info(`Serialized ${t.length} entities from level "${e}"`),r}async deserialize(e){this._log.debug(`Deserializing save data (version ${e.version}, level "${e.levelName}")...`),await this._levelManager.transition(e.levelName);let t=[];for(let e of this._entityManager.getAllEntities())t.push(e.id);for(let e of t)await this._entityManager.destroyEntity(e);let n=new Map;for(let t of e.entities){let e=await this._entityManager.createEntity(t.type,{name:t.name,initialState:t.state,tags:t.tags});if(n.set(t.id,e.id),t.transform&&e.node){let{position:n,rotation:r,scaling:i}=t.transform;e.node.position.x=n.x,e.node.position.y=n.y,e.node.position.z=n.z,e.node.rotation.x=r.x,e.node.rotation.y=r.y,e.node.rotation.z=r.z,e.node.scaling.x=i.x,e.node.scaling.y=i.y,e.node.scaling.z=i.z}}for(let t of e.entities)if(t.parentId){let e=n.get(t.parentId),r=n.get(t.id);e&&r?this._entityManager.addChild(e,r):this._log.warn(`Could not restore parent-child: ${t.id} -> ${t.parentId} (mapped: ${r} -> ${e})`)}for(let t of this._afterDeserializeHooks)t(e.custom);this._log.info(`Deserialized ${e.entities.length} entities for level "${e.levelName}"`)}async $teardown(){this._beforeSerializeHooks=[],this._afterDeserializeHooks=[],this._log.info(`SaveManager torn down`)}},ve=class{_keyboardDevice;_keysState={};_onDeviceConnected;_onInputChanged;constructor(){this._keyboardDevice={id:`keyboard-0`,name:`Keyboard`,type:`keyboard`,connected:!0},this._setupKeyboardEvents(),setTimeout(()=>this._notifyDeviceConnected(),0)}onDeviceConnected(e){this._onDeviceConnected=e}onInputChanged(e){this._onInputChanged=e}getState(){return{type:`keyboard`,keys:{...this._keysState},delta:{}}}getDevice(){return{...this._keyboardDevice}}$teardown(){return window.removeEventListener(`keydown`,this._handleKeyDown),window.removeEventListener(`keyup`,this._handleKeyUp),Promise.resolve()}_setupKeyboardEvents(){this._handleKeyDown=this._handleKeyDown.bind(this),this._handleKeyUp=this._handleKeyUp.bind(this),window.addEventListener(`keydown`,this._handleKeyDown),window.addEventListener(`keyup`,this._handleKeyUp)}_handleKeyDown(e){this._keysState[e.code]=!0;let t={};t[e.code]=!0;let n={type:`keyboard`,keys:{...this._keysState},delta:t,event:e};this._notifyInputChanged(n)}_handleKeyUp(e){this._keysState[e.code]=!1;let t={};t[e.code]=!1;let n={type:`keyboard`,keys:{...this._keysState},delta:t,event:e};this._notifyInputChanged(n)}_notifyDeviceConnected(){this._onDeviceConnected&&this._onDeviceConnected(this._keyboardDevice)}_notifyInputChanged(e){this._onInputChanged&&this._onInputChanged(this._keyboardDevice,e)}},ye=class{_targetElement;_mouseDevice;_buttonState={};_axesState={};_position={absolute:{x:0,y:0},relative:{x:0,y:0}};_wheelState={deltaX:0,deltaY:0,deltaZ:0,deltaMode:0};_onDeviceConnected;_onInputChanged;constructor(e=document.body){this._targetElement=e,this._mouseDevice={id:`mouse-0`,name:`Mouse`,type:`mouse`,connected:!0},this._setupMouseEvents(),this._axesState[`axis-x`]=0,this._axesState[`axis-y`]=0,setTimeout(()=>this._notifyDeviceConnected(),0)}onDeviceConnected(e){this._onDeviceConnected=e}onInputChanged(e){this._onInputChanged=e}getState(){return{type:`mouse`,buttons:{...this._buttonState},axes:{...this._axesState},position:{absolute:{...this._position.absolute},relative:{...this._position.relative}},wheel:{...this._wheelState}}}getDevice(){return{...this._mouseDevice}}$teardown(){return this._targetElement.removeEventListener(`pointerdown`,this._handlePointerDown),this._targetElement.removeEventListener(`pointerup`,this._handlePointerUp),this._targetElement.removeEventListener(`pointermove`,this._handlePointerMove),this._targetElement.removeEventListener(`wheel`,this._handleMouseWheel),Promise.resolve()}_setupMouseEvents(){this._handlePointerDown=this._handlePointerDown.bind(this),this._handlePointerUp=this._handlePointerUp.bind(this),this._handlePointerMove=this._handlePointerMove.bind(this),this._handleMouseWheel=this._handleMouseWheel.bind(this),this._targetElement.addEventListener(`pointerdown`,this._handlePointerDown),this._targetElement.addEventListener(`pointerup`,this._handlePointerUp),this._targetElement.addEventListener(`pointermove`,this._handlePointerMove),this._targetElement.addEventListener(`wheel`,this._handleMouseWheel)}_handlePointerDown(e){if(e.pointerType!==`mouse`)return;let t=`button-${e.button}`;this._buttonState[t]={pressed:!0},this._notifyInputChanged({type:`mouse`,event:e,buttons:{...this._buttonState},axes:{...this._axesState},position:{...this._position}})}_handlePointerUp(e){if(e.pointerType!==`mouse`)return;let t=`button-${e.button}`;this._buttonState[t]={pressed:!1},this._notifyInputChanged({type:`mouse`,event:e,buttons:{...this._buttonState},axes:{...this._axesState},position:{...this._position}})}_handlePointerMove(e){e.pointerType===`mouse`&&(this._position={absolute:{x:e.clientX,y:e.clientY},relative:{x:e.movementX,y:e.movementY}},this._axesState[`axis-x`]=e.clientX,this._axesState[`axis-y`]=e.clientY,this._notifyInputChanged({type:`mouse`,event:e,buttons:{...this._buttonState},axes:{...this._axesState},position:{...this._position}}))}_handleMouseWheel(e){this._wheelState={deltaX:e.deltaX,deltaY:e.deltaY,deltaZ:e.deltaZ,deltaMode:e.deltaMode},this._notifyInputChanged({type:`mouse`,event:e,buttons:{...this._buttonState},axes:{...this._axesState},position:{...this._position},wheel:{...this._wheelState}})}_notifyDeviceConnected(){this._onDeviceConnected&&this._onDeviceConnected(this._mouseDevice)}_notifyInputChanged(e){this._onInputChanged&&this._onInputChanged(this._mouseDevice,e)}},be=class{_gamepadDevices={};_buttonStates={};_axesStates={};_onDeviceConnected;_onDeviceDisconnected;_onInputChanged;constructor(){this._setupGamepadEvents()}onDeviceConnected(e){this._onDeviceConnected=e}onDeviceDisconnected(e){this._onDeviceDisconnected=e}onInputChanged(e){this._onInputChanged=e}getDevices(){return Object.values(this._gamepadDevices).map(e=>({...e}))}getStates(){let e={};return Object.keys(this._buttonStates).forEach(t=>{let n=Number(t),r=this._buttonStates[n]||{},i=this._axesStates[n]||{};e[n]={type:`gamepad`,buttons:{...r},axes:{...i}}}),e}getDevice(e){let t=this._gamepadDevices[e];return t?{...t}:null}getState(e){let t=this._buttonStates[e],n=this._axesStates[e];return!t&&!n?null:{type:`gamepad`,buttons:t?{...t}:{},axes:n?{...n}:{}}}pollGamepads(){if(!navigator.getGamepads)return;let e=navigator.getGamepads();for(let t of e){if(!t)continue;let e=t.index;if(!this._gamepadDevices[e]){this._handleGamepadConnected(t);continue}let n=this._gamepadDevices[e];if(!n)continue;let r=this._buttonStates[e]||{},i=this._axesStates[e]||{},a={},o=!1;t.buttons.forEach((e,t)=>{let n=`button-${t}`,i={pressed:e.pressed,touched:e.touched,value:e.value};a[n]=i;let s=r[n];(!s||s.pressed!==i.pressed||s.touched!==i.touched||s.value!==i.value)&&(o=!0)});let s={},c=!1;if(t.axes.forEach((e,t)=>{let n=`axis-${t}`;s[n]=e,i[n]!==e&&(c=!0)}),this._buttonStates[e]=a,this._axesStates[e]=s,o||c){let e={type:`gamepad`,buttons:{...a},axes:{...s}};this._notifyInputChanged(n,e)}}}$teardown(){return window.removeEventListener(`gamepadconnected`,this._handleGamepadConnected),window.removeEventListener(`gamepaddisconnected`,this._handleGamepadDisconnected),Promise.resolve()}_setupGamepadEvents(){if(this._handleGamepadConnected=this._handleGamepadConnected.bind(this),this._handleGamepadDisconnected=this._handleGamepadDisconnected.bind(this),window.addEventListener(`gamepadconnected`,this._handleGamepadConnected),window.addEventListener(`gamepaddisconnected`,this._handleGamepadDisconnected),navigator.getGamepads){let e=navigator.getGamepads();for(let t of e)t&&this._handleGamepadConnected(t)}}_handleGamepadConnected(e){let t=e instanceof GamepadEvent?e.gamepad:e,n=t.index,r={id:`gamepad-${n}`,name:t.id,type:`gamepad`,connected:!0,index:n,mapping:t.mapping,axes:Array.from(t.axes),buttons:Array.from(t.buttons)};this._gamepadDevices[n]=r;let i={};Array.from(t.buttons).forEach((e,t)=>{i[`button-${t}`]={pressed:e.pressed,touched:e.touched,value:e.value}}),this._buttonStates[n]=i;let a={};Array.from(t.axes).forEach((e,t)=>{a[`axis-${t}`]=e}),this._axesStates[n]=a,this._notifyDeviceConnected(r),this._notifyInputChanged(r,{type:`gamepad`,buttons:{...i},axes:{...a},event:e instanceof GamepadEvent?e:void 0})}_handleGamepadDisconnected(e){let t=e.gamepad.index,n=this._gamepadDevices[t];n&&(n.connected=!1,this._notifyDeviceDisconnected(n),this._gamepadDevices[t]=void 0,this._buttonStates[t]=void 0,this._axesStates[t]=void 0)}_notifyDeviceConnected(e){this._onDeviceConnected&&this._onDeviceConnected(e)}_notifyDeviceDisconnected(e){this._onDeviceDisconnected&&this._onDeviceDisconnected(e)}_notifyInputChanged(e,t){this._onInputChanged&&this._onInputChanged(e,t)}},xe=class{_eventBus;_keyboardRA;_mouseRA;_gamepadRA;_log;constructor(e,t,n){this._eventBus=e,this._log=n?.getLogger(`UserInputManager`)||new y(`UserInputManager`),this._log.info(`Initializing UserInputManager`),this._log.debug(`Initializing input resource access classes`),this._keyboardRA=new ve,this._mouseRA=new ye(t),this._gamepadRA=new be,this._log.debug(`Registering input event callbacks`),this._keyboardRA.onDeviceConnected(this._publishDeviceConnected.bind(this)),this._keyboardRA.onInputChanged(this._publishInputChanged.bind(this)),this._mouseRA.onDeviceConnected(this._publishDeviceConnected.bind(this)),this._mouseRA.onInputChanged(this._publishInputChanged.bind(this)),this._gamepadRA.onDeviceConnected(this._publishDeviceConnected.bind(this)),this._gamepadRA.onDeviceDisconnected(this._publishDeviceDisconnected.bind(this)),this._gamepadRA.onInputChanged(this._publishInputChanged.bind(this)),this._log.info(`UserInputManager initialized successfully`)}_publishDeviceConnected(e){this._log.debug(`Device connected: ${e.id} (${e.name})`),this._eventBus.publish({type:`input:device:connected`,payload:{device:e}})}_publishDeviceDisconnected(e){this._log.debug(`Device disconnected: ${e.id} (${e.name})`),this._eventBus.publish({type:`input:device:disconnected`,payload:{device:e}})}_publishInputChanged(e,t){this._log.trace(`Input changed: ${e.id}`,t),this._eventBus.publish({type:`input:changed`,payload:{deviceId:e.id,device:e,state:t}})}$teardown(){return this._log.info(`Tearing down UserInputManager`),this._log.debug(`Cleaning up input resource access instances`),Promise.all([this._keyboardRA.$teardown(),this._mouseRA.$teardown(),this._gamepadRA.$teardown()]).then(()=>{this._log.info(`UserInputManager torn down successfully`)})}listDevices(){this._log.debug(`Getting all connected input devices`);let e=[];return e.push(this._keyboardRA.getDevice()),e.push(this._mouseRA.getDevice()),e.push(...this._gamepadRA.getDevices()),this._log.debug(`Found ${e.length} connected devices`),e}getDevice(e){if(this._log.debug(`Getting device: ${e}`),e.startsWith(`keyboard-`))return this._keyboardRA.getDevice();if(e.startsWith(`mouse-`))return this._mouseRA.getDevice();if(e.startsWith(`gamepad-`)){let t=parseInt(e.split(`-`)[1],10);return this._gamepadRA.getDevice(t)}return this.listDevices().find(t=>t.id===e)||(this._log.warn(`Device not found: ${e}`),null)}pollGamepads(){this._gamepadRA.pollGamepads()}};async function W(e,n){let r=new t.WebGPUEngine(e,n);return await r.initAsync(),r}function G(e,n){return new t.Engine(e,n.antialias,n.options,n.adaptToDeviceRatio)}function Se(e){return new t.NullEngine(e)}async function Ce(e,t,n=!1){if(e===null)return console.debug(`Using Null Engine`),Se(t);if(n)if(`options`in t){let e=t;t={...e,options:{...e.options,useLargeWorldRendering:!0}}}else t={...t,useLargeWorldRendering:!0};let r=t.engine||`auto`;if(r===`webgpu`)if(j())try{return console.debug(`Using forced WebGPU engine`),await W(e,t)}catch(e){throw console.error(`Forced WebGPU initialization failed:`,e),Error(`Forced WebGPU failed to initialize. If WebGPU is required, check browser compatibility.`,{cause:e})}else throw Error(`WebGPU was forced but is not available in this browser.`);if(r===`webgl`)return console.debug(`Using forced WebGL engine`),G(e,t);if(j())try{return console.debug(`Using WebGPU`),W(e,t).catch(n=>(console.warn(`WebGPU initialization failed, falling back to WebGL:`,n),G(e,t)))}catch(e){console.warn(`WebGPU initialization failed, falling back to WebGL:`,e)}else console.warn(`WebGPU not supported, falling back to WebGL.`);return console.debug(`Using WebGL`),G(e,t)}async function we(){return await(0,a.default)()}function Te(e){return new t.HavokPlugin(!0,e)}var K=class{_entityManager;constructor(e){this._entityManager=e}pickEntity(e,t,n,r){let i=e.pick(t,n);return this._toEntityResult(i,r)}pickEntities(e,t,n,r){let i=e.multiPick(t,n);if(!i)return[];let a=[];for(let e of i){let t=this._toEntityResult(e,r);t&&a.push(t)}return a}pickEntityWithRay(e,t,n){let r=e.pickWithRay(t);return r?this._toEntityResult(r,n):null}pickEntitiesWithRay(e,t,n){let r=e.multiPickWithRay(t);if(!r)return[];let i=[];for(let e of r){let t=this._toEntityResult(e,n);t&&i.push(t)}return i}pickEntityForward(e,t,n=1e3,r){let i=t.getForwardRay(n);return this.pickEntityWithRay(e,i,r)}_toEntityResult(e,t){if(!e.hit||!e.pickedMesh||!e.pickedPoint)return null;let n=e.pickedMesh,r=this._entityManager.getByNode(n);if(!r){let e=n.parent;for(;e&&(r=this._entityManager.getByNode(e),!r);)e=e.parent}if(!r)return null;if(t){if(t.type&&r.type!==t.type)return null;if(t.tags){for(let e of t.tags)if(!r.hasTag(e))return null}if(t.predicate&&!t.predicate(r))return null}let i=e.getNormal(!0,!0);return{entity:r,point:e.pickedPoint,distance:e.distance,normal:i??{x:0,y:0,z:0},mesh:n,pickingInfo:e}}},q=class{_nextId=0;_delays=[];_intervals=[];_cooldowns=[];delay(e,t){if(e<0)throw RangeError(`delay() requires ms >= 0, got ${e}`);let n=this._nextId++;return this._delays.push({id:n,remaining:e,callback:t}),()=>{this._delays=this._delays.filter(e=>e.id!==n)}}interval(e,t){if(e<=0)throw RangeError(`interval() requires ms > 0, got ${e}`);let n=this._nextId++;return this._intervals.push({id:n,periodMs:e,elapsed:0,callback:t}),()=>{this._intervals=this._intervals.filter(e=>e.id!==n)}}cooldown(e){let t={id:this._nextId++,periodMs:e,remaining:0};return this._cooldowns.push(t),{get ready(){return t.remaining<=0},reset(){t.remaining=t.periodMs}}}cancelAll(){this._delays=[],this._intervals=[];for(let e of this._cooldowns)e.remaining=0;this._cooldowns=[]}tick(e){let t=[];for(let n of this._delays)n.remaining-=e,n.remaining<=0&&(n.callback(),t.push(n.id));t.length>0&&(this._delays=this._delays.filter(e=>!t.includes(e.id)));for(let t of this._intervals)for(t.elapsed+=e;t.elapsed>=t.periodMs;)t.callback(),t.elapsed-=t.periodMs;for(let t of this._cooldowns)t.remaining>0&&(t.remaining=Math.max(0,t.remaining-e))}},Ee=[`trace`,`debug`,`info`,`warn`,`error`,`none`],De=class extends F{name=`sound`;eventSubscriptions=[];_sounds=new Map;_initialized=!1;_audioManager=null;_node=null;_log=new y(`SoundBehavior`);onNodeAttached(e,t){if(this._initialized)return;if(this._node=e,this._initialized=!0,this._log=t.logger.getLogger(`SoundBehavior`),t.managers.audioManager)this._audioManager=t.managers.audioManager;else{this._log.warn(`No AudioManager configured. Sounds will not be created.`);return}if(!this.entity)return;let n=this.entity.state.sounds;if(n)for(let[e,t]of Object.entries(n))this._createSound(e,t)}processEvent(e,t){return!1}onReset(e){this._disposeSounds()}async destroy(){this._disposeSounds()}_createSound(e,t){if(!this._audioManager||!this.entity)return;let n=this.entity.id,r=this._node;this._audioManager.createSound(`${n}_${e}`,t.url,t.channel,{loop:t.loop??!1,autoplay:t.autoplay??!1,volume:t.volume??1,spatialEnabled:t.spatial??!1,spatialMaxDistance:t.maxDistance??100,spatialDistanceModel:`linear`}).then(n=>{this._sounds.set(e,n),(t.spatial??!1)&&r&&n.spatial.attach(r)}).catch(t=>{this._log.error(`Failed to create sound "${e}": ${t}`)})}_disposeSounds(){for(let e of this._sounds.values())e.dispose();this._sounds.clear(),this._initialized=!1,this._node=null,this._audioManager=null}play(e,t){let n=this._getSound(e);n&&(t===void 0?n.play():n.play({startOffset:t}))}stop(e){if(e){let t=this._sounds.get(e);t&&t.stop()}else for(let e of this._sounds.values())e.stop()}pause(e){let t=this._getSound(e);t&&t.pause()}setVolume(e,t){if(t){let n=this._sounds.get(t);n&&(n.volume=e)}else for(let t of this._sounds.values())t.volume=e}isPlaying(e){return this._getSound(e)?.state===t.SoundState.Started}getSoundNames(){return Array.from(this._sounds.keys())}hasSound(e){return this._sounds.has(e)}registerSound(e,t){if(!this._initialized||!this._audioManager)throw Error(`SoundBehavior must be initialized before registering sounds`);let n=this._sounds.get(e);n&&n.dispose(),this._createSound(e,t)}unregisterSound(e){let t=this._sounds.get(e);t&&(t.dispose(),this._sounds.delete(e))}_getSound(e){if(e)return this._sounds.get(e);if(this._sounds.size===1)return this._sounds.values().next().value}},J=class{_currentState;transitions=new Map;wildcardTransitions=new Map;enterCallbacks=new Map;exitCallbacks=new Map;eventBus;constructor(e,t){this._currentState=e,this.eventBus=t}get currentState(){return this._currentState}addTransition(e,t,n){let r=`${e}->${t}`;this.transitions.set(r,{guard:n})}addTransitionFromAny(e,t){this.wildcardTransitions.set(e,{guard:t})}onEnter(e,t){let n=this.enterCallbacks.get(e)??[];n.push(t),this.enterCallbacks.set(e,n)}onExit(e,t){let n=this.exitCallbacks.get(e)??[];n.push(t),this.exitCallbacks.set(e,n)}canTransition(e){let t=`${this._currentState}->${e}`;return this.transitions.has(t)||this.wildcardTransitions.has(e)}transition(e){let t=`${this._currentState}->${e}`,n=this.transitions.get(t);if(n||=this.wildcardTransitions.get(e),!n)throw Error(`No transition defined from '${this._currentState}' to '${e}'.`);if(n.guard&&!n.guard())throw Error(`Guard rejected transition from '${this._currentState}' to '${e}'.`);let r=this._currentState,i=this.exitCallbacks.get(r);if(i)for(let e of i)e();this.eventBus&&this.eventBus.publish({type:`state:exit:${r}`,payload:{from:r,to:e}}),this._currentState=e;let a=this.enterCallbacks.get(e);if(a)for(let e of a)e();this.eventBus&&this.eventBus.publish({type:`state:enter:${e}`,payload:{from:r,to:e}})}},Y=class extends F{name=`stateMachine`;eventSubscriptions=[];stateMachine;stateKey;constructor(){super(),this.stateMachine=new J(``),this.stateKey=``}get currentState(){return this.stateMachine.currentState}transition(e){this.stateMachine.transition(e),this.entity&&(this.entity.state[this.stateKey]=e,this.$emitStateChanged())}processEvent(e,t){return!1}static create(e){let{initialState:t,stateKey:n,transitions:r,wildcardTransitions:i}=e;class a extends Y{constructor(){super(),this.stateKey=n,this.stateMachine=new J(t);for(let e of r)this.stateMachine.addTransition(e.from,e.to,e.guard);if(i)for(let e of i)this.stateMachine.addTransitionFromAny(e.to,e.guard)}}return a}},X=new y(`ColliderHandler`);function Z(e,n,r){let i=e.getScene();new t.PhysicsAggregate(e,n,{mass:r},i)}function Oe(e,n){let r=e.getChildMeshes().find(e=>e.metadata?.collider_mesh===!0);r instanceof t.Mesh?(r.isVisible=!1,Z(r,t.PhysicsShapeType.MESH,n)):Z(e,t.PhysicsShapeType.MESH,n)}function Q(e){e.registerPropertyHandler(`collider`,(e,n,r,i)=>{let a=n;if(X.debug(`Processing collider: ${e.name} -> ${a} (isMesh: ${e instanceof t.Mesh})`),!r.scene)throw Error(`Scene not available for collider handler`);if(!(e instanceof t.Mesh)){X.warn(`Skipping collider for ${e.name}: not a Mesh instance`);return}let o=e.metadata?.collider_mass??0;switch(a){case`box`:Z(e,t.PhysicsShapeType.BOX,o);break;case`sphere`:Z(e,t.PhysicsShapeType.SPHERE,o);break;case`mesh`:Oe(e,o);break;case`none`:e.checkCollisions=!1;break;default:X.warn(`Unknown collider type: ${a}`)}if(e.metadata?.collider_kinematic){let t=e;t.physicsBody&&(t.physicsBody.disablePreStep=!1)}})}var ke=new y(`LodHandler`);function Ae(e){return e.split(`,`).map(e=>parseFloat(e.trim())).filter(e=>!isNaN(e))}function je(e){e.registerPropertyHandler(`lod_distances`,(e,n,r,i)=>{let a=n;if(!(e instanceof t.Mesh))return;let o=Ae(a);if(o.length===0){ke.warn(`Invalid lod_distances value: ${a}`);return}let s=e.getChildMeshes(!0),c=Math.min(o.length,s.length);for(let t=0;t<c;t++)e.addLODLevel(o[t],s[t]);let l=o[o.length-1];e.addLODLevel(l,null)})}function Me(e){e.registerPropertyHandler(`occluder`,(e,n,r,i)=>{n&&e instanceof t.Mesh&&(e.isOccluder=!0,e.isVisible=!1)})}function Ne(e,t){let n={},r=`${t}_`;for(let[t,i]of Object.entries(e))if(t.startsWith(r)){let e=t.slice(r.length);n[e]=i}return n}var Pe=new y(`SoundHandler`);function Fe(e){e.registerPropertyHandler(`sound`,(e,t,n,r)=>{let i=t;if(!n.scene)throw Error(`Scene not available for sound handler`);let a=Ne(e.metadata,`sound`),o=a.volume??1,s=a.loop??!0,c=a.spatial??!0,l=a.distance??100,u=a.autoplay??!0,d=a.channel??`ambient`,f=r.managers.audioManager;if(!f){Pe.warn(`No AudioManager available. Sound for "${e.name}" skipped.`);return}f.createSound(`${e.name}_sound`,i,d,{loop:s,autoplay:u,volume:o,spatialEnabled:c,spatialMaxDistance:l,spatialDistanceModel:`linear`}).then(t=>{c&&t.spatial.attach(e)}).catch(t=>{Pe.error(`Failed to create sound for node "${e.name}": ${t}`)})})}function Ie(e,n,r,i,a){let o=a===`enter`?t.ActionManager.OnIntersectionEnterTrigger:t.ActionManager.OnIntersectionExitTrigger,s=`trigger:${a}`;e.registerAction(new t.ExecuteCodeAction({trigger:o,parameter:{mesh:i}},e=>{r.publish({type:s,payload:{trigger:n,other:e.source}})}))}function Le(e,t,n,r){Ie(e,t,n,r,`enter`),Ie(e,t,n,r,`exit`)}function Re(e){e.registerPropertyHandler(`trigger`,(e,n,r,i)=>{let a=n,o=r.scene;if(!o)throw Error(`Scene not available for trigger handler`);if(!(e instanceof t.AbstractMesh))return;let s=i.eventBus;e.isVisible=!1,e.checkCollisions=!0;let c=e.actionManager??new t.ActionManager(o);e.actionManager=c;for(let t of o.meshes)t!==e&&!t.metadata?.trigger&&Le(c,a,s,t);let l=o.onNewMeshAddedObservable.add(t=>{t!==e&&!t.metadata?.trigger&&Le(c,a,s,t)});e.onDisposeObservable.add(()=>{o.onNewMeshAddedObservable.remove(l)})})}function $(e){e.registerPropertyHandler(`visible`,(e,n,r,i)=>{if(!(e instanceof t.AbstractMesh))return;let a=!0;typeof n==`boolean`?a=n:typeof n==`string`?a=n.toLowerCase()===`true`||n===`1`:typeof n==`number`&&(a=n!==0),e.isVisible=a})}function ze(e){Q(e),je(e),Me(e),Fe(e),Re(e),$(e)}async function Be(e,t,n={}){let r=new b(n.logLevel||`debug`),i=r.getLogger(`SAGE`);i.info(`Initializing SAGE Game Engine v${h}...`),i.debug(`Creating rendering engine...`);let a=await Ce(e,{antialias:!0,adaptToDeviceRatio:!0,...n.renderOptions},n.largeWorldRendering??!1);i.debug(`Creating physics engine...`);let o=await we(),s=Te(o);i.debug(`Creating event bus...`);let c=new x(r);i.debug(`Creating scene engine...`);let l=new C(e,a,o,r);i.debug(`Creating managers...`);let u=new w(c,l,r),d=new xe(c,e,r),f=new ce(c,r),p=new de(c,r,f),m=new _e(c,r),_=new U(p,m,r),v=new le(a,c,p,d,m,r);i.debug(`Creating raycast helper...`);let y=new K(p);i.debug(`Creating game timer...`);let T=new q;v.registerFrameCallback(e=>T.tick(e*1e3));let E,D;n.audioChannels&&n.audioChannels.length>0&&(i.debug(`Creating audio engine...`),E=new S(r),await E.initialize(),i.debug(`Creating audio manager...`),D=new H(E,r),await D.initialize(n.audioChannels)),i.info(`SAGE Game Engine v${h} initialized successfully.`),i.debug(`Loading entities...`);for(let e of t)p.registerEntityDefinition(e);if(i.debug(`Registering default input bindings...`),n.bindings)for(let e of n.bindings)f.registerBinding(e);let O=new g(e,a,s,c,r,y,T,{sceneEngine:l,audioEngine:E},{assetManager:u,bindingManager:f,entityManager:p,gameManager:v,inputManager:d,levelManager:m,saveManager:_,audioManager:D},n.largeWorldRendering??!1);return p.$setGameEngine(O),m.$setGameEngine(O),O}e.AssetManager=w,e.AudioEngine=S,e.AudioManager=H,e.ConsoleBackend=_,e.GameEngine=g,e.GameEntity=I,e.GameEntityBehavior=F,e.GameEventBus=x,e.GameLevel=V,e.GameTimer=q,e.Level=z,e.LogLevels=Ee,e.LoggingUtility=b,e.NullBackend=v,e.OutlineManager=B,e.RaycastHelper=K,e.SAGELogger=y,e.SaveManager=U,e.SceneEngine=C,e.SoundBehavior=De,e.StateMachine=J,e.StateMachineBehavior=Y,e.VERSION=h,e.applyPostProcessing=R,e.bindingTypes=T,e.collectPrefixedProperties=Ne,e.createGameEngine=Be,e.generateId=P,e.isGamepadState=k,e.isKeyboardState=D,e.isMouseState=O,e.registerAllPropertyHandlers=ze,e.registerColliderHandler=Q,e.registerLodHandler=je,e.registerOccluderHandler=Me,e.registerSoundHandler=Fe,e.registerTriggerHandler=Re,e.registerVisibleHandler=$,e.validDeviceTypes=E});
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`@babylonjs/core`),require(`@babylonjs/loaders/dynamic`),require(`@babylonjs/core/Cameras/geospatialCamera`),require(`@babylonjs/core/Lights/Clustered/clusteredLightContainer`),require(`@babylonjs/havok`)):typeof define==`function`&&define.amd?define([`exports`,`@babylonjs/core`,`@babylonjs/loaders/dynamic`,`@babylonjs/core/Cameras/geospatialCamera`,`@babylonjs/core/Lights/Clustered/clusteredLightContainer`,`@babylonjs/havok`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.SAGameEngine={},e.BABYLON,e.BABYLON,e.BABYLON,e.BABYLON,e.HavokPhysics))})(this,function(e,t,n,r,i,a){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var o=Object.create,s=Object.defineProperty,c=Object.getOwnPropertyDescriptor,l=Object.getOwnPropertyNames,u=Object.getPrototypeOf,d=Object.prototype.hasOwnProperty,f=(e,t)=>()=>(e&&(t=e(e=0)),t),p=(e,t)=>{let n={};for(var r in e)s(n,r,{get:e[r],enumerable:!0});return t||s(n,Symbol.toStringTag,{value:`Module`}),n},m=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=l(t),a=0,o=i.length,u;a<o;a++)u=i[a],!d.call(e,u)&&u!==n&&s(e,u,{get:(e=>t[e]).bind(null,u),enumerable:!(r=c(t,u))||r.enumerable});return e};a=((e,t,n)=>(n=e==null?{}:o(u(e)),m(t||!e||!e.__esModule?s(n,`default`,{value:e,enumerable:!0}):n,e)))(a);var h=`0.9.1`,g=class{canvas;renderEngine;physics;managers;engines;eventBus;logger;raycast;timer;largeWorldRendering;started=!1;_log;_beforeStartHook=null;_onStartHook=null;_onTeardownHook=null;constructor(e,t,n,r,i,a,o,s,c,l=!1){this.canvas=e,this.renderEngine=t,this.physics=n,this.eventBus=r,this.logger=i,this.raycast=a,this.timer=o,this.engines=s,this.managers=c,this.largeWorldRendering=l,this._log=i.getLogger(`GameEngine`)}onBeforeStart(e){if(this._beforeStartHook!==null)throw Error(`A beforeStart hook is already registered. Only one hook is allowed per lifecycle event.`);this._beforeStartHook=e}onStart(e){if(this._onStartHook!==null)throw Error(`An onStart hook is already registered. Only one hook is allowed per lifecycle event.`);this._onStartHook=e}onTeardown(e){if(this._onTeardownHook!==null)throw Error(`An onTeardown hook is already registered. Only one hook is allowed per lifecycle event.`);this._onTeardownHook=e}subscribe(e,t){return this.eventBus.subscribe(e,t)}subscribeAction(e,t){return this.eventBus.subscribe(`action:${e}`,t)}async start(){this.started?this._log.warn(`Game engine is already started. Skipping start.`):(this._log.info(`Starting SkewedAspect Game Engine (Version: ${h})...`),this._beforeStartHook&&(this._log.debug(`Executing beforeStart hook...`),await this._beforeStartHook(this)),await this.managers.gameManager.start(),this._log.info(`Game engine started successfully`),this._onStartHook&&(this._log.debug(`Executing onStart hook...`),await this._onStartHook(this)),this.started=!0)}async stop(){if(this.started){this._log.info(`Stopping SkewedAspect Game Engine (Version: ${h})...`);let e=null;if(this._onTeardownHook)try{this._log.debug(`Executing onTeardown hook...`),await this._onTeardownHook(this)}catch(t){this._log.error(`Error in onTeardown hook: ${t}`),e||=t}for(let t of Object.keys(this.managers)){let n=this.managers[t];if(n)try{this._log.debug(`Tearing down manager: ${t}`),await n.$teardown()}catch(n){this._log.error(`Error tearing down manager ${t}: ${n}`),e||=n}}for(let t of Object.keys(this.engines)){let n=this.engines[t];if(n)try{this._log.debug(`Tearing down engine: ${t}`),await n.$teardown()}catch(n){this._log.error(`Error tearing down engine ${t}: ${n}`),e||=n}}if(this.started=!1,this._log.info(`Game engine stopped successfully`),e)throw e}else this._log.warn(`Game engine is not started. Skipping stop.`)}},_=class{timers;constructor(){this.timers=new Map}getStyleForLevel(e){let t={trace:`color: #999999`,debug:`color: #00AAAA`,info:`color: #00AA00`,warn:`color: #AAAA00`,error:`color: #AA0000`,timer:`color: #AA00AA`,none:`color: inherit`};return t[e]||t.none}formatMessage(e,t){let n=new Date;return[`${`[${n.getHours()%12||12}:${n.getMinutes().toString().padStart(2,`0`)}:${n.getSeconds().toString().padStart(2,`0`)} ${n.getHours()>=12?`PM`:`AM`}]`} %c${t.toUpperCase()}%c (${e}): `,this.getStyleForLevel(t),``,``]}trace(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`trace`);console.debug(r,i,a,o,t,...n)}debug(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`debug`);console.debug(r,i,a,o,t,...n)}info(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`info`);console.info(r,i,a,o,t,...n)}warn(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`warn`);console.warn(r,i,a,o,t,...n)}error(e,t,...n){let[r,i,a,o]=this.formatMessage(e,`error`);console.error(r,i,a,o,t,...n)}time(e,t){let n=`${e}:${t}`;this.timers.set(n,performance.now())}timeEnd(e,t){let n=`${e}:${t}`,r=this.timers.get(n);if(r!==void 0){let i=performance.now()-r;this.timers.delete(n);let[a,o,s,c]=this.formatMessage(e,`timer`);console.info(`${a} Timer '${t}' completed in ${i.toFixed(2)}ms`,o,s,c)}else console.warn(`[${e}]: Timer '${t}' does not exist`)}},v=class{trace(e,t,...n){}debug(e,t,...n){}info(e,t,...n){}warn(e,t,...n){}error(e,t,...n){}time(e,t){}timeEnd(e,t){}},y=class{category;backend;minLevel;constructor(e,t=`none`,n=new v){this.category=e,this.backend=n,this.minLevel=t}updateSettings(e,t){this.backend=e,this.minLevel=t}trace(e,...t){this.shouldLog(`trace`)&&this.backend.trace(this.category,e,...t)}debug(e,...t){this.shouldLog(`debug`)&&this.backend.debug(this.category,e,...t)}info(e,...t){this.shouldLog(`info`)&&this.backend.info(this.category,e,...t)}warn(e,...t){this.shouldLog(`warn`)&&this.backend.warn(this.category,e,...t)}error(e,...t){this.shouldLog(`error`)&&this.backend.error(this.category,e,...t)}time(e){this.minLevel!==`none`&&this.backend.time(this.category,e)}timeEnd(e){this.minLevel!==`none`&&this.backend.timeEnd(this.category,e)}shouldLog(e){if(this.minLevel===`none`)return!1;switch(this.minLevel){case`trace`:return!0;case`debug`:return e!==`trace`;case`info`:return e===`info`||e===`warn`||e===`error`;case`warn`:return e===`warn`||e===`error`;case`error`:return e===`error`;default:return!1}}},b=class{backend;level;loggers;constructor(e=`debug`,t=new _){this.backend=t,this.level=e,this.loggers=new Map}setBackend(e){this.backend=e,this.loggers.forEach(e=>{e instanceof y&&e.updateSettings(this.backend,this.level)})}setLevel(e){this.level=e,this.loggers.forEach(e=>{e instanceof y&&e.updateSettings(this.backend,this.level)})}getLevel(){return this.level}getLogger(e){let t=this.loggers.get(e);return t||(t=new y(e,this.level,this.backend),this.loggers.set(e,t)),t}},x=class{directMap=new Map;patternSubs=new Set;_log;constructor(e){this._log=e?.getLogger(`EventBus`)||new y(`EventBus`)}subscribe(e,t){return this._log.trace(`Subscribe request for: ${e.toString()}`),e instanceof RegExp||typeof e==`string`&&e.includes(`*`)?this.subscribePattern(e,t):this.subscribeExact(e,t)}subscribeExact(e,t){this._log.debug(`Adding exact subscription for: ${e}`);let n=this.directMap.get(e);n||(n=new Set,this.directMap.set(e,n));let r={callback:e=>t(e)};return n.add(r),()=>{this._log.debug(`Removing exact subscription for: ${e}`),n.delete(r),n.size===0&&this.directMap.delete(e)}}subscribePattern(e,t){let n;if(typeof e==`string`){let t=e.replace(/[-/\\^$+?.()|[\]{}]/g,`\\$&`).replace(/\*/g,`.*`);n=RegExp(`^${t}$`),this._log.debug(`Adding pattern subscription for string: ${e}, regex: ${n.toString()}`)}else n=e,this._log.debug(`Adding pattern subscription for regex: ${n.toString()}`);let r={pattern:n,callback:e=>t(e)};return this.patternSubs.add(r),()=>{this._log.debug(`Removing pattern subscription: ${n.toString()}`),this.patternSubs.delete(r)}}publish(e){this._log.trace(`Publishing event: ${e.type}`,e);let t=0,n=this.directMap.get(e.type);if(n){t+=n.size;for(let t of n)Promise.resolve().then(()=>t.callback(e)).catch(t=>{this._log.error(`Error in event handler for '${e.type}':`,t)})}for(let n of this.patternSubs)n.pattern&&n.pattern.test(e.type)&&(t++,Promise.resolve().then(()=>n.callback(e)).catch(t=>{this._log.error(`Error in pattern event handler for '${e.type}':`,t)}));t===0?this._log.debug(`No subscribers found for event: ${e.type}`):this._log.trace(`Event ${e.type} dispatched to ${t} subscribers`)}async $teardown(){this._log.debug(`Tearing down EventBus`),this.directMap.clear(),this.patternSubs.clear()}},S=class{_engine=null;_mainBus=null;_buses=new Map;_log;constructor(e){this._log=e.getLogger(`AudioEngine`)}async initialize(){this._log.info(`Initializing audio engine...`),this._engine=await(0,t.CreateAudioEngineAsync)(),this._mainBus=await this._engine.createMainBusAsync(`main`),this._log.info(`Audio engine initialized.`)}async $teardown(){this._log.info(`Tearing down audio engine...`),this._buses.clear(),this._mainBus=null,this._engine&&=(this._engine.dispose(),null),this._log.info(`Audio engine torn down.`)}async createBus(e){if(!this._engine||!this._mainBus)throw Error(`AudioEngine not initialized`);this._log.debug(`Creating audio bus: ${e}`);let t=await this._engine.createBusAsync(e,{outBus:this._mainBus});return this._buses.set(e,t),t}getBus(e){return this._buses.get(e)}async createSound(e,t,n,r){if(!this._engine)throw Error(`AudioEngine not initialized`);let i=n??this._mainBus??void 0,a={...r,...i?{outBus:i}:{}};return this._engine.createSoundAsync(e,t,a)}setMasterVolume(e){if(!this._engine)throw Error(`AudioEngine not initialized`);this._engine.volume=e}getMasterVolume(){return this._engine?this._engine.volume:1}setBusVolume(e,t){e.volume=t}};(0,n.registerBuiltInLoaders)();var C=class{_canvas;_engine;_havok;_log;constructor(e,t,n,r){this._canvas=e,this._engine=t,this._havok=n,this._log=r.getLogger(`SceneEngine`)}createScene(){return new t.Scene(this._engine)}enablePhysics(e,n=new t.Vector3(0,-9.8,0),r){this._log.debug(`Enabling physics with gravity (${n.x}, ${n.y}, ${n.z})...`);let i=r===void 0?void 0:{floatingOriginWorldRadius:r},a=new t.HavokPlugin(!0,this._havok,i);e.enablePhysics(n,a)}createFreeCamera(e,n,r,i){let a=new t.FreeCamera(e,n,r);return a.setTarget(t.Vector3.Zero()),i??=this._canvas,i&&a.attachControl(i,!0),a}createHemisphericLight(e,n,r,i=.7){let a=new t.HemisphericLight(e,n,r);return a.intensity=i,a}createDirectionalLight(e,n,r,i=1){let a=new t.DirectionalLight(e,n,r);return a.intensity=i,a}createPointLight(e,n,r,i=1){let a=new t.PointLight(e,n,r);return a.intensity=i,a}createSpotLight(e,n,r,i,a,o,s=1){let c=new t.SpotLight(e,n,r,i,a,o);return c.intensity=s,c}createRectAreaLight(e,n,r,i,a,o=1){let s=new t.RectAreaLight(e,n,r,i,a);return s.intensity=o,s}createSphere(e,n={},r){return t.MeshBuilder.CreateSphere(e,{diameter:1,segments:32,...n},r)}createBox(e,n={},r){return t.MeshBuilder.CreateBox(e,{size:1,...n},r)}createGround(e,n={},r){return t.MeshBuilder.CreateGround(e,{width:6,height:6,subdivisions:2,...n},r)}createCylinder(e,n={},r){return t.MeshBuilder.CreateCylinder(e,{height:2,diameterTop:1,diameterBottom:1,...n},r)}addPhysics(e,n,r={},i){return new t.PhysicsAggregate(e,n,{mass:1,restitution:.75,friction:.5,...r},i)}async loadModel(e,n){this._log.debug(`Loading model: ${e}`);try{let r=await(0,t.LoadAssetContainerAsync)(`${e}`,n);return this._log.debug(`Model loaded successfully: ${e}`),r}catch(t){throw this._log.error(`Failed to load model: ${e}`,t),t}}async importMeshes(e,n,r){this._log.debug(`Importing meshes from: ${n}`);try{let i=e.length>0?{meshNames:e}:void 0,a=await(0,t.ImportMeshAsync)(`${n}`,r,i);return this._log.debug(`Meshes imported successfully: ${e.length>0?e.join(`, `):`all`}`),a}catch(e){throw this._log.error(`Failed to import meshes from: ${n}`,e),e}}async $teardown(){this._log.info(`Tearing down SceneEngine`),this._log.info(`SceneEngine torn down successfully`)}},ee=class{_eventBus;_sceneEngine;_log;_containers=new Map;_sourceMeshes=new Map;constructor(e,t,n){this._eventBus=e,this._sceneEngine=t,this._log=n?.getLogger(`AssetManager`)??new y(`AssetManager`)}_parsePath(e){let t=e.indexOf(`#`);return t===-1?{basePath:e}:{basePath:e.substring(0,t),fragment:e.substring(t+1)}}async _loadContainer(e){let t=this._containers.get(e);if(t)return t.refCount++,t.container;this._log.debug(`Loading asset container: ${e}`);let n=this._sceneEngine.createScene();try{let t=await this._sceneEngine.loadModel(e,n);return this._containers.set(e,{container:t,refCount:1}),this._log.debug(`Cached asset container: ${e}`),t}finally{n.dispose()}}_findMeshInContainer(e,t,n){let r=e.meshes.find(e=>e.name===t);if(!r){let r=e.meshes.map(e=>e.name).join(`, `);throw Error(`Mesh '${t}' not found in container for '${n}'. Available meshes: ${r}`)}return r}async load(e){let{basePath:t,fragment:n}=this._parsePath(e);if(n){let r=this._sourceMeshes.get(e);if(r){let e=this._containers.get(t);return e&&e.refCount++,r}let i=await this._loadContainer(t),a=this._findMeshInContainer(i,n,e);return this._sourceMeshes.set(e,a),a}return this._loadContainer(t)}instance(e){let t=this._sourceMeshes.get(e);if(!t)throw Error(`Asset '${e}' is not loaded. Call load() first.`);return t.createInstance(`${t.name}-instance`)}clone(e){let t=this._sourceMeshes.get(e);if(!t)throw Error(`Asset '${e}' is not loaded. Call load() first.`);return t.clone(`${t.name}-clone`)}async preload(e){let t=e.length;for(let n=0;n<e.length;n++)await this.load(e[n]),this._eventBus.publish({type:`asset:progress`,payload:{path:e[n],loaded:n+1,total:t}});this._eventBus.publish({type:`asset:complete`,payload:{paths:e}})}dispose(e){let{basePath:t}=this._parsePath(e),n=this._containers.get(t);if(n&&(n.refCount--,n.refCount<=0)){this._log.debug(`Disposing asset container: ${t} (refCount reached 0)`);for(let[e,n]of this._sourceMeshes)e.startsWith(`${t}#`)&&(n.dispose(),this._sourceMeshes.delete(e));n.container.dispose(),this._containers.delete(t)}}disposeAll(){for(let e of this._containers.values())e.container.dispose();for(let e of this._sourceMeshes.values())e.dispose();this._containers.clear(),this._sourceMeshes.clear()}async $teardown(){this.disposeAll()}},w=[`trigger`,`toggle`,`value`],T=[`keyboard`,`mouse`,`gamepad`];function E(e){return e.type===`keyboard`}function D(e){return e.type===`mouse`}function O(e){return e.type===`gamepad`}var te=class{type=`trigger`;action;context;deviceID;deviceType;reader;_edgeMode;_threshold;_lastDigitalState=!1;get options(){return{edgeMode:this._edgeMode,threshold:this._threshold}}constructor(e,t,n,r,i={}){this.action=e,this.deviceID=t,this.deviceType=n,this.reader=r,this.context=i.context,this._edgeMode=i.edgeMode??`rising`,this._threshold=i.threshold??.5}process(e,t){let n=this.reader.getValue(e)??!1,r=typeof n==`boolean`?n:n>=this._threshold,i=!1;switch(this._edgeMode){case`rising`:i=r&&!this._lastDigitalState;break;case`falling`:i=!r&&this._lastDigitalState;break;case`both`:i=r!==this._lastDigitalState;break}if(this._lastDigitalState=r,i){let e;if(this.action.type===`analog`){let t=typeof n==`number`?n:n?1:0;if(this.action.minValue!==void 0||this.action.maxValue!==void 0){let e=this.action.minValue??0,n=this.action.maxValue??1;t=e+t*(n-e)}e=t}else e=!0;let r={type:`action:${this.action.name}`,payload:{value:e,deviceId:this.deviceID,context:this.context}};t.publish(r)}}resetEdgeState(e){if(e){let t=this.reader.getValue(e)??!1;this._lastDigitalState=typeof t==`boolean`?t:t>=this._threshold}else this._lastDigitalState=!1}toJSON(){return{type:this.type,action:this.action.name,input:{deviceID:this.deviceID,...this.reader.toJSON()},context:this.context,options:this.options}}},ne=class{type=`toggle`;action;context;deviceID;deviceType;reader;_invert;_threshold;_initialState;_onValue;_offValue;_lastDigitalState=!1;_toggleState;get state(){return this._toggleState}set state(e){this._toggleState=e}get onValue(){return this._onValue}get offValue(){return this._offValue}get value(){return this.action.type===`analog`?this._toggleState?this.action.maxValue??1:this.action.minValue??0:this._toggleState?this._onValue:this._offValue}get options(){return{invert:this._invert,threshold:this._threshold,initialState:this._initialState,onValue:this._onValue,offValue:this._offValue}}constructor(e,t,n,r,i={}){this.action=e,this.deviceID=t,this.deviceType=n,this.reader=r,this.context=i.context,this._invert=i.invert??!1,this._threshold=i.threshold??.5,this._initialState=i.initialState??!1,this._toggleState=this._initialState,this._onValue=i.onValue??!0,this._offValue=i.offValue??!1}process(e,t){let n=this.reader.getValue(e)??!1,r=typeof n==`boolean`?n:n>=this._threshold,i=this._invert?!r&&this._lastDigitalState:r&&!this._lastDigitalState;this._lastDigitalState=r,i&&(this._toggleState=!this._toggleState,t.publish({type:`action:${this.action.name}`,payload:{value:this.value,deviceId:this.deviceID,context:this.context}}))}resetEdgeState(e){if(e){let t=this.reader.getValue(e)??!1;this._lastDigitalState=typeof t==`boolean`?t:t>=this._threshold}else this._lastDigitalState=!1}reset(){this._toggleState=this._initialState}toJSON(){return{type:this.type,action:this.action.name,input:{deviceID:this.deviceID,...this.reader.toJSON()},state:this._toggleState,context:this.context,options:this.options}}},re=class{type=`value`;action;context;deviceID;deviceType;reader;_scale;_offset;_invert;_emitOnChange;_deadzone;_onValue;_offValue;_min;_max;_lastValue;get options(){return{scale:this._scale,offset:this._offset,invert:this._invert,emitOnChange:this._emitOnChange,deadzone:this._deadzone,onValue:this._onValue,offValue:this._offValue,min:this._min,max:this._max}}constructor(e,t,n,r,i={}){this.action=e,this.deviceID=t,this.deviceType=n,this.reader=r,this.context=i.context,this._scale=i.scale??1,this._offset=i.offset??0,this._invert=i.invert??!1,this._emitOnChange=i.emitOnChange??!0,this._deadzone=i.deadzone??0,this._onValue=i.onValue??!0,this._offValue=i.offValue??!1;let a=this.action.type===`analog`?this.action.minValue??-1/0:-1/0;this._min=i.min??a;let o=this.action.type===`analog`?this.action.maxValue??1/0:1/0;this._max=i.max??o}process(e,t){let n=this.reader.getValue(e);if(n===void 0)return;let r=typeof n==`boolean`?n?1:0:n,i=this._deadzone>0&&Math.abs(r)<this._deadzone?0:r;i=(this._invert?-i:i)*this._scale+this._offset,i=Math.max(this._min,Math.min(this._max,i)),!(this._emitOnChange&&this._lastValue===i)&&(this._lastValue=i,t.publish({type:`action:${this.action.name}`,payload:{value:i,deviceId:this.deviceID,context:this.context}}))}resetEdgeState(e){}toJSON(){return{type:this.type,action:this.action.name,input:{deviceID:this.deviceID,...this.reader.toJSON()},context:this.context,options:this.options}}},ie=class e{sourceType=`key`;sourceKey;useDelta;constructor(e,t={}){this.sourceKey=e,this.useDelta=t.useDelta||!1}getValue(e){if(e.type!==`keyboard`)return;let t=e;return this.useDelta?t.delta[this.sourceKey]:t.keys[this.sourceKey]}static fromString(t,n={}){return new e(t,n)}toJSON(){return{type:`keyboard`,sourceType:this.sourceType,sourceKey:this.sourceKey,options:{useDelta:this.useDelta}}}},ae=class e{sourceType;sourceKey;constructor(e,t){this.sourceType=e,this.sourceKey=t}getValue(e){if(e.type===`mouse`)switch(this.sourceType){case`button`:{let t=e.buttons[this.sourceKey];return t?t.pressed:void 0}case`position`:{let[t,n]=this.sourceKey.split(`:`);return!t||!n||t!==`absolute`&&t!==`relative`||n!==`x`&&n!==`y`?void 0:e.position[t][n]}case`wheel`:{let t=this.sourceKey===`deltaX`||this.sourceKey===`deltaY`||this.sourceKey===`deltaZ`;return e.wheel&&t?e.wheel[this.sourceKey]:void 0}default:return}}static fromString(t){let[n,...r]=t.split(`:`),i=r.join(`:`);if(!n||!i)throw Error(`Invalid mouse source format: ${t}`);return new e(n,i)}toJSON(){return{type:`mouse`,sourceType:this.sourceType,sourceKey:this.sourceKey}}},oe=class e{sourceType;sourceKey;useAnalogValue;deadzone;invert;constructor(e,t,n={}){this.sourceType=e,this.sourceKey=t,this.useAnalogValue=n.useAnalogValue||!1,this.deadzone=n.deadzone||.1,this.invert=n.invert||!1}getValue(e){if(e.type===`gamepad`)switch(this.sourceType){case`button`:{let t=e.buttons[this.sourceKey];return t?this.useAnalogValue?t.value:t.pressed:void 0}case`axis`:{let t=e.axes[this.sourceKey];return t===void 0?void 0:(Math.abs(t)<this.deadzone&&(t=0),this.invert?-t:t)}default:return}}static fromString(t,n={}){let[r,i]=t.split(`:`);if(!r||!i)throw Error(`Invalid gamepad source format: ${t}`);return new e(r,i,n)}toJSON(){return{type:`gamepad`,sourceType:this.sourceType,sourceKey:this.sourceKey,options:{useAnalogValue:this.useAnalogValue,deadzone:this.deadzone,invert:this.invert}}}},se=.05,ce=.5,le=class{_bindings=new Map;_actions=new Map;_contexts=new Map;_activeContexts=new Set;_lastActiveDeviceType=null;_lastInputState=new Map;_captureState=null;_eventBus;_inputUnsubscribe=null;_log;constructor(e,t){this._eventBus=e,this._inputUnsubscribe=this._eventBus.subscribe(`input:changed`,e=>{let t=e.payload;t&&this.$handleInput(t.device,t.state)}),this._log=t?.getLogger(`BindingManager`)||new y(`BindingManager`),this._log.debug(`BindingManager initialized`)}_isBindingContextActive(e){return e.context?this._activeContexts.has(e.context):!0}_getOrCreateContext(e,t=!0){let n=this._contexts.get(e);return n||(n={name:e,exclusive:t},this._contexts.set(e,n),this._log.debug(`Auto-created context "${e}" (exclusive: ${t})`)),n}_deactivateExclusiveContexts(e){let t=[];for(let n of this._activeContexts)n!==e&&this._contexts.get(n)?.exclusive&&(this._activeContexts.delete(n),t.push(n));return t}_shouldUpdateActiveDevice(e){return D(e)?Object.values(e.buttons).some(e=>e.pressed)||e.wheel!==void 0:O(e)?Object.values(e.buttons).some(e=>e.pressed)||Object.values(e.axes).some(e=>Math.abs(e)>se):!0}_createBindingFromDefinition(e){let t=this._actions.get(e.action);if(!t)return this._log.warn(`Cannot create binding: Action "${e.action}" not found.`),null;let{deviceID:n,...r}=e.input;switch(e.type){case`trigger`:return new te(t,n,e.input.type,this._createInputSourceFromDefinition(r),{...e.options||{},context:e.context});case`toggle`:return new ne(t,n,e.input.type,this._createInputSourceFromDefinition(r),{...e.options||{},context:e.context});case`value`:return new re(t,n,e.input.type,this._createInputSourceFromDefinition(r),{...e.options||{},context:e.context});default:return this._log.error(`Binding type not implemented: ${e.type}`),null}}_createInputSourceFromDefinition(e){switch(e.type){case`keyboard`:return new ie(e.sourceKey,e.options);case`mouse`:{let t=e.sourceType;if(!(t===`button`||t===`position`||t===`wheel`))throw Error(`Invalid mouse source type: ${t}`);return new ae(t,e.sourceKey)}case`gamepad`:{let t=e.sourceType;if(!(t===`button`||t===`axis`))throw Error(`Invalid gamepad source type: ${t}`);return new oe(t,e.sourceKey,e.options)}default:throw Error(`Unsupported input source type: ${e.type}`)}}_processCaptureInput(e,t){let n=this._captureState;if(!n)return;let{options:r}=n;if(!r.deviceTypes.includes(e.type))return;let i=null;if(E(t)){if(t.event?.repeat===!0)return;for(let n of Object.keys(t.delta))if(t.delta[n]===!0){i={type:`keyboard`,sourceType:`key`,sourceKey:n,deviceID:e.id};break}}else if(D(t)){for(let n of Object.keys(t.buttons))if(t.buttons[n].pressed){i={type:`mouse`,sourceType:`button`,sourceKey:n,deviceID:e.id};break}}else if(O(t)){for(let n of Object.keys(t.buttons))if(t.buttons[n].pressed){i={type:`gamepad`,sourceType:`button`,sourceKey:n,deviceID:e.id};break}if(!i){for(let n of Object.keys(t.axes))if(Math.abs(t.axes[n])>ce){i={type:`gamepad`,sourceType:`axis`,sourceKey:n,deviceID:e.id};break}}}if(!i||r.sourceTypes&&!r.sourceTypes.includes(i.sourceType))return;let{resolve:a}=n;this._cleanupCapture(),a(i)}_cleanupCapture(){let e=this._captureState;if(!e)return;e.abortCleanup&&e.abortCleanup();let{snapshot:t}=e;this._captureState=null,this.deactivateContext(`__sage_capture__`),this._contexts.delete(`__sage_capture__`);for(let e of t)this.activateContext(e)}$handleInput(e,t){if(this._lastInputState.set(e.id,t),this._shouldUpdateActiveDevice(t)&&e.type!==this._lastActiveDeviceType){let t=this._lastActiveDeviceType;this._lastActiveDeviceType=e.type,this._eventBus.publish({type:`input:device-type:changed`,payload:{deviceType:e.type,previousDeviceType:t}})}if(this._captureState){this._processCaptureInput(e,t);return}let n=this._bindings.get(e.id);if(!(!n||n.length===0)&&!(this._activeContexts.size===0&&n.some(e=>e.context)))for(let e of n)this._isBindingContextActive(e)&&e.process(t,this._eventBus)}registerAction(e){if(this._log.debug(`Registering action "${e.name}"`),this._actions.has(e.name)){let t=`Action "${e.name}" already registered.`;throw this._log.error(t),Error(t)}this._actions.set(e.name,e),this._log.debug(`Action "${e.name}" registered successfully`)}getAction(e){this._log.trace(`Getting action "${e}"`);let t=this._actions.get(e)??null;return t||this._log.debug(`Action "${e}" not found`),t}get lastActiveDeviceType(){return this._lastActiveDeviceType}captureInput(e){if(this._captureState)throw Error(`captureInput() already in progress`);let t=[...this._activeContexts];return this.registerContext(`__sage_capture__`,!0),this.activateContext(`__sage_capture__`),new Promise((n,r)=>{let i={resolve:n,reject:r,options:e,snapshot:t};if(this._captureState=i,e.signal){if(e.signal.aborted){this._cleanupCapture(),r(e.signal.reason??Error(`captureInput() aborted`));return}let t=()=>{let t=e.signal?.reason??Error(`captureInput() aborted`);this._cleanupCapture(),r(t)};e.signal.addEventListener(`abort`,t,{once:!0}),i.abortCleanup=()=>{e.signal?.removeEventListener(`abort`,t)}}})}registerContext(e,t=!0){let n=this._getOrCreateContext(e,t);return n.exclusive!==t&&(n.exclusive=t,this._log.info(`Updated context "${e}" exclusivity: ${t}`)),n}activateContext(e){let t=this._getOrCreateContext(e).exclusive;this._log.debug(`Activating context "${e}" (exclusive: ${t})`);let n=this._activeContexts.has(e);if(t){let t=this._deactivateExclusiveContexts(e);t.length>0&&this._log.info(`Deactivated exclusive contexts: ${t.join(`, `)}`)}if(!n){this._activeContexts.add(e);for(let[t,n]of this._bindings.entries()){let r=this._lastInputState.get(t);for(let t of n)t.context===e&&t.resetEdgeState(r)}this._log.info(`Context "${e}" activated${t?` as exclusive`:``}`)}}deactivateContext(e){this._log.debug(`Deactivating context "${e}"`),this._activeContexts.has(e)?(this._activeContexts.delete(e),this._log.info(`Context "${e}" deactivated`)):this._log.debug(`Context "${e}" was not active`)}getActiveContexts(){return[...this._activeContexts]}isContextActive(e){return this._activeContexts.has(e)}getContext(e){return this._contexts.get(e)||null}$registerBinding(e){if(!w.includes(e.type))throw Error(`Invalid binding type: ${e.type}`);e.context&&!this._contexts.has(e.context)&&this.registerContext(e.context),this._bindings.has(e.deviceID)||this._bindings.set(e.deviceID,[]),this._bindings.get(e.deviceID)?.push(e),this._log.debug(`Registered ${e.type} binding for "${e.action.name}" in context "${e.context||null}"`)}registerBinding(e){let t=this._createBindingFromDefinition(e);t?this.$registerBinding(t):this._log.error(`Failed to create binding for action "${e.action}" with type "${e.type}"`)}unregisterBindings(e,t=null){this._log.debug(`Unregistering all bindings for action "${e}" in context "${t}"`);let n=0;for(let[r,i]of this._bindings.entries()){let a=i.filter(r=>{let i=r.context||null,a=r.action.name!==e||i!==t;return a||n++,a});a.length!==i.length&&this._bindings.set(r,a)}this._log.info(`Removed ${n} bindings for action "${e}" in context "${t}"`)}getBindingsForAction(e,t,n){let r=[];for(let i of this._bindings.values())for(let a of i){if(a.action.name!==e||t!==void 0&&a.deviceType!==t)continue;let i=a.context||null;n!==void 0&&i!==n||r.push(a)}return r}exportConfiguration(){this._log.debug(`Exporting binding configuration`);let e=[...this._actions.values()].map(e=>e.type===`analog`?{name:e.name,type:e.type,label:e.label,minValue:e.minValue??0,maxValue:e.maxValue??1}:e),t=[];for(let e of this._bindings.values())for(let n of e)t.push(n.toJSON());let n=[...this._contexts.values()].map(e=>({name:e.name,exclusive:e.exclusive,active:this._activeContexts.has(e.name)}));return this._log.info(`Configuration exported: ${e.length} actions, ${t.length} bindings, ${n.length} contexts`),{actions:e,bindings:t,contexts:n}}importConfiguration(e){this._log.debug(`Importing binding configuration`),this._bindings.clear(),this._actions.clear(),this._contexts.clear(),this._activeContexts.clear();for(let t of e.actions)this.registerAction(t);for(let t of e.contexts)this.registerContext(t.name,t.exclusive),t.active&&this.activateContext(t.name);for(let t of e.bindings)try{this.registerBinding(t)}catch(e){e instanceof Error&&this._log.error(`Failed to import binding for action "${t.action}": ${e.message}`)}this._log.info(`Configuration imported: ${e.actions.length} actions, ${e.bindings.length} bindings, ${e.contexts.length} contexts`)}async $teardown(){if(this._log.info(`Tearing down BindingManager`),this._captureState){let{reject:e}=this._captureState;this._cleanupCapture(),e(Error(`BindingManager torn down during captureInput()`))}this._inputUnsubscribe&&=(this._inputUnsubscribe(),null),this._bindings.clear(),this._lastInputState.clear(),this._actions.clear(),this._contexts.clear(),this._activeContexts.clear(),this._lastActiveDeviceType=null,this._log.info(`BindingManager torn down successfully`)}};function k(){return typeof window<`u`&&window.document!==void 0}function A(){return k()&&!!window.navigator.gpu}for(var ue=class{_engine;_eventBus;_entityManager;_inputManager;_levelManager;_log;_boundRenderLoop;_boundResizeHandler;_frameCallbacks=[];_nextCallbackId=0;_paused=!1;started=!1;constructor(e,t,n,r,i,a){this._engine=e,this._eventBus=t,this._entityManager=n,this._inputManager=r,this._levelManager=i,this._log=a?.getLogger(`GameManager`)||new y(`GameManager`),this._boundRenderLoop=this._renderLoop.bind(this),this._boundResizeHandler=this._resizeHandler.bind(this),k()&&window.addEventListener(`resize`,this._boundResizeHandler)}get currentScene(){return this._levelManager.currentLevel?.scene??null}get isPaused(){return this._paused}registerFrameCallback(e){let t=this._nextCallbackId++;return this._frameCallbacks.push({id:t,callback:e}),this._log.debug(`Registered frame callback (id: ${t})`),()=>{let e=this._frameCallbacks.findIndex(e=>e.id===t);e!==-1&&(this._frameCallbacks.splice(e,1),this._log.debug(`Unregistered frame callback (id: ${t})`))}}_renderLoop(){let e=this._engine.getDeltaTime()/1e3;if(this._inputManager.pollGamepads(),!this._paused){this._entityManager.$frameUpdate(e);for(let{callback:t}of this._frameCallbacks)try{t(e)}catch(e){this._log.error(`Error in frame callback:`,e)}}this.currentScene&&this.currentScene.render()}_resizeHandler(){this.started&&this._engine.resize()}async start(){this._engine.runRenderLoop(this._boundRenderLoop),this.started=!0,this._log.info(`SkewedAspect Game Engine started successfully`)}async stop(){this.started=!1,this._engine.stopRenderLoop(),this._log.info(`SkewedAspect Game Engine stopped successfully`)}pause(e){if(this._paused){this._log.debug(`Game is already paused`);return}this._paused=!0,this._log.info(`Game paused`,e?`(reason: ${e})`:``),this._eventBus.publish({type:`game:paused`,payload:{reason:e}})}resume(e){if(!this._paused){this._log.debug(`Game is not paused`);return}this._paused=!1,this._log.info(`Game resumed`,e?`(reason: ${e})`:``),this._eventBus.publish({type:`game:resumed`,payload:{reason:e}})}async $teardown(){this._log.info(`Tearing down GameManager`),this.started&&await this.stop(),k()&&window.removeEventListener(`resize`,this._boundResizeHandler),this._frameCallbacks=[],this._log.info(`GameManager torn down successfully`)}},j=256,M=[];j--;)M[j]=(j+256).toString(16).substring(1);function de(e){e||=16;var t=``,n=0;return function(){if(!t||n===256){for(t=``,n=(1+e)/2|0;n--;)t+=M[256*Math.random()|0];t=t.substring(n=0,e-2)}return t+M[n++]}}var N=de(16),P=class{entity=null;$emit(e){if(!this.entity)throw Error(`Entity is not set for this behavior.`);e.senderID=this.entity.id,this.entity.eventBus.publish(e)}$emitStateChanged(e,t){if(!this.entity)throw Error(`Entity is not set for this behavior.`);this.$emit({type:`entity:state-changed`,payload:{entityId:this.entity.id,entityType:this.entity.type,state:e??this.entity.state,changes:t}})}$setEntity(e){this.entity=e}},F=class{id;type;name;node;children=[];parent=null;state;behaviors=[];eventBus;_gameEngine=null;_entityManager=null;_tags=new Set;subscriptions=new Map;constructor(e,t,n,r,i){this.id=N(),this.type=e,this.name=i,this.state=n,this.eventBus=t;for(let e of r)this.attachBehavior(new e)}get tags(){return this._tags}hasTag(e){return this._tags.has(e)}$addTag(e){this._tags.add(e)}$removeTag(e){return this._tags.delete(e)}getChildNode(e){if(this.node)return this.node.getChildren(t=>t.name===e,!1)[0]}findNode(e){if(!this.node)return;let t=this.node;for(let n of e.split(`/`))t=t?.getChildren(e=>e.name===n,!1)[0];return t}$attachToNode(e,t){this.node=e,this._gameEngine=t;for(let n of this.behaviors)n.onNodeAttached?.(e,t);for(let t of this.children)t.node&&(t.node.parent=e)}$detachFromNode(){this.unhighlight();for(let e of this.children)e.node&&(e.node.parent=null);for(let e of this.behaviors)e.onNodeDetached?.();this.node=void 0,this._gameEngine=null}highlight(e){let t=this._gameEngine?.managers?.levelManager?.currentLevel?.outlines;t&&t.highlightEntity(e,this)}unhighlight(e){let t=this._gameEngine?.managers?.levelManager?.currentLevel?.outlines;t&&(e?t.unhighlightEntity(e,this):t.unhighlightEntityAll(this))}$addChild(e){this.children.push(e),e.parent=this,this.node&&e.node&&(e.node.parent=this.node)}$removeChild(e){let t=this.children.indexOf(e);t!==-1&&(this.children.splice(t,1),e.parent=null,e.node&&(e.node.parent=null))}$setEntityManager(e){this._entityManager=e}async send(e,t,n){if(e===this.id)return this.$send(t,n,this.id);if(!this._entityManager)throw Error(`Entity is not registered with an EntityManager.`);return this._entityManager.send(e,t,n,this.id)}async request(e,t,n){if(e===this.id)return this.$request(t,n,this.id);if(!this._entityManager)throw Error(`Entity is not registered with an EntityManager.`);return this._entityManager.request(e,t,n,this.id)}async $send(e,t,n){await this.$processEvent({type:e,targetID:this.id,senderID:n,payload:t})}async $request(e,t,n){let r={type:e,targetID:this.id,senderID:n,payload:t},i;for(let e of this.behaviors)try{let t=await e.processRequest?.(r,this.state);if(t!==void 0)return{success:!0,value:t}}catch(e){i=e instanceof Error?e.message:String(e)}return{success:!1,error:i??`No handler for request "${e}" on entity "${this.name??this.id}"`}}$reset(e,t){this.state={...e},this._tags.clear();for(let e of t)this._tags.add(e);this.node&&this.$detachFromNode();for(let e of this.behaviors)e.onReset?.(this.state);for(let e of this.children)e.$reset({},[]);this.parent=null}async $processEvent(e){for(let t of this.behaviors)if(await t.processEvent(e,this.state))break}$update(e){for(let t of this.behaviors)t.update?.(e,this.state);for(let t of this.children)t.$update(e)}async $destroy(){for(let e of[...this.children])await e.$destroy();this.children.length=0;for(let e of this.behaviors)await e.destroy?.();for(let e of this.subscriptions.values())e.unsubscribe();this.behaviors=[],this.subscriptions.clear()}findBehaviorIndex(e){return this.behaviors.findIndex(t=>t.constructor===e)}hasBehavior(e){return this.findBehaviorIndex(e)!==-1}getBehavior(e){return this.behaviors.find(t=>t.constructor===e)}attachBehavior(e,t){if(this.hasBehavior(e.constructor))throw Error(`Behavior ${e.constructor.name} is already attached to this entity.`);for(let t of e.eventSubscriptions){let e=this.subscriptions.get(t);if(e)e.count++;else{let e=this.eventBus.subscribe(t,this.$processEvent.bind(this));this.subscriptions.set(t,{count:1,unsubscribe:e})}}if(!t)this.behaviors.push(e);else{if([t.before,t.after,t.at].filter(e=>e!==void 0).length>1)throw Error(`attachBehavior: specify only one of before, after, or at`);if(t.before){let n=this.findBehaviorIndex(t.before);if(n===-1)throw Error(`attachBehavior: no behavior of type ${t.before.name} found`);this.behaviors.splice(n,0,e)}else if(t.after){let n=this.findBehaviorIndex(t.after);if(n===-1)throw Error(`attachBehavior: no behavior of type ${t.after.name} found`);this.behaviors.splice(n+1,0,e)}else if(t.at!==void 0){let n=Math.max(0,Math.min(t.at,this.behaviors.length));this.behaviors.splice(n,0,e)}else this.behaviors.push(e)}e.$setEntity(this),this.node&&this._gameEngine&&e.onNodeAttached?.(this.node,this._gameEngine)}detachBehavior(e){let t=this.findBehaviorIndex(e);if(t===-1)return!1;let n=this.behaviors[t];this.node&&n.onNodeDetached?.();for(let e of n.eventSubscriptions){let t=this.subscriptions.get(e);t&&(t.count--,t.count<=0&&(t.unsubscribe(),this.subscriptions.delete(e)))}return this.behaviors.splice(t,1),n.$setEntity(null),!0}},fe=class{eventBus;entities=new Map;entityDefinitions=new Map;_gameEngine=null;bindingManager;_log;_entitiesByName=new Map;_entitiesByType=new Map;_entitiesByTag=new Map;_entitiesByNode=new Map;_pools=new Map;constructor(e,t,n){this.eventBus=e,this.bindingManager=n,this._log=t?.getLogger(`EntityManager`)||new y(`EntityManager`),this._log.info(`EntityManager initialized`)}$setGameEngine(e){this._gameEngine=e}get _engine(){if(!this._gameEngine)throw Error(`EntityManager: gameEngine not set. Call $setGameEngine() first.`);return this._gameEngine}_getOrCreateSet(e,t){let n=e.get(t);return n||(n=new Set,e.set(t,n)),n}_indexEntity(e){this._getOrCreateSet(this._entitiesByType,e.type).add(e),e.name&&this._getOrCreateSet(this._entitiesByName,e.name).add(e);for(let t of e.tags)this._getOrCreateSet(this._entitiesByTag,t).add(e);e.node&&this._entitiesByNode.set(e.node,e)}_unindexEntity(e){let t=this._entitiesByType.get(e.type);if(t&&(t.delete(e),t.size===0&&this._entitiesByType.delete(e.type)),e.name){let t=this._entitiesByName.get(e.name);t&&(t.delete(e),t.size===0&&this._entitiesByName.delete(e.name))}for(let t of e.tags){let n=this._entitiesByTag.get(t);n&&(n.delete(e),n.size===0&&this._entitiesByTag.delete(t))}e.node&&this._entitiesByNode.delete(e.node)}_resolveInheritance(e){let t=e.extends;if(!t)return e;let n=this.entityDefinitions.get(t);if(!n){let e=`Cannot extend "${t}": base entity definition is not registered.`;throw this._log.error(e),Error(e)}let r;return(n.tags||e.tags)&&(r=[...new Set([...n.tags??[],...e.tags??[]])]),{type:e.type,name:e.name??n.name,mesh:e.mesh??n.mesh,defaultState:{...n.defaultState,...e.defaultState},behaviors:e.behaviors??n.behaviors,actions:e.actions??n.actions,tags:r,poolable:e.poolable??n.poolable,poolSize:e.poolSize??n.poolSize,children:e.children??n.children,onBeforeCreate:e.onBeforeCreate??n.onBeforeCreate,onCreate:e.onCreate??n.onCreate,onBeforeDestroy:e.onBeforeDestroy??n.onBeforeDestroy,onDestroy:e.onDestroy??n.onDestroy}}_areActionsCompatible(e,t){return!(e.type!==t.type||e.type===`analog`&&t.type===`analog`&&(t.minValue!==void 0&&e.minValue!==t.minValue||t.maxValue!==void 0&&e.maxValue!==t.maxValue))}_registerEntityActions(e){if(e.actions)for(let t of e.actions)try{let n=this.bindingManager.getAction(t.name);n?this._areActionsCompatible(n,t)?this._log.trace(`Action "${t.name}" already registered with compatible options, skipping registration`):this._log.warn(`Action "${t.name}" already registered with different options. Entity "${e.type}" requires: ${JSON.stringify(t)}, but found: ${JSON.stringify(n)}`):(this._log.debug(`Registering action "${t.name}" from entity type "${e.type}"`),this.bindingManager.registerAction(t))}catch(e){this._log.debug(`Failed to register action "${t.name}": ${e instanceof Error?e.message:String(e)}`)}}$frameUpdate(e){this._log.trace(`Updating ${this.entities.size} entities with dt=${e}`);for(let t of this.entities.values())t.parent||t.$update(e)}registerEntityDefinition(e){this._log.debug(`Registering entity definition: ${e.type}`);let t=e.extends?this._resolveInheritance(e):e;this._registerEntityActions(t),this.entityDefinitions.set(t.type,t),t.poolSize&&t.poolSize>0&&this.prewarm(t.type,t.poolSize)}getDefinition(e){return this.entityDefinitions.get(e)}async createEntity(e,t={}){this._log.debug(`Creating entity of type: ${e}`);let n=this.entityDefinitions.get(e);if(!n){let t=`Entity type ${e} is not registered.`;throw this._log.error(t),Error(t)}if(n.poolable){let r=this._pools.get(e);if(r&&r.length>0){this._log.trace(`Recycling entity from pool for type: ${e}`);let i=r.pop();if(i.$reset(n.defaultState,n.tags??[]),i.$setEntityManager(this),t.initialState&&(i.state={...i.state,...t.initialState}),t.tags)for(let e of t.tags)i.$addTag(e);return t.node&&i.$attachToNode(t.node,this._engine),this.entities.set(i.id,i),this._indexEntity(i),this._log.debug(`Recycled entity ${i.id} from pool`),i}}this._log.trace(`Using entity definition with ${n.behaviors?.length||0} behaviors`);let r={...n.defaultState,...t.initialState};if(n.onBeforeCreate){this._log.trace(`Calling onBeforeCreate hook for entity type: ${e}`);let t=await n.onBeforeCreate(r);t!==void 0&&(r=t)}let i=t.name??n.name,a=new F(n.type,this.eventBus,r,n.behaviors,i);if(a.$setEntityManager(this),t.node&&a.$attachToNode(t.node,this._engine),n.tags)for(let e of n.tags)a.$addTag(e);if(t.tags)for(let e of t.tags)a.$addTag(e);if(this.entities.set(a.id,a),this._indexEntity(a),this._log.debug(`Entity created with ID: ${a.id}${i?` (name: ${i})`:``}`),n.onCreate){this._log.trace(`Calling onCreate hook for entity type: ${e}`);let t=await n.onCreate(a.state);t!==void 0&&(a.state=t)}if(n.children)for(let e of n.children){let t={...e.initialState};e.position&&(t._position=e.position),e.rotation&&(t._rotation=e.rotation);let n=await this.createEntity(e.type,{name:e.name,initialState:t});a.$addChild(n)}return a}async destroyEntity(e){this._log.debug(`Destroying entity: ${e}`);let t=this.entities.get(e);if(t){let n=this.entityDefinitions.get(t.type);if(n?.poolable){this._log.trace(`Pooling entity ${e} instead of destroying`);for(let e of[...t.children])await this.destroyEntity(e.id);t.parent&&t.parent.$removeChild(t),this._unindexEntity(t),t.$reset(n.defaultState,n.tags??[]);let r=this._pools.get(t.type);r||(r=[],this._pools.set(t.type,r)),r.push(t),this.entities.delete(e),this._log.debug(`Entity ${e} pooled`);return}for(let e of[...t.children])await this.destroyEntity(e.id);if(t.parent&&t.parent.$removeChild(t),n?.onBeforeDestroy){this._log.trace(`Calling onBeforeDestroy hook for entity: ${e}`);let r=await n.onBeforeDestroy(t.state);r!==void 0&&(t.state=r)}this._unindexEntity(t),await t.$destroy(),n?.onDestroy&&(this._log.trace(`Calling onDestroy hook for entity: ${e}`),await n.onDestroy(t.state)),this.entities.delete(e),this._log.debug(`Entity ${e} destroyed`)}else this._log.warn(`Attempted to destroy non-existent entity: ${e}`)}getEntity(e){return this._log.trace(`Getting entity: ${e}`),this.entities.get(e)??null}addEntity(e){this._log.debug(`Adding existing entity: ${e.id} (type: ${e.type})`),e.$setEntityManager(this),this.entities.set(e.id,e),this._indexEntity(e)}removeEntity(e){this._log.debug(`Removing entity: ${e}`);let t=this.entities.get(e);t?(this._unindexEntity(t),this.entities.delete(e),this._log.debug(`Entity ${e} removed`)):this._log.warn(`Attempted to remove non-existent entity: ${e}`)}addChild(e,t){let n=this.entities.get(e),r=this.entities.get(t);return n?r?(n.$addChild(r),this._log.trace(`Added child ${t} to parent ${e}`),!0):(this._log.warn(`addChild: child entity "${t}" not found`),!1):(this._log.warn(`addChild: parent entity "${e}" not found`),!1)}removeChild(e,t){let n=this.entities.get(e),r=this.entities.get(t);return n?r?(n.$removeChild(r),this._log.trace(`Removed child ${t} from parent ${e}`),!0):(this._log.warn(`removeChild: child entity "${t}" not found`),!1):(this._log.warn(`removeChild: parent entity "${e}" not found`),!1)}getByName(e){let t=this._entitiesByName.get(e);if(t&&t.size>0)return t.values().next().value}getEntitiesByName(e){let t=this._entitiesByName.get(e);return t?Array.from(t):[]}getByType(e){let t=this._entitiesByType.get(e);return t?Array.from(t):[]}getByTag(e){let t=this._entitiesByTag.get(e);return t?Array.from(t):[]}getByTags(e,t=`all`){if(e.length===0)return[];if(t===`any`){let t=new Set;for(let n of e){let e=this._entitiesByTag.get(n);if(e)for(let n of e)t.add(n)}return Array.from(t)}else{let t=e.map(e=>this._entitiesByTag.get(e)).filter(e=>e!==void 0);if(t.length!==e.length)return[];t.sort((e,t)=>e.size-t.size);let[n,...r]=t;return Array.from(n).filter(e=>r.every(t=>t.has(e)))}}getAllEntities(){return this.entities.values()}get entityCount(){return this.entities.size}async send(e,t,n,r){let i=this.entities.get(e)??this.getByName(e);if(!i){this._log.warn(`send: entity "${e}" not found`);return}await i.$send(t,n,r)}async request(e,t,n,r){let i=this.entities.get(e)??this.getByName(e);return i?i.$request(t,n,r):{success:!1,error:`Entity "${e}" not found`}}addTag(e,t){let n=typeof e==`string`?this.entities.get(e):e;return n?n.hasTag(t)?!1:(n.$addTag(t),this._getOrCreateSet(this._entitiesByTag,t).add(n),this._log.trace(`Added tag "${t}" to entity ${n.id}`),!0):(this._log.warn(`addTag: entity not found`),!1)}removeTag(e,t){let n=typeof e==`string`?this.entities.get(e):e;if(!n)return this._log.warn(`removeTag: entity not found`),!1;if(!n.$removeTag(t))return!1;let r=this._entitiesByTag.get(t);return r&&(r.delete(n),r.size===0&&this._entitiesByTag.delete(t)),this._log.trace(`Removed tag "${t}" from entity ${n.id}`),!0}getByNode(e){return this._entitiesByNode.get(e)}attachToNode(e,t){let n=typeof e==`string`?this.entities.get(e):e;return n?this._entitiesByNode.has(t)?(this._log.warn(`attachToNode: node already has an attached entity`),!1):(n.node&&this._entitiesByNode.delete(n.node),n.$attachToNode(t,this._engine),this._entitiesByNode.set(t,n),this._log.trace(`Attached entity ${n.id} to node "${t.name}"`),!0):(this._log.warn(`attachToNode: entity not found`),!1)}detachFromNode(e){let t=typeof e==`string`?this.entities.get(e):e;if(!t)return this._log.warn(`detachFromNode: entity not found`),!1;if(!t.node)return!1;this._entitiesByNode.delete(t.node);let n=t.node.name;return t.$detachFromNode(),this._log.trace(`Detached entity ${t.id} from node "${n}"`),!0}async prewarm(e,t){this._log.debug(`Prewarming pool for "${e}" with ${t} entities`);for(let n=0;n<t;n++){let t=await this.createEntity(e);await this.destroyEntity(t.id)}}async drainPool(e){let t=this._pools.get(e);if(t){this._log.debug(`Draining pool for "${e}" (${t.length} entities)`);for(let e of t)await e.$destroy();t.length=0,this._pools.delete(e)}}async $teardown(){this._log.info(`Tearing down EntityManager with ${this.entities.size} entities`);let e=[...this.entities.keys()];for(let t of e)try{await this.destroyEntity(t)}catch(e){this._log.error(`Error destroying entity ${t}: ${e}`)}for(let e of[...this._pools.keys()])try{await this.drainPool(e)}catch(t){this._log.error(`Error draining pool for "${e}": ${t}`)}this.entityDefinitions.clear(),this._entitiesByName.clear(),this._entitiesByType.clear(),this._entitiesByTag.clear(),this._entitiesByNode.clear(),this._log.info(`EntityManager torn down successfully`)}},pe={hable:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,reinhard:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,hejidawson:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,photographic:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,aces:t.ImageProcessingConfiguration.TONEMAPPING_ACES};function me(e,n){if(!e.activeCamera)return;n.volumetric&&console.warn(`[SAGE] Volumetric lighting requires renderer: "frameGraph". Ignored in pipeline mode.`);let r=new t.DefaultRenderingPipeline(`sage-default`,!0,e,e.cameras);if(n.bloom&&(r.bloomEnabled=!0,n.bloom.weight!==void 0&&(r.bloomWeight=n.bloom.weight),n.bloom.threshold!==void 0&&(r.bloomThreshold=n.bloom.threshold),n.bloom.scale!==void 0&&(r.bloomScale=n.bloom.scale),n.bloom.kernel!==void 0&&(r.bloomKernel=n.bloom.kernel)),n.tonemap&&(r.imageProcessingEnabled=!0,r.imageProcessing.toneMappingEnabled=!0,n.tonemap.operator&&(r.imageProcessing.toneMappingType=pe[n.tonemap.operator]??t.ImageProcessingConfiguration.TONEMAPPING_STANDARD)),n.grain&&(r.grainEnabled=!0,n.grain.intensity!==void 0&&(r.grain.intensity=n.grain.intensity),n.grain.animated!==void 0&&(r.grain.animated=n.grain.animated)),n.vignette&&(r.imageProcessingEnabled=!0,r.imageProcessing.vignetteEnabled=!0,n.vignette.weight!==void 0&&(r.imageProcessing.vignetteWeight=n.vignette.weight),n.vignette.stretch!==void 0&&(r.imageProcessing.vignetteStretch=n.vignette.stretch),n.vignette.color&&(r.imageProcessing.vignetteColor=new t.Color4(n.vignette.color.r,n.vignette.color.g,n.vignette.color.b,1))),n.sharpen&&(r.sharpenEnabled=!0,n.sharpen.edge!==void 0&&(r.sharpen.edgeAmount=n.sharpen.edge),n.sharpen.color!==void 0&&(r.sharpen.colorAmount=n.sharpen.color)),n.chromaticAberration&&(r.chromaticAberrationEnabled=!0,n.chromaticAberration.amount!==void 0&&(r.chromaticAberration.aberrationAmount=n.chromaticAberration.amount)),n.ssao){let r=new t.SSAO2RenderingPipeline(`sage-ssao`,e,{ssaoRatio:.5,blurRatio:1});n.ssao.radius!==void 0&&(r.radius=n.ssao.radius),n.ssao.samples!==void 0&&(r.samples=n.ssao.samples),n.ssao.totalStrength!==void 0&&(r.totalStrength=n.ssao.totalStrength)}}var he=p({applyFrameGraphPostProcessing:()=>ge});async function ge(e,n){let r=e.activeCamera;if(!r)return;let i=e.getEngine(),a=new t.FrameGraph(e),o=a.textureManager.createRenderTargetTexture(`sage-fg-color`,{size:{width:100,height:100},sizeIsPercentage:!0,options:{formats:[t.Constants.TEXTUREFORMAT_RGBA],samples:4}}),s=new t.FrameGraphClearTextureTask(`sage-clear`,a);s.targetTexture=o,a.addTask(s);let c=new t.FrameGraphObjectRendererTask(`sage-render`,a,e);c.targetTexture=s.outputTexture,c.depthTexture=s.outputDepthTexture,c.camera=r,c.objectList={meshes:e.meshes,particleSystems:e.particleSystems},c.isMainObjectRenderer=!0,a.addTask(c);let l=c.outputTexture;if(n.ssao){let i=new t.FrameGraphGeometryRendererTask(`sage-geometry`,a,e);a.addTask(i);let o=new t.FrameGraphSSAO2RenderingPipelineTask(`sage-ssao`,a,.5,1);o.sourceTexture=l,o.depthTexture=i.geometryViewDepthTexture,o.normalTexture=i.geometryViewNormalTexture,o.camera=r,a.addTask(o),n.ssao.radius!==void 0&&(o.ssao.radius=n.ssao.radius),n.ssao.samples!==void 0&&(o.ssao.samples=n.ssao.samples),n.ssao.totalStrength!==void 0&&(o.ssao.totalStrength=n.ssao.totalStrength),l=o.outputTexture}if(n.bloom){let e=new t.FrameGraphBloomTask(`sage-bloom`,a,n.bloom.weight??.5,n.bloom.kernel??128,n.bloom.threshold??.1,!0,n.bloom.scale??.5);e.sourceTexture=l,a.addTask(e),l=e.outputTexture}if(n.grain){let e=new t.FrameGraphGrainTask(`sage-grain`,a);e.sourceTexture=l,a.addTask(e),n.grain.intensity!==void 0&&(e.postProcess.intensity=n.grain.intensity),n.grain.animated!==void 0&&(e.postProcess.animated=n.grain.animated),l=e.outputTexture}if(n.sharpen){let e=new t.FrameGraphSharpenTask(`sage-sharpen`,a);e.sourceTexture=l,a.addTask(e),n.sharpen.edge!==void 0&&(e.postProcess.edgeAmount=n.sharpen.edge),n.sharpen.color!==void 0&&(e.postProcess.colorAmount=n.sharpen.color),l=e.outputTexture}if(n.chromaticAberration){let e=new t.FrameGraphChromaticAberrationTask(`sage-chromab`,a);e.sourceTexture=l,a.addTask(e),n.chromaticAberration.amount!==void 0&&(e.postProcess.aberrationAmount=n.chromaticAberration.amount),l=e.outputTexture}if(n.tonemap||n.vignette){let e=new t.FrameGraphImageProcessingTask(`sage-imgproc`,a);if(e.sourceTexture=l,a.addTask(e),n.tonemap&&(e.postProcess.toneMappingEnabled=!0,n.tonemap.operator&&(e.postProcess.toneMappingType=I[n.tonemap.operator]??t.ImageProcessingConfiguration.TONEMAPPING_STANDARD)),n.vignette&&(e.postProcess.vignetteEnabled=!0,n.vignette.weight!==void 0&&(e.postProcess.vignetteWeight=n.vignette.weight),n.vignette.stretch!==void 0&&(e.postProcess.vignetteStretch=n.vignette.stretch),n.vignette.color)){let t=n.vignette.color;e.postProcess.vignetteColor.set(t.r,t.g,t.b,1)}l=e.outputTexture}n.volumetric&&console.warn(`[SAGE] Volumetric lighting config is recognized but automatic light detection is not yet supported. Use scene.frameGraph to add FrameGraphVolumetricLightingTask manually.`);let u=new t.FrameGraphCopyToBackbufferColorTask(`sage-output`,a);u.sourceTexture=l,a.addTask(u),e.cameraToUseForPointers=r,e.frameGraph=a,i.onResizeObservable.add(async()=>{await a.buildAsync()}),await a.buildAsync()}var I,_e=f((()=>{I={hable:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,reinhard:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,hejidawson:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,photographic:t.ImageProcessingConfiguration.TONEMAPPING_STANDARD,aces:t.ImageProcessingConfiguration.TONEMAPPING_ACES}}));async function L(e,t){if(t.renderer===`frameGraph`){let{applyFrameGraphPostProcessing:n}=await Promise.resolve().then(()=>(_e(),he));await n(e,t)}else me(e,t)}function R(e){return{x:e.x,y:e.y,z:e.z}}function z(e){return new t.Vector3(e.x,e.y,e.z)}function B(e){return{x:e.x,y:e.y,z:e.z,w:e.w}}function ve(e){return new t.Quaternion(e.x,e.y,e.z,e.w)}var V=class{name;_log;_context;_scene=null;clusteredLights=null;outlines=null;constructor(e,t){this.name=e.name,this._context=t,this._log=t.logger?.getLogger(`Level:${e.name}`)??new y(`Level:${e.name}`,`info`)}get scene(){return this._scene}get isLoaded(){return this._scene!==null}get gameEngine(){return this._context.gameEngine}get propertyHandlers(){return this._context.propertyHandlers}$emitProgress(e,t){this.gameEngine.eventBus.publish({type:`level:progress`,payload:{levelName:this.name,progress:e,message:t}})}async load(){if(this._scene)return this._log.warn(`Level ${this.name} is already loaded`),this._scene;this._log.info(`Loading level: ${this.name}`),this._log.time(`level-${this.name}-load`);try{return this.$emitProgress(0,`Starting level load...`),this._scene=await this.buildScene(),this.$emitProgress(100,`Level loaded successfully`),this.gameEngine.eventBus.publish({type:`level:complete`,payload:{levelName:this.name,message:`Level loaded successfully`,level:this}}),this._log.timeEnd(`level-${this.name}-load`),this._log.info(`Level ${this.name} loaded successfully`),this._scene}catch(e){throw this._log.error(`Failed to load level ${this.name}:`,e),this.gameEngine.eventBus.publish({type:`level:error`,payload:{levelName:this.name,message:`Failed to load level`,error:e}}),e}}async $dispose(){if(this._scene){this._log.info(`Disposing level: ${this.name}`);let e=this._context.gameEngine?.managers?.entityManager;if(e?.getAllEntities)for(let t of e.getAllEntities())t.node&&t.node.getScene()===this._scene&&e.detachFromNode(t);this.outlines&&=(this.outlines.dispose(),null),this.clusteredLights&&=(this.clusteredLights.dispose(),null),this._scene.dispose(),this._scene=null}}},H=class{_scene;_layers=new Map;constructor(e,t){if(this._scene=e,t)for(let[e,n]of Object.entries(t))this.create(e,n)}get(e){return this._layers.get(e)?.layer}create(e,n){let r=this._layers.get(e);if(r)return r.layer;let i=new t.SelectionOutlineLayer(`sage-outline-${e}`,this._scene);return n?.color&&(i.outlineColor=new t.Color3(n.color.r,n.color.g,n.color.b)),n?.thickness!==void 0&&(i.outlineThickness=n.thickness),n?.occlusionStrength!==void 0&&(i.occlusionStrength=n.occlusionStrength),this._layers.set(e,{layer:i,entities:new Set}),i}highlightEntity(e,t){let n=this._layers.get(e);n&&(n.entities.add(t),this._rebuildSelection(n))}unhighlightEntity(e,t){let n=this._layers.get(e);n&&(n.entities.delete(t),this._rebuildSelection(n))}unhighlightEntityAll(e){for(let t of this._layers.values())t.entities.delete(e)&&this._rebuildSelection(t)}clear(e){let t=this._layers.get(e);t&&(t.entities.clear(),t.layer.clearSelection())}dispose(){for(let e of this._layers.values())e.entities.clear(),e.layer.dispose();this._layers.clear()}_rebuildSelection(e){e.layer.clearSelection();for(let t of e.entities)if(t.node){let n=t.node.getChildMeshes();n.length>0&&e.layer.addSelection(n)}}},U=class extends V{_config;_spawnPoints=[];_entityNodes=[];_spawnedEntities=[];_levelSounds=new Map;_playingSoundsBeforeDeactivate=new Set;constructor(e,t){super(e,t),this._config=e}get config(){return this._config}get spawnedEntities(){return this._spawnedEntities}async buildScene(){let{sceneEngine:e}=this.gameEngine.engines,{assetManager:t}=this.gameEngine.managers;this._config.preload&&this._config.preload.length>0&&(this.$emitProgress(2,`Preloading assets...`),await t.preload(this._config.preload));let n=e.createScene();if(this._scene=n,this._config.physics&&(this.$emitProgress(5,`Initializing physics...`),await this._enablePhysics(n)),this._config.scene&&(this.$emitProgress(10,`Loading scene file...`),await this._loadSceneFile(n)),this._config.environment&&(this.$emitProgress(15,`Setting up environment...`),this._processEnvironment(n)),this._processCameras(n),this._processLights(n),this._setupClustering(n),this._config.postProcessing){let e=this._config.postProcessing.renderer??`pipeline`;this._log.info(`Applying post-processing with ${e} renderer`),await L(n,this._config.postProcessing)}return this.outlines=new H(n,this._config.outlines),this.$emitProgress(50,`Processing scene properties...`),await this._processNodeProperties(n),this.$emitProgress(70,`Spawning entities...`),await this._processSpawnPoints(),this.$emitProgress(85,`Configuring entities...`),await this._processEntityNodes(),this._config.sounds&&(this.$emitProgress(95,`Loading sounds...`),await this._processLevelSounds()),n}async _loadSceneFile(e){let t=this._getScenePath();if(t){this._log.debug(`Loading scene file: ${t}`),this._isRightHanded()&&(e.useRightHandedSystem=!0,this._log.debug(`Scene set to right-handed coordinate system`));try{await this.gameEngine.engines.sceneEngine.importMeshes([],t,e),this._log.debug(`Scene file loaded successfully`)}catch(e){throw this._log.error(`Failed to load scene file: ${t}`,e),e}}}_getScenePath(){if(this._config.scene)return typeof this._config.scene==`string`?this._config.scene:this._config.scene.path}_isRightHanded(){return!this._config.scene||typeof this._config.scene==`string`?!1:this._config.scene.rightHanded===!0}_processEnvironment(e){let n=this._config.environment;if(n){if(n.ibl&&(e.environmentTexture=this._createEnvironmentTexture(n.ibl,n.iblResolution??256,e),this._log.debug(`IBL set from: ${n.ibl}`)),n.skybox){let r=this._getFileExtension(n.skybox);if(r===`.hdr`||r===`.env`){if(!n.ibl){let t=n.iblResolution??256;e.environmentTexture=this._createEnvironmentTexture(n.skybox,t,e)}e.environmentTexture&&e.createDefaultSkybox(e.environmentTexture,!0,n.skyboxSize??1e3,0,!1)}else{let r=n.skyboxSize??1e3,i=t.MeshBuilder.CreateSphere(`skybox`,{diameter:r,segments:32},e),a=new t.StandardMaterial(`skybox-mat`,e);a.backFaceCulling=!1,a.disableLighting=!0,a.emissiveTexture=new t.Texture(n.skybox,e),i.material=a,i.infiniteDistance=!0}this._log.debug(`Skybox set from: ${n.skybox}`)}n.rotation!==void 0&&e.environmentTexture&&(e.environmentTexture.rotationY=n.rotation)}}_createEnvironmentTexture(e,n,r){return this._getFileExtension(e)===`.env`?t.CubeTexture.CreateFromPrefilteredData(e,r):new t.HDRCubeTexture(e,r,n)}_getFileExtension(e){let t=e.lastIndexOf(`.`);return t>=0?e.slice(t).toLowerCase():``}_processCameras(e){let t=this._config.cameras,n=this.gameEngine.canvas;if(!t){e.cameras.length>0&&(e.activeCamera=e.cameras[0],n&&e.activeCamera.attachControl(n,!0),this._log.debug(`Activated imported camera: "${e.activeCamera.name}"`));return}let r=null;for(let[n,i]of Object.entries(t)){let t=e.getCameraByName(n);if(t)this._applyCameraConfig(t,i),this._log.debug(`Applied overrides to camera: "${n}"`),(!r||i.active)&&(r=t);else if(i.type){let t=this._createCamera(n,i,e);this._log.debug(`Created ${i.type} camera: "${n}"`),(!r||i.active)&&(r=t)}else this._log.warn(`Camera "${n}" not found in scene and no type specified — skipping`)}if(r){e.activeCamera=r;let i=t[r.name];n&&i?.attachControl!==!1&&r.attachControl(n,!0)}}_createCamera(e,n,i){let a=n.position?z(n.position):t.Vector3.Zero(),o;switch(n.type){case`arcRotate`:{let r=n.target?z(n.target):t.Vector3.Zero();o=new t.ArcRotateCamera(e,n.alpha??Math.PI/2,n.beta??Math.PI/3,n.radius??10,r,i);break}case`universal`:o=new t.UniversalCamera(e,a,i);break;case`geospatial`:{if(!n.planetRadius)return this._log.warn(`Geospatial camera "${e}" requires planetRadius -- skipping`),new t.FreeCamera(e,t.Vector3.Zero(),i);let a=new r.GeospatialCamera(e,i,{planetRadius:n.planetRadius});n.center&&(a.center=z(n.center)),n.yaw!==void 0&&(a.yaw=n.yaw),n.pitch!==void 0&&(a.pitch=n.pitch),n.radius!==void 0&&(a.radius=n.radius),n.checkCollisions!==void 0&&(a.checkCollisions=n.checkCollisions),n.radiusMin!==void 0&&(a.limits.radiusMin=n.radiusMin),n.radiusMax!==void 0&&(a.limits.radiusMax=n.radiusMax),n.pitchMin!==void 0&&(a.limits.pitchMin=n.pitchMin),n.pitchMax!==void 0&&(a.limits.pitchMax=n.pitchMax),n.yawMin!==void 0&&(a.limits.yawMin=n.yawMin),n.yawMax!==void 0&&(a.limits.yawMax=n.yawMax),o=a;break}default:o=new t.FreeCamera(e,a,i);break}return this._applyCameraConfig(o,n),o}_applyCameraConfig(e,n){n.fov!==void 0&&(e.fov=n.fov),n.position&&(e.position=z(n.position)),n.minZ!==void 0&&(e.minZ=n.minZ),n.maxZ!==void 0&&(e.maxZ=n.maxZ),e instanceof t.FreeCamera&&(n.speed!==void 0&&(e.speed=n.speed),n.rotation&&(e.rotation=z(n.rotation))),e instanceof t.ArcRotateCamera&&(n.target&&e.setTarget(z(n.target)),n.lowerRadiusLimit!==void 0&&(e.lowerRadiusLimit=n.lowerRadiusLimit),n.upperRadiusLimit!==void 0&&(e.upperRadiusLimit=n.upperRadiusLimit),n.lowerBetaLimit!==void 0&&(e.lowerBetaLimit=n.lowerBetaLimit),n.upperBetaLimit!==void 0&&(e.upperBetaLimit=n.upperBetaLimit),n.wheelPrecision!==void 0&&(e.wheelPrecision=n.wheelPrecision)),e instanceof r.GeospatialCamera&&(n.yaw!==void 0&&(e.yaw=n.yaw),n.pitch!==void 0&&(e.pitch=n.pitch),n.radius!==void 0&&(e.radius=n.radius),n.center&&(e.center=z(n.center)),n.checkCollisions!==void 0&&(e.checkCollisions=n.checkCollisions),n.radiusMin!==void 0&&(e.limits.radiusMin=n.radiusMin),n.radiusMax!==void 0&&(e.limits.radiusMax=n.radiusMax),n.pitchMin!==void 0&&(e.limits.pitchMin=n.pitchMin),n.pitchMax!==void 0&&(e.limits.pitchMax=n.pitchMax))}_processLights(e){let t=this._config.lights;if(t)for(let[n,r]of Object.entries(t)){let t=e.getLightByName(n);t?(this._applyLightConfig(t,r),this._log.debug(`Applied overrides to light: "${n}"`)):r.type?(this._createLight(n,r,e),this._log.debug(`Created ${r.type} light: "${n}"`)):this._log.warn(`Light "${n}" not found in scene and no type specified — skipping`)}}_createLight(e,n,r){let i=n.direction?z(n.direction):new t.Vector3(0,-1,0),a=n.position?z(n.position):t.Vector3.Zero();switch(n.type){case`hemispheric`:{let a=new t.HemisphericLight(e,i,r);n.groundColor&&(a.groundColor=this._toColor3(n.groundColor)),this._applyLightConfig(a,n);break}case`directional`:{let a=new t.DirectionalLight(e,i,r);this._applyLightConfig(a,n);break}case`point`:{let i=new t.PointLight(e,a,r);this._applyLightConfig(i,n);break}case`spot`:{let o=new t.SpotLight(e,a,i,n.angle??Math.PI/3,n.exponent??2,r);this._applyLightConfig(o,n);break}case`rectarea`:{let o=new t.RectAreaLight(e,a,n.width??1,n.height??1,r);if(this._applyLightConfig(o,n),n.direction){let n=new t.TransformNode(`${e}_pivot`,r);n.position=o.position.clone(),o.position=t.Vector3.Zero(),o.parent=n,n.lookAt(n.position.subtract(i))}break}}}_applyLightConfig(e,t){t.intensity!==void 0&&(e.intensity=t.intensity),t.diffuse&&(e.diffuse=this._toColor3(t.diffuse)),t.specular&&(e.specular=this._toColor3(t.specular))}_setupClustering(e){let n=this._config.clustering;if(!n?.enabled)return;let r=e.lights.filter(e=>i.ClusteredLightContainer.IsLightSupported(e));if(r.length===0){this._log.debug(`Clustering enabled but no eligible lights found`);return}for(let t of r)e.removeLight(t);let a=new i.ClusteredLightContainer(`sage-clustered`,r,e);n.horizontalTiles!==void 0&&(a.horizontalTiles=n.horizontalTiles),n.verticalTiles!==void 0&&(a.verticalTiles=n.verticalTiles),n.depthSlices!==void 0&&(a.depthSlices=n.depthSlices),n.maxRange!==void 0&&(a.maxRange=n.maxRange),this.clusteredLights=a,this._log.debug(`Clustered lighting enabled: ${r.length} lights, ${a.horizontalTiles}x${a.verticalTiles} tiles, ${a.depthSlices} depth slices`);for(let n of e.materials)n instanceof t.PBRMaterial&&(n.useGLTFLightFalloff=!0)}_toColor3(e){return new t.Color3(e.r,e.g,e.b)}async _enablePhysics(e){let n=this._config.physics,r=new t.Vector3(0,-9.81,0);typeof n==`object`&&n.gravity&&(r=new t.Vector3(n.gravity.x,n.gravity.y,n.gravity.z));let i=this.gameEngine.largeWorldRendering?1e5:void 0;this._log.debug(`Enabling physics with gravity: ${r.toString()}`),await this.gameEngine.engines.sceneEngine.enablePhysics(e,r,i)}async _processNodeProperties(e){let t=e.transformNodes.concat(e.meshes);for(let e of t){let t=this._getNormalizedMetadata(e);t&&Object.keys(t).length>0&&(e.metadata={...e.metadata,...t},await this._processNodeMetadata(e))}this._log.debug(`Processed ${t.length} nodes, found ${this._spawnPoints.length} spawn points and ${this._entityNodes.length} entity nodes`)}_getNormalizedMetadata(e){if(!e.metadata)return null;let t=e.metadata?.gltf?.extras;return t?{...t}:e.metadata}async _processNodeMetadata(e){`spawn`in e.metadata&&this._spawnPoints.push({name:e.metadata.spawn,position:e.getAbsolutePosition(),rotation:e.absoluteRotationQuaternion.clone(),scaling:e.scaling.clone(),node:e}),`entity`in e.metadata&&this._entityNodes.push({type:e.metadata.entity,node:e}),await this._runPropertyHandlers(e)}async _runPropertyHandlers(e){for(let[t,n]of this.propertyHandlers)if(t in e.metadata)try{await n(e,e.metadata[t],this,this.gameEngine)}catch(n){this._log.error(`Error in property handler '${t}' for node '${e.name}':`,n)}}async _processSpawnPoints(){for(let e of this._spawnPoints){let t=this._config.spawns?.[e.name];t?await this._processSpawnPoint(e,t):this._log.warn(`No spawn definition found for '${e.name}', skipping`)}}async _processSpawnPoint(e,t){try{let n=await this._spawnEntity(t,e);this._spawnedEntities.push(n),e.node.dispose(),this._log.debug(`Spawned entity '${t.entity}' at spawn point '${e.name}'`)}catch(t){this._log.error(`Failed to spawn entity at spawn point '${e.name}':`,t)}}async _spawnEntity(e,t){let n={...e.config,position:R(t.position),rotation:B(t.rotation),scaling:R(t.scaling)},r=await this.gameEngine.managers.entityManager.createEntity(e.entity,{name:e.name,tags:e.tags,initialState:n});return await this._createEntityMesh(r,t),r}async _createEntityMesh(e,n){if(!this._scene)return;let r=this.gameEngine.managers.entityManager.getDefinition(e.type);if(!r?.mesh)return;let i=r.mesh,a=this._scene,o=new t.TransformNode(`entity-${e.id}`,a);o.position.copyFrom(n.position),o.rotationQuaternion=n.rotation.clone(),o.scaling.copyFrom(n.scaling);let s;switch(i.source){case`box`:s=[t.MeshBuilder.CreateBox(`${e.type}-mesh`,{size:i.params?.size??1,width:i.params?.width,height:i.params?.height,depth:i.params?.depth},a)];break;case`sphere`:s=[t.MeshBuilder.CreateSphere(`${e.type}-mesh`,{diameter:i.params?.diameter??1,segments:i.params?.segments??16},a)];break;case`capsule`:s=[t.MeshBuilder.CreateCapsule(`${e.type}-mesh`,{height:i.params?.height??1.8,radius:i.params?.radius??.4},a)];break;case`cylinder`:s=[t.MeshBuilder.CreateCylinder(`${e.type}-mesh`,{height:i.params?.height??1,diameter:i.params?.diameter??1},a)];break;default:{let e=await this.gameEngine.engines.sceneEngine.importMeshes([],i.source,a);if(e.meshes.length===0){this._log.warn(`No meshes loaded from: ${i.source}`),o.dispose();return}s=e.meshes;for(let t of e.transformNodes)t.parent||=o;break}}for(let e of s)e.parent||=o;i.scale&&(typeof i.scale==`number`?o.scaling.setAll(i.scale):o.scaling.set(i.scale.x,i.scale.y,i.scale.z));let c=i.material;if(c?.color){let n=c.color;if(c.type===`pbr`){let r=new t.PBRMaterial(`${e.type}-material`,a);r.albedoColor=this._toColor3(n),c.emissive&&(r.emissiveColor=this._toColor3(c.emissive)),r.metallic=c.metallic??0,r.roughness=c.roughness??1;for(let e of s)e.material=r}else{let r=new t.StandardMaterial(`${e.type}-material`,a);r.diffuseColor=this._toColor3(n),c.emissive&&(r.emissiveColor=this._toColor3(c.emissive));for(let e of s)e.material=r}}let l=[...s,...o.getChildren()];for(let e of l){let t=this._getNormalizedMetadata(e);t&&Object.keys(t).length>0&&(e.metadata={...e.metadata,...t},await this._runPropertyHandlers(e))}this.gameEngine.managers.entityManager.attachToNode(e,o)}async _processEntityNodes(){for(let e of this._entityNodes)await this._processEntityNode(e)}async _processEntityNode(e){let t=this._config.entities?.[e.type];try{let n={...t?.config,position:R(e.node.position)},r=await this.gameEngine.managers.entityManager.createEntity(e.type,{name:t?.name??e.node.name,tags:t?.tags,initialState:n,node:e.node});this._spawnedEntities.push(r),this._log.debug(`Created entity '${e.type}' for node '${e.node.name}'`)}catch(t){this._log.error(`Failed to create entity for node '${e.node.name}':`,t)}}async _processLevelSounds(){let e=this._config.sounds;if(!e)return;let{audioManager:t}=this.gameEngine.managers;if(!t){this._log.warn(`No AudioManager configured. Level sounds will not be created.`);return}for(let[n,r]of Object.entries(e))try{let e=await t.createSound(n,r.url,r.channel,{loop:r.loop??!1,volume:r.volume??1});this._levelSounds.set(n,e),r.autoplay&&e.play(),this._log.debug(`Created level sound: "${n}"`)}catch(e){this._log.error(`Failed to create level sound "${n}":`,e)}}async onDeactivate(){for(let[e,n]of this._levelSounds)n.state===t.SoundState.Started&&(this._playingSoundsBeforeDeactivate.add(e),n.pause())}async onActivate(){for(let e of this._playingSoundsBeforeDeactivate){let t=this._levelSounds.get(e);t&&t.play()}this._playingSoundsBeforeDeactivate.clear()}async $dispose(){let{entityManager:e}=this.gameEngine.managers,t=this._spawnedEntities.map(async t=>{try{await e.destroyEntity(t.id)}catch(e){this._log.error(`Failed to destroy entity ${t.id}:`,e)}});await Promise.all(t),this._spawnedEntities=[],this._spawnPoints=[],this._entityNodes=[];for(let e of this._levelSounds.values())e.dispose();this._levelSounds.clear(),this._playingSoundsBeforeDeactivate.clear(),await super.$dispose()}},ye=class{_eventBus;_gameEngine=null;_log;_logger;_levelConfigs=new Map;_levelClasses=new Map;_loadedLevels=new Map;_propertyHandlers=new Map;_currentLevel=null;_eventUnsubscribers=[];get currentLevel(){return this._currentLevel}get propertyHandlers(){return this._propertyHandlers}constructor(e,t){this._eventBus=e,this._logger=t,this._log=t?.getLogger(`LevelManager`)||new y(`LevelManager`),this._eventUnsubscribers.push(this._eventBus.subscribe(`level:progress`,e=>{this._handleProgress(e.payload)})),this._eventUnsubscribers.push(this._eventBus.subscribe(`level:complete`,e=>{this._handleComplete(e.payload)})),this._eventUnsubscribers.push(this._eventBus.subscribe(`level:error`,e=>{this._handleError(e.payload)})),this._log.info(`LevelManager initialized`)}$setGameEngine(e){this._gameEngine=e}_handleProgress(e){this._log.debug(`Level ${e.levelName} progress: ${e.progress}%`)}_handleComplete(e){this._log.info(`Level ${e.levelName} loaded successfully`)}_handleError(e){this._log.error(`Level ${e.levelName} error: ${e.message}`)}_createContext(){if(!this._gameEngine)throw Error(`LevelManager: gameEngine not set. Call $setGameEngine() first.`);return{gameEngine:this._gameEngine,propertyHandlers:this._propertyHandlers,logger:this._logger}}_createLevelInstance(e){let t=this._createContext();if(e.class){let n=this._levelClasses.get(e.class);if(!n)throw Error(`Level class '${e.class}' is not registered.`);return this._log.debug(`Creating level '${e.name}' using class '${e.class}'`),new n(e,t)}return this._log.debug(`Creating level '${e.name}' using GameLevel`),new U(e,t)}registerLevelConfig(e){this._levelConfigs.has(e.name)&&this._log.warn(`Level config '${e.name}' is already registered. Overwriting.`),this._levelConfigs.set(e.name,e),this._log.info(`Registered level config: ${e.name}`)}registerLevelClass(e,t){this._levelClasses.has(e)&&this._log.warn(`Level class '${e}' is already registered. Overwriting.`),this._levelClasses.set(e,t),this._log.info(`Registered level class: ${e}`)}registerPropertyHandler(e,t){this._propertyHandlers.has(e)&&this._log.warn(`Property handler '${e}' is already registered. Overwriting.`),this._propertyHandlers.set(e,t),this._log.debug(`Registered property handler: ${e}`)}unregisterPropertyHandler(e){this._propertyHandlers.delete(e)}clearAllPropertyHandlers(){this._propertyHandlers.clear()}getLevel(e){return this._loadedLevels.get(e)||null}getLevelConfig(e){return this._levelConfigs.get(e)||null}async loadLevel(e){let t;if(typeof e==`string`){let n=this._levelConfigs.get(e);if(!n)throw Error(`Level config '${e}' is not registered.`);t=n}else t=e,this.registerLevelConfig(t);let n=this._loadedLevels.get(t.name);if(n)return this._log.warn(`Level '${t.name}' is already loaded`),n;this._log.debug(`Loading level: ${t.name}`);let r=this._createLevelInstance(t);return await r.load(),this._loadedLevels.set(t.name,r),r}async activateLevel(e){let t=await this.loadLevel(e);return this._currentLevel=t,this._log.info(`Activated level: ${t.name}`),t}async unloadLevel(e){let t=this._loadedLevels.get(e);t&&(this._currentLevel===t&&(this._currentLevel=null),await t.$dispose(),this._loadedLevels.delete(e),this._log.info(`Unloaded level: ${e}`))}async unloadCurrentLevel(){this._currentLevel?await this.unloadLevel(this._currentLevel.name):this._log.warn(`No current level to unload`)}async transition(e,t){let n=this._currentLevel;try{this._eventBus.publish({type:`level:transition:start`,payload:{from:n?.name,to:e}}),n?.onDeactivate&&await n.onDeactivate(),this._eventBus.publish({type:`level:transition:progress`,payload:{stage:`loading`,levelName:e}});let r=await this.loadLevel(e);if(t?.preloadOnly)return;n&&!t?.keepAlive&&await this.unloadLevel(n.name),this._currentLevel=r,r.onActivate&&await r.onActivate(),this._eventBus.publish({type:`level:transition:complete`,payload:{levelName:e}})}catch(t){throw this._eventBus.publish({type:`level:transition:error`,payload:{from:n?.name,to:e,error:t}}),t}}async $teardown(){for(let e of this._eventUnsubscribers)e();this._eventUnsubscribers=[];for(let e of this._loadedLevels.values())await e.$dispose();this._loadedLevels.clear(),this._levelConfigs.clear(),this._currentLevel=null}},W=class{_audioEngine;_channels=new Map;_masterMuted=!1;_masterVolumeBeforeMute=1;_log;constructor(e,t){this._audioEngine=e,this._log=t.getLogger(`AudioManager`)}async initialize(e){this._log.info(`Initializing audio channels: ${e.join(`, `)}`);for(let t of e){let e=await this._audioEngine.createBus(t);this._channels.set(t,{bus:e,volume:1,muted:!1})}this._log.info(`Audio channels initialized.`)}async $teardown(){this._log.info(`Tearing down audio manager...`);for(let e of this._channels.values())e.bus.dispose();this._channels.clear(),this._log.info(`Audio manager torn down.`)}async createSound(e,t,n,r){let i;if(n){let t=this._channels.get(n);t?i=t.bus:this._log.warn(`Unknown audio channel "${n}", sound "${e}" will use default bus.`)}return this._audioEngine.createSound(e,t,i,r)}setMasterVolume(e){this._masterMuted?this._masterVolumeBeforeMute=e:this._audioEngine.setMasterVolume(e)}getMasterVolume(){return this._masterMuted?this._masterVolumeBeforeMute:this._audioEngine.getMasterVolume()}setMasterMuted(e){e!==this._masterMuted&&(this._masterMuted=e,e?(this._masterVolumeBeforeMute=this._audioEngine.getMasterVolume(),this._audioEngine.setMasterVolume(0)):this._audioEngine.setMasterVolume(this._masterVolumeBeforeMute))}isMasterMuted(){return this._masterMuted}setChannelVolume(e,t){let n=this._channels.get(e);if(!n){this._log.warn(`Unknown audio channel: ${e}`);return}n.volume=t,n.muted||this._audioEngine.setBusVolume(n.bus,t)}getChannelVolume(e){return this._channels.get(e)?.volume??1}setChannelMuted(e,t){let n=this._channels.get(e);if(!n){this._log.warn(`Unknown audio channel: ${e}`);return}t!==n.muted&&(n.muted=t,this._audioEngine.setBusVolume(n.bus,t?0:n.volume))}isChannelMuted(e){return this._channels.get(e)?.muted??!1}getChannels(){return Array.from(this._channels.keys())}},G=class{_entityManager;_levelManager;_log;_beforeSerializeHooks=[];_afterDeserializeHooks=[];constructor(e,t,n){this._entityManager=e,this._levelManager=t,this._log=n?.getLogger(`SaveManager`)||new y(`SaveManager`),this._log.info(`SaveManager initialized`)}onBeforeSerialize(e){this._beforeSerializeHooks.push(e)}onAfterDeserialize(e){this._afterDeserializeHooks.push(e)}serialize(){this._log.debug(`Serializing game state...`);let e=this._levelManager.currentLevel?.name??``,t=[];for(let e of this._entityManager.getAllEntities()){let n={id:e.id,type:e.type,tags:Array.from(e.tags),state:structuredClone(e.state)};if(e.name&&(n.name=e.name),e.parent&&(n.parentId=e.parent.id),e.node){let{position:t,rotation:r,scaling:i}=e.node;n.transform={position:{x:t.x,y:t.y,z:t.z},rotation:{x:r.x,y:r.y,z:r.z},scaling:{x:i.x,y:i.y,z:i.z}}}t.push(n)}let n={};for(let e of this._beforeSerializeHooks){let t=e();n={...n,...t}}let r={version:1,levelName:e,entities:t,custom:n};return this._log.info(`Serialized ${t.length} entities from level "${e}"`),r}async deserialize(e){this._log.debug(`Deserializing save data (version ${e.version}, level "${e.levelName}")...`),await this._levelManager.transition(e.levelName);let t=[];for(let e of this._entityManager.getAllEntities())t.push(e.id);for(let e of t)await this._entityManager.destroyEntity(e);let n=new Map;for(let t of e.entities){let e=await this._entityManager.createEntity(t.type,{name:t.name,initialState:t.state,tags:t.tags});if(n.set(t.id,e.id),t.transform&&e.node){let{position:n,rotation:r,scaling:i}=t.transform;e.node.position.x=n.x,e.node.position.y=n.y,e.node.position.z=n.z,e.node.rotation.x=r.x,e.node.rotation.y=r.y,e.node.rotation.z=r.z,e.node.scaling.x=i.x,e.node.scaling.y=i.y,e.node.scaling.z=i.z}}for(let t of e.entities)if(t.parentId){let e=n.get(t.parentId),r=n.get(t.id);e&&r?this._entityManager.addChild(e,r):this._log.warn(`Could not restore parent-child: ${t.id} -> ${t.parentId} (mapped: ${r} -> ${e})`)}for(let t of this._afterDeserializeHooks)t(e.custom);this._log.info(`Deserialized ${e.entities.length} entities for level "${e.levelName}"`)}async $teardown(){this._beforeSerializeHooks=[],this._afterDeserializeHooks=[],this._log.info(`SaveManager torn down`)}},be=class{_keyboardDevice;_keysState={};_onDeviceConnected;_onInputChanged;constructor(){this._keyboardDevice={id:`keyboard-0`,name:`Keyboard`,type:`keyboard`,connected:!0},this._setupKeyboardEvents(),setTimeout(()=>this._notifyDeviceConnected(),0)}onDeviceConnected(e){this._onDeviceConnected=e}onInputChanged(e){this._onInputChanged=e}getState(){return{type:`keyboard`,keys:{...this._keysState},delta:{}}}getDevice(){return{...this._keyboardDevice}}$teardown(){return window.removeEventListener(`keydown`,this._handleKeyDown),window.removeEventListener(`keyup`,this._handleKeyUp),Promise.resolve()}_setupKeyboardEvents(){this._handleKeyDown=this._handleKeyDown.bind(this),this._handleKeyUp=this._handleKeyUp.bind(this),window.addEventListener(`keydown`,this._handleKeyDown),window.addEventListener(`keyup`,this._handleKeyUp)}_handleKeyDown(e){this._keysState[e.code]=!0;let t={};t[e.code]=!0;let n={type:`keyboard`,keys:{...this._keysState},delta:t,event:e};this._notifyInputChanged(n)}_handleKeyUp(e){this._keysState[e.code]=!1;let t={};t[e.code]=!1;let n={type:`keyboard`,keys:{...this._keysState},delta:t,event:e};this._notifyInputChanged(n)}_notifyDeviceConnected(){this._onDeviceConnected&&this._onDeviceConnected(this._keyboardDevice)}_notifyInputChanged(e){this._onInputChanged&&this._onInputChanged(this._keyboardDevice,e)}},xe=class{_targetElement;_mouseDevice;_buttonState={};_axesState={};_position={absolute:{x:0,y:0},relative:{x:0,y:0}};_wheelState={deltaX:0,deltaY:0,deltaZ:0,deltaMode:0};_onDeviceConnected;_onInputChanged;constructor(e=document.body){this._targetElement=e,this._mouseDevice={id:`mouse-0`,name:`Mouse`,type:`mouse`,connected:!0},this._setupMouseEvents(),this._axesState[`axis-x`]=0,this._axesState[`axis-y`]=0,setTimeout(()=>this._notifyDeviceConnected(),0)}onDeviceConnected(e){this._onDeviceConnected=e}onInputChanged(e){this._onInputChanged=e}getState(){return{type:`mouse`,buttons:{...this._buttonState},axes:{...this._axesState},position:{absolute:{...this._position.absolute},relative:{...this._position.relative}},wheel:{...this._wheelState}}}getDevice(){return{...this._mouseDevice}}$teardown(){return this._targetElement.removeEventListener(`pointerdown`,this._handlePointerDown),this._targetElement.removeEventListener(`pointerup`,this._handlePointerUp),this._targetElement.removeEventListener(`pointermove`,this._handlePointerMove),this._targetElement.removeEventListener(`wheel`,this._handleMouseWheel),Promise.resolve()}_setupMouseEvents(){this._handlePointerDown=this._handlePointerDown.bind(this),this._handlePointerUp=this._handlePointerUp.bind(this),this._handlePointerMove=this._handlePointerMove.bind(this),this._handleMouseWheel=this._handleMouseWheel.bind(this),this._targetElement.addEventListener(`pointerdown`,this._handlePointerDown),this._targetElement.addEventListener(`pointerup`,this._handlePointerUp),this._targetElement.addEventListener(`pointermove`,this._handlePointerMove),this._targetElement.addEventListener(`wheel`,this._handleMouseWheel)}_handlePointerDown(e){if(e.pointerType!==`mouse`)return;let t=`button-${e.button}`;this._buttonState[t]={pressed:!0},this._notifyInputChanged({type:`mouse`,event:e,buttons:{...this._buttonState},axes:{...this._axesState},position:{...this._position}})}_handlePointerUp(e){if(e.pointerType!==`mouse`)return;let t=`button-${e.button}`;this._buttonState[t]={pressed:!1},this._notifyInputChanged({type:`mouse`,event:e,buttons:{...this._buttonState},axes:{...this._axesState},position:{...this._position}})}_handlePointerMove(e){e.pointerType===`mouse`&&(this._position={absolute:{x:e.clientX,y:e.clientY},relative:{x:e.movementX,y:e.movementY}},this._axesState[`axis-x`]=e.clientX,this._axesState[`axis-y`]=e.clientY,this._notifyInputChanged({type:`mouse`,event:e,buttons:{...this._buttonState},axes:{...this._axesState},position:{...this._position}}))}_handleMouseWheel(e){this._wheelState={deltaX:e.deltaX,deltaY:e.deltaY,deltaZ:e.deltaZ,deltaMode:e.deltaMode},this._notifyInputChanged({type:`mouse`,event:e,buttons:{...this._buttonState},axes:{...this._axesState},position:{...this._position},wheel:{...this._wheelState}})}_notifyDeviceConnected(){this._onDeviceConnected&&this._onDeviceConnected(this._mouseDevice)}_notifyInputChanged(e){this._onInputChanged&&this._onInputChanged(this._mouseDevice,e)}},Se=class{_gamepadDevices={};_buttonStates={};_axesStates={};_onDeviceConnected;_onDeviceDisconnected;_onInputChanged;constructor(){this._setupGamepadEvents()}onDeviceConnected(e){this._onDeviceConnected=e}onDeviceDisconnected(e){this._onDeviceDisconnected=e}onInputChanged(e){this._onInputChanged=e}getDevices(){return Object.values(this._gamepadDevices).map(e=>({...e}))}getStates(){let e={};return Object.keys(this._buttonStates).forEach(t=>{let n=Number(t),r=this._buttonStates[n]||{},i=this._axesStates[n]||{};e[n]={type:`gamepad`,buttons:{...r},axes:{...i}}}),e}getDevice(e){let t=this._gamepadDevices[e];return t?{...t}:null}getState(e){let t=this._buttonStates[e],n=this._axesStates[e];return!t&&!n?null:{type:`gamepad`,buttons:t?{...t}:{},axes:n?{...n}:{}}}pollGamepads(){if(!navigator.getGamepads)return;let e=navigator.getGamepads();for(let t of e){if(!t)continue;let e=t.index;if(!this._gamepadDevices[e]){this._handleGamepadConnected(t);continue}let n=this._gamepadDevices[e];if(!n)continue;let r=this._buttonStates[e]||{},i=this._axesStates[e]||{},a={},o=!1;t.buttons.forEach((e,t)=>{let n=`button-${t}`,i={pressed:e.pressed,touched:e.touched,value:e.value};a[n]=i;let s=r[n];(!s||s.pressed!==i.pressed||s.touched!==i.touched||s.value!==i.value)&&(o=!0)});let s={},c=!1;if(t.axes.forEach((e,t)=>{let n=`axis-${t}`;s[n]=e,i[n]!==e&&(c=!0)}),this._buttonStates[e]=a,this._axesStates[e]=s,o||c){let e={type:`gamepad`,buttons:{...a},axes:{...s}};this._notifyInputChanged(n,e)}}}$teardown(){return window.removeEventListener(`gamepadconnected`,this._handleGamepadConnected),window.removeEventListener(`gamepaddisconnected`,this._handleGamepadDisconnected),Promise.resolve()}_setupGamepadEvents(){if(this._handleGamepadConnected=this._handleGamepadConnected.bind(this),this._handleGamepadDisconnected=this._handleGamepadDisconnected.bind(this),window.addEventListener(`gamepadconnected`,this._handleGamepadConnected),window.addEventListener(`gamepaddisconnected`,this._handleGamepadDisconnected),navigator.getGamepads){let e=navigator.getGamepads();for(let t of e)t&&this._handleGamepadConnected(t)}}_handleGamepadConnected(e){let t=e instanceof GamepadEvent?e.gamepad:e,n=t.index,r={id:`gamepad-${n}`,name:t.id,type:`gamepad`,connected:!0,index:n,mapping:t.mapping,axes:Array.from(t.axes),buttons:Array.from(t.buttons)};this._gamepadDevices[n]=r;let i={};Array.from(t.buttons).forEach((e,t)=>{i[`button-${t}`]={pressed:e.pressed,touched:e.touched,value:e.value}}),this._buttonStates[n]=i;let a={};Array.from(t.axes).forEach((e,t)=>{a[`axis-${t}`]=e}),this._axesStates[n]=a,this._notifyDeviceConnected(r),this._notifyInputChanged(r,{type:`gamepad`,buttons:{...i},axes:{...a},event:e instanceof GamepadEvent?e:void 0})}_handleGamepadDisconnected(e){let t=e.gamepad.index,n=this._gamepadDevices[t];n&&(n.connected=!1,this._notifyDeviceDisconnected(n),this._gamepadDevices[t]=void 0,this._buttonStates[t]=void 0,this._axesStates[t]=void 0)}_notifyDeviceConnected(e){this._onDeviceConnected&&this._onDeviceConnected(e)}_notifyDeviceDisconnected(e){this._onDeviceDisconnected&&this._onDeviceDisconnected(e)}_notifyInputChanged(e,t){this._onInputChanged&&this._onInputChanged(e,t)}},Ce=class{_eventBus;_keyboardRA;_mouseRA;_gamepadRA;_log;constructor(e,t,n){this._eventBus=e,this._log=n?.getLogger(`UserInputManager`)||new y(`UserInputManager`),this._log.info(`Initializing UserInputManager`),this._log.debug(`Initializing input resource access classes`),this._keyboardRA=new be,this._mouseRA=new xe(t),this._gamepadRA=new Se,this._log.debug(`Registering input event callbacks`),this._keyboardRA.onDeviceConnected(this._publishDeviceConnected.bind(this)),this._keyboardRA.onInputChanged(this._publishInputChanged.bind(this)),this._mouseRA.onDeviceConnected(this._publishDeviceConnected.bind(this)),this._mouseRA.onInputChanged(this._publishInputChanged.bind(this)),this._gamepadRA.onDeviceConnected(this._publishDeviceConnected.bind(this)),this._gamepadRA.onDeviceDisconnected(this._publishDeviceDisconnected.bind(this)),this._gamepadRA.onInputChanged(this._publishInputChanged.bind(this)),this._log.info(`UserInputManager initialized successfully`)}_publishDeviceConnected(e){this._log.debug(`Device connected: ${e.id} (${e.name})`),this._eventBus.publish({type:`input:device:connected`,payload:{device:e}})}_publishDeviceDisconnected(e){this._log.debug(`Device disconnected: ${e.id} (${e.name})`),this._eventBus.publish({type:`input:device:disconnected`,payload:{device:e}})}_publishInputChanged(e,t){this._log.trace(`Input changed: ${e.id}`,t),this._eventBus.publish({type:`input:changed`,payload:{deviceId:e.id,device:e,state:t}})}$teardown(){return this._log.info(`Tearing down UserInputManager`),this._log.debug(`Cleaning up input resource access instances`),Promise.all([this._keyboardRA.$teardown(),this._mouseRA.$teardown(),this._gamepadRA.$teardown()]).then(()=>{this._log.info(`UserInputManager torn down successfully`)})}listDevices(){this._log.debug(`Getting all connected input devices`);let e=[];return e.push(this._keyboardRA.getDevice()),e.push(this._mouseRA.getDevice()),e.push(...this._gamepadRA.getDevices()),this._log.debug(`Found ${e.length} connected devices`),e}getDevice(e){if(this._log.debug(`Getting device: ${e}`),e.startsWith(`keyboard-`))return this._keyboardRA.getDevice();if(e.startsWith(`mouse-`))return this._mouseRA.getDevice();if(e.startsWith(`gamepad-`)){let t=parseInt(e.split(`-`)[1],10);return this._gamepadRA.getDevice(t)}return this.listDevices().find(t=>t.id===e)||(this._log.warn(`Device not found: ${e}`),null)}pollGamepads(){this._gamepadRA.pollGamepads()}};async function K(e,n){let r=new t.WebGPUEngine(e,n);return await r.initAsync(),r}function q(e,n){return new t.Engine(e,n.antialias,n.options,n.adaptToDeviceRatio)}function we(e){return new t.NullEngine(e)}async function Te(e,t,n=!1){if(e===null)return console.debug(`Using Null Engine`),we(t);if(n)if(`options`in t){let e=t;t={...e,options:{...e.options,useLargeWorldRendering:!0}}}else t={...t,useLargeWorldRendering:!0};let r=t.engine||`auto`;if(r===`webgpu`)if(A())try{return console.debug(`Using forced WebGPU engine`),await K(e,t)}catch(e){throw console.error(`Forced WebGPU initialization failed:`,e),Error(`Forced WebGPU failed to initialize. If WebGPU is required, check browser compatibility.`,{cause:e})}else throw Error(`WebGPU was forced but is not available in this browser.`);if(r===`webgl`)return console.debug(`Using forced WebGL engine`),q(e,t);if(A())try{return console.debug(`Using WebGPU`),K(e,t).catch(n=>(console.warn(`WebGPU initialization failed, falling back to WebGL:`,n),q(e,t)))}catch(e){console.warn(`WebGPU initialization failed, falling back to WebGL:`,e)}else console.warn(`WebGPU not supported, falling back to WebGL.`);return console.debug(`Using WebGL`),q(e,t)}async function Ee(){return await(0,a.default)()}function De(e){return new t.HavokPlugin(!0,e)}var J=class{_entityManager;constructor(e){this._entityManager=e}pickEntity(e,t,n,r){let i=e.pick(t,n);return this._toEntityResult(i,r)}pickEntities(e,t,n,r){let i=e.multiPick(t,n);if(!i)return[];let a=[];for(let e of i){let t=this._toEntityResult(e,r);t&&a.push(t)}return a}pickEntityWithRay(e,t,n){let r=e.pickWithRay(t);return r?this._toEntityResult(r,n):null}pickEntitiesWithRay(e,t,n){let r=e.multiPickWithRay(t);if(!r)return[];let i=[];for(let e of r){let t=this._toEntityResult(e,n);t&&i.push(t)}return i}pickEntityForward(e,t,n=1e3,r){let i=t.getForwardRay(n);return this.pickEntityWithRay(e,i,r)}_toEntityResult(e,t){if(!e.hit||!e.pickedMesh||!e.pickedPoint)return null;let n=e.pickedMesh,r=this._entityManager.getByNode(n);if(!r){let e=n.parent;for(;e&&(r=this._entityManager.getByNode(e),!r);)e=e.parent}if(!r)return null;if(t){if(t.type&&r.type!==t.type)return null;if(t.tags){for(let e of t.tags)if(!r.hasTag(e))return null}if(t.predicate&&!t.predicate(r))return null}let i=e.getNormal(!0,!0);return{entity:r,point:e.pickedPoint,distance:e.distance,normal:i??{x:0,y:0,z:0},mesh:n,pickingInfo:e}}},Oe=class{_nextId=0;_delays=[];_intervals=[];_cooldowns=[];delay(e,t){if(e<0)throw RangeError(`delay() requires ms >= 0, got ${e}`);let n=this._nextId++;return this._delays.push({id:n,remaining:e,callback:t}),()=>{this._delays=this._delays.filter(e=>e.id!==n)}}interval(e,t){if(e<=0)throw RangeError(`interval() requires ms > 0, got ${e}`);let n=this._nextId++;return this._intervals.push({id:n,periodMs:e,elapsed:0,callback:t}),()=>{this._intervals=this._intervals.filter(e=>e.id!==n)}}cooldown(e){let t={id:this._nextId++,periodMs:e,remaining:0};return this._cooldowns.push(t),{get ready(){return t.remaining<=0},reset(){t.remaining=t.periodMs}}}cancelAll(){this._delays=[],this._intervals=[];for(let e of this._cooldowns)e.remaining=0;this._cooldowns=[]}tick(e){let t=[];for(let n of this._delays)n.remaining-=e,n.remaining<=0&&(n.callback(),t.push(n.id));t.length>0&&(this._delays=this._delays.filter(e=>!t.includes(e.id)));for(let t of this._intervals)for(t.elapsed+=e;t.elapsed>=t.periodMs;)t.callback(),t.elapsed-=t.periodMs;for(let t of this._cooldowns)t.remaining>0&&(t.remaining=Math.max(0,t.remaining-e))}},ke=[`trace`,`debug`,`info`,`warn`,`error`,`none`],Ae=class extends P{name=`sound`;eventSubscriptions=[];_sounds=new Map;_initialized=!1;_audioManager=null;_node=null;_log=new y(`SoundBehavior`);onNodeAttached(e,t){if(this._initialized)return;if(this._node=e,this._initialized=!0,this._log=t.logger.getLogger(`SoundBehavior`),t.managers.audioManager)this._audioManager=t.managers.audioManager;else{this._log.warn(`No AudioManager configured. Sounds will not be created.`);return}if(!this.entity)return;let n=this.entity.state.sounds;if(n)for(let[e,t]of Object.entries(n))this._createSound(e,t)}processEvent(e,t){return!1}onReset(e){this._disposeSounds()}async destroy(){this._disposeSounds()}_createSound(e,t){if(!this._audioManager||!this.entity)return;let n=this.entity.id,r=this._node;this._audioManager.createSound(`${n}_${e}`,t.url,t.channel,{loop:t.loop??!1,autoplay:t.autoplay??!1,volume:t.volume??1,spatialEnabled:t.spatial??!1,spatialMaxDistance:t.maxDistance??100,spatialDistanceModel:`linear`}).then(n=>{this._sounds.set(e,n),(t.spatial??!1)&&r&&n.spatial.attach(r)}).catch(t=>{this._log.error(`Failed to create sound "${e}": ${t}`)})}_disposeSounds(){for(let e of this._sounds.values())e.dispose();this._sounds.clear(),this._initialized=!1,this._node=null,this._audioManager=null}play(e,t){let n=this._getSound(e);n&&(t===void 0?n.play():n.play({startOffset:t}))}stop(e){if(e){let t=this._sounds.get(e);t&&t.stop()}else for(let e of this._sounds.values())e.stop()}pause(e){let t=this._getSound(e);t&&t.pause()}setVolume(e,t){if(t){let n=this._sounds.get(t);n&&(n.volume=e)}else for(let t of this._sounds.values())t.volume=e}isPlaying(e){return this._getSound(e)?.state===t.SoundState.Started}getSoundNames(){return Array.from(this._sounds.keys())}hasSound(e){return this._sounds.has(e)}registerSound(e,t){if(!this._initialized||!this._audioManager)throw Error(`SoundBehavior must be initialized before registering sounds`);let n=this._sounds.get(e);n&&n.dispose(),this._createSound(e,t)}unregisterSound(e){let t=this._sounds.get(e);t&&(t.dispose(),this._sounds.delete(e))}_getSound(e){if(e)return this._sounds.get(e);if(this._sounds.size===1)return this._sounds.values().next().value}},Y=class{_currentState;transitions=new Map;wildcardTransitions=new Map;enterCallbacks=new Map;exitCallbacks=new Map;eventBus;constructor(e,t){this._currentState=e,this.eventBus=t}get currentState(){return this._currentState}addTransition(e,t,n){let r=`${e}->${t}`;this.transitions.set(r,{guard:n})}addTransitionFromAny(e,t){this.wildcardTransitions.set(e,{guard:t})}onEnter(e,t){let n=this.enterCallbacks.get(e)??[];n.push(t),this.enterCallbacks.set(e,n)}onExit(e,t){let n=this.exitCallbacks.get(e)??[];n.push(t),this.exitCallbacks.set(e,n)}canTransition(e){let t=`${this._currentState}->${e}`;return this.transitions.has(t)||this.wildcardTransitions.has(e)}transition(e){let t=`${this._currentState}->${e}`,n=this.transitions.get(t);if(n||=this.wildcardTransitions.get(e),!n)throw Error(`No transition defined from '${this._currentState}' to '${e}'.`);if(n.guard&&!n.guard())throw Error(`Guard rejected transition from '${this._currentState}' to '${e}'.`);let r=this._currentState,i=this.exitCallbacks.get(r);if(i)for(let e of i)e();this.eventBus&&this.eventBus.publish({type:`state:exit:${r}`,payload:{from:r,to:e}}),this._currentState=e;let a=this.enterCallbacks.get(e);if(a)for(let e of a)e();this.eventBus&&this.eventBus.publish({type:`state:enter:${e}`,payload:{from:r,to:e}})}},je=class extends P{name=`stateMachine`;eventSubscriptions=[];stateMachine;stateKey;constructor(){super(),this.stateMachine=new Y(``),this.stateKey=``}get currentState(){return this.stateMachine.currentState}transition(e){this.stateMachine.transition(e),this.entity&&(this.entity.state[this.stateKey]=e,this.$emitStateChanged())}processEvent(e,t){return!1}static create(e){let{initialState:t,stateKey:n,transitions:r,wildcardTransitions:i}=e;class a extends je{constructor(){super(),this.stateKey=n,this.stateMachine=new Y(t);for(let e of r)this.stateMachine.addTransition(e.from,e.to,e.guard);if(i)for(let e of i)this.stateMachine.addTransitionFromAny(e.to,e.guard)}}return a}},X=new y(`ColliderHandler`);function Z(e,n,r){let i=e.getScene();new t.PhysicsAggregate(e,n,{mass:r},i)}function Me(e,n){let r=e.getChildMeshes().find(e=>e.metadata?.collider_mesh===!0);r instanceof t.Mesh?(r.isVisible=!1,Z(r,t.PhysicsShapeType.MESH,n)):Z(e,t.PhysicsShapeType.MESH,n)}function Ne(e){e.registerPropertyHandler(`collider`,(e,n,r,i)=>{let a=n;if(X.debug(`Processing collider: ${e.name} -> ${a} (isMesh: ${e instanceof t.Mesh})`),!r.scene)throw Error(`Scene not available for collider handler`);if(!(e instanceof t.Mesh)&&a===`mesh`){X.warn(`Skipping mesh collider for ${e.name}: not a Mesh instance`);return}let o=e.metadata?.collider_mass??0;switch(a){case`box`:Z(e,t.PhysicsShapeType.BOX,o);break;case`sphere`:Z(e,t.PhysicsShapeType.SPHERE,o);break;case`mesh`:Me(e,o);break;case`none`:e instanceof t.AbstractMesh&&(e.checkCollisions=!1);break;default:X.warn(`Unknown collider type: ${a}`)}if(e.metadata?.collider_kinematic){let t=e;t.physicsBody&&(t.physicsBody.disablePreStep=!1)}})}var Pe=new y(`LodHandler`);function Fe(e){return e.split(`,`).map(e=>parseFloat(e.trim())).filter(e=>!isNaN(e))}function Q(e){e.registerPropertyHandler(`lod_distances`,(e,n,r,i)=>{let a=n;if(!(e instanceof t.Mesh))return;let o=Fe(a);if(o.length===0){Pe.warn(`Invalid lod_distances value: ${a}`);return}let s=e.getChildMeshes(!0),c=Math.min(o.length,s.length);for(let t=0;t<c;t++)e.addLODLevel(o[t],s[t]);let l=o[o.length-1];e.addLODLevel(l,null)})}function Ie(e){e.registerPropertyHandler(`occluder`,(e,n,r,i)=>{n&&e instanceof t.Mesh&&(e.isOccluder=!0,e.isVisible=!1)})}function Le(e,t){let n={},r=`${t}_`;for(let[t,i]of Object.entries(e))if(t.startsWith(r)){let e=t.slice(r.length);n[e]=i}return n}var Re=new y(`SoundHandler`);function ze(e){e.registerPropertyHandler(`sound`,(e,t,n,r)=>{let i=t;if(!n.scene)throw Error(`Scene not available for sound handler`);let a=Le(e.metadata,`sound`),o=a.volume??1,s=a.loop??!0,c=a.spatial??!0,l=a.distance??100,u=a.autoplay??!0,d=a.channel??`ambient`,f=r.managers.audioManager;if(!f){Re.warn(`No AudioManager available. Sound for "${e.name}" skipped.`);return}f.createSound(`${e.name}_sound`,i,d,{loop:s,autoplay:u,volume:o,spatialEnabled:c,spatialMaxDistance:l,spatialDistanceModel:`linear`}).then(t=>{c&&t.spatial.attach(e)}).catch(t=>{Re.error(`Failed to create sound for node "${e.name}": ${t}`)})})}function Be(e,n,r,i,a){let o=a===`enter`?t.ActionManager.OnIntersectionEnterTrigger:t.ActionManager.OnIntersectionExitTrigger,s=`trigger:${a}`;e.registerAction(new t.ExecuteCodeAction({trigger:o,parameter:{mesh:i}},e=>{r.publish({type:s,payload:{trigger:n,other:e.source}})}))}function Ve(e,t,n,r){Be(e,t,n,r,`enter`),Be(e,t,n,r,`exit`)}function He(e){e.registerPropertyHandler(`trigger`,(e,n,r,i)=>{let a=n,o=r.scene;if(!o)throw Error(`Scene not available for trigger handler`);if(!(e instanceof t.AbstractMesh))return;let s=i.eventBus;e.isVisible=!1,e.checkCollisions=!0;let c=e.actionManager??new t.ActionManager(o);e.actionManager=c;for(let t of o.meshes)t!==e&&!t.metadata?.trigger&&Ve(c,a,s,t);let l=o.onNewMeshAddedObservable.add(t=>{t!==e&&!t.metadata?.trigger&&Ve(c,a,s,t)});e.onDisposeObservable.add(()=>{o.onNewMeshAddedObservable.remove(l)})})}function $(e){e.registerPropertyHandler(`visible`,(e,n,r,i)=>{if(!(e instanceof t.AbstractMesh))return;let a=!0;typeof n==`boolean`?a=n:typeof n==`string`?a=n.toLowerCase()===`true`||n===`1`:typeof n==`number`&&(a=n!==0),e.isVisible=a})}function Ue(e){Ne(e),Q(e),Ie(e),ze(e),He(e),$(e)}async function We(e,t,n={}){let r=new b(n.logLevel||`debug`),i=r.getLogger(`SAGE`);i.info(`Initializing SAGE Game Engine v${h}...`),i.debug(`Creating rendering engine...`);let a=await Te(e,{antialias:!0,adaptToDeviceRatio:!0,...n.renderOptions},n.largeWorldRendering??!1);i.debug(`Creating physics engine...`);let o=await Ee(),s=De(o);i.debug(`Creating event bus...`);let c=new x(r);i.debug(`Creating scene engine...`);let l=new C(e,a,o,r);i.debug(`Creating managers...`);let u=new ee(c,l,r),d=new Ce(c,e,r),f=new le(c,r),p=new fe(c,r,f),m=new ye(c,r),_=new G(p,m,r),v=new ue(a,c,p,d,m,r);i.debug(`Creating raycast helper...`);let y=new J(p);i.debug(`Creating game timer...`);let w=new Oe;v.registerFrameCallback(e=>w.tick(e*1e3));let T,E;n.audioChannels&&n.audioChannels.length>0&&(i.debug(`Creating audio engine...`),T=new S(r),await T.initialize(),i.debug(`Creating audio manager...`),E=new W(T,r),await E.initialize(n.audioChannels)),i.info(`SAGE Game Engine v${h} initialized successfully.`),i.debug(`Loading entities...`);for(let e of t)p.registerEntityDefinition(e);if(i.debug(`Registering default input bindings...`),n.bindings)for(let e of n.bindings)f.registerBinding(e);let D=new g(e,a,s,c,r,y,w,{sceneEngine:l,audioEngine:T},{assetManager:u,bindingManager:f,entityManager:p,gameManager:v,inputManager:d,levelManager:m,saveManager:_,audioManager:E},n.largeWorldRendering??!1);return p.$setGameEngine(D),m.$setGameEngine(D),D}e.AssetManager=ee,e.AudioEngine=S,e.AudioManager=W,e.ConsoleBackend=_,e.GameEngine=g,e.GameEntity=F,e.GameEntityBehavior=P,e.GameEventBus=x,e.GameLevel=U,e.GameTimer=Oe,e.Level=V,e.LogLevels=ke,e.LoggingUtility=b,e.NullBackend=v,e.OutlineManager=H,e.RaycastHelper=J,e.SAGELogger=y,e.SaveManager=G,e.SceneEngine=C,e.SoundBehavior=Ae,e.StateMachine=Y,e.StateMachineBehavior=je,e.VERSION=h,e.applyPostProcessing=L,e.bindingTypes=w,e.collectPrefixedProperties=Le,e.createGameEngine=We,e.generateId=N,e.isGamepadState=O,e.isKeyboardState=E,e.isMouseState=D,e.registerAllPropertyHandlers=Ue,e.registerColliderHandler=Ne,e.registerLodHandler=Q,e.registerOccluderHandler=Ie,e.registerSoundHandler=ze,e.registerTriggerHandler=He,e.registerVisibleHandler=$,e.toQuatObject=B,e.toQuaternion=ve,e.toVec3Object=R,e.toVector3=z,e.validDeviceTypes=T});
|
|
2
2
|
//# sourceMappingURL=sage.umd.js.map
|