(()=>{var e,t,i={949:(e,t,i)=>{var o={"./missions/GeneratorCrisis.js":[664,664],"./missions/InventoryTest.js":[392,392],"./missions/NightshiftHorror.js":[208,208],"./objectives/MedicalLabObjectives.js":[961,961],"./objectives/ObjectiveTemplates.js":[41,41],"./scenes/AncientTemple.js":[99,96,99],"./scenes/DataCenter.js":[713,96,713],"./scenes/FractureReductionSurgery.js":[314,96,314],"./scenes/GarageScene.js":[585,585],"./scenes/IndustrialFacility.js":[386,96,386],"./scenes/KitbashGallery.js":[565,565],"./scenes/MedicalBayScene.js":[881,881],"./scenes/MedicalComplex.js":[205,96,205],"./scenes/MilitaryCompound.js":[942,96,942],"./scenes/MobileGarageScene.js":[507,507],"./scenes/MobileTavernScene.js":[826,826],"./scenes/NPCSystemTest19.js":[258,258],"./scenes/PuzzleTestScene.js":[433,433],"./scenes/QuantumLab.js":[420,96,420],"./scenes/ResearchFacility.js":[260,96,260],"./scenes/SecurityComplex.js":[364,96,364],"./scenes/SystemsTestingLab.js":[543,96,543],"./scenes/TavernScene.js":[576,576],"./scenes/UndergroundBunker.js":[614,96,614]};function n(e){if(!i.o(o,e))return Promise.resolve().then(()=>{var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t});var t=o[e],n=t[0];return Promise.all(t.slice(1).map(i.e)).then(()=>i(n))}n.keys=()=>Object.keys(o),n.id=949,e.exports=n}},o={};function n(e){var t=o[e];if(void 0!==t)return t.exports;var s=o[e]={exports:{}};return i[e](s,s.exports,n),s.exports}n.m=i,n.d=(e,t)=>{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((t,i)=>(n.f[i](e,t),t),[])),n.u=e=>e+".bundle.js",n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e={},t="playcanvas-game:",n.l=(i,o,s,a)=>{if(e[i])e[i].push(o);else{var r,l;if(void 0!==s)for(var c=document.getElementsByTagName("script"),h=0;h{r.onerror=r.onload=null,clearTimeout(g);var n=e[i];if(delete e[i],r.parentNode&&r.parentNode.removeChild(r),n&&n.forEach(e=>e(o)),t)return t(o)},g=setTimeout(p.bind(null,void 0,{type:"timeout",target:r}),12e4);r.onerror=p.bind(null,r.onerror),r.onload=p.bind(null,r.onload),l&&document.head.appendChild(r)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+"");var t=n.g.document;if(!e&&t&&(t.currentScript&&"SCRIPT"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var i=t.getElementsByTagName("script");if(i.length)for(var o=i.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=i[o--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),n.p=e})(),(()=>{var e={792:0};n.f.j=(t,i)=>{var o=n.o(e,t)?e[t]:void 0;if(0!==o)if(o)i.push(o[2]);else{var s=new Promise((i,n)=>o=e[t]=[i,n]);i.push(o[2]=s);var a=n.p+n.u(t),r=new Error;n.l(a,i=>{if(n.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var s=i&&("load"===i.type?"missing":i.type),a=i&&i.target&&i.target.src;r.message="Loading chunk "+t+" failed.\n("+s+": "+a+")",r.name="ChunkLoadError",r.type=s,r.request=a,o[1](r)}},"chunk-"+t,t)}};var t=(t,i)=>{var o,s,[a,r,l]=i,c=0;if(a.some(t=>0!==e[t])){for(o in r)n.o(r,o)&&(n.m[o]=r[o]);l&&l(n)}for(t&&t(i);c{"use strict";const e={engine:{preferWebGl2:!0,antialias:!0,alpha:!1,preserveDrawingBuffer:!1},game:{catalogPodiums:9999,autoSaveInterval:6e4,debugMode:!1},assets:{fonts:{main:"assets/fonts/arial.json"},data:{catalog:"assets/data/catalog.json"},models:{npc_base:"assets/models/npc_base.glb"}},layers:{world:0,ui:1,debug:15}};class t{constructor(e){this.app=e,this.handlers=new Map,this.catalogHandlers=new Map,this.currentCatalogIndex=void 0,this.registerEvents();try{this.setupCatalogTracking()}catch(e){console.warn("[EventBus] Failed to setup catalog tracking:",e)}}setupCatalogTracking(){this.app.on("content:loading",this.onCatalogLoading,this),this.app.on("content:unloading",this.onCatalogUnloading,this)}onCatalogLoading(e){console.log(`[EventBus] Starting event tracking for catalog ${e}`),this.currentCatalogIndex=e,this.catalogHandlers.has(e)||this.catalogHandlers.set(e,new Map)}onCatalogUnloading(){void 0!==this.currentCatalogIndex&&this.cleanupCatalogHandlers(this.currentCatalogIndex),this.currentCatalogIndex=void 0}cleanupCatalogHandlers(e){console.log(`[EventBus] Cleaning up event handlers for catalog ${e}`);const t=this.catalogHandlers.get(e);if(!t)return;let i=0;for(const[e,o]of t.entries()){for(const t of o)try{this.app.off(e,t.handler,t.scope),i++}catch(t){console.warn(`[EventBus] Failed to remove handler for ${e}:`,t)}o.clear()}this.catalogHandlers.delete(e),console.log(`[EventBus] Cleaned up ${i} event handlers for catalog ${e}`),this.app.fire("events:catalogCleaned",e,i)}registerEvents(){this.defineEvent("core:initialized"),this.defineEvent("core:systemsReady"),this.defineEvent("catalog:select"),this.defineEvent("catalog:loaded"),this.defineEvent("catalog:exit"),this.defineEvent("content:loading"),this.defineEvent("content:loaded"),this.defineEvent("content:unloading"),this.defineEvent("content:exit"),this.defineEvent("game:save"),this.defineEvent("game:load"),this.defineEvent("game:pause"),this.defineEvent("game:resume"),this.defineEvent("npc:interact"),this.defineEvent("npc:spawn"),this.defineEvent("npc:remove"),this.defineEvent("item:pickup"),this.defineEvent("item:drop"),this.defineEvent("item:use"),this.defineEvent("skill:unlock"),this.defineEvent("skill:upgrade"),this.defineEvent("objective:start"),this.defineEvent("objective:complete"),this.defineEvent("objective:attempt"),this.defineEvent("ui:notification"),this.defineEvent("ui:message"),this.defineEvent("ui:toggle"),this.defineEvent("debug:command"),this.defineEvent("debug:log")}defineEvent(e){this.handlers.has(e)||this.handlers.set(e,new Set)}on(e,t,i,o=!1){this.app.on(e,t,i);const n={handler:t,scope:i};if(this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(n),o&&void 0!==this.currentCatalogIndex){const t=this.catalogHandlers.get(this.currentCatalogIndex);t&&(t.has(e)||t.set(e,new Set),t.get(e).add(n),console.log(`[EventBus] Handler for '${e}' tagged for catalog ${this.currentCatalogIndex}`))}}onCatalogScoped(e,t,i){return this.on(e,t,i,!0)}off(e,t,i){if(this.app.off(e,t,i),this.handlers.has(e)){const o=this.handlers.get(e);o.forEach(e=>{e.handler===t&&e.scope===i&&o.delete(e)})}}fire(e,...t){this.app.fire(e,...t)}cleanupScope(e){this.handlers.forEach((t,i)=>{t.forEach(o=>{o.scope===e&&(this.app.off(i,o.handler,o.scope),t.delete(o))})})}cleanup(){this.handlers.forEach((e,t)=>{e.forEach(e=>{this.app.off(t,e.handler,e.scope)}),e.clear()}),this.handlers.clear()}}class i{constructor(e,t){this.camera=e,this.app=t,this.interactionDistance=25,this.highlightColor=new pc.Color(.4,.4,0,1),this.highlightedEntity=null,this.originalEmissive=new pc.Color,this.initialized=!1,this.lastCursorTarget=null,this.updateCount=0}initialize(){const e=this.app.scene.layers.getLayerById(pc.LAYERID_IMMEDIATE);if(e&&this.camera.camera){const t=this.camera.camera.layers;-1===t.indexOf(e.id)&&(this.camera.camera.layers=t.concat(e.id))}this.debugRaycast=!1,this.debugLines=[],this.initialized=!0,console.log("Interaction Controller active. Look at objects to highlight, press 'F' to interact."),console.log("[Interaction] Camera entity:",this.camera),console.log("[Interaction] Camera has forward property:",!!this.camera.forward),console.log("[Interaction] App has systems.rigidbody:",!!this.app.systems.rigidbody),console.log("[Interaction] Ammo loaded:","undefined"!=typeof Ammo),this.app.on("update",this.update,this),this.app.on("content:exit",this.cleanupHighlights,this),this.app.on("catalog:exit",this.cleanupHighlights,this),setTimeout(()=>{console.log("[Interaction] Testing if update is running... Update count:",this.updateCount),console.log("[Interaction] Physics system ready:",!!this.app.systems.rigidbody),console.log("[Interaction] Ammo available:","undefined"!=typeof Ammo);const e=this.app.root.find(e=>e.collision);console.log("[Interaction] Entities with collision:",e.length),e.forEach(e=>{console.log(` - ${e.name}: type=${e.collision.type}, enabled=${e.enabled}`)})},2e3)}update(e){try{if(this.updateCount++,!this.initialized)return;if(this.updateCount%60==0&&("undefined"!=typeof Ammo&&this.app.systems.rigidbody||console.warn("[Interaction] Physics not ready - Ammo:","undefined"!=typeof Ammo,"Rigidbody:",!!this.app.systems.rigidbody)),"undefined"==typeof Ammo||!this.app.systems.rigidbody)return void this.manualIntersectionCheck();const e=this.camera.getPosition(),t=this.camera.forward||new pc.Vec3(0,0,-1),i=(new pc.Vec3).copy(t).mulScalar(this.interactionDistance).add(e),o=this.app.systems.rigidbody.raycastFirst(e,i);let n=o?o.entity:null;if(n&&n.tags&&n.tags.has("npc_body_part")){console.log("[Interaction] Hit NPC body part:",n.name,"looking for parent NPC");let e=n.parent;for(;e;){if(e.tags&&e.tags.has("npc")){console.log("[Interaction] Found parent NPC:",e.name),n=e;break}e=e.parent}}o&&(console.log("[Interaction] HIT:",o.entity.name,"Tags:",o.entity.tags?Array.from(o.entity.tags._list):"No tags"),o.entity.tags&&o.entity.tags.has("npc")&&console.log("[Interaction] NPC HIT Details:",{name:o.entity.name,hasNpcData:!!o.entity.npcData,collision:o.entity.collision?o.entity.collision.type:"No collision",enabled:o.entity.enabled})),this.updateCount%300==0&&(console.log("[Interaction] Raycast from:",e.toString(),"to:",i.toString()),console.log("[Interaction] Physics ready - Ammo:","undefined"!=typeof Ammo,"Rigidbody:",!!this.app.systems.rigidbody)),n!==this.highlightedEntity&&(this.highlightedEntity&&(this.setHighlight(this.highlightedEntity,!1),this.app.fire("interaction:unhover")),n&&(this.setHighlight(n,!0),this.app.fire("interaction:hover",n),this.checkCursorInteraction(n)),this.highlightedEntity=n),this.app.keyboard&&this.app.keyboard.wasPressed&&this.app.keyboard.wasPressed(pc.KEY_F)&&this.highlightedEntity&&this.interactWith(this.highlightedEntity),this.app.keyboard&&this.app.keyboard.wasPressed&&this.app.keyboard.wasPressed(pc.KEY_T)&&this.performDebugRaycast(),this.app.keyboard&&this.app.keyboard.wasPressed&&this.app.keyboard.wasPressed(pc.KEY_Y)&&this.listInteractiveEntities(),this.app.keyboard&&this.app.keyboard.wasPressed&&this.app.keyboard.wasPressed(pc.KEY_R)&&(this.debugRaycast=!this.debugRaycast,console.log("[Interaction] Raycast debug visualization:",this.debugRaycast?"ON":"OFF"),this.updateDebugOverlay()),this.debugRaycast&&this.drawRaycastDebug()}catch(e){console.error("[Interaction] Error in update:",e)}}interactWith(e){if(console.log("Interacting with:",e.name),console.log("Entity tags:",e.tags?Array.from(e.tags._list):"No tags"),e.tags&&e.tags.has("catalog_podium")){const t=e.name.match(/Podium_(\d+)/);if(t){const e=parseInt(t[1]);console.log(`[Interaction] Loading content ${e}`),this.showMissionBriefing(e,()=>{this.app.core&&this.app.core.loadContent&&this.app.core.loadContent(e).catch(e=>{console.error("Failed to load content:",e),this.app.fire("ui:notification",{text:"Failed to load content",type:"error"})})})}return}if(e.tags&&e.tags.has("npc"))return console.log("[Interaction] NPC detected:",e.name,"Has npcData:",!!e.npcData),e.npcData?this.app.fire("npc:interact",e.npcData.id):this.app.fire("npc:interact",e.name),void this.app.fire("interaction:triggered",e.name);e&&e.name&&(this.app.fire("interaction:triggered",e.name),this.app.fire("entity:interact",e.name))}setHighlight(e,t){e&&"MonitorScreen"===e.name||e&&e.model&&e.model.meshInstances&&(e._originalMaterials||(e._originalMaterials=new Map,e.model.meshInstances.forEach((t,i)=>{t&&t.material&&e._originalMaterials.set(i,{emissive:t.material.emissive.clone(),emissiveIntensity:t.material.emissiveIntensity})})),e.model.meshInstances.forEach((i,o)=>{if(!i||!i.material)return;const n=i.material,s=e._originalMaterials.get(o);t?(n.emissive.set(this.highlightColor.r,this.highlightColor.g,this.highlightColor.b),n.emissiveIntensity=.5):s&&(n.emissive.copy(s.emissive),n.emissiveIntensity=s.emissiveIntensity),n.update()}))}checkCursorInteraction(e){if(!e.tags)return;let t=!1,i=null;e.tags._list.forEach(e=>{"ui_display_cursor"===e||"cursor"===e?(t=!0,i="cursor"):e.startsWith("ui_display_")&&(i=e.replace("ui_display_",""))}),t||"cursor"===i?this.lastCursorTarget!==e&&(this.app.fire("ui:focus",{entity:e,type:i,gameEvent:"ui:focus"}),this.lastCursorTarget=e):this.lastCursorTarget===e&&(this.lastCursorTarget=null)}performDebugRaycast(){console.log("[Interaction] === DEBUG RAYCAST TEST ==="),console.log("[Interaction] Camera position:",this.camera.getPosition().toString());const e=this.camera.forward||new pc.Vec3(0,0,-1);console.log("[Interaction] Camera forward:",e.toString()),console.log("[Interaction] Interaction distance:",this.interactionDistance);const t=this.camera.getPosition(),i=(new pc.Vec3).copy(e).mulScalar(this.interactionDistance).add(t);console.log("[Interaction] Raycast from:",t.toString(),"to:",i.toString());const o=this.app.systems.rigidbody.raycastFirst(t,i);if(o)console.log("[Interaction] HIT:",o.entity.name),console.log("[Interaction] Hit point:",o.point.toString()),console.log("[Interaction] Hit distance:",t.distance(o.point)),console.log("[Interaction] Entity tags:",o.entity.tags?Array.from(o.entity.tags._list):"No tags");else{console.log("[Interaction] NO HIT - checking for entities in area...");const e=this.app.root.findByTag("interactive");console.log("[Interaction] Found",e.length,"interactive entities:"),e.forEach(e=>{const i=t.distance(e.getPosition());console.log(` - ${e.name}: distance ${i.toFixed(2)}, enabled: ${e.enabled}, has collision: ${!!e.collision}`)})}console.log("[Interaction] Ammo available:","undefined"!=typeof Ammo),console.log("[Interaction] Rigidbody system:",!!this.app.systems.rigidbody),console.log("[Interaction] === END DEBUG TEST ===")}manualIntersectionCheck(){const e=this.camera.getPosition(),t=this.camera.forward||new pc.Vec3(0,0,-1),i=this.app.root.findByTag("interactive");let o=null,n=this.interactionDistance;i.forEach(i=>{if(!i.enabled)return;const s=i.getPosition(),a=(new pc.Vec3).sub2(s,e),r=a.length();r>n||(a.normalize(),t.dot(a)>.7&&(n=r,o=i))}),o!==this.highlightedEntity&&(this.highlightedEntity&&(this.setHighlight(this.highlightedEntity,!1),this.app.fire("interaction:unhover")),o&&(this.setHighlight(o,!0),this.app.fire("interaction:hover",o),console.log("[Interaction] Manual detection:",o.name)),this.highlightedEntity=o),this.app.keyboard&&this.app.keyboard.wasPressed&&this.app.keyboard.wasPressed(pc.KEY_F)&&this.highlightedEntity&&this.interactWith(this.highlightedEntity)}listInteractiveEntities(){console.log("[Interaction] === INTERACTIVE ENTITIES LIST ===");const e=this.app.root.findByTag("interactive");console.log("[Interaction] Found",e.length,"interactive entities:"),e.forEach((e,t)=>{const i=e.getPosition(),o=this.camera.getPosition().distance(i);console.log(` ${t+1}. ${e.name}:`),console.log(` - Position: ${i.toString()}`),console.log(` - Distance: ${o.toFixed(2)}`),console.log(` - Enabled: ${e.enabled}`),console.log(` - Has collision: ${!!e.collision}`),console.log(` - Has rigidbody: ${!!e.rigidbody}`),console.log(` - Tags: ${e.tags?Array.from(e.tags._list).join(", "):"None"}`),e.collision&&(console.log(` - Collision type: ${e.collision.type}`),"cylinder"===e.collision.type&&console.log(` - Cylinder radius: ${e.collision.radius}, height: ${e.collision.height}`)),console.log("")});const t=this.app.root.findByTag("catalog_podium");console.log("[Interaction] Found",t.length,"catalog podium entities"),console.log("[Interaction] === END INTERACTIVE ENTITIES LIST ===")}drawRaycastDebug(){this.clearDebugLines();const e=this.camera.getPosition(),t=this.camera.forward||new pc.Vec3(0,0,-1),i=(new pc.Vec3).copy(t).mulScalar(this.interactionDistance).add(e);if(this.drawDebugLine(e,i,pc.Color.CYAN),this.highlightedEntity){const t=this.highlightedEntity.getPosition();this.drawDebugLine(e,t,pc.Color.GREEN),this.drawDebugSphere(t,pc.Color.RED,.2),this.showHitInfo(this.highlightedEntity)}this.drawDebugSphere(i,pc.Color.YELLOW,.1)}drawDebugLine(e,t,i){this.app.drawLine?this.app.drawLine(e,t,i):this.createLineEntity(e,t,i)}drawDebugSphere(e,t,i=.1){const o=new pc.Entity("DebugSphere");o.addComponent("model",{type:"sphere"}),o.setPosition(e),o.setLocalScale(i,i,i),o.tags.add("debug_visualization");const n=new pc.StandardMaterial;n.diffuse=t,n.emissive=t,n.emissiveIntensity=.5,n.update(),o.model&&o.model.meshInstances[0]&&(o.model.meshInstances[0].material=n),this.app.root.addChild(o),this.debugLines.push(o),setTimeout(()=>{o&&o.destroy&&o.destroy()},100)}createLineEntity(e,t,i){const o=new pc.Entity("DebugLine");o.addComponent("model",{type:"cylinder"});const n=(new pc.Vec3).add2(e,t).mulScalar(.5),s=(new pc.Vec3).sub2(t,e).length();o.setPosition(n),o.setLocalScale(.01,s/2,.01),s>0&&o.lookAt(t,pc.Vec3.UP),o.tags.add("debug_visualization");const a=new pc.StandardMaterial;a.diffuse=i,a.emissive=i,a.emissiveIntensity=.7,a.update(),o.model&&o.model.meshInstances[0]&&(o.model.meshInstances[0].material=a),this.app.root.addChild(o),this.debugLines.push(o),setTimeout(()=>{o&&o.destroy&&o.destroy()},100)}clearDebugLines(){this.debugLines.forEach(e=>{e&&e.destroy&&e.destroy()}),this.debugLines=[],this.app.root.findByTag("debug_visualization").forEach(e=>{e&&e.destroy&&e.destroy()})}showHitInfo(e){this.updateDebugOverlay(e)}updateDebugOverlay(e=null){if(!window.location.search.includes("debug=raycast")&&"true"!==localStorage.getItem("debug_overlay")&&!this.debugRaycast||!window.location.search.includes("debug"))return;let t=document.getElementById("raycast-debug-overlay");t||(t=document.createElement("div"),t.id="raycast-debug-overlay",t.style.cssText="\n position: fixed;\n top: 10px;\n left: 10px;\n background: rgba(0, 0, 0, 0.8);\n color: white;\n padding: 10px;\n border-radius: 5px;\n font-family: monospace;\n font-size: 12px;\n z-index: 10001;\n max-width: 300px;\n line-height: 1.4;\n pointer-events: none;\n ",document.body.appendChild(t));const i=this.camera.getPosition(),o=this.camera.forward||new pc.Vec3(0,0,-1);let n="";if(e){const t=e.getPosition(),o=i.distance(t);n=`\n Hit Entity: ${e.name}
\n Position: ${t.x.toFixed(2)}, ${t.y.toFixed(2)}, ${t.z.toFixed(2)}
\n Distance: ${o.toFixed(2)}
\n Tags: ${e.tags?Array.from(e.tags._list).join(", "):"None"}
\n `}t.innerHTML=`\n Raycast Debug
\n Debug Viz: ${this.debugRaycast?"ON":"OFF"}
\n Camera: ${i.x.toFixed(2)}, ${i.y.toFixed(2)}, ${i.z.toFixed(2)}
\n Forward: ${o.x.toFixed(2)}, ${o.y.toFixed(2)}, ${o.z.toFixed(2)}
\n Range: ${this.interactionDistance}
\n ${n}\n \n `}cleanupHighlights(){this.highlightedEntity&&(this.setHighlight(this.highlightedEntity,!1),this.highlightedEntity=null),this.app.root.find(e=>e._originalMaterials).forEach(e=>{e._originalMaterials&&e.model&&e.model.meshInstances&&e.model.meshInstances.forEach((t,i)=>{if(t&&t.material){const o=e._originalMaterials.get(i);o&&(t.material.emissive.copy(o.emissive),t.material.emissiveIntensity=o.emissiveIntensity,t.material.update())}})})}destroy(){this.cleanupHighlights(),this.app&&this.app.off("update",this.update,this),this.removeBriefingOverlay(),this.initialized=!1}async showMissionBriefing(e,t){try{const i=await fetch("assets/data/catalog.json"),o=(await i.json()).entries.find(t=>t.id===e);if(!o||!o.briefing)return this.app.fire("ui:notification",{text:`Loading ${o?o.title:`Content ${e}`}...`,type:"info"}),void t();this.createBriefingOverlay(o,t)}catch(i){console.error("[Interaction] Failed to load catalog data:",i),this.app.fire("ui:notification",{text:`Loading Content ${e}...`,type:"info"}),t()}}createBriefingOverlay(e,t){this.removeBriefingOverlay();const i=document.createElement("div");i.className="mission-briefing-backdrop",i.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.8);\n z-index: 10000;\n display: flex;\n justify-content: center;\n align-items: center;\n font-family: 'Courier New', monospace;\n color: #fff;\n padding: 20px;\n box-sizing: border-box;\n ";const o=document.createElement("div");o.style.cssText="\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n border: 2px solid #4a9eff;\n border-radius: 8px;\n padding: 30px;\n max-width: 600px;\n max-height: 80vh;\n overflow-y: auto;\n box-shadow: 0 10px 30px rgba(74, 158, 255, 0.3);\n ";const n={scene:"#4CAF50",mission:"#FF9800",test:"#2196F3"}[e.type]||"#757575";o.innerHTML=`\n
\n
\n ${e.type.toUpperCase()}\n
\n

\n ${e.title}\n

\n

\n ${e.description}\n

\n
\n \n
\n

Mission Briefing

\n
\n Setting:\n
${e.briefing.setting}
\n
\n
\n Your Role:\n
${e.briefing.role}
\n
\n
\n \n
\n

Primary Objectives

\n
    \n ${e.briefing.objectives.map(e=>`
  • ${e}
  • `).join("")}\n
\n
\n \n
\n

Controls

\n
    \n ${e.briefing.controls.map(e=>`
  • ${e}
  • `).join("")}\n
\n
\n \n
\n

Strategic Tips

\n
    \n ${e.briefing.tips.map(e=>`
  • ${e}
  • `).join("")}\n
\n
\n \n
\n \n \n
\n `,i.appendChild(o),document.body.appendChild(i),this.currentBriefing=i;const s=i.querySelector("#start-mission-btn"),a=i.querySelector("#cancel-mission-btn");s.onmouseover=()=>s.style.transform="translateY(-2px)",s.onmouseout=()=>s.style.transform="translateY(0)",a.onmouseover=()=>{a.style.borderColor="#4a9eff",a.style.color="#4a9eff"},a.onmouseout=()=>{a.style.borderColor="#666",a.style.color="#ccc"},s.onclick=()=>{this.removeBriefingOverlay(),this.app.fire("ui:notification",{text:`Loading ${e.title}...`,type:"info"}),t()},a.onclick=()=>{this.removeBriefingOverlay()},i.onclick=e=>{e.target===i&&this.removeBriefingOverlay()};const r=e=>{"Escape"===e.key&&(this.removeBriefingOverlay(),document.removeEventListener("keydown",r))};document.addEventListener("keydown",r),setTimeout(()=>s.focus(),100)}removeBriefingOverlay(){this.currentBriefing&&(this.currentBriefing.remove(),this.currentBriefing=null)}}class o{constructor(e,t={}){this.entity=e,this.onInteract=t.onInteract||(()=>{}),console.log(`[InteractionComponent] Entity ${e.name} set up for interactions`)}destroy(){}}class s{constructor(e,t){this.app=e,this.core=t,this.interceptEntityCreation()}interceptEntityCreation(){const e=this,t=pc.Entity.prototype.addChild;pc.Entity.prototype.addChild=function(i){return t.call(this,i),e.core.state.catalogIndex>=0&&!i.tags.has("persistent")&&(i.tags.add(`catalog_${e.core.state.catalogIndex}`),i.tags.add("content_entity")),i}}create(e,t={}){const i=new pc.Entity(e);if(t.tags&&t.tags.forEach(e=>i.tags.add(e)),t.components)for(const[e,o]of Object.entries(t.components))i.addComponent(e,o);if(t.position&&i.setPosition(...t.position),t.rotation&&i.setEulerAngles(...t.rotation),t.scale&&i.setLocalScale(...t.scale),t.scripts)try{i.addComponent("script"),i.script?t.scripts.forEach(e=>{i.script.create(e)}):console.warn(`[EntityFactory] Script component not available for entity: ${e}`)}catch(t){console.error(`[EntityFactory] Failed to add scripts to entity ${e}:`,t)}return i}createUI(e,t){return this.create(e,{...t,tags:[...t.tags||[],"ui","persistent"]})}createNPC(e,t){const i=this.create(e,{tags:["npc","interactive"],components:{collision:{type:"capsule",radius:.8,height:2.5},rigidbody:{type:"kinematic"}},position:t.position||[0,0,0],scale:t.scale||[1,1,1]}),n=new pc.Entity(`${e}_FallbackVisual`);n.addComponent("model",{type:"capsule"}),n.setLocalScale(.5,1.8,.5),n.setLocalPosition(0,1,0),i.addChild(n),i._fallbackVisual=n,this.createNPCBody(i,t),setTimeout(()=>{try{i._fallbackVisual&&i._body&&(i._fallbackVisual.destroy(),i._fallbackVisual=null)}catch(e){}},0),i.interaction=new o(i,{interactionDistance:t.interactionDistance||3,onInteract:t=>{i._interacting?console.warn(`[NPC] Already interacting with ${e}, ignoring duplicate call`):(i._interacting=!0,console.log(`[NPC] Talking to ${e}`),this.app.fire("ui:notification",{text:`Talking to ${e}`,type:"info"}),this.app.fire("npc:interact",e),setTimeout(()=>{i._interacting=!1},1e3))}}),this.addNPCNameLabel(i,t.displayName||e),t.dialogue&&(i.npcData={id:e,dialogue:t.dialogue,name:t.displayName||e});try{const o=this.core?this.core.getSystem("npcmanager"):null;o&&o.npcs?(o.npcs.set(e,{id:e,entity:i,displayName:t.displayName||e,dialogue:t.dialogue,state:"idle",health:100}),console.log(`[EntityFactory] Registered NPC with NPCManager: ${e}`)):console.log(`[EntityFactory] NPCManager not available, NPC created without registration: ${e}`)}catch(e){console.warn(`[EntityFactory] Failed to register NPC with NPCManager: ${e.message}`)}return i.cleanupLabel=()=>{i._labelUpdateHandler&&(this.app.off("update",i._labelUpdateHandler),i._labelUpdateHandler=null),i._nameLabel&&(i._nameLabel.destroy(),i._nameLabel=null),i._interacting=!1,npcManager&&npcManager.npcs&&npcManager.npcs.delete(e)},i}addNPCNameLabel(e,t){let i=null;this.app.fontManager&&this.app.fontManager.initialized&&(i=this.app.fontManager.getFontAssetId());const o=this.createUI(`${e.name}_NameLabel`,{components:{element:{type:pc.ELEMENTTYPE_TEXT,text:t.replace(/_/g," ").toUpperCase(),fontSize:20,color:new pc.Color(1,1,0,1),fontAsset:i,anchor:[.5,.5,.5,.5],pivot:[.5,.5],width:200,height:30,autoWidth:!1,autoHeight:!1,outlineColor:new pc.Color(0,0,0,1),outlineThickness:.2},screen:{referenceResolution:new pc.Vec2(200,30),scaleMode:pc.SCALEMODE_NONE,screenSpace:!1}},position:[0,4.5,0],scale:[.03,.03,.03],tags:["ui","npc_label","persistent"]});e.addChild(o),e._nameLabel=o;const n=()=>{if(o&&o.enabled){let e=null;if(e=this.app.root.findByName("Player"),!e){const t=this.app.root.findComponents("camera");t.length>0&&(e=t[0].entity)}if(!e&&this.app.systems.camera&&this.app.systems.camera.cameras.length>0&&(e=this.app.systems.camera.cameras[0].entity),e)try{const t=o.getPosition(),i=e.getPosition(),n=new pc.Vec3;n.sub2(i,t),n.y=0,n.normalize();const s=Math.atan2(n.x,n.z)*pc.math.RAD_TO_DEG;o.setEulerAngles(0,s,0)}catch(e){o.setEulerAngles(0,0,0)}}};this.app.on("update",n),e._labelUpdateHandler=n,console.log(`[EntityFactory] Created NPC label for ${t} using podium-style rendering`)}createNPCBody(e,t){const i=t.bodyColor||[.2,.4,.8],o=t.headColor||[.9,.7,.6],n=t.armColor||[.1,.1,.1],s=t.legColor||[.2,.2,.6],a=new pc.Entity(`${e.name}_Body`);a.addComponent("model",{type:"capsule"}),a.setLocalScale(.5,1,.5),a.setLocalPosition(0,1.5,0),a.tags.add("npc_body_part");const r=new pc.StandardMaterial;r.diffuse.set(i[0],i[1],i[2]),r.update(),setTimeout(()=>{a.model&&a.model.model&&a.model.model.meshInstances&&(a.model.model.meshInstances[0].material=r)},0),e.addChild(a),e._body=a,this.createNPCHead(e,o),this.createNPCLimbs(e,n,s),console.log(`[EntityFactory] Created detailed body for NPC: ${e.name}`)}createNPCHead(e,t){const i=new pc.Entity(`${e.name}_Head`);i.addComponent("model",{type:"sphere"}),i.setLocalScale(.4,.4,.4),i.setLocalPosition(0,2.3,0),i.tags.add("npc_body_part");const o=new pc.StandardMaterial;o.diffuse.set(t[0],t[1],t[2]),o.update(),setTimeout(()=>{i.model&&i.model.model&&i.model.model.meshInstances&&(i.model.model.meshInstances[0].material=o)},0),e.addChild(i),e._head=i}createNPCLimbs(e,t,i){const o=new pc.StandardMaterial;o.diffuse.set(t[0],t[1],t[2]),o.update();const n=new pc.Entity(`${e.name}_LeftArm`);n.addComponent("model",{type:"cylinder"}),n.setLocalScale(.1,.5,.1),n.setLocalPosition(-.4,1.5,0),n.tags.add("npc_body_part"),setTimeout(()=>{n.model&&n.model.model&&n.model.model.meshInstances&&(n.model.model.meshInstances[0].material=o)},0),e.addChild(n),e._leftArm=n;const s=new pc.Entity(`${e.name}_RightArm`);s.addComponent("model",{type:"cylinder"}),s.setLocalScale(.1,.5,.1),s.setLocalPosition(.4,1.5,0),s.tags.add("npc_body_part"),setTimeout(()=>{s.model&&s.model.model&&s.model.model.meshInstances&&(s.model.model.meshInstances[0].material=o)},0),e.addChild(s),e._rightArm=s;const a=new pc.StandardMaterial;a.diffuse.set(i[0],i[1],i[2]),a.update();const r=new pc.Entity(`${e.name}_LeftLeg`);r.addComponent("model",{type:"cylinder"}),r.setLocalScale(.15,.8,.15),r.setLocalPosition(-.2,.2,0),r.tags.add("npc_body_part"),setTimeout(()=>{r.model&&r.model.model&&r.model.model.meshInstances&&(r.model.model.meshInstances[0].material=a)},0),e.addChild(r),e._leftLeg=r;const l=new pc.Entity(`${e.name}_RightLeg`);l.addComponent("model",{type:"cylinder"}),l.setLocalScale(.15,.8,.15),l.setLocalPosition(.2,.2,0),l.tags.add("npc_body_part"),setTimeout(()=>{l.model&&l.model.model&&l.model.model.meshInstances&&(l.model.model.meshInstances[0].material=a)},0),e.addChild(l),e._rightLeg=l,e._animationTime=0,e._animationSpeed=2,e._isAnimating=!0,e._headOriginalY=e._head.getLocalPosition().y,e._leftArmOriginalX=e._leftArm.getLocalPosition().x,e._rightArmOriginalX=e._rightArm.getLocalPosition().x}updateNPCAnimation(e,t){if(e._isAnimating&&t){if(e._animationTime+=t*e._animationSpeed,e._head){const t=.1,i=3,o=Math.sin(e._animationTime*i)*t,n=e._head.getLocalPosition();e._head.setLocalPosition(n.x,e._headOriginalY+o,n.z)}if(e._leftArm&&e._rightArm){const t=.15,i=2.5,o=Math.sin(e._animationTime*i)*t,n=Math.sin((e._animationTime+Math.PI)*i)*t,s=e._leftArm.getLocalPosition(),a=e._rightArm.getLocalPosition();e._leftArm.setLocalPosition(e._leftArmOriginalX+o,s.y,s.z),e._rightArm.setLocalPosition(e._rightArmOriginalX+n,a.y,a.z)}}}setNPCAnimation(e,t){void 0!==e._isAnimating&&(e._isAnimating=t,console.log(`[EntityFactory] NPC ${e.name} animation ${t?"enabled":"disabled"}`))}setNPCRandomTarget(e){e._randomTarget||(e._randomTarget=new pc.Vec3);const t=e.getPosition(),i=Math.random()*Math.PI*2,o=5*Math.random();e._randomTarget.set(t.x+Math.cos(i)*o,t.y,t.z+Math.sin(i)*o),e._targetReached=!1,e._moveStartTime=Date.now(),console.log(`[EntityFactory] Set random target for ${e.name} at [${e._randomTarget.x.toFixed(1)}, ${e._randomTarget.y.toFixed(1)}, ${e._randomTarget.z.toFixed(1)}]`)}updateNPCMovement(e,t){if(!e._randomTarget||e._targetReached||!t)return;const i=e.getPosition();if(i.distance(e._randomTarget)<.5)return e._targetReached=!0,void setTimeout(()=>{e&&e.entity&&this.setNPCRandomTarget(e)},2e3+3e3*Math.random());const o=(new pc.Vec3).sub2(e._randomTarget,i).normalize();e.translate(1.5*o.x*t,0,1.5*o.z*t);const n=Math.atan2(o.x,o.z)*pc.math.RAD_TO_DEG;e.setEulerAngles(0,n,0)}createLight(e,t={}){return this.create(`${e}Light`,{components:{light:{type:e,...t}},position:t.position,rotation:t.rotation})}createCamera(e="Camera",t={}){return this.create(e,{components:{camera:{clearColor:new pc.Color(.4,.4,.5),...t.camera}},position:t.position||[0,5,10],rotation:t.rotation||[-25,0,0],tags:t.tags||["persistent"]})}createFloor(e=[100,1,100]){const t=this.create("Floor",{components:{model:{type:"box"},collision:{type:"box",halfExtents:[e[0]/2,.1,e[2]/2]},rigidbody:{type:"static"}},position:[0,-.1,0],scale:[e[0],.2,e[2]],rotation:[0,0,0],tags:["ground","persistent"]});if(t.model){const e=new pc.StandardMaterial;e.diffuse=new pc.Color(.6,.6,.6),e.specular=new pc.Color(.1,.1,.1),e.shininess=25,e.update(),t.model.material=e}return t}createPodium(e,t){const i=this.create(`Podium_${e}`,{components:{model:{type:"cylinder"},collision:{type:"cylinder",radius:2,height:.25},rigidbody:{type:"static"}},position:t,scale:[2,.5,2],tags:["interactive","catalog_podium","persistent"]});return this.setupPodiumMaterial(i),this.addPodiumLabel(i,e),i.interaction=new o(i,{interactionDistance:4,onInteract:async t=>{try{const t=await fetch("assets/data/catalog.json"),i=(await t.json()).entries.find(t=>t.id===e),o=i?i.title:`Content ${e}`;console.log(`[Podium] Loading ${o}`),this.app.fire("ui:notification",{text:`Loading ${o}...`,type:"info"}),this.core&&this.core.loadContent&&this.core.loadContent(e).catch(e=>{console.error("Failed to load content:",e),this.app.fire("ui:notification",{text:`Failed to load ${o}`,type:"error"})})}catch(t){console.error("Failed to load content info:",t),console.log(`[Podium] Loading content ${e}`),this.app.fire("ui:notification",{text:`Loading content ${e}...`,type:"info"}),this.core&&this.core.loadContent&&this.core.loadContent(e)}}}),i}setupPodiumMaterial(e){this.applyPodiumMaterial(e),setTimeout(()=>{this.applyPodiumMaterial(e)},100),setTimeout(()=>{this.applyPodiumMaterial(e)},500)}applyPodiumMaterial(e){if(e.model&&e.model.meshInstances&&e.model.meshInstances.length>0){const t=new pc.StandardMaterial;return t.diffuse=new pc.Color(.5,.7,.9),t.emissive=new pc.Color(.1,.2,.3),t.shininess=30,t.metalness=.1,t.update(),e.model.meshInstances.forEach(i=>{i&&(i.material=t,console.log(`[EntityFactory] Applied material to podium ${e.name}`))}),e.model.enabled=!1,e.model.enabled=!0,!0}return!1}async addPodiumLabel(e,t){try{const i=await fetch("assets/data/catalog.json"),o=(await i.json()).entries.find(e=>e.id===t);if(!o)return void console.warn(`[EntityFactory] No catalog entry found for index ${t}`);let n=null;this.app.fontManager&&this.app.fontManager.initialized&&(n=this.app.fontManager.getFontAssetId());const s=((e,t=15,i=10)=>{if(!e)return"";const o=e.split(" "),n=[];let s="";for(const e of o){if(n.length>=i)break;const o=s?`${s} ${e}`:e;o.length<=t?s=o:s?(n.push(s),s=e.length<=t?e:e.substring(0,t)):n.push(e.substring(0,t))}return s&&n.length{i.screen&&(i.screen.screenSpace=!1),e.addChild(i),console.log(`[EntityFactory] Added label ${i.name} to podium ${t}`)});const l=()=>{[a,r].forEach(e=>{if(e&&e.enabled){let t=null;if(t=this.app.root.findByName("Player"),!t){const e=this.app.root.findComponents("camera");e.length>0&&(t=e[0].entity)}if(!t&&this.app.systems.camera&&this.app.systems.camera.cameras.length>0&&(t=this.app.systems.camera.cameras[0].entity),t)try{const i=e.getPosition(),o=t.getPosition(),n=new pc.Vec3;n.sub2(o,i).normalize();const s=new pc.Mat4;s.setFromAxisAngle(pc.Vec3.UP,Math.atan2(n.x,n.z)),e.setRotation(s.getEulerAngles())}catch(i){try{e.lookAt(t.getPosition()),e.rotateLocal(0,180,0)}catch(e){}}}})};this.app.on("update",l),e._podiumLabelUpdateHandler=l}catch(e){console.error(`[EntityFactory] Failed to add label to podium ${t}:`,e)}}}class a{constructor(e){this.app=e,this.state={catalogIndex:-1,currentContent:null,isLoading:!1,isPaused:!1},this.sceneResources={eventHandlers:[],intervals:[],timeouts:[]},this.eventBus=new t(e),this.entityFactory=new s(e,this),this.systems={},e.core=this}async initialize(){console.log("[CoreManager] Initializing..."),this.initializeLayers(),await this.registerSystems(),this.setupEventHandlers(),this.app.fire("core:initialized"),console.log("[CoreManager] Initialization complete")}initializeLayers(){const e=this.app.scene.layers,t=e.getLayerById(0);t&&(t.name="World");const i=new pc.Layer({name:"UI",id:1,opaqueSortMode:pc.SORTMODE_MANUAL,transparentSortMode:pc.SORTMODE_MANUAL});e.push(i);const o=new pc.Layer({name:"Debug",id:15,enabled:!1});e.push(o)}async registerSystems(){const e=["StatsManager","InventoryManager","SkillTreeManager","NPCManager","ObjectiveManager","DialogueSystem","SaveLoadManager","CompletionRecord"];for(const t of e)try{this.systems[t.toLowerCase()]={name:t,initialized:!1},console.log(`[CoreManager] Registered system: ${t}`)}catch(e){console.error(`[CoreManager] Failed to load system ${t}:`,e)}for(const e of Object.values(this.systems))e.initialize&&await e.initialize(),e.initialized=!0;this.app.fire("core:systemsReady")}setupEventHandlers(){this.app.on("catalog:select",this.loadContent,this),this.app.on("content:exit",this.exitToVisualCatalog,this),this.app.on("game:save",this.saveGame,this),this.app.on("game:load",this.loadGame,this),this.app.on("game:pause",this.pauseGame,this),this.app.on("game:resume",this.resumeGame,this)}async loadContent(e){if(this.state.isLoading)console.warn("[CoreManager] Already loading content");else{console.log(`[CoreManager] Loading content index: ${e}`),this.state.isLoading=!0,this.debugAmmoState("LOAD_START"),"undefined"!=typeof Ammo?(window.AmmoBackup=Ammo,console.log("[CoreManager] Refreshed Ammo factory backup before content load")):window.AmmoBackup||window.AmmoFactory||console.warn("[CoreManager] No Ammo factory available to backup before content load"),this.app.fire("content:loading",e),this.state.catalogIndex>=0&&await this.cleanupContent(),this.state.catalogIndex=e;try{this.updateProgress(10,"Loading catalog data...");const t=(await this.loadCatalogData()).content[e];if(!t)throw new Error(`Invalid content index: ${e}`);let i;if(this.state.currentContent=t,this.updateProgress(30,`Loading ${t.name||t.script}...`),t.script){console.log(`[CoreManager] Attempting to load: ../content/${t.type}s/${t.script}.js`);try{const e=await n(949)(`./${t.type}s/${t.script}.js`);if(this.debugAmmoState("POST_WEBPACK_IMPORT"),i=e.default,!i)throw new Error(`Failed to load content module: ${t.script} - module.default is ${i}`);console.log(`[CoreManager] Successfully loaded content module: ${t.script}`)}catch(e){throw console.error(`[CoreManager] Failed to import module ${t.script}:`,e),new Error(`Module loading failed for ${t.script}: ${e.message}`)}if(this.currentContentInstance=i,this.updateProgress(50,"Building scene..."),i.build){const t=await i.build(this.app,this.app.sceneBuilder,this.entityFactory);t&&(t.tags.add(`catalog_${e}`),t.tags.add("content_entity"),this.app.root.addChild(t))}if(this.updateProgress(70,"Initializing content..."),i.onLoad&&i.onLoad(this.app,this),this.updateProgress(85,"Setting up scene..."),this.app.platformCameraManager&&this.app.camera&&i){const e=t.script||t.name||"unknown";console.log(`[CoreManager] Initializing camera system for scene: ${e}`),this.app.platformCameraManager.initializeForScene(e,this.app.camera)}const o=this.app.root.findByName("CatalogRoom");o&&(o.enabled=!1);const s=this.app.root.findByName("SpawnPoint"),a=this.app.root.findByTag("player"),r=a&&a.length>0?a[0]:null;s&&r&&s.getPosition&&r.setPosition&&(r.setPosition(s.getPosition()),r.rigidbody&&r.rigidbody.linearVelocity&&r.rigidbody.angularVelocity&&(r.rigidbody.linearVelocity=new pc.Vec3(0,0,0),r.rigidbody.angularVelocity=new pc.Vec3(0,0,0))),this.updateProgress(95,"Finalizing..."),"undefined"==typeof Ammo&&(console.warn("[CoreManager] Ammo lost during content load, restoring..."),window.AmmoBackup&&"function"==typeof window.AmmoBackup?(window.Ammo=window.AmmoBackup,console.log("[CoreManager] Restored Ammo from backup during content load")):window.AmmoFactory&&"function"==typeof window.AmmoFactory&&(window.Ammo=window.AmmoFactory,console.log("[CoreManager] Restored Ammo from startup factory during content load"))),console.log(`[CoreManager] Successfully loaded content: ${t.script}`),console.log(`[CoreManager] Physics check - Ammo: ${"undefined"!=typeof Ammo}, Rigidbody: ${!!this.app.systems.rigidbody}`)}for(const i of Object.values(this.systems))i.onContentLoaded&&i.onContentLoaded(e,t);this.updateProgress(100,"Complete!"),this.app.fire("content:loaded",e,t),this.showContentBriefing(e)}catch(t){console.error("[CoreManager] Failed to load content:",t),this.state.catalogIndex=-1,this.state.currentContent=null,this.currentContentInstance=null,this.app.fire("ui:notification",{text:`Failed to load content: ${t.message}`,type:"error",duration:5e3}),this.app.fire("content:loadError",e,t),this.updateProgress(0,"Load failed")}finally{this.state.isLoading=!1}}}trackEventHandler(e,t){return this.sceneResources.eventHandlers.push({event:e,handler:t}),t}trackInterval(e){return this.sceneResources.intervals.push(e),e}trackTimeout(e){return this.sceneResources.timeouts.push(e),e}cleanupSceneResources(){console.log("[CoreManager] Cleaning up scene resources..."),this.sceneResources.eventHandlers.forEach(({event:e,handler:t})=>{this.app.off(e,t)}),this.sceneResources.eventHandlers=[],this.sceneResources.intervals.forEach(e=>clearInterval(e)),this.sceneResources.intervals=[],this.sceneResources.timeouts.forEach(e=>clearTimeout(e)),this.sceneResources.timeouts=[],console.log("[CoreManager] Scene resources cleaned")}async cleanupContent(){console.log("[CoreManager] Cleaning up content"),console.log("[CoreManager] Physics before cleanup:",!!this.app.systems.rigidbody,"Ammo:","undefined"!=typeof Ammo),this.app.fire("content:unloading"),this.cleanupSceneResources(),this.currentContentInstance&&this.currentContentInstance.onUnload&&this.currentContentInstance.onUnload(this.app,this),this.app.root.findByTag(`catalog_${this.state.catalogIndex}`).forEach(e=>{e.tags.has("persistent")||e.destroy()}),this.app.root.findByTag("content_entity").forEach(e=>{e.tags.has("persistent")||e.destroy()});for(const e of Object.values(this.systems))e.onContentUnloaded&&e.onContentUnloaded();this.state.catalogIndex=-1,this.state.currentContent=null,this.currentContentInstance=null}exitToVisualCatalog(){console.log("[CoreManager] Exiting to visual catalog"),this.debugAmmoState("CATALOG_EXIT_START"),this.cleanupContent().then(()=>{if(this.debugAmmoState("POST_CONTENT_CLEANUP"),this.app.fire("catalog:exit"),"undefined"==typeof Ammo){console.warn("[CoreManager] Global Ammo undefined, attempting restoration...");let e=!1;window.AmmoBackup&&"function"==typeof window.AmmoBackup?(console.log("[CoreManager] Restoring Ammo from recent backup"),window.Ammo=window.AmmoBackup,e=!0):window.AmmoFactory&&"function"==typeof window.AmmoFactory&&(console.log("[CoreManager] Restoring Ammo from startup factory"),window.Ammo=window.AmmoFactory,e=!0),e&&"undefined"!=typeof Ammo?(console.log("[CoreManager] Ammo successfully restored"),this.app.systems.rigidbody&&console.log("[CoreManager] Physics system still exists, Ammo restoration complete")):(console.error("[CoreManager] Ammo restoration failed - no valid backup available"),this.handleAmmoLoss())}else console.log("[CoreManager] Ammo.js still available");this.debugAmmoState("POST_RESTORATION"),this.currentContentInstance=null,this.resetSceneToDefault();const e=this.app.root.findByName("CatalogRoom");if(e){e.enabled=!0;try{this.rebuildPhysicsComponents(e)}catch(e){console.error("[CoreManager] Physics rebuild failed during catalog restore:",e)}try{this.restoreCatalogRoomLighting(e)}catch(e){console.error("[CoreManager] Failed to restore catalog room lighting:",e)}try{const t=e.findByTag("catalog_podium");console.log(`[CoreManager] Found ${t.length} podiums to restore`),t.forEach((e,t)=>{if(e){e.enabled=!0;try{this.entityFactory&&this.entityFactory.applyPodiumMaterial&&this.entityFactory.applyPodiumMaterial(e)}catch(e){console.error(`[CoreManager] Failed to apply material to podium ${t}:`,e)}e.collision&&(e.collision.enabled=!0),e.rigidbody&&(e.rigidbody.enabled=!0),this.restorePodiumLabels(e),console.log(`[CoreManager] Restored podium ${t}: enabled=${e.enabled}, collision=${!!e.collision}, rigidbody=${!!e.rigidbody}`)}})}catch(e){console.error("[CoreManager] Error restoring podiums:",e)}try{this.restoreCatalogRoomMaterials(e)}catch(e){console.error("[CoreManager] Failed to restore catalog room materials:",e)}const t=e.findByName("TestCube");t&&(t.enabled=!0,t.collision&&(t.collision.enabled=!0),t.rigidbody&&(t.rigidbody.enabled=!0))}const t=this.app.root.findByName("Floor");if(t&&(t.enabled=!0,t.collision&&(t.collision.enabled=!0),t.rigidbody&&(t.rigidbody.enabled=!0),t.model)){const e=new pc.StandardMaterial;e.diffuse=new pc.Color(.6,.6,.6),e.specular=new pc.Color(.1,.1,.1),e.shininess=25,e.update(),t.model.material=e}const i=this.app.root.findByTag("player"),o=i&&i.length>0?i[0]:null;o&&o.setPosition&&(o.setPosition(0,1,0),o.rigidbody&&o.rigidbody.linearVelocity&&o.rigidbody.angularVelocity&&(o.rigidbody.linearVelocity=new pc.Vec3(0,0,0),o.rigidbody.angularVelocity=new pc.Vec3(0,0,0))),this.app.interactionController&&!this.app.interactionController.initialized&&(this.app.interactionController.initialize(),console.log("[CoreManager] Re-initialized interaction controller"));for(const e of Object.values(this.systems))e.onContentLoaded&&"function"==typeof e.onContentLoaded&&e.onContentLoaded(-1,{name:"Catalog Room",type:"catalog"});this.app.fire("ui:notification",{text:"Returned to Catalog Room",type:"info"}),this.app.scene.ambientLight=new pc.Color(.6,.6,.6),console.log("[CoreManager] Set final ambient lighting:",this.app.scene.ambientLight);try{this.app.platformCameraManager&&this.app.camera&&this.app.platformCameraManager.initializeForScene("catalog",this.app.camera)}catch(e){console.warn("[CoreManager] Camera re-init for catalog failed:",e)}try{this.app.sceneTransition&&this.app.sceneTransition.endTransition()}catch(e){console.warn("[CoreManager] Failed to end transition overlay:",e)}console.log("[CoreManager] Catalog room restoration complete"),console.log("[CoreManager] Physics ready:",!!this.app.systems.rigidbody),console.log("[CoreManager] Ammo available:","undefined"!=typeof Ammo)}).catch(e=>{console.error("[CoreManager] Error during catalog exit:",e);try{const e=this.app.root.findByName("CatalogRoom");e&&!e.enabled&&(e.enabled=!0,this.app.scene.ambientLight=new pc.Color(.8,.8,.8))}catch(e){console.error("[CoreManager] Fallback catalog restoration also failed:",e)}this.app.fire("ui:notification",{text:"Error returning to catalog - trying fallback recovery",type:"warning"});try{this.app.sceneTransition&&this.app.sceneTransition.endTransition()}catch(e){console.warn("[CoreManager] Failed to end transition overlay (fallback):",e)}})}resetSceneToDefault(){console.log("[CoreManager] Resetting scene to default state"),this.app.scene.ambientLight=new pc.Color(.3,.3,.3),this.app.root.find(e=>e.light).forEach(e=>{e.tags.has("persistent")?(e.enabled=!0,e.light&&(e.light.castShadows=!1),"Light"===e.name||"directional"===e.light.type?(e.light.color=new pc.Color(1,.9,.7),e.light.intensity=1):"RoomCeilingLight"===e.name&&(e.light.color=new pc.Color(1,.95,.8),e.light.intensity=2,e.light.type=pc.LIGHTTYPE_POINT)):e.parent&&e.parent.enabled||(e.enabled=!1)}),this.app.root&&this.app.root.findByTag("screen").forEach(e=>{e.tags.has("persistent")||e.destroy()}),console.log("[CoreManager] Scene reset complete")}rebuildPhysicsComponents(e){try{if(e.collision)try{e.collision.enabled=!0}catch(t){console.warn(`[CoreManager] Failed to enable collision for entity ${e.name}:`,t)}if(e.rigidbody)try{if(e.rigidbody.enabled=!0,e.rigidbody.body&&this.app.systems.rigidbody&&"undefined"!=typeof Ammo)try{const t=this.app.systems.rigidbody.dynamicsWorld;if(t&&t.getCollisionObjectArray)try{const i=t.getCollisionObjectArray();let o=!1;for(let t=0;t0&&e.children.forEach(e=>{try{this.rebuildPhysicsComponents(e)}catch(t){console.warn(`[CoreManager] Failed to rebuild physics for child entity ${e.name}:`,t)}})}catch(t){console.warn(`[CoreManager] Failed to rebuild physics components for entity ${e.name}:`,t)}}restoreCatalogRoomLighting(e){try{if(!e)return void console.warn("[CoreManager] No catalog room provided for lighting restoration");console.log("[CoreManager] Restoring catalog room lighting");const t=e.find(e=>e.light);if(t.forEach(e=>{e&&e.light&&(e.enabled=!0,e.light.castShadows=!1,"RoomCeilingLight"===e.name&&(e.light.color=new pc.Color(1,.95,.8),e.light.intensity=2,e.light.type=pc.LIGHTTYPE_POINT,console.log("[CoreManager] Restored ceiling light")))}),0===t.length&&this.entityFactory){console.log("[CoreManager] No lights found, creating new ceiling light");try{const t=this.entityFactory.createLight("point",{color:new pc.Color(1,.95,.8),intensity:2,range:30,position:[0,7,0]});t&&(t.name="RoomCeilingLight",t.tags.add("persistent"),t.light&&(t.light.castShadows=!1),e.addChild(t))}catch(e){console.error("[CoreManager] Failed to create ceiling light:",e)}}else this.entityFactory||console.warn("[CoreManager] EntityFactory not available for light creation")}catch(e){console.error("[CoreManager] Error in restoreCatalogRoomLighting:",e)}}restoreCatalogRoomMaterials(e){try{if(!e)return void console.warn("[CoreManager] No catalog room provided for materials restoration");console.log("[CoreManager] Restoring catalog room materials");const t=e.findByName("Floor");if(t&&t.model&&t.model.meshInstances)try{const e=new pc.StandardMaterial;e.diffuse=new pc.Color(.6,.6,.6),e.specular=new pc.Color(.1,.1,.1),e.shininess=25,e.update(),t.model.meshInstances.forEach(t=>{t&&(t.material=e)})}catch(e){console.error("[CoreManager] Failed to restore floor material:",e)}const i=e.find(e=>e.name&&e.name.includes("Wall"));i&&i.length>0&&i.forEach(e=>{if(e&&e.model&&e.model.meshInstances)try{const t=new pc.StandardMaterial;t.diffuse=new pc.Color(.8,.8,.8),t.specular=new pc.Color(.1,.1,.1),t.shininess=20,t.update(),e.model.meshInstances.forEach(e=>{e&&(e.material=t)})}catch(e){console.error("[CoreManager] Failed to restore wall material:",e)}})}catch(e){console.error("[CoreManager] Error in restoreCatalogRoomMaterials:",e)}}restorePodiumLabels(e){try{if(!e||!e.find)return void console.warn("[CoreManager] Invalid podium provided for label restoration");const t=e.find(e=>e.screen);t&&t.length>0&&t.forEach(e=>{if(e){e.enabled=!0;const t=e.find(e=>e.element&&e.element.type===pc.ELEMENTTYPE_TEXT);t&&t.length>0&&t.forEach(e=>{e&&(e.enabled=!0,e.element&&(e.element.color=new pc.Color(1,1,.2)))})}})}catch(e){console.error("[CoreManager] Error in restorePodiumLabels:",e)}}async loadCatalogData(){try{const e=await fetch("assets/data/catalog.json");return{content:(await e.json()).entries.map((e,t)=>({id:t,name:e.title,type:e.type,script:e.script}))}}catch(e){return console.error("[CoreManager] Failed to load catalog data:",e),{content:[{id:0,name:"Tavern Scene",type:"scene",script:"TavernScene"},{id:1,name:"Medical Bay",type:"scene",script:"MedicalBayScene"},{id:2,name:"Generator Crisis",type:"mission",script:"GeneratorCrisis"},{id:3,name:"Nightshift Horror",type:"mission",script:"NightshiftHorror"}]}}}saveGame(e=0){console.log(`[CoreManager] Saving game to slot ${e}`),this.systems.saveloadmanager&&this.systems.saveloadmanager.save&&this.systems.saveloadmanager.save(e)}loadGame(e=0){console.log(`[CoreManager] Loading game from slot ${e}`),this.systems.saveloadmanager&&this.systems.saveloadmanager.load&&this.systems.saveloadmanager.load(e)}pauseGame(){this.state.isPaused||(console.log("[CoreManager] Pausing game"),this.state.isPaused=!0,this.app.timeScale=0,this.app.fire("game:paused"))}resumeGame(){this.state.isPaused&&(console.log("[CoreManager] Resuming game"),this.state.isPaused=!1,this.app.timeScale=1,this.app.fire("game:resumed"))}getSystem(e){return this.systems[e.toLowerCase()]}updateProgress(e,t){this.app.sceneTransition&&(this.app.sceneTransition.updateProgress(e),t&&this.app.sceneTransition.loadingText&&(this.app.sceneTransition.loadingText.element.text=t)),this.app.fire("content:progress",e,t)}async showContentBriefing(e){try{const t=await fetch("assets/data/catalog.json"),i=(await t.json()).entries.find(t=>t.id===e);if(i&&i.briefing){console.log(`[CoreManager] Showing briefing for ${i.title}`);let t=`📋 BRIEFING: ${i.title}\n\n`;t+=`🎯 ROLE: ${i.briefing.role}\n`,t+=`📍 SETTING: ${i.briefing.setting}\n\n`,i.briefing.objectives&&i.briefing.objectives.length>0&&(t+="✅ OBJECTIVES:\n",i.briefing.objectives.forEach((e,i)=>{t+=` ${i+1}. ${e}\n`}),t+="\n"),i.briefing.controls&&i.briefing.controls.length>0&&(t+="🎮 CONTROLS:\n",i.briefing.controls.forEach(e=>{t+=` • ${e}\n`}),t+="\n"),i.briefing.tips&&i.briefing.tips.length>0&&(t+="💡 TIPS:\n",i.briefing.tips.forEach(e=>{t+=` • ${e}\n`})),this.app.fire("ui:notification",{text:t,type:"info",duration:15e3}),this.app.fire("content:briefing",e,i.briefing)}}catch(t){console.warn(`[CoreManager] Failed to show briefing for content ${e}:`,t)}}destroy(){this.app.off("catalog:select",this.loadContent,this),this.app.off("content:exit",this.exitToVisualCatalog,this),this.app.off("game:save",this.saveGame,this),this.app.off("game:load",this.loadGame,this),this.app.off("game:pause",this.pauseGame,this),this.app.off("game:resume",this.resumeGame,this);for(const e of Object.values(this.systems))e.destroy&&e.destroy();this.eventBus.cleanup()}handleAmmoLoss(){console.error("[CoreManager] Ammo.js completely lost! Physics will not work."),this.app.fire("ui:notification",{text:"Critical: Physics system lost. Please reload the page.",type:"error",duration:5e3})}debugAmmoState(e){const t={location:e,timestamp:Date.now(),ammo:{global:typeof Ammo,factory:typeof window.AmmoFactory,backup:typeof window.AmmoBackup,instance:typeof window.AmmoInstance},physics:{rigidbodySystem:!!this.app.systems.rigidbody,dynamicsWorld:!!this.app.systems.rigidbody?.dynamicsWorld,bodyCount:this.app.systems.rigidbody?.dynamicsWorld?.getCollisionObjectArray()?.size()||0},memory:{wasmSupported:"undefined"!=typeof WebAssembly,wasmMemory:void 0!==WebAssembly?.Memory},globals:Object.keys(window).filter(e=>e.toLowerCase().includes("ammo"))};return console.group(`🔍 AMMO DEBUG: ${e}`),console.table(t.ammo),console.table(t.physics),console.log("Global Ammo refs:",t.globals),console.log("Full state:",t),console.groupEnd(),window.ammoDebugLog||(window.ammoDebugLog=[]),window.ammoDebugLog.push(t),t}}class r{constructor(e){this.app=e,this.loadedAssets=new Map,this.loadingPromises=new Map,this.catalogAssets=new Map,this.assetCatalogMap=new Map,this.currentCatalogIndex=void 0;try{this.setupCatalogTracking()}catch(e){console.warn("[AssetLoader] Failed to setup catalog tracking:",e)}}setupCatalogTracking(){this.app.on("content:loading",this.onCatalogLoading,this),this.app.on("content:unloading",this.onCatalogUnloading,this)}onCatalogLoading(e){console.log(`[AssetLoader] Starting asset tracking for catalog ${e}`),this.catalogAssets.has(e)||this.catalogAssets.set(e,new Set),this.currentCatalogIndex=e}onCatalogUnloading(){void 0!==this.currentCatalogIndex&&this.cleanupCatalogAssets(this.currentCatalogIndex),this.currentCatalogIndex=void 0}cleanupCatalogAssets(e){console.log(`[AssetLoader] Cleaning up assets for catalog ${e}`);const t=this.catalogAssets.get(e);if(!t)return;let i=0;for(const e of t){const t=this.loadedAssets.get(e);if(t&&"function"==typeof t.destroy)try{t.destroy(),i++}catch(t){console.warn(`[AssetLoader] Failed to destroy asset ${e}:`,t)}this.loadedAssets.delete(e),this.assetCatalogMap.delete(e)}this.catalogAssets.delete(e),console.log(`[AssetLoader] Cleaned up ${i} assets for catalog ${e}`),this.app.fire("assets:catalogCleaned",e,i)}trackAsset(e,t){if(void 0!==this.currentCatalogIndex){const t=this.catalogAssets.get(this.currentCatalogIndex);t&&(t.add(e),this.assetCatalogMap.set(e,this.currentCatalogIndex),console.log(`[AssetLoader] Asset ${e} tagged for catalog ${this.currentCatalogIndex}`))}this.loadedAssets.set(e,t)}async loadJSON(e){if(this.loadedAssets.has(e))return this.loadedAssets.get(e);if(this.loadingPromises.has(e))return this.loadingPromises.get(e);const t=this.fetchJSON(e);this.loadingPromises.set(e,t);try{const i=await t;return this.trackAsset(e,i),this.loadingPromises.delete(e),i}catch(t){throw this.loadingPromises.delete(e),t}}async fetchJSON(e){try{const t=await fetch(e);if(!t.ok)throw new Error(`Failed to load ${e}: ${t.status}`);return await t.json()}catch(t){throw console.error(`[AssetLoader] Failed to load JSON from ${e}:`,t),t}}async loadTexture(e,t={}){const i=`texture_${e}`;return this.loadedAssets.has(i)?this.loadedAssets.get(i):new Promise((o,n)=>{const s=new pc.Texture(this.app.graphicsDevice,{name:t.name||e,...t}),a=new Image;a.crossOrigin="anonymous",a.onload=()=>{s.setSource(a),this.trackAsset(i,s),o(s)},a.onerror=()=>{n(new Error(`Failed to load texture: ${e}`))},a.src=e})}async loadModel(e,t={}){const i=`model_${e}`;return this.loadedAssets.has(i)?this.loadedAssets.get(i):new Promise((t,o)=>{this.app.assets.loadFromUrl(e,"model",(n,s)=>{n?o(new Error(`Failed to load model: ${e} - ${n}`)):(this.trackAsset(i,s),t(s))})})}async loadFont(e,t={}){const i=`font_${e}`;return this.loadedAssets.has(i)?this.loadedAssets.get(i):new Promise((t,o)=>{this.app.assets.loadFromUrl(e,"font",(n,s)=>{n?o(new Error(`Failed to load font: ${e} - ${n}`)):(this.trackAsset(i,s),t(s))})})}async loadSound(e,t={}){const i=`sound_${e}`;return this.loadedAssets.has(i)?this.loadedAssets.get(i):new Promise((t,o)=>{this.app.assets.loadFromUrl(e,"audio",(n,s)=>{n?o(new Error(`Failed to load sound: ${e} - ${n}`)):(this.trackAsset(i,s),t(s))})})}async loadMultiple(e){const t=e.map(e=>{switch(e.type){case"json":return this.loadJSON(e.url);case"texture":return this.loadTexture(e.url,e.options);case"model":return this.loadModel(e.url,e.options);case"font":return this.loadFont(e.url,e.options);case"sound":return this.loadSound(e.url,e.options);default:return Promise.reject(new Error(`Unknown asset type: ${e.type}`))}});try{const i=await Promise.all(t);return e.map((e,t)=>({...e,data:i[t]}))}catch(e){throw console.error("[AssetLoader] Failed to load multiple assets:",e),e}}async preloadGameAssets(){console.log("[AssetLoader] Preloading game assets...");const e=[{id:"catalog",type:"json",url:"assets/data/catalog.json"}];try{const t=await this.loadMultiple(e);return console.log("[AssetLoader] Game assets preloaded:",t.length),t}catch(e){throw console.error("[AssetLoader] Failed to preload game assets:",e),e}}createMaterial(e,t={}){const i=new pc.StandardMaterial;i.name=e,t.diffuseColor&&(i.diffuse=t.diffuseColor),t.emissiveColor&&(i.emissive=t.emissiveColor),t.specular&&(i.specular=t.specular),void 0!==t.shininess&&(i.shininess=t.shininess),void 0!==t.opacity&&(i.opacity=t.opacity,t.opacity<1&&(i.blendType=pc.BLEND_NORMAL)),t.diffuseMap&&(i.diffuseMap=t.diffuseMap),t.normalMap&&(i.normalMap=t.normalMap),i.update();const o=`material_${e}`;return this.trackAsset(o,i),i}getMaterial(e){const t=`material_${e}`;return this.loadedAssets.get(t)}getAsset(e){return this.loadedAssets.get(e)}hasAsset(e){return this.loadedAssets.has(e)}clearCache(){for(const[e,t]of this.loadedAssets.entries())t&&"function"==typeof t.destroy&&t.destroy();this.loadedAssets.clear(),this.loadingPromises.clear()}clearCacheExcept(e){const t=[];for(const i of this.loadedAssets.keys())e.includes(i)||t.push(i);t.forEach(e=>{const t=this.loadedAssets.get(e);t&&"function"==typeof t.destroy&&t.destroy(),this.loadedAssets.delete(e)})}getMemoryUsage(){let e=0,t=0,i=0,o=0;for(const[n,s]of this.loadedAssets.entries())if(n.startsWith("texture_")){if(t++,s.getSource&&s.getSource()){const t=s.getSource();e+=t.width*t.height*4}}else n.startsWith("model_")?i++:n.startsWith("sound_")&&o++;return{totalAssets:this.loadedAssets.size,textureCount:t,modelCount:i,audioCount:o,estimatedSize:e}}}class l{constructor(e,t){this.app=e,this.factory=t,this.assetLoader=this.app.assetLoader}buildScene(e){const t=this.factory.create(e.name,{tags:["scene_root"]});return e.environment&&this.buildEnvironment(t,e.environment),e.objects&&e.objects.forEach(e=>{const i=this.buildObject(e);t.addChild(i)}),e.lighting&&this.setupLighting(t,e.lighting),e.camera&&this.setupCamera(t,e.camera),t}buildEnvironment(e,t){if(t.floor){const i=this.buildFloor(t.floor);e.addChild(i)}if(t.walls&&t.walls.forEach(t=>{const i=this.buildWall(t);i&&e.addChild(i)}),t.ceiling){const i=this.buildCeiling(t.ceiling);e.addChild(i)}t.skybox&&this.setSkybox(t.skybox)}buildFloor(e){const t=this.factory.create("Floor",{components:{model:{type:"plane"},collision:{type:"box",halfExtents:[(e.size?e.size[0]:50)/2,.1,(e.size?e.size[2]:50)/2]},rigidbody:{type:"static"}},scale:e.size||[100,1,100],position:e.position||[0,0,0],rotation:[0,0,0]});return e.material?this.applyMaterial(t,e.material):this.applyMaterial(t,{diffuseColor:new pc.Color(.5,.5,.5)}),t}buildWall(e){if(!e.position)return console.warn("[SceneBuilder] Wall creation attempted without position data - skipping"),null;console.log(`[SceneBuilder] Creating wall "${e.name}" at position:`,e.position,"with scale:",e.scale||[.2,4,10]);const t=this.factory.create(e.name||"Wall",{components:{model:{type:"box"},collision:{type:"box",halfExtents:e.halfExtents||[.1,2,5]},rigidbody:{type:"static"}},position:e.position,rotation:e.rotation||[0,0,0],scale:e.scale||[.2,4,10]});return setTimeout(()=>{if(t.model&&t.model.meshInstances&&t.model.meshInstances.length>0){let i;if(e.material&&this.assetLoader&&this.assetLoader.createMaterial)try{i=this.assetLoader.createMaterial(e.material.name||"WallMaterial",e.material)}catch(e){console.warn("[SceneBuilder] Failed to create material via AssetLoader:",e)}i||(i=new pc.StandardMaterial,i.diffuse=e.material?.diffuseColor||new pc.Color(.8,.8,.8),i.specular=new pc.Color(.1,.1,.1),i.shininess=25,i.update()),t.model.meshInstances.forEach(e=>{e&&(e.material=i)}),console.log(`[SceneBuilder] Applied material to wall ${t.name}`)}},100),t}buildCeiling(e){const t=this.factory.create("Ceiling",{components:{model:{type:"plane"}},scale:e.size||[100,1,100],position:[0,e.height||4,0],rotation:[180,0,0]});return e.material?this.applyMaterial(t,e.material):this.applyMaterial(t,{diffuseColor:new pc.Color(.9,.9,.9)}),t}buildObject(e){const t={components:{},position:e.position,rotation:e.rotation,scale:e.scale,tags:e.tags||[]};e.model&&("string"==typeof e.model?t.components.model={type:e.model}:t.components.model=e.model),e.physics&&(e.physics.collision&&(t.components.collision=e.physics.collision),e.physics.rigidbody&&(t.components.rigidbody=e.physics.rigidbody)),e.light&&(t.components.light=e.light),e.scripts&&(t.scripts=e.scripts);const i=this.factory.create(e.name,t);return e.material&&this.applyMaterial(i,e.material),e.children&&e.children.forEach(e=>{const t=this.buildObject(e);i.addChild(t)}),i}setupLighting(e,t){if(t.ambient&&(this.app.scene.ambientLight=t.ambient.color||new pc.Color(.4,.4,.4)),t.directional){const i=this.factory.createLight("directional",{color:t.directional.color||new pc.Color(1,1,1),intensity:t.directional.intensity||1,castShadows:!1!==t.directional.shadows,position:t.directional.position||[5,10,5],rotation:t.directional.rotation||[45,30,0]});e.addChild(i)}t.pointLights&&t.pointLights.forEach((t,i)=>{const o=this.factory.createLight("point",{color:t.color||new pc.Color(1,1,1),intensity:t.intensity||1,range:t.range||10,castShadows:!1!==t.shadows,position:t.position||[0,3,0]});e.addChild(o)}),t.spotLights&&t.spotLights.forEach((t,i)=>{const o=this.factory.createLight("spot",{color:t.color||new pc.Color(1,1,1),intensity:t.intensity||1,range:t.range||10,innerConeAngle:t.innerAngle||20,outerConeAngle:t.outerAngle||40,castShadows:!1!==t.shadows,position:t.position||[0,5,0],rotation:t.rotation||[-90,0,0]});e.addChild(o)})}setupCamera(e,t){const i=this.factory.createCamera("SceneCamera",{camera:{clearColor:t.clearColor||new pc.Color(.4,.4,.5),fov:t.fov||75,nearClip:t.nearClip||.1,farClip:t.farClip||1e3},position:t.position||[0,5,10],rotation:t.rotation||[-25,0,0]});return e.addChild(i),i}applyMaterial(e,t){if(!e.model)return;let i;i="string"==typeof t?this.assetLoader.getMaterial(t):this.assetLoader.createMaterial(t.name||"GeneratedMaterial",t),i&&e.model.meshInstances&&e.model.meshInstances.forEach(e=>{e.material=i})}setSkybox(e){e.color?(this.app.scene.skyboxMip=0,this.app.scene.skyboxIntensity=e.intensity||1,this.app.scene.setSkybox(null),this.app.scene.ambientLight=e.color):e.texture&&console.warn("[SceneBuilder] Texture skyboxes not implemented yet")}createRoom(e){const{size:t=[20,4,20],position:i=[0,0,0],doorways:o=[],lighting:n="standard"}=e,s=this.factory.create("Room",{position:i}),a=this.buildFloor({size:[t[0],1,t[2]],position:[0,0,0]});s.addChild(a),this.createRoomWalls(s,t,o);const r=this.buildCeiling({size:[t[0],1,t[2]],height:t[1]});return s.addChild(r),"standard"===n&&this.addStandardRoomLighting(s,t),s}createRoomWalls(e,t,i){const o=t[1],n=.5;[{pos:[t[0]/2,o/2,0],rot:[0,0,0],scale:[n,o,t[2]]},{pos:[-t[0]/2,o/2,0],rot:[0,0,0],scale:[n,o,t[2]]},{pos:[0,o/2,t[2]/2],rot:[0,0,0],scale:[t[0],o,n]},{pos:[0,o/2,-t[2]/2],rot:[0,0,0],scale:[t[0],o,n]}].forEach((t,o)=>{if(i.some(e=>e.wall===o))console.log(`[SceneBuilder] Doorway in wall ${o} - simplified implementation`);else{const i=this.buildWall({name:`Wall_${o}`,position:t.pos,rotation:t.rot,scale:t.scale,halfExtents:[t.scale[0]/2,t.scale[1]/2,t.scale[2]/2]});i&&e.addChild(i)}})}addStandardRoomLighting(e,t){const i=this.factory.createLight("point",{color:new pc.Color(1,.95,.8),intensity:2,range:.8*Math.max(...t),position:[0,t[1]-.5,0]});i.name="RoomCeilingLight",i.tags.add("persistent"),e.addChild(i)}createCorridor(e){const{length:t=20,width:i=4,height:o=4,position:n=[0,0,0],direction:s="z"}=e,a=this.factory.create("Corridor",{position:n}),r={size:"x"===s?[t,o,i]:[i,o,t],position:[0,0,0],lighting:"corridor"},l=this.createRoom(r);return a.addChild(l),a}createFurniture(e,t=[0,0,0]){const i={table:{name:"Table",model:"box",scale:[2,.1,1],position:[t[0],t[1]+.8,t[2]],physics:{collision:{type:"box",halfExtents:[1,.05,.5]},rigidbody:{type:"static"}},material:{diffuseColor:new pc.Color(.6,.4,.2)}},chair:{name:"Chair",model:"box",scale:[.6,1.6,.6],position:[t[0],t[1]+.8,t[2]],physics:{collision:{type:"box",halfExtents:[.3,.8,.3]},rigidbody:{type:"static"}},material:{diffuseColor:new pc.Color(.4,.2,.1)}}}[e];return i?this.buildObject(i):(console.warn(`[SceneBuilder] Unknown furniture type: ${e}`),null)}createProp(e,t=[0,0,0]){const i={crate:{name:"Crate",model:"box",scale:[1,1,1],position:t,physics:{collision:{type:"box",halfExtents:[.5,.5,.5]},rigidbody:{type:"static"}},material:{diffuseColor:new pc.Color(.8,.6,.4)},tags:["interactive","container"]},barrel:{name:"Barrel",model:"cylinder",scale:[.6,1.2,.6],position:t,physics:{collision:{type:"cylinder",radius:.3,height:1.2},rigidbody:{type:"static"}},material:{diffuseColor:new pc.Color(.4,.3,.2)},tags:["interactive","container"]}}[e];return i?this.buildObject(i):(console.warn(`[SceneBuilder] Unknown prop type: ${e}`),null)}}class c{static init(e){window.debug={app:e,core:()=>e.core,state:()=>{const t=e.core;if(!t)return console.log("=== DEBUG STATE ==="),void console.log("Core not initialized yet");if(console.log("=== DEBUG STATE ==="),console.log("Catalog Index:",t.state?.catalogIndex),console.log("Current Content:",t.state?.currentContent),console.log("Systems:",t.systems?Object.keys(t.systems):"No systems"),e.root&&e.root.find){const i=e.root.find(()=>!0),o=e.root.findByTag("persistent"),n=t.state?.catalogIndex||-1,s=n>=0?e.root.findByTag(`catalog_${n}`):[];console.log(`Entities - Total: ${i.length}, Persistent: ${o.length}, Content: ${s.length}`)}},loadContent:t=>e.fire("catalog:select",t),exitToCatalog:()=>e.fire("content:exit"),save:(t=0)=>e.fire("game:save",t),load:(t=0)=>e.fire("game:load",t),testTransition:(t="fade")=>{e.sceneTransition&&(e.sceneTransition.setTransitionType(t),e.fire("transition:start",{type:t,message:"Testing transition...",showProgress:!0}),setTimeout(()=>e.fire("transition:end"),2e3))},memoryStats:()=>{e.sceneMemory&&console.log("Scene Memory Stats:",e.sceneMemory.getMemoryStats())},find:t=>e.root.findByName(t),findByTag:t=>e.root.findByTag(t),listEntities:()=>{e.root.find(()=>!0).forEach(e=>{console.log(`${e.name} [${Array.from(e.tags).join(", ")}]`)})},inspectSystem:t=>{if(!e.core||!e.core.getSystem)return void console.log("Core not available");const i=e.core.getSystem(t);if(i)return console.log(`=== ${t.toUpperCase()} SYSTEM ===`),console.log(i),i;console.log(`System not found: ${t}`),console.log("Available systems:",e.core.systems?Object.keys(e.core.systems):"No systems")},giveItem:(t,i=1)=>{e.fire("item:pickup",t,i)},giveExp:t=>{e.fire("stats:modify","experience",t)},unlockSkill:t=>{e.fire("skill:unlock",t)},startObjective:t=>{e.fire("objective:start",t)},spawnNPC:(t,i,o=[0,0,0])=>{e.fire("npc:spawn",{template:t,id:i,position:o})},listNPCs:()=>{if(!e.core||!e.core.getSystem)return void console.log("Core not available");const t=e.core.getSystem("npcmanager");if(t&&t.npcs)return console.log("NPCs:",Array.from(t.npcs.keys())),t.npcs;console.log("No NPCs found or NPC manager not available")},stats:()=>{if(console.log("=== PERFORMANCE STATS ==="),e.stats&&(console.log("Draw Calls:",e.stats.drawCalls||"N/A"),console.log("Triangles:",e.stats.triangles||"N/A"),e.stats.frame&&e.stats.frame.dt&&(console.log("FPS:",Math.round(1e3/e.stats.frame.dt)),console.log("Frame Time:",e.stats.frame.dt.toFixed(2)+"ms"))),e.assetLoader&&e.assetLoader.getMemoryUsage){const t=e.assetLoader.getMemoryUsage();console.log("Asset Memory:",t)}},memoryUsage:()=>{if(performance&&performance.memory){const e=performance.memory;console.log("=== MEMORY USAGE ==="),console.log("Used:",(e.usedJSHeapSize/1024/1024).toFixed(2)+" MB"),console.log("Total:",(e.totalJSHeapSize/1024/1024).toFixed(2)+" MB"),console.log("Limit:",(e.jsHeapSizeLimit/1024/1024).toFixed(2)+" MB")}else console.log("Memory API not available")},fireEvent:(t,...i)=>{console.log(`Firing event: ${t}`,i),e.fire(t,...i)},teleport:(t,i,o)=>{e.player?(e.player.setPosition(t,i,o),console.log(`Teleported to ${t}, ${i}, ${o}`)):console.log("Player not found")},wireframe:(t=!0)=>{e.root.find(e=>e.model).forEach(e=>{e.model.meshInstances&&e.model.meshInstances.forEach(e=>{e.material&&(e.material.wireframe=t)})}),console.log("Wireframe "+(t?"enabled":"disabled"))},testMobile:()=>{console.log("=== MOBILE TESTING ===");const t=/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),i="ontouchstart"in window||navigator.maxTouchPoints>0;console.log("User Agent:",navigator.userAgent),console.log("Is Mobile UA:",t),console.log("Has Touch:",i),console.log("Max Touch Points:",navigator.maxTouchPoints),console.log("Screen Size:",window.innerWidth+"x"+window.innerHeight),e.touchController?(console.log("Touch Controller available:",!!e.touchController),e.touchController.testRaycastAtScreenCenter&&(console.log("Testing touch raycast..."),e.touchController.testRaycastAtScreenCenter())):console.log("Touch Controller not available"),localStorage.setItem("mobile_debug","true"),localStorage.setItem("debug_overlay","true"),console.log("Mobile debug overlays enabled")},testRaycast:()=>{console.log("=== RAYCAST TESTING ==="),e.interactionController?(console.log("Interaction Controller available"),e.interactionController.performDebugRaycast&&e.interactionController.performDebugRaycast(),void 0!==e.interactionController.debugRaycast&&(e.interactionController.debugRaycast=!0,e.interactionController.updateDebugOverlay(),console.log("Raycast debug visualization enabled"))):console.log("Interaction Controller not available");const t=e.root.findByTag("interactive");console.log("Interactive entities found:",t.length),t.forEach((e,t)=>{const i=e.getPosition();console.log(` ${t+1}. ${e.name} at (${i.x.toFixed(1)}, ${i.y.toFixed(1)}, ${i.z.toFixed(1)})`)})},platformInfo:()=>{console.log("=== PLATFORM INFO ==="),console.log("Navigator:",{userAgent:navigator.userAgent,platform:navigator.platform,maxTouchPoints:navigator.maxTouchPoints,cookieEnabled:navigator.cookieEnabled,onLine:navigator.onLine}),console.log("Window:",{innerWidth:window.innerWidth,innerHeight:window.innerHeight,devicePixelRatio:window.devicePixelRatio,orientation:screen.orientation?screen.orientation.angle:"N/A"}),console.log("Touch Support:",{ontouchstart:"ontouchstart"in window,maxTouchPoints:navigator.maxTouchPoints>0,msMaxTouchPoints:navigator.msMaxTouchPoints>0}),e.platformCamera?console.log("Platform Camera Manager:",e.platformCamera.platform):console.log("Platform Camera Manager not available")},touchDebug:()=>{console.log("=== TOUCH DEBUG ==="),localStorage.setItem("mobile_debug","true"),localStorage.setItem("debug_overlay","true"),window.location.search+=(window.location.search?"&":"?")+"debug=mobile",e.touchController&&e.touchController.createOrUpdateDebugOverlay&&e.touchController.createOrUpdateDebugOverlay(),e.touchController&&e.touchController.provideTouchFeedback&&e.touchController.provideTouchFeedback({x:window.innerWidth/2,y:window.innerHeight/2}),console.log("Touch debug mode enabled - check screen for overlays")},disableDebug:()=>{console.log("=== DISABLING DEBUG MODE ==="),localStorage.removeItem("mobile_debug"),localStorage.removeItem("debug_overlay");const t=document.getElementById("touch-debug-overlay");t&&t.remove();const i=document.getElementById("raycast-debug-overlay");i&&i.remove();const o=document.getElementById("mobile-debug-info");o&&o.remove(),e.interactionController&&void 0!==e.interactionController.debugRaycast&&(e.interactionController.debugRaycast=!1,e.interactionController.clearDebugLines()),console.log("Debug mode disabled - overlays removed, interaction should work normally")},testSceneLoad:(t=0)=>{console.log(`=== TESTING SCENE LOAD ${t} ===`),window.debug.disableDebug(),e.core?e.core.loadContent?(console.log("Core manager available, attempting to load content..."),e.core.loadContent(t).then(()=>{console.log(`Scene ${t} loaded successfully`)}).catch(e=>{console.error(`Failed to load scene ${t}:`,e)})):console.error("loadContent method not available on core"):console.error("Core manager not available")},showDebugLayer:(t=!0)=>{const i=e.scene.layers.getLayerById(15);i&&(i.enabled=t,console.log("Debug layer "+(t?"shown":"hidden")))},createDebugSphere:(t=[0,0,0],i=new pc.Color(1,0,0))=>{const o=new pc.Entity("DebugSphere");o.addComponent("model",{type:"sphere"}),o.setPosition(...t),o.setLocalScale(.2,.2,.2),o.tags.add("debug");const n=new pc.StandardMaterial;return n.diffuse=i,n.emissive=i,n.emissiveIntensity=.3,n.update(),o.model.meshInstances[0].material=n,e.root.addChild(o),o},clearDebugObjects:()=>{const t=e.root.findByTag("debug");t.forEach(e=>e.destroy()),console.log(`Cleared ${t.length} debug objects`)},help:()=>{console.log("=== DEBUG COMMANDS ==="),console.log("State inspection:"),console.log(" debug.state() - Show game state"),console.log(" debug.inspectSystem(name) - Inspect system"),console.log(" debug.find(name) - Find entity by name"),console.log(" debug.findByTag(tag) - Find entities by tag"),console.log(" debug.listEntities() - List all entities"),console.log(" debug.listNPCs() - List all NPCs"),console.log(""),console.log("Game actions:"),console.log(" debug.loadContent(index) - Load content"),console.log(" debug.exitToCatalog() - Exit to catalog"),console.log(" debug.save(slot) - Save game"),console.log(" debug.load(slot) - Load game"),console.log(""),console.log("Scene management:"),console.log(" debug.testTransition(type) - Test transition (fade/slide/circle)"),console.log(" debug.memoryStats() - Show scene memory stats"),console.log(""),console.log("Player actions:"),console.log(" debug.giveItem(id, quantity) - Give item"),console.log(" debug.giveExp(amount) - Give experience"),console.log(" debug.unlockSkill(id) - Unlock skill"),console.log(" debug.teleport(x, y, z) - Teleport player"),console.log(""),console.log("Performance:"),console.log(" debug.stats() - Show performance stats"),console.log(" debug.memoryUsage() - Show memory usage"),console.log(""),console.log("Visual debugging:"),console.log(" debug.wireframe(enabled) - Toggle wireframe"),console.log(" debug.createDebugSphere(pos, color) - Create debug sphere"),console.log(" debug.clearDebugObjects() - Clear debug objects"),console.log(""),console.log("Keyboard shortcuts:"),console.log(" Alt+D - Show state"),console.log(" Alt+S - Quick save"),console.log(" Alt+L - Quick load"),console.log(" F1 - Toggle debug layer"),console.log(" F2 - Toggle wireframe"),console.log(" F3 - Show stats")}};let t=Date.now(),i=0;e.on("frameend",()=>{i++;const o=Date.now();if(o-t>1e3){if(window.debugStats){const n=Math.round(i/((o-t)/1e3));window.debugStats.fps=n,window.debugStats.drawCalls=e.stats.drawCalls,window.debugStats.triangles=e.stats.triangles}i=0,t=o}}),e.keyboard&&e.keyboard.on(pc.EVENT_KEYDOWN,t=>{if(t.altKey)switch(t.key){case pc.KEY_D:t.preventDefault(),window.debug.state();break;case pc.KEY_S:t.preventDefault(),window.debug.save();break;case pc.KEY_L:t.preventDefault(),window.debug.load()}else switch(t.key){case pc.KEY_F1:t.preventDefault();const i=e.scene.layers.getLayerById(15);i&&(i.enabled=!i.enabled);break;case pc.KEY_F2:t.preventDefault();const o=window.debugWireframeEnabled||!1;window.debug.wireframe(!o),window.debugWireframeEnabled=!o;break;case pc.KEY_F3:t.preventDefault(),window.debug.stats()}}),window.debugStats={fps:0,drawCalls:0,triangles:0},console.log("Debug interface ready. Use window.debug or debug.help()"),console.log("Keyboard shortcuts: Ctrl+D, Ctrl+S, Ctrl+L, F1, F2, F3")}static createDebugUI(e){if(!e.core||!e.core.entityFactory||!e.core.entityFactory.createUI)return void console.warn("[Debug] Cannot create debug UI - core not ready");const t=e.core.entityFactory.createUI("DebugPanel",{components:{element:{type:pc.ELEMENTTYPE_GROUP,anchor:[0,1,0,1],pivot:[0,1],margin:[10,-10,0,0],width:250,height:150}}});if(!t)return void console.warn("[Debug] Failed to create debug panel");const i=e.core.entityFactory.createUI("DebugBackground",{components:{element:{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],color:new pc.Color(0,0,0,.7)}}});i&&t.addChild(i);const o=e.core.entityFactory.createUI("FPSText",{components:{element:{type:pc.ELEMENTTYPE_TEXT,anchor:[0,1,1,1],pivot:[0,1],margin:[5,-5,5,25],fontSize:14,text:"FPS: 0",color:new pc.Color(1,1,1),fontAsset:e.fontManager?e.fontManager.getFontAssetId():null}}});return t.addChild(o),setInterval(()=>{window.debugStats&&o&&o.element&&(o.element.text=`FPS: ${window.debugStats.fps||0}\nDraw Calls: ${window.debugStats.drawCalls||0}\nTriangles: ${window.debugStats.triangles||0}`)},500),t.enabled=!1,e.keyboard&&e.keyboard.on(pc.EVENT_KEYDOWN,e=>{e.key===pc.KEY_F4&&(e.preventDefault(),t&&(t.enabled=!t.enabled))}),e.root.addChild(t),t}}class h{constructor(e,t){this.app=e,this.uiRoot=t,this.elements=new Map,this.zones=new Map,this.initialized=!1}initialize(){this.setupUIZones(),this.initialized=!0,console.log("[UILayoutManager] UI layout management initialized")}setupUIZones(){this.zones.set("top-left",{x:0,y:600,width:350,height:120}),this.zones.set("top-center",{x:360,y:600,width:560,height:120}),this.zones.set("top-right",{x:930,y:600,width:350,height:120}),this.zones.set("middle-left",{x:0,y:250,width:350,height:340}),this.zones.set("middle-center",{x:360,y:250,width:560,height:340}),this.zones.set("middle-right",{x:930,y:250,width:350,height:340}),this.zones.set("bottom-left",{x:0,y:0,width:350,height:240}),this.zones.set("bottom-center",{x:360,y:0,width:560,height:240}),this.zones.set("bottom-right",{x:930,y:0,width:350,height:240}),this.anchorZones=new Map,this.anchorZones.set([0,1],"top-left"),this.anchorZones.set([.5,1],"top-center"),this.anchorZones.set([1,1],"top-right"),this.anchorZones.set([0,.5],"middle-left"),this.anchorZones.set([.5,.5],"middle-center"),this.anchorZones.set([1,.5],"middle-right"),this.anchorZones.set([0,0],"bottom-left"),this.anchorZones.set([.5,0],"bottom-center"),this.anchorZones.set([1,0],"bottom-right")}registerElement(e,t,i=null){if(!t||!t.element)return console.warn(`[UILayoutManager] Invalid element registration: ${e}`),!1;const o=this.getElementBounds(t),n=i||this.findBestZone(o);return this.elements.set(e,{element:t,bounds:o,zone:n,originalPosition:{x:o.x,y:o.y}}),console.log(`[UILayoutManager] Registered element ${e} in zone ${n}`),!0}getElementBounds(e){if(!e.element)return{x:0,y:0,width:0,height:0};const t=e.element;let i=t.x||0,o=t.y||0,n=t.width||100,s=t.height||50;if(t.anchor){const e=1280,a=720;i=t.anchor[0]*e+i,o=t.anchor[1]*a+o,t.pivot&&(i-=t.pivot[0]*n,o-=t.pivot[1]*s)}return{x:i,y:o,width:n,height:s}}findBestZone(e){let t="middle-center",i=-1;for(const[o,n]of this.zones){const s=this.calculateZoneScore(e,n);s>i&&(i=s,t=o)}return t}calculateZoneScore(e,t){const i=e.width<=t.width,o=e.height<=t.height;return i&&o?100-this.calculateZoneOverlap(t):-1}calculateZoneOverlap(e){let t=0;for(const[i,o]of this.elements)o.zone===e&&(t+=20);return Math.min(t,90)}detectOverlaps(){const e=[],t=Array.from(this.elements.values());for(let i=0;it.overlapArea-e.overlapArea);for(const t of e)this.repositionElement(t.elementB,t.elementA);const t=this.detectOverlaps();return t.length>0?(console.warn(`[UILayoutManager] ${t.length} overlaps could not be resolved`),!1):(console.log("[UILayoutManager] All overlaps resolved"),!0)}repositionElement(e,t){const i=Array.from(this.zones.keys()).filter(e=>e!==t.zone);for(const t of i){const i=this.zones.get(t),o=this.calculateZonePosition(e.bounds,i);if(o)return this.moveElementToPosition(e.element,o),e.zone=t,e.bounds={...e.bounds,...o},console.log(`[UILayoutManager] Moved element to zone ${t}`),!0}return console.warn("[UILayoutManager] Could not find suitable position for element"),!1}calculateZonePosition(e,t){return e.width<=t.width&&e.height<=t.height?{x:t.x+10,y:t.y+t.height-e.height-10}:null}moveElementToPosition(e,t){if(e.element){const i=1280,o=720;if(e.element.anchor){const n=e.element.anchor[0]*i,s=e.element.anchor[1]*o;e.element.x=t.x-n,e.element.y=t.y-s}else e.element.x=t.x,e.element.y=t.y}}validateLayout(){if(!this.initialized)return console.warn("[UILayoutManager] Layout manager not initialized"),!1;const e=this.detectOverlaps();return e.length>0?(console.warn(`[UILayoutManager] Layout validation failed: ${e.length} overlaps found`),this.resolveOverlaps()):(console.log("[UILayoutManager] Layout validation passed"),!0)}getLayoutReport(){const e=this.detectOverlaps(),t=new Map;for(const[e,i]of this.elements){const o=i.zone;t.has(o)||t.set(o,[]),t.get(o).push(e)}return{totalElements:this.elements.size,overlaps:e.length,zoneUsage:Object.fromEntries(t)}}destroy(){this.elements.clear(),this.zones.clear(),this.initialized=!1}}class d{constructor(e){this.app=e,this.uiRoot=null,this.elements=new Map,this.layoutManager=null,this.initialized=!1,this.activeNotifications=[],this.persistentNotifications=new Map,this.isResourceScene=!1}initialize(){this.uiRoot=new pc.Entity("UIRoot"),this.uiRoot.addComponent("screen",{referenceResolution:new pc.Vec2(1280,720),scaleMode:pc.SCALEMODE_BLEND,scaleBlend:.5,screenSpace:!0}),this.uiRoot.tags.add("ui","persistent"),this.app.root.addChild(this.uiRoot),this.layoutManager=new h(this.app,this.uiRoot),this.layoutManager.initialize(),this.createAnchorContainers(),this.createHUD(),this.createStatsDisplay(),this.createResourcesDisplay(),this.createObjectiveDisplay(),this.createControlsHelp(),this.createNotificationSystem(),this.createPersistentNotificationArea(),this.createInventoryDisplay(),this.registerUIElements(),this.validateAndFixLayout(),this.setupEventListeners(),this.initialized=!0,console.log("[UIManager] Initialized with layout management")}createAnchorContainers(){this.containers=new Map,this.containers.set("top-left",this.createContainer("TopLeftContainer",[0,1,0,1],[0,1],0,0,350,120)),this.containers.set("top-center",this.createContainer("TopCenterContainer",[.5,1,.5,1],[.5,1],0,0,560,120)),this.containers.set("top-right",this.createContainer("TopRightContainer",[1,1,1,1],[1,1],0,0,350,120)),this.containers.set("middle-left",this.createContainer("MiddleLeftContainer",[0,.5,0,.5],[0,.5],0,0,350,340)),this.containers.set("middle-center",this.createContainer("MiddleCenterContainer",[.5,.5,.5,.5],[.5,.5],0,0,560,340)),this.containers.set("middle-right",this.createContainer("MiddleRightContainer",[1,.5,1,.5],[1,.5],0,0,350,340)),this.containers.set("bottom-left",this.createContainer("BottomLeftContainer",[0,0,0,0],[0,0],0,0,350,240)),this.containers.set("bottom-center",this.createContainer("BottomCenterContainer",[.5,0,.5,0],[.5,0],0,0,560,240)),this.containers.set("bottom-right",this.createContainer("BottomRightContainer",[1,0,1,0],[1,0],0,0,350,240)),console.log("[UIManager] Created anchored containers for UI zones")}createContainer(e,t,i,o,n,s,a){const r=new pc.Entity(e);return r.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:t,pivot:i,x:o,y:n,width:s,height:a,useInput:!1}),this.uiRoot.addChild(r),r}createHUD(){const e=this.containers.get("top-center");this.elements.set("hud",e);const t=new pc.Entity("HealthGroup");t.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[0,1,0,1],pivot:[0,1],x:20,y:0,width:240,height:70,useInput:!1}),e.addChild(t);const i=new pc.Entity("HealthLabel");i.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[.5,1,.5,1],pivot:[.5,1],x:0,y:-10,fontSize:12,text:"HEALTH",color:new pc.Color(1,1,1),fontAsset:this.app.fontManager.getFontAssetId(),autoWidth:!1,width:200,height:15,alignment:[.5,.5]}),t.addChild(i);const o=new pc.Entity("HealthBarBg");o.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[.5,1,.5,1],pivot:[.5,1],x:0,y:-25,width:200,height:30,color:new pc.Color(.2,.2,.2),opacity:.8}),t.addChild(o);const n=new pc.Entity("HealthBarFill");n.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,0,1],pivot:[0,.5],margin:[2,2,2,2],width:196,color:new pc.Color(.2,.8,.2)}),o.addChild(n),this.elements.set("healthFill",n);const s=new pc.Entity("HealthText");s.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[.5,.5,.5,.5],pivot:[.5,.5],fontSize:16,text:"100 / 100",color:new pc.Color(1,1,1),fontAsset:this.app.fontManager.getFontAssetId(),autoWidth:!1,autoHeight:!1,width:200,height:30,alignment:[.5,.5]}),o.addChild(s),this.elements.set("healthText",s);const a=new pc.Entity("StaminaGroup");a.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[1,1,1,1],pivot:[1,1],x:-20,y:0,width:240,height:70,useInput:!1}),e.addChild(a);const r=new pc.Entity("StaminaLabel");r.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[.5,1,.5,1],pivot:[.5,1],x:0,y:-10,fontSize:12,text:"STAMINA",color:new pc.Color(1,1,1),fontAsset:this.app.fontManager.getFontAssetId(),autoWidth:!1,width:200,height:15,alignment:[.5,.5]}),a.addChild(r);const l=new pc.Entity("StaminaBarBg");l.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[.5,1,.5,1],pivot:[.5,1],x:0,y:-25,width:200,height:30,color:new pc.Color(.2,.2,.3),opacity:.8}),a.addChild(l);const c=new pc.Entity("StaminaBarFill");c.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,0,1],pivot:[0,.5],margin:[2,2,2,2],width:196,color:new pc.Color(.2,.4,.7)}),l.addChild(c),this.elements.set("staminaFill",c);const h=new pc.Entity("StaminaText");h.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[.5,.5,.5,.5],pivot:[.5,.5],fontSize:16,text:"100 / 100",color:new pc.Color(1,1,1),fontAsset:this.app.fontManager.getFontAssetId(),autoWidth:!1,autoHeight:!1,width:200,height:30,alignment:[.5,.5]}),l.addChild(h),this.elements.set("staminaText",h),setTimeout(()=>{i.element&&(i.element.outlineColor=new pc.Color(0,0,0),i.element.outlineThickness=.2),r.element&&(r.element.outlineColor=new pc.Color(0,0,0),r.element.outlineThickness=.2)},10)}createStatsDisplay(){const e=this.containers.get("middle-left"),t=new pc.Entity("StatsDisplay");t.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[0,1,1,1],pivot:[.5,1],x:0,y:-10,fontSize:12,text:"Level: 1\nXP: 0 / 100\nSkill Points: 0\n\nSTR: 10 AGI: 10\nINT: 10 CHA: 10",color:new pc.Color(1,1,1),fontAsset:this.app.fontManager.getFontAssetId(),autoWidth:!1,width:340,height:120,alignment:[.5,.5],wrapLines:!0}),e.addChild(t),this.elements.set("statsText",t),setTimeout(()=>{t.element&&(t.element.outlineColor=new pc.Color(0,0,0),t.element.outlineThickness=.3)},10)}createResourcesDisplay(){const e=this.containers.get("middle-right");this.elements.set("resourceContainer",e),e.enabled=!1;const t=new pc.Entity("ResourceBg");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.5,.5],color:new pc.Color(.1,.1,.2),opacity:.7}),e.addChild(t);const i=new pc.Entity("ResourceText");i.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[.5,.5,.5,.5],pivot:[.5,.5],fontSize:12,text:"RESOURCES\nWater: 100% | Power: 100%\nFuel: 100% | Materials: 100%",color:new pc.Color(.8,1,.8),fontAsset:this.app.fontManager.getFontAssetId(),autoWidth:!1,autoHeight:!1,width:240,height:70,alignment:[.5,.5],wrapLines:!0}),e.addChild(i),this.elements.set("resourceText",i)}createObjectiveDisplay(){const e=this.containers.get("top-right");this.elements.set("objectives",e);const t=new pc.Entity("ObjectivesBg");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.5,1],y:-25,height:90,color:new pc.Color(0,0,0),opacity:.6,useInput:!1}),e.addChild(t);const i=new pc.Entity("ObjectivesTitle");i.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[.5,1,.5,1],pivot:[.5,1],y:-5,fontSize:18,text:"OBJECTIVES",color:new pc.Color(1,.9,0),fontAsset:this.app.fontManager.getFontAssetId(),height:25}),e.addChild(i);const o=new pc.Entity("ObjectivesList");o.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[0,0,1,1],pivot:[.5,.5],margin:[10,40,10,10],fontSize:14,text:"No active objectives",color:new pc.Color(.9,.9,.9),fontAsset:this.app.fontManager.getFontAssetId(),autoHeight:!1,wrapLines:!0,useInput:!1}),t.addChild(o),this.elements.set("objectivesList",o),this.elements.set("objectivesBg",t)}createControlsHelp(){const e=this.containers.get("bottom-right");this.elements.set("controls",e);const t=new pc.Entity("ControlsBg");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,.8],pivot:[.5,.5],margin:[5,5,5,5],color:new pc.Color(0,0,0),opacity:.7}),e.addChild(t);const i=new pc.Entity("ControlsText");i.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[0,0,1,1],pivot:[.5,.5],margin:[10,10,10,10],fontSize:12,text:"CONTROLS\n\nWASD - Move Camera\nQ/E - Rotate Camera\nArrow Keys - Look\nF - Interact\nI - Instructions\n\nAlt+D/S/L - Debug\nF1-F3 - Debug Tools",color:new pc.Color(1,1,1),fontAsset:this.app.fontManager.getFontAssetId(),autoHeight:!1,wrapLines:!0,alignment:[0,.5]}),t.addChild(i)}createNotificationSystem(){const e=this.containers.get("middle-center");this.elements.set("notifications",e)}createPersistentNotificationArea(){const e=this.containers.get("top-left");this.elements.set("persistentNotifications",e);const t=new pc.Entity("PersistentNotifBg");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,.5,1,1],pivot:[.5,.5],color:new pc.Color(0,0,0),opacity:.6,margin:[5,5,5,5]}),t.enabled=!1,e.addChild(t),this.elements.set("persistentBg",t)}setupEventListeners(){this.app.on("ui:notification",this.showNotification,this),this.app.on("ui:persistent",this.showPersistentNotification,this),this.app.on("ui:persistent:remove",this.removePersistentNotification,this),this.app.on("objective:started",this.updateObjectives,this),this.app.on("objective:completed",this.updateObjectives,this),this.app.on("objective:failed",this.updateObjectives,this),this.app.on("objective:progress",this.updateObjectives,this),this.app.on("stats:changed",this.updateStats,this),this.app.on("player:healthChanged",this.updateHealth,this),this.app.on("resources:changed",this.updateResourcesDisplay,this),this.app.on("content:loaded",this.onSceneLoaded,this),this.app.on("content:loading",()=>this.setStatsUIVisible(!1),this),this.app.on("content:unloading",()=>this.setStatsUIVisible(!1),this),this.app.keyboard&&this.app.keyboard.on(pc.EVENT_KEYDOWN,e=>{e.key===pc.KEY_I&&this.showInstructions()}),this.app.on("inventory:itemAdded",this.updateInventoryDisplay,this),this.app.on("inventory:itemRemoved",this.updateInventoryDisplay,this),this.app.on("inventory:itemUpdated",this.updateInventoryDisplay,this),this.app.on("inventory:restored",this.updateInventoryDisplay,this),this.app.on("inventory:cleared",this.updateInventoryDisplay,this),this.app.on("content:loading",this.clearInventoryOnSceneChange,this),this.app.on("content:unloading",this.clearInventoryOnSceneChange,this)}onSceneLoaded(e,t){const i=-1===e||t&&"catalog"===t.type;this.isCatalog=i,this.setStatsUIVisible(!i),this.isResourceScene=!(!t||"SystemsTestingLab"!==t.script),this.setResourcesUIVisible(this.isResourceScene),this.setObjectiveUIVisible(!i),this.setInventoryUIVisible(!i),this.forceUIUpdate()}setStatsUIVisible(e){const t=this.elements.get("hud");t&&(t.enabled=e);const i=this.app.root.findByName("HealthGroup");i&&(i.enabled=e);const o=this.app.root.findByName("StaminaGroup");o&&(o.enabled=e);const n=this.elements.get("statsText");n&&(n.enabled=e)}setResourcesUIVisible(e){const t=this.elements.get("resourceContainer");t&&(t.enabled=e)}setObjectiveUIVisible(e){const t=this.elements.get("objectives");t&&(t.enabled=e);const i=this.elements.get("objectivesBg");i&&(i.enabled=e);const o=this.elements.get("objectivesList");o&&(o.enabled=e)}setInventoryUIVisible(e){const t=this.elements.get("inventory");t&&(t.enabled=e)}showNotification(e){const{text:t,type:i="info",duration:o=3e3}=e,n=new pc.Entity("Notification");n.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[0,.8,1,1],pivot:[.5,1],height:60,margin:[10,10,10,10]});const s=new pc.Entity("NotifBg");s.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.5,.5],color:this.getNotificationColor(i),opacity:.9}),n.addChild(s);const a=new pc.Entity("NotifText");a.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[0,0,1,1],pivot:[.5,.5],margin:[8,8,8,8],fontSize:13,text:t,color:new pc.Color(1,1,1),fontAsset:this.app.fontManager.getFontAssetId(),autoHeight:!1,wrapLines:!0,alignment:[.5,.5]}),s.addChild(a);const r=this.elements.get("notifications");if(!r)return void console.warn("[UIManager] Notifications container not found");r.addChild(n);const l={entity:n,timeoutId:null};this.activeNotifications.push(l),n.setLocalScale(.8,.8,1);let c=.8;const h=()=>{c+=.04,c<=1&&(n.setLocalScale(c,c,1),setTimeout(h,16))};h(),l.timeoutId=setTimeout(()=>{this.removeNotification(l)},o)}removeNotification(e){if(!e)return;e.timeoutId&&clearTimeout(e.timeoutId);const t=this.activeNotifications.indexOf(e);t>-1&&this.activeNotifications.splice(t,1),e.entity&&e.entity.destroy&&e.entity.destroy()}dismissCurrentNotification(){if(this.activeNotifications.length>0){const e=this.activeNotifications[this.activeNotifications.length-1];this.removeNotification(e)}}showPersistentNotification(e){const{id:t,text:i,type:o="info",color:n=null}=e;if(!t)return void console.warn("[UIManager] Persistent notification requires an id");this.removePersistentNotification({id:t});const s=this.elements.get("persistentNotifications"),a=this.elements.get("persistentBg");if(!s||!a)return;0===this.persistentNotifications.size&&(a.enabled=!0);const r=new pc.Entity(`PersistentNotif_${t}`);r.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[0,0,1,0],pivot:[.5,1],y:-(18*this.persistentNotifications.size+8),width:290,height:16,fontSize:12,text:i,color:n||this.getNotificationColor(o),fontAsset:this.app.fontManager.getFontAssetId(),autoHeight:!1,wrapLines:!1,alignment:[.5,.5]}),s.addChild(r),this.persistentNotifications.set(t,r),this.repositionPersistentNotifications()}removePersistentNotification(e){const{id:t}=e;if(!t)return;const i=this.persistentNotifications.get(t);if(i){if(i.destroy(),this.persistentNotifications.delete(t),0===this.persistentNotifications.size){const e=this.elements.get("persistentBg");e&&(e.enabled=!1)}this.repositionPersistentNotifications()}}repositionPersistentNotifications(){let e=0;for(const[t,i]of this.persistentNotifications)i.element&&(i.element.y=-(18*e+8),e++)}updatePersistentNotification(e){const{id:t,text:i,color:o=null}=e;if(!t)return;const n=this.persistentNotifications.get(t);n&&n.element&&(n.element.text=i,o&&(n.element.color=o))}getNotificationColor(e){switch(e){case"success":return new pc.Color(.2,.7,.2);case"error":return new pc.Color(.7,.2,.2);case"warning":return new pc.Color(.7,.5,.2);default:return new pc.Color(.2,.4,.7)}}updateObjectives(){const e=this.app.gameSystems?.objectivemanager;if(!e||!e.getActiveObjectives)return;const t=e.getActiveObjectives(),i=this.elements.get("objectivesList");if(!i||!i.element)return;let o="";o=0===t.length?"No active objectives":t.map(e=>{let t=`• ${e.name||"Unknown"}`;return"counter"===e.type&&(t+=` (${e.progress||0}/${e.target||1})`),null!==e.timeRemaining&&void 0!==e.timeRemaining&&(t+=` [${Math.ceil(e.timeRemaining)}s]`),t}).join("\n\n"),i.element.text=o,this.resizeObjectivesContainer(t.length)}resizeObjectivesContainer(e){const t=this.elements.get("objectives"),i=this.elements.get("objectivesBg");if(!t||!i)return;const o=Math.max(60,50*e),n=Math.min(400,Math.max(110,o+50));t.element&&(t.element.height=n),i&&i.element&&(i.element.height=n-30)}updateStats(e,t,i){"health"!==e&&"maxHealth"!==e||this.updateHealthBar(),"stamina"!==e&&"maxStamina"!==e||this.updateStaminaBar(),this.updateStatsDisplay(),this.updateAttributesDisplay()}updateHealthBar(){const e=this.app.core;if(!e)return;const t=e.getSystem("statsmanager");if(!t)return;const i=t.getStat("health"),o=t.getStat("maxHealth"),n=this.elements.get("healthFill"),s=this.elements.get("healthText");if(n&&n.element){const e=o>0?i/o:0,t=196*e;this.animateBarWidth(n,t,300),n.element.color=e>.6?new pc.Color(.2,.8,.2):e>.3?new pc.Color(.9,.7,.2):new pc.Color(.8,.2,.2)}s&&s.element&&(s.element.text=`${Math.floor(i)} / ${Math.floor(o)}`)}updateStaminaBar(){const e=this.app.core;if(!e)return void console.warn("[UIManager] No core system found for stamina update");const t=e.getSystem("statsmanager");if(!t)return void console.warn("[UIManager] No stats manager found for stamina update");const i=t.getStat("stamina"),o=t.getStat("maxStamina");console.log(`[UIManager] Updating stamina bar: ${i}/${o}`);const n=this.elements.get("staminaFill"),s=this.elements.get("staminaText");if(n&&n.element){const e=o>0?i/o:0,t=196*e;console.log(`[UIManager] Stamina bar: ${100*e}% -> width ${t}px`),this.animateBarWidth(n,t,400),n.element.color=e>.7?new pc.Color(.2,.8,.2):e>.3?new pc.Color(.8,.8,.2):new pc.Color(.8,.4,.2)}else console.warn("[UIManager] Stamina fill element not found");s&&s.element?s.element.text=`${Math.floor(i)} / ${Math.floor(o)}`:console.warn("[UIManager] Stamina text element not found")}updateStatsDisplay(){const e=this.app.core;if(!e)return;const t=e.getSystem("statsmanager");if(!t)return;const i=this.elements.get("statsText");if(i&&i.element){const e=t.getStat("level"),o=t.getStat("experience"),n=t.getStat("skillPoints"),s="function"==typeof t.getExperienceForLevel?t.getExperienceForLevel(e+1):100*e,a=t.getStat("strength"),r=t.getStat("agility"),l=t.getStat("intelligence"),c=t.getStat("charisma");i.element.text=`Level: ${e}\nXP: ${Math.floor(o)} / ${s}\nSkill Points: ${n}\n\nSTR: ${a} AGI: ${r}\nINT: ${l} CHA: ${c}`}}updateAttributesDisplay(){this.updateStatsDisplay()}updateResourcesDisplay(e){if(!this.isResourceScene){const e=this.elements.get("resourceContainer");return void(e&&(e.enabled=!1))}const t=this.elements.get("resourceText"),i=this.elements.get("resourceContainer");if(!t||!t.element)return;if(!e||this.shouldHideResources(e))return void(i&&(i.enabled=!1));const o=this.getRelevantResources(e);if(0===o.length)return void(i&&(i.enabled=!1));i&&(i.enabled=!0);const n=[];n.push("RESOURCES");for(let e=0;e100===e||void 0===e)}getRelevantResources(e){const t=[];return Object.entries(e).forEach(([e,i])=>{if(void 0!==i&&100!==i){const o=e.charAt(0).toUpperCase()+e.slice(1);t.push(`${o}: ${Math.floor(i)}%`)}}),t}forceUIUpdate(){setTimeout(()=>{this.updateHealthBar(),this.updateStaminaBar(),this.updateStatsDisplay(),this.updateAttributesDisplay(),setTimeout(()=>{console.log("[UIManager] Forcing stamina bar update check"),this.updateStaminaBar()},200)},100)}updateHealth(e){const t=this.elements.get("healthFill"),i=this.elements.get("healthText");if(t&&t.element&&e&&void 0!==e.current&&void 0!==e.max){const i=Math.max(0,Math.min(1,e.current/e.max));t.element.width=196*i,t.element.color=i>.5?new pc.Color(.2,.8,.2):i>.25?new pc.Color(.8,.8,.2):new pc.Color(.8,.2,.2)}i&&i.element&&e&&void 0!==e.current&&void 0!==e.max&&(i.element.text=`${Math.ceil(e.current)} / ${e.max}`)}toggleControls(){const e=this.elements.get("controls");e&&void 0!==e.enabled&&(e.enabled=!e.enabled)}createInventoryDisplay(){const e=this.containers.get("bottom-left");this.elements.set("inventory",e);const t=new pc.Entity("InventoryBackground");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,.2,1,1],pivot:[.5,.5],margin:[5,5,5,5],color:new pc.Color(.1,.1,.1,.8),opacity:.9}),e.addChild(t);const i=new pc.Entity("InventoryTitle");i.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[0,1,1,1],pivot:[.5,1],y:-5,height:25,fontSize:16,text:"INVENTORY (1-9 to use)",color:new pc.Color(1,1,.8),fontAsset:this.app.fontManager?.getFontAssetId(),alignment:[.5,.5]}),e.addChild(i);const o=new pc.Entity("InventoryList");o.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[0,0,1,1],pivot:[0,1],margin:[10,10,35,10],fontSize:11,text:"No items collected yet...",color:new pc.Color(.9,.9,.9),fontAsset:this.app.fontManager?.getFontAssetId(),alignment:[0,1],wrapLines:!0,autoHeight:!1}),e.addChild(o),this.elements.set("inventoryList",o),e.enabled=!1}registerUIElements(){const e=[{name:"hud",element:this.containers.get("top-center"),zone:"top-center"},{name:"objectives",element:this.containers.get("top-right"),zone:"top-right"},{name:"controls",element:this.containers.get("bottom-right"),zone:"bottom-right"},{name:"notifications",element:this.containers.get("middle-center"),zone:"middle-center"},{name:"persistentNotifications",element:this.containers.get("top-left"),zone:"top-left"},{name:"inventory",element:this.containers.get("bottom-left"),zone:"bottom-left"},{name:"stats",element:this.containers.get("middle-left"),zone:"middle-left"},{name:"resources",element:this.containers.get("middle-right"),zone:"middle-right"}];for(const t of e)t.element&&(this.layoutManager.registerElement(t.name,t.element,t.zone)||console.warn(`[UIManager] Failed to register ${t.name}`));console.log("[UIManager] Container-based UI elements registered with layout manager")}validateAndFixLayout(){if(this.layoutManager&&!this.layoutManager.validateLayout()){console.warn("[UIManager] Layout validation failed, attempting fixes...");const e=this.layoutManager.getLayoutReport();console.log("[UIManager] Layout report:",e),this.applyLayoutFixes()}}applyLayoutFixes(){const e=this.elements.get("resourceContainer"),t=this.elements.get("objectives");e&&t&&e.element&&(e.element.x=-350,console.log("[UIManager] Applied fix: Moved resource container further left"));const i=this.elements.get("controls");i&&i.element&&(i.element.x=-280,console.log("[UIManager] Applied fix: Adjusted controls positioning"));const o=this.elements.get("persistentNotifications");o&&o.element&&(o.element.y=-50,console.log("[UIManager] Applied fix: Adjusted persistent notifications position"))}checkForOverlaps(){if(!this.layoutManager)return[];const e=this.layoutManager.detectOverlaps();return e.length>0&&(console.warn(`[UIManager] Detected ${e.length} UI element overlaps`),e.forEach((e,t)=>{console.warn(`[UIManager] Overlap ${t+1}: ${e.elementA.name} with ${e.elementB.name} (area: ${e.overlapArea}px²)`)})),e}updateInventoryDisplay(){if(this.isCatalog){const e=this.elements.get("inventory");return void(e&&(e.enabled=!1))}const e=this.app.core?.getSystem("inventorymanager"),t=this.elements.get("inventoryList"),i=this.elements.get("inventory");if(!e||!t||!i)return;const o=[];for(const[t,i]of e.items.entries()){const n=e.getItemInfo(i.id);if(n&&t<9){const e=i.quantity>1?` x${i.quantity}`:"",s=t+1;o.push(`[${s}] ${n.name}${e}`)}}0===o.length?(t.element.text="No items collected yet...",i.enabled=!1):(t.element.text=o.join("\n"),i.enabled=!0)}clearInventoryOnSceneChange(){const e=this.elements.get("inventoryList"),t=this.elements.get("inventory");e&&e.element&&(e.element.text="No items collected yet..."),t&&(t.enabled=!1)}showInstructions(){this.app.fire("ui:request_instructions")}animateBarWidth(e,t,i=300){if(!e||!e.element)return;const o=e.element.width,n=t-o,s=Date.now();e.animationId&&clearInterval(e.animationId);const a=()=>{const t=Date.now()-s,r=Math.min(t/i,1),l=1-Math.pow(1-r,3),c=o+n*l;e.element.width=c,r<1?e.animationId=setTimeout(a,16):delete e.animationId};a()}destroy(){this.app.off("ui:notification",this.showNotification,this),this.app.off("ui:persistent",this.showPersistentNotification,this),this.app.off("ui:persistent:remove",this.removePersistentNotification,this),this.app.off("objective:started",this.updateObjectives,this),this.app.off("objective:completed",this.updateObjectives,this),this.app.off("objective:failed",this.updateObjectives,this),this.app.off("objective:progress",this.updateObjectives,this),this.app.off("stats:changed",this.updateStats,this),this.app.off("player:healthChanged",this.updateHealth,this),this.app.off("resources:changed",this.updateResourcesDisplay,this),this.app.off("content:loaded",this.forceUIUpdate,this),this.app.off("inventory:itemAdded",this.updateInventoryDisplay,this),this.app.off("inventory:itemRemoved",this.updateInventoryDisplay,this),this.app.off("inventory:itemUpdated",this.updateInventoryDisplay,this),this.app.off("inventory:restored",this.updateInventoryDisplay,this),this.app.off("inventory:cleared",this.updateInventoryDisplay,this),this.app.off("content:loading",this.clearInventoryOnSceneChange,this),this.app.off("content:unloading",this.clearInventoryOnSceneChange,this),this.persistentNotifications.clear(),this.layoutManager&&(this.layoutManager.destroy(),this.layoutManager=null),this.uiRoot&&this.uiRoot.destroy()}}class p{constructor(e){this.app=e,this.fonts=new Map,this.defaultFont=null,this.initialized=!1}async initialize(){console.log("[FontManager] Initializing fonts..."),await this.createCanvasFont(),this.initialized=!0,console.log("[FontManager] Fonts initialized"),this.app.fire("fontmanager:ready")}async createCanvasFont(){const e=new pc.Asset("defaultFont","font",{url:null}),t=new pc.CanvasFont(this.app,{color:new pc.Color(1,1,1,1),fontName:"Arial",fontSize:32,fontWeight:"normal"});return t.createTextures("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?-+*/=()[]{}:;'\"@#$%^&_~`|\\<>"),e.resource=t,e.loaded=!0,this.app.assets.add(e),this.defaultFont=e,this.fonts.set("default",e),console.log("[FontManager] Canvas font created and registered"),e}getFont(e="default"){return this.fonts.get(e)||this.defaultFont}getFontAssetId(e="default"){const t=this.getFont(e);return t?t.id:null}createTextElementOptions(e,t={}){const i=this.getFont(t.fontName);return{type:pc.ELEMENTTYPE_TEXT,anchor:t.anchor||[.5,.5,.5,.5],pivot:t.pivot||[.5,.5],fontSize:t.fontSize||16,text:e,color:t.color||new pc.Color(1,1,1),fontAsset:i?i.id:null,autoWidth:!1!==t.autoWidth,autoHeight:!1!==t.autoHeight,wrapLines:t.wrapLines||!1,lineHeight:t.lineHeight||1.2,spacing:t.spacing||1,useInput:!1,batchGroupId:-1}}createTextEntity(e,t,i={}){const o=new pc.Entity(e);return o.addComponent("element",this.createTextElementOptions(t,i)),i.position&&o.setLocalPosition(...i.position),o}fixTextElement(e){if(e&&e.element&&e.element.type===pc.ELEMENTTYPE_TEXT){const t=this.getFont();t&&!e.element.fontAsset&&(e.element.fontAsset=t.id),e.element.color&&0!==e.element.color.a||(e.element.color=new pc.Color(1,1,1,1)),e.element.fontSize<12&&(e.element.fontSize=16)}}fixAllTextElements(){const e=this.app.root.findComponents("element").filter(e=>e.type===pc.ELEMENTTYPE_TEXT);e.forEach(e=>{const t=this.getFont();t&&!e.fontAsset&&(e.fontAsset=t.id)}),console.log(`[FontManager] Fixed ${e.length} text elements`)}}class g{constructor(e){this.app=e,this.cursorEntity=null,this.cursorDot=null,this.cursorRing=null,this.isActive=!1,this.targetEntity=null,this.animationTime=0,this.states={IDLE:"idle",HOVER:"hover",FOCUS:"focus",LOCKED:"locked"},this.currentState=this.states.IDLE,this.initialize()}initialize(){this.cursorEntity=new pc.Entity("CursorUI"),this.cursorEntity.addComponent("element",{type:"group",anchor:new pc.Vec4(.5,.5,.5,.5),pivot:new pc.Vec2(.5,.5),width:64,height:64,useInput:!1}),this.cursorDot=new pc.Entity("CursorDot"),this.cursorDot.addComponent("element",{type:"image",anchor:new pc.Vec4(.5,.5,.5,.5),pivot:new pc.Vec2(.5,.5),width:4,height:4,color:new pc.Color(1,1,1,.8),useInput:!1}),this.cursorEntity.addChild(this.cursorDot),this.cursorRing=new pc.Entity("CursorRing"),this.cursorRing.addComponent("element",{type:"image",anchor:new pc.Vec4(.5,.5,.5,.5),pivot:new pc.Vec2(.5,.5),width:32,height:32,color:new pc.Color(1,1,1,.5),opacity:.5,useInput:!1}),this.createRingTexture(),this.cursorEntity.addChild(this.cursorRing);let e=this.app.root.findByName("UICamera");e||(e=new pc.Entity("UICamera"),e.addComponent("camera",{clearColorBuffer:!1,clearDepthBuffer:!1,layers:[this.app.scene.layers.getLayerByName("UI").id],renderTarget:null,priority:1}),this.app.root.addChild(e));let t=this.app.root.findByName("CursorScreen");t||(t=new pc.Entity("CursorScreen"),t.addComponent("screen",{referenceResolution:new pc.Vec2(1280,720),scaleBlend:.5,scaleMode:"blend",screenSpace:!0}),t.screen.priority=1,this.app.root.addChild(t)),t.addChild(this.cursorEntity),this.app.on("ui:focus",this.onFocusEvent,this),this.app.on("interaction:hover",this.onHoverEvent,this),this.app.on("interaction:unhover",this.onUnhoverEvent,this),this.app.on("update",this.update,this),console.log("[CursorSystem] Initialized")}createRingTexture(){const e=document.createElement("canvas");e.width=64,e.height=64;const t=e.getContext("2d");t.strokeStyle="white",t.lineWidth=2,t.beginPath(),t.arc(32,32,28,0,2*Math.PI),t.stroke();const i=new pc.Texture(this.app.graphicsDevice,{width:64,height:64,format:pc.PIXELFORMAT_R8_G8_B8_A8});i.setSource(e),i.addressU=pc.ADDRESS_CLAMP_TO_EDGE,i.addressV=pc.ADDRESS_CLAMP_TO_EDGE,this.cursorRing.element.texture=i}onFocusEvent(e){console.log("[CursorSystem] Focus event:",e),this.setState(this.states.FOCUS),this.targetEntity=e.entity||null,this.animateToFocus()}onHoverEvent(e){this.currentState!==this.states.FOCUS&&(this.setState(this.states.HOVER),this.targetEntity=e)}onUnhoverEvent(){this.currentState===this.states.HOVER&&(this.setState(this.states.IDLE),this.targetEntity=null)}setState(e){if(this.currentState!==e)switch(this.currentState=e,console.log("[CursorSystem] State changed to:",e),e){case this.states.IDLE:this.cursorDot.element.color=new pc.Color(1,1,1,.8),this.cursorRing.element.color=new pc.Color(1,1,1,.5),this.cursorRing.element.width=32,this.cursorRing.element.height=32;break;case this.states.HOVER:this.cursorDot.element.color=new pc.Color(1,1,0,1),this.cursorRing.element.color=new pc.Color(1,1,0,.7),this.cursorRing.element.width=40,this.cursorRing.element.height=40;break;case this.states.FOCUS:this.cursorDot.element.color=new pc.Color(0,1,1,1),this.cursorRing.element.color=new pc.Color(0,1,1,.8);break;case this.states.LOCKED:this.cursorDot.element.color=new pc.Color(1,0,0,1),this.cursorRing.element.color=new pc.Color(1,0,0,.8)}}animateToFocus(){let e=0;const t=()=>{if(e>=3||this.currentState!==this.states.FOCUS)return;let i=0;const o=n=>{if(i+=n/.3,i>=1)return e++,this.cursorRing.element.width=32,this.cursorRing.element.height=32,void(e<3&&setTimeout(t,100));const s=1+.5*Math.sin(i*Math.PI);this.cursorRing.element.width=32*s,this.cursorRing.element.height=32*s,requestAnimationFrame(()=>o(.016))};o(.016)};t()}update(e){if(this.isActive||this.currentState!==this.states.IDLE){if(this.animationTime+=e,this.currentState===this.states.HOVER){const e=1+.1*Math.sin(2*this.animationTime);this.cursorRing.element.width=40*e,this.cursorRing.element.height=40*e}this.currentState===this.states.FOCUS&&this.cursorRing.setLocalEulerAngles(0,0,45*this.animationTime)}}show(){this.isActive=!0,this.cursorEntity.enabled=!0}hide(){this.isActive=!1,this.cursorEntity.enabled=!1}destroy(){this.app.off("ui:focus",this.onFocusEvent,this),this.app.off("interaction:hover",this.onHoverEvent,this),this.app.off("interaction:unhover",this.onUnhoverEvent,this),this.app.off("update",this.update,this),this.cursorEntity&&this.cursorEntity.destroy()}}class m{constructor(e,t){this.entity=e,this.app=t,this.movementSpeed=5,this.keyboardRotationSpeed=90,this.pitchLimit=89,this.useControlManager=!1,this.eulers=new pc.Vec3,this.pitch=0,this.yaw=0,this.moveDirection=new pc.Vec3,this.usePhysicsMovement=!1,this.playerEntity=null,this.barrierScenes=["QuantumLab","UndergroundBunker","AncientTemple"],this.initialized=!1}initialize(){console.log("CameraController: Initializing..."),this.detectBarrierScene();const e=this.entity.getEulerAngles();this.eulers.set(e.x,e.y,e.z),this.pitch=this.eulers.x,this.yaw=this.eulers.y,this.initialized=!0;const t=this.usePhysicsMovement?"Physics-based":"Direct";console.log(`CameraController initialized (${t} movement). Controls:`),console.log("- WASD: Move"),console.log("- Q/E: Rotate"),console.log("- Arrow Keys: Look up/down"),this.app.on("update",e=>this.update(e))}detectBarrierScene(){const e=this.app.systems?.core||this.app.core;let t="unknown";if(e?.currentScene&&(t=e.currentScene.constructor.name||e.currentScene.name||e.currentScene.id||"unknown"),e?.sceneData?.script&&(t=e.sceneData.script),console.log(`[CameraController] Checking scene: ${t}`),this.barrierScenes.some(e=>t.includes(e)))if(this.playerEntity=this.app.root.findByTag("player")[0],this.playerEntity&&this.playerEntity.rigidbody){this.usePhysicsMovement=!0,console.log(`[CameraController] *** BARRIER SCENE DETECTED: ${t} - USING PHYSICS MOVEMENT ***`);const e=this.playerEntity.getPosition();this.entity.setPosition(e.x,e.y+1.5,e.z)}else console.warn("[CameraController] Player entity with rigidbody not found for physics movement"),this.usePhysicsMovement=!1,setTimeout(()=>{if(this.playerEntity=this.app.root.findByTag("player")[0],this.playerEntity&&this.playerEntity.rigidbody){this.usePhysicsMovement=!0,console.log(`[CameraController] *** BARRIER SCENE DETECTED (delayed): ${t} - USING PHYSICS MOVEMENT ***`);const e=this.playerEntity.getPosition();this.entity.setPosition(e.x,e.y+1.5,e.z)}},1e3);else console.log(`[CameraController] Regular scene: ${t} - Using direct movement`)}update(e){this.initialized&&this.shouldProcessInput()&&(this.processMovement(e),this.processRotation(e),this.entity.setLocalEulerAngles(this.pitch,this.yaw,0))}shouldProcessInput(){return!0}processMovement(e){const t=this.movementSpeed;let i=0,o=0;if(this.app.keyboard&&this.app.keyboard.isPressed&&(this.app.keyboard.isPressed(pc.KEY_W)&&(o=1),this.app.keyboard.isPressed(pc.KEY_S)&&(o=-1),this.app.keyboard.isPressed(pc.KEY_A)&&(i=-1),this.app.keyboard.isPressed(pc.KEY_D)&&(i=1)),0===i&&0===o||(this.usePhysicsMovement&&this.playerEntity&&this.playerEntity.rigidbody?this.processPhysicsMovement(i,o,t,e):this.processDirectMovement(i,o,t,e)),this.usePhysicsMovement&&this.playerEntity){const e=this.playerEntity.getPosition();this.entity.setPosition(e.x,e.y+1.5,e.z)}}processDirectMovement(e,t,i,o){const n=this.entity.forward.clone();n.y=0,n.normalize();const s=this.entity.right.clone();s.y=0,s.normalize(),this.moveDirection.set(0,0,0),this.moveDirection.add(n.mulScalar(t)),this.moveDirection.add(s.mulScalar(e)),this.moveDirection.lengthSq()>0&&(this.moveDirection.normalize().mulScalar(i*o),this.entity.translate(this.moveDirection))}processPhysicsMovement(e,t,i,o){const n=this.entity.forward.clone();n.y=0,n.normalize();const s=this.entity.right.clone();if(s.y=0,s.normalize(),this.moveDirection.set(0,0,0),this.moveDirection.add(n.mulScalar(t)),this.moveDirection.add(s.mulScalar(e)),this.moveDirection.lengthSq()>0){this.moveDirection.normalize();const e=this.moveDirection.clone().mulScalar(50*i);e.y=0,this.playerEntity.rigidbody.applyForce(e);const t=this.playerEntity.rigidbody.linearVelocity;t.x*=.8,t.z*=.8,this.playerEntity.rigidbody.linearVelocity=t}}processRotation(e){let t=0;this.app.keyboard&&this.app.keyboard.isPressed&&(this.app.keyboard.isPressed(pc.KEY_Q)&&(t=1),this.app.keyboard.isPressed(pc.KEY_E)&&(t=-1)),this.yaw+=t*this.keyboardRotationSpeed*e;let i=0;this.app.keyboard&&this.app.keyboard.isPressed&&(this.app.keyboard.isPressed(pc.KEY_UP)&&(i=-1),this.app.keyboard.isPressed(pc.KEY_DOWN)&&(i=1)),this.pitch+=i*this.keyboardRotationSpeed*e,this.pitch=pc.math.clamp(this.pitch,-this.pitchLimit,this.pitchLimit)}destroy(){this.initialized=!1}}class u{static detect(){const e=navigator.userAgent,t="ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0,i=/Android.*Mobile|iPhone|iPod|BlackBerry|Windows Phone|Opera Mini/i,o=/iPad|Android(?!.*Mobile)|Kindle|Silk|PlayBook|BB10.*Touch/i,n=i.test(e),s=o.test(e)||t&&!n&&window.innerWidth>=768&&window.innerWidth<=1024,a=/Mac OS X/.test(e)&&t,r=/Android/i.test(e)&&!/Mobile/i.test(e),l=s||a||r,c=(n||l)&&!(/Windows NT|Mac OS X.*(?!.*Touch)|Linux x86_64/i.test(e)&&!t),h={width:window.innerWidth,height:window.innerHeight,ratio:window.innerWidth/window.innerHeight,dpr:window.devicePixelRatio||1};let d="desktop";return n?d="mobile":l&&(d="tablet"),{isMobile:c,isTablet:l,isPhone:n,isDesktop:!c,deviceType:d,hasTouch:t,userAgent:e,screen:h,debug:{userAgent:e,hasTouch:t,mobileRegex:i.test(e),tabletRegex:o.test(e),isIPadOS:a,isAndroidTablet:r,screenWidth:h.width,screenHeight:h.height,maxTouchPoints:navigator.maxTouchPoints||0}}}static shouldUseMobileControls(){const e=this.detect();return e.isMobile||e.isTablet}static getRecommendedCameraSystem(){const e=this.detect();return e.isPhone?"mobile-first":e.isTablet?"tablet-optimized":"desktop"}}class y{constructor(e,t,i){this.entity=e,this.app=t,this.sceneConfig=i,this.cameraAngles=i.cameraAngles||[],this.currentAngleIndex=0,this.transitionSpeed=2,this.isTransitioning=!1,this.touchZones=i.touchZones||[],this.initialized=!1}initialize(){this.cameraAngles.length>0&&this.setToAngle(0,!1),this.setupTouchZones(),this.setupKeyboardShortcuts(),this.initialized=!0,console.log(`[MobileFirstCameraController] Initialized with ${this.cameraAngles.length} angles`),this.sceneConfig.mobileUI?.showTouchHints&&this.showTouchHints()}setToAngle(e,t=!0){if(e<0||e>=this.cameraAngles.length)return;const i=this.cameraAngles[e];t&&!this.isTransitioning?this.transitionToAngle(i):(this.entity.setPosition(i.position),this.entity.setEulerAngles(i.rotation)),this.currentAngleIndex=e,this.app.fire("camera:angleChanged",{index:e,angle:i,name:i.name,total:this.cameraAngles.length}),this.updateAngleIndicator()}transitionToAngle(e){if(this.isTransitioning)return;this.isTransitioning=!0;const t=this.entity.getPosition().clone(),i=this.entity.getEulerAngles().clone(),o=e.position,n=e.rotation;let s=0;const a=1/this.transitionSpeed;this.app.fire("camera:transitionStart");const r=()=>{s+=this.app.dt/a,s=Math.min(s,1);const e=this.easeInOutQuad(s),l=(new pc.Vec3).lerp(t,o,e);this.entity.setPosition(l);const c=(new pc.Vec3).lerp(i,n,e);this.entity.setEulerAngles(c),s<1?requestAnimationFrame(r):(this.isTransitioning=!1,this.app.fire("camera:transitionComplete"))};r()}easeInOutQuad(e){return e<.5?2*e*e:(4-2*e)*e-1}setupTouchZones(){this.app.touch&&this.app.touch.on(pc.EVENT_TOUCHEND,this.onTouchEnd,this)}onTouchEnd(e){if(this.isTransitioning)return;const t=e.changedTouches[0],i=t.x/window.innerWidth,o=t.y/window.innerHeight;for(const e of this.touchZones)if(this.isPointInZone(i,o,e.area)){this.handleTouchZoneAction(e.action);break}}isPointInZone(e,t,i){return e>=i.x&&e<=i.x+i.width&&t>=i.y&&t<=i.y+i.height}handleTouchZoneAction(e){switch(e){case"nextAngle":this.nextAngle();break;case"previousAngle":this.previousAngle();break;case"showAngleMenu":this.showAngleMenu()}}nextAngle(){if(this.cameraAngles.length<=1)return;const e=(this.currentAngleIndex+1)%this.cameraAngles.length;this.setToAngle(e,!0),this.showAngleChangeFeedback("next")}previousAngle(){if(this.cameraAngles.length<=1)return;const e=(this.currentAngleIndex-1+this.cameraAngles.length)%this.cameraAngles.length;this.setToAngle(e,!0),this.showAngleChangeFeedback("previous")}showAngleMenu(){if(!this.sceneConfig.mobileUI?.angleMenu)return;const e=this.cameraAngles.map((e,t)=>({name:e.name,description:e.description,active:t===this.currentAngleIndex,index:t}));this.app.fire("ui:showAngleMenu",{items:e,onSelect:e=>{this.setToAngle(e,!0),this.app.fire("ui:hideAngleMenu")}})}setupKeyboardShortcuts(){this.app.keyboard&&this.app.on("update",this.checkKeyboardInput,this)}checkKeyboardInput(e){if(this.app.keyboard&&!this.isTransitioning){for(let e=0;e0&&(this.app.fire("ui:showTouchHints",{hints:e}),setTimeout(()=>{this.app.fire("ui:hideTouchHints")},5e3))}goToAngle(e){e>=0&&ethis.touchThreshold&&(this.isTouchStarted=!1)}}onTouchEnd(e){if(!this.enabled)return;if(!this.isTouchStarted||0===e.changedTouches.length)return;const t=e.changedTouches[0],i=new pc.Vec2(t.x,t.y),o=this.touchStartPos.distance(i),n=Date.now();othis.handleJoystickMove(e),onEnd:()=>this.handleJoystickEnd()}),this.app.fire("ui:showMobileControlZones",{movementZone:{x:0,y:0,width:.3,height:1},lookZone:{x:.3,y:0,width:.7,height:1}})}onTouchStart(e){for(let t=0;tthis.joystickRadius&&(t.normalize().mulScalar(this.joystickRadius),this.joystickCurrent.copy(this.joystickCenter).add(t));const i=t.clone().mulScalar(1/this.joystickRadius);this.handleJoystickMove(i),this.app.fire("ui:updateVirtualJoystick",{center:this.joystickCenter,current:this.joystickCurrent,active:!0})}endJoystick(){this.joystickActive=!1,this.joystickTouchId=null,this.handleJoystickEnd(),this.app.fire("ui:updateVirtualJoystick",{active:!1})}handleJoystickMove(e){this.virtualMovement=new pc.Vec2(e.x,-e.y)}handleJoystickEnd(){this.virtualMovement=new pc.Vec2(0,0)}startLookTouch(e,t){this.lookTouchActive=!0,this.lookTouchId=t,this.lastLookTouchPos.copy(e)}updateLookTouch(e){if(!this.lookTouchActive)return;const t=e.x-this.lastLookTouchPos.x,i=e.y-this.lastLookTouchPos.y;this.yaw-=t*this.lookSensitivity,this.pitch+=i*this.lookSensitivity,this.pitch=pc.math.clamp(this.pitch,-this.pitchLimit,this.pitchLimit),this.lastLookTouchPos.copy(e)}endLookTouch(){this.lookTouchActive=!1,this.lookTouchId=null}update(e){this.initialized&&(this.processVirtualMovement(e),this.entity.setLocalEulerAngles(this.pitch,this.yaw,0))}processVirtualMovement(e){if(!this.virtualMovement||0===this.virtualMovement.x&&0===this.virtualMovement.y)return;const t=this.movementSpeed,i=this.entity.forward.clone();i.y=0,i.normalize();const o=this.entity.right.clone();o.y=0,o.normalize(),this.moveDirection.set(0,0,0),this.moveDirection.add(i.mulScalar(this.virtualMovement.y)),this.moveDirection.add(o.mulScalar(this.virtualMovement.x)),this.moveDirection.lengthSq()>0&&(this.moveDirection.normalize().mulScalar(t*e),this.entity.translate(this.moveDirection))}processKeyboardInput(e){if(!this.app.keyboard)return;let t=0,i=0;if(this.app.keyboard.isPressed(pc.KEY_W)&&(i=1),this.app.keyboard.isPressed(pc.KEY_S)&&(i=-1),this.app.keyboard.isPressed(pc.KEY_A)&&(t=-1),this.app.keyboard.isPressed(pc.KEY_D)&&(t=1),0!==t||0!==i){const o=this.movementSpeed,n=this.entity.forward.clone();n.y=0,n.normalize();const s=this.entity.right.clone();s.y=0,s.normalize(),this.moveDirection.set(0,0,0),this.moveDirection.add(n.mulScalar(i)),this.moveDirection.add(s.mulScalar(t)),this.moveDirection.lengthSq()>0&&(this.moveDirection.normalize().mulScalar(o*e),this.entity.translate(this.moveDirection))}let o=0;this.app.keyboard.isPressed(pc.KEY_Q)&&(o=1),this.app.keyboard.isPressed(pc.KEY_E)&&(o=-1),this.yaw+=90*o*e;let n=0;this.app.keyboard.isPressed(pc.KEY_UP)&&(n=-1),this.app.keyboard.isPressed(pc.KEY_DOWN)&&(n=1),this.pitch+=90*n*e,this.pitch=pc.math.clamp(this.pitch,-this.pitchLimit,this.pitchLimit)}destroy(){this.initialized=!1,this.app.touch&&(this.app.touch.off(pc.EVENT_TOUCHSTART,this.onTouchStart,this),this.app.touch.off(pc.EVENT_TOUCHMOVE,this.onTouchMove,this),this.app.touch.off(pc.EVENT_TOUCHEND,this.onTouchEnd,this)),this.app&&this.app.off("update",this.update,this),this.app.fire("ui:hideVirtualJoystick"),this.app.fire("ui:hideMobileControlZones"),console.log("[MobileCameraController] Mobile camera controls destroyed")}}const v={MobileTavern:{designPriority:"mobile-first",fallbackMode:"full-3d",cameraAngles:[{name:"Bar Area",position:new pc.Vec3(-8,3,4),rotation:new pc.Vec3(-15,-30,0),description:"Bar counter and bartender view",coverage:["bar","bartender","drinks"]},{name:"Seating Area",position:new pc.Vec3(5,4,8),rotation:new pc.Vec3(-20,150,0),description:"Tables and patron interactions",coverage:["tables","patrons","quest_giver"]},{name:"Performance Corner",position:new pc.Vec3(-2,2,-8),rotation:new pc.Vec3(-10,0,0),description:"Bard performance area",coverage:["bard","stage","audience"]},{name:"Quest Board",position:new pc.Vec3(10,3,2),rotation:new pc.Vec3(-15,90,0),description:"Mission board and notices",coverage:["quest_board","notices"]}],touchZones:[{area:{x:0,y:0,width:.15,height:1},action:"previousAngle",visual:"arrow-left"},{area:{x:.85,y:0,width:.15,height:1},action:"nextAngle",visual:"arrow-right"},{area:{x:.15,y:0,width:.7,height:.25},action:"showAngleMenu",gesture:"doubletap"}],mobileUI:{showAngleIndicator:!0,showTouchHints:!0,angleMenu:!0}},MobileGarage:{designPriority:"mobile-first",fallbackMode:"full-3d",cameraAngles:[{name:"Overview",position:new pc.Vec3(0,8,18),rotation:new pc.Vec3(-25,0,0),description:"Full garage overview",coverage:["all_areas"]},{name:"Workbench",position:new pc.Vec3(-8,3,12),rotation:new pc.Vec3(-20,-30,0),description:"Workbench and tools area",coverage:["workbench","tools","cabinet"]},{name:"Practice Objects",position:new pc.Vec3(-12,4,0),rotation:new pc.Vec3(-25,-60,0),description:"Practice shapes and objects",coverage:["practice_objects","shapes"]},{name:"Storage Shelves",position:new pc.Vec3(8,3,0),rotation:new pc.Vec3(-15,60,0),description:"Component storage and displays",coverage:["shelves","storage","components"]}],touchZones:[{area:{x:0,y:0,width:.2,height:1},action:"previousAngle",visual:"arrow-left"},{area:{x:.8,y:0,width:.2,height:1},action:"nextAngle",visual:"arrow-right"}],mobileUI:{showAngleIndicator:!0,showTouchHints:!0,angleMenu:!1}},TavernScene:{designPriority:"desktop-first",fallbackMode:"mobile-simplified",mobileSimplification:{cameraAngles:[{name:"Tavern Overview",position:new pc.Vec3(0,6,12),rotation:new pc.Vec3(-30,0,0),description:"Simple overhead view",coverage:["all"]}]}},GarageScene:{designPriority:"desktop-first",fallbackMode:"mobile-simplified",mobileSimplification:{cameraAngles:[{name:"Garage Overview",position:new pc.Vec3(0,10,20),rotation:new pc.Vec3(-30,0,0),description:"Bird's eye view of garage",coverage:["all"]}]}},MedicalBayScene:{designPriority:"desktop-first",fallbackMode:"mobile-simplified",mobileSimplification:{cameraAngles:[{name:"Medical Bay Overview",position:new pc.Vec3(0,8,15),rotation:new pc.Vec3(-35,0,0),description:"Medical facility overview",coverage:["all"]}]}},NightshiftHorror:{designPriority:"desktop-first",fallbackMode:"mobile-simplified",mobileSimplification:{cameraAngles:[{name:"Control Room View",position:new pc.Vec3(0,5,12),rotation:new pc.Vec3(-25,0,0),description:"Control room perspective",coverage:["all"]}]}},GeneratorCrisis:{designPriority:"desktop-first",fallbackMode:"mobile-simplified",mobileSimplification:{cameraAngles:[{name:"Generator Hall Overview",position:new pc.Vec3(0,12,18),rotation:new pc.Vec3(-30,0,0),description:"Generator facility overview",coverage:["all"]}]}},InventoryTest:{designPriority:"desktop-first",fallbackMode:"mobile-simplified",mobileSimplification:{cameraAngles:[{name:"Test Area Overview",position:new pc.Vec3(0,6,10),rotation:new pc.Vec3(-30,0,0),description:"Test area overview",coverage:["all"]}]}},catalog:{designPriority:"desktop-first",fallbackMode:"mobile-simplified",mobileSimplification:{cameraAngles:[{name:"Menu Overview",position:new pc.Vec3(0,6,10),rotation:new pc.Vec3(-30,0,0),description:"Main menu and scene selection",coverage:["all"]}]}}};class C{constructor(e){this.app=e,this.platform=u.detect(),this.currentController=null,this.touchController=null,this.userPreference=this.loadUserPreference(),console.log("[PlatformAdaptiveCameraManager] Initialized for platform:",this.platform.deviceType),console.log("[PlatformAdaptiveCameraManager] Platform details:",this.platform.debug)}detectPlatform(){const e=navigator.userAgent,t="ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0,i=/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(e),o=window.innerWidth<=768&&window.innerHeight<=1024,n=i||t&&o,s=/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(e),a=!n,r=!n&&!s,l={userAgent:e,hasTouch:t,isMobileUA:i,isSmallScreen:o,isMobile:n,isTablet:s,screenWidth:window.innerWidth,screenHeight:window.innerHeight,maxTouchPoints:navigator.maxTouchPoints||0};return(window.location.search.includes("debug=mobile")||localStorage.getItem("mobile_debug"))&&this.showMobileDebugInfo(l),{type:n?s?"tablet":"mobile":"desktop",capabilities:{touch:n||s,keyboard:a,mouse:r,gamepad:!1},screenSize:{width:window.innerWidth,height:window.innerHeight,ratio:window.innerWidth/window.innerHeight},debug:l}}showMobileDebugInfo(e){const t=document.createElement("div");t.id="mobile-debug-info",t.style.cssText="\n position: fixed;\n top: 10px;\n left: 10px;\n background: rgba(0, 0, 0, 0.8);\n color: white;\n padding: 10px;\n border-radius: 5px;\n font-family: monospace;\n font-size: 12px;\n z-index: 10000;\n max-width: 300px;\n line-height: 1.4;\n ";const i=e.isMobile?e.isTablet?"tablet":"mobile":"desktop";t.innerHTML=`\n Mobile Detection Debug
\n Platform: ${i}
\n User Agent: ${e.userAgent.substring(0,50)}...
\n Has Touch: ${e.hasTouch}
\n Touch Points: ${e.maxTouchPoints}
\n Mobile UA: ${e.isMobileUA}
\n Small Screen: ${e.isSmallScreen}
\n Is Mobile: ${e.isMobile}
\n Is Tablet: ${e.isTablet}
\n Screen: ${e.screenWidth}x${e.screenHeight}
\n \n `;const o=document.getElementById("mobile-debug-info");o&&o.remove(),document.body.appendChild(t)}initializeForScene(e,t){console.log(`[PlatformAdaptiveCameraManager] Initializing for scene: ${e}, platform: ${this.platform.type}`);const i=v[e];if(!i)return console.log(`[PlatformAdaptiveCameraManager] No config for scene "${e}", using standard controller`),void this.initializeStandardController(t);const o=i.designPriority,n=this.checkUserOverride(),s=this.platform.isMobile||this.platform.isTablet;console.log(`[PlatformAdaptiveCameraManager] Scene config found - Priority: ${o}, Platform: ${this.platform.deviceType}, Override: ${n}`),"mobile-first"===o?s&&!n?(console.log("[PlatformAdaptiveCameraManager] Using mobile-first controller"),this.initializeMobileFirstController(t,i)):(console.log("[PlatformAdaptiveCameraManager] Using fallback controller"),this.initializeFallbackController(t,i)):s?(console.log("[PlatformAdaptiveCameraManager] Using mobile fallback controller"),this.initializeMobileFallbackController(t,i)):(console.log("[PlatformAdaptiveCameraManager] Using standard controller"),this.initializeStandardController(t)),this.setupControlSwitching(t,i)}initializeStandardController(e){this.destroyCurrentControllers(),this.currentController=new m(e,this.app),this.currentController.initialize(),console.log("[PlatformAdaptiveCameraManager] Initialized standard 3D camera controller")}initializeMobileFirstController(e,t){this.destroyCurrentControllers(),this.currentController=new y(e,this.app,t),this.currentController.initialize(),this.touchController=new f(e,this.app),this.touchController.initialize(),this.app.fire("ui:showMobileControls",{showAngleButtons:!0,showTouchHints:t.mobileUI?.showTouchHints,angleMenu:t.mobileUI?.angleMenu}),console.log("[PlatformAdaptiveCameraManager] Initialized mobile-first 2.5D camera controller")}initializeFallbackController(e,t){this.destroyCurrentControllers(),this.platform.capabilities.keyboard&&this.platform.capabilities.mouse?this.currentController=new m(e,this.app):this.platform.capabilities.touch&&(this.currentController=new b(e,this.app)),this.currentController.initialize(),this.app.fire("ui:showControlSwitch",{current:"fallback",alternative:"mobile-2.5d"}),console.log("[PlatformAdaptiveCameraManager] Initialized fallback 3D camera controller")}initializeMobileFallbackController(e,t){this.destroyCurrentControllers();const i={...t.mobileSimplification,touchZones:[{area:{x:.3,y:.3,width:.4,height:.4},action:"interact",visual:"tap-hint"}],mobileUI:{showTouchHints:!0,showAngleIndicator:!1,angleMenu:!1}};this.currentController=new y(e,this.app,i),this.currentController.initialize(),this.touchController=new f(e,this.app),this.touchController.initialize(),this.app.fire("ui:showMobileControls",{showAngleButtons:!1,showTouchHints:!0,simplified:!0}),console.log("[PlatformAdaptiveCameraManager] Initialized mobile fallback controller")}setupControlSwitching(e,t){this._switchHandlersSetup||(this._switchHandlersSetup=!0,this.app.on("camera:switchToMobile",()=>{this.switchToMobileFirst(e,t)}),this.app.on("camera:switchToFallback",()=>{this.switchToFallback(e,t)}),this.app.on("camera:switchToStandard",()=>{this.switchToStandard(e)}))}switchToMobileFirst(e,t){this.saveUserPreference("mobile-first"),this.initializeMobileFirstController(e,t)}switchToFallback(e,t){this.saveUserPreference("fallback"),this.initializeFallbackController(e,t)}switchToStandard(e){this.saveUserPreference("standard"),this.initializeStandardController(e)}destroyCurrentControllers(){this.currentController&&this.currentController.destroy&&(this.currentController.destroy(),this.currentController=null),this.touchController&&this.touchController.destroy&&(this.touchController.destroy(),this.touchController=null)}checkUserOverride(){return this.userPreference&&"auto"!==this.userPreference}loadUserPreference(){try{return localStorage.getItem("camera_preference")||"auto"}catch(e){return"auto"}}saveUserPreference(e){try{localStorage.setItem("camera_preference",e),this.userPreference=e}catch(e){console.warn("[PlatformAdaptiveCameraManager] Could not save user preference")}}destroy(){this.destroyCurrentControllers(),this.app.off("camera:switchToMobile"),this.app.off("camera:switchToFallback"),this.app.off("camera:switchToStandard")}}class w{constructor(e){this.app=e,this.currentCameraConfig=null,this.roomBounds=null,this.debug=window.location.search.includes("debug=coords")||localStorage.getItem("coord_debug"),this.debugOverlay=null}initialize(e,t){this.currentCameraConfig=e,this.roomBounds=t||this.estimateRoomBounds(),this.debug&&(console.log("[CoordinateCalculationSystem] Initialized with config:",e),console.log("[CoordinateCalculationSystem] Room bounds:",this.roomBounds)),this.debugOverlay&&(this.debugOverlay.log("CoordinateCalculationSystem initialized","info"),e&&this.debugOverlay.log(`Cam config: ${e.name||"unnamed"}`,"debug"))}setDebugOverlay(e){this.debugOverlay=e}calculateButtonPosition(e,t){if(!e||!t)return{valid:!1,reason:"Missing entity or camera"};const i=e.getPosition(),o=t.camera||t;if(!o||!o.worldToScreen)return{valid:!1,reason:"Invalid camera component"};const n=this.getCurrentCameraConfig(t),s=o.worldToScreen(i),a={screenPos:s,worldPos:i,valid:s.z>0,confidence:1,adjustments:{}};return a.valid?(this.applyDepthCorrection(a,i,t,n),this.applyCameraAngleCorrection(a,i,t,n),this.applyRoomBoundsValidation(a,i),this.applyViewportOptimization(a),a.confidence=this.calculateConfidenceScore(a),this.debug&&this.logPositionCalculation(e,a),a):(a.reason="Entity behind camera",a)}getCurrentCameraConfig(e){if(!this.currentCameraConfig){const t=e.entity||e;if(t){const e=t.getPosition(),i=t.getEulerAngles();return{position:e,rotation:i,height:e.y,angle:i.x,distance:Math.sqrt(e.x*e.x+e.z*e.z)}}}return this.currentCameraConfig}applyDepthCorrection(e,t,i,o){if(!o)return;const n=i.entity?i.entity.getPosition():o.position||new pc.Vec3,s=t.distance(n),a=Math.abs(o.height||n.y)/10,r=Math.abs(o.angle||0)/90,l={scale:Math.max(.5,1-s/50),heightOffset:a*r*20,distanceToCamera:s};e.adjustments.depth=l,e.screenPos.y-=l.heightOffset,s>30&&(e.confidence*=Math.max(.3,1-(s-30)/50))}applyCameraAngleCorrection(e,t,i,o){if(!o)return;const n=o.angle||0,s=o.height||0;if(Math.abs(n)>5){const i=t.y-s,o=n*Math.PI/180,a=i*Math.sin(o)*10,r=i*Math.cos(o)*2;e.adjustments.angle={heightDifference:i,screenOffsetX:r,screenOffsetY:a},e.screenPos.x+=r,e.screenPos.y+=a}}applyRoomBoundsValidation(e,t){if(!this.roomBounds)return;const i=t.x>=this.roomBounds.min.x&&t.x<=this.roomBounds.max.x&&t.z>=this.roomBounds.min.z&&t.z<=this.roomBounds.max.z;e.adjustments.bounds={inBounds:i,roomBounds:this.roomBounds},i||(e.confidence*=.5)}applyViewportOptimization(e){const t={width:window.innerWidth,height:window.innerHeight};let i=e.screenPos.x,o=e.screenPos.y;i=Math.max(80,Math.min(t.width-40-40,i)),o=Math.max(60,Math.min(t.height-40-20,o));const n=i!==e.screenPos.x||o!==e.screenPos.y;e.adjustments.viewport={original:{x:e.screenPos.x,y:e.screenPos.y},adjusted:{x:i,y:o},wasAdjusted:n,viewport:t},e.screenPos.x=i,e.screenPos.y=o,n&&Math.sqrt(Math.pow(i-e.adjustments.viewport.original.x,2)+Math.pow(o-e.adjustments.viewport.original.y,2))>50&&(e.confidence*=.7)}calculateConfidenceScore(e){let t=e.confidence||1;return e.adjustments.depth&&e.adjustments.depth.distanceToCamera>25&&(t*=.8),e.adjustments.bounds&&!e.adjustments.bounds.inBounds&&(t*=.6),e.adjustments.viewport&&e.adjustments.viewport.wasAdjusted&&(t*=.9),Math.max(.1,Math.min(1,t))}estimateRoomBounds(){if(!this.app.root)return null;const e=[];if(this.app.root.forEach(t=>{t.getPosition&&e.push(t.getPosition())}),0===e.length)return null;const t={min:new pc.Vec3(1/0,1/0,1/0),max:new pc.Vec3(-1/0,-1/0,-1/0)};return e.forEach(e=>{t.min.x=Math.min(t.min.x,e.x),t.min.y=Math.min(t.min.y,e.y),t.min.z=Math.min(t.min.z,e.z),t.max.x=Math.max(t.max.x,e.x),t.max.y=Math.max(t.max.y,e.y),t.max.z=Math.max(t.max.z,e.z)}),t.min.sub(new pc.Vec3(5,5,5)),t.max.add(new pc.Vec3(5,5,5)),t}updateCameraConfig(e){this.currentCameraConfig=e,this.debug&&console.log("[CoordinateCalculationSystem] Updated camera config:",e)}logPositionCalculation(e,t){console.group(`[CoordinateCalculationSystem] ${e.name||"Entity"} positioning:`),console.log("World Position:",t.worldPos),console.log("Screen Position:",t.screenPos),console.log("Confidence:",t.confidence.toFixed(2)),console.log("Valid:",t.valid),t.adjustments.depth&&console.log("Depth Adjustment:",t.adjustments.depth),t.adjustments.angle&&console.log("Angle Adjustment:",t.adjustments.angle),t.adjustments.bounds&&console.log("Bounds Check:",t.adjustments.bounds),t.adjustments.viewport&&console.log("Viewport Adjustment:",t.adjustments.viewport),console.groupEnd()}getPositioningStats(){return{roomBounds:this.roomBounds,cameraConfig:this.currentCameraConfig,viewport:{width:window.innerWidth,height:window.innerHeight}}}}class S{constructor(e){this.app=e,this.debugContainer=null,this.messageContainer=null,this.gridContainer=null,this.positioningIndicators=new Map,this.touchIndicators=new Map,this.buttonInfoContainer=null,this.messages=[],this.maxMessages=15,this.isEnabled=!1,this.shouldEnable=this.checkIfShouldEnable(),this.shouldEnable&&this.initialize()}checkIfShouldEnable(){const e=/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||navigator.maxTouchPoints>0||screen.width<=768,t=window.location.search.includes("debug")||localStorage.getItem("mobile_debug")||localStorage.getItem("coord_debug");return e||t}initialize(){this.createDebugContainer(),this.createMessageSystem(),this.createGridSystem(),this.createButtonInfoSystem(),this.createControlPanel(),this.setupTouchTracking(),this.isEnabled=!0,this.log("MobileDebugOverlay initialized","info"),this.log(`Screen: ${window.innerWidth}x${window.innerHeight}`,"info"),this.log(`User Agent: ${navigator.userAgent.substring(0,50)}...`,"info")}createDebugContainer(){this.debugContainer=document.createElement("div"),this.debugContainer.id="mobile-debug-overlay",this.debugContainer.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n pointer-events: none;\n z-index: 9999;\n font-family: 'Courier New', monospace;\n font-size: 12px;\n ",document.body.appendChild(this.debugContainer)}createMessageSystem(){this.messageContainer=document.createElement("div");const e=this.checkIfShouldEnable(),t=window.innerWidth<=768,i=window.innerWidth,o=window.innerHeight;this.messageContainer.style.cssText=`\n position: absolute;\n top: ${e||t?"160px":"10px"};\n left: 10px;\n max-width: ${Math.min(i-40,e||t?280:350)}px;\n max-height: ${Math.min(o-200,300)}px;\n overflow-y: auto;\n overflow-x: hidden;\n background: rgba(0, 0, 0, 0.85);\n color: #00ff00;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #333;\n font-size: 10px;\n line-height: 1.2;\n pointer-events: auto;\n z-index: 10001;\n word-wrap: break-word;\n `,this.debugContainer.appendChild(this.messageContainer)}createButtonInfoSystem(){this.buttonInfoContainer=document.createElement("div"),this.buttonInfoContainer.style.cssText="\n position: absolute;\n bottom: 10px;\n left: 10px;\n max-width: 300px;\n background: rgba(0, 0, 0, 0.85);\n color: #00ff00;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #333;\n font-size: 11px;\n line-height: 1.3;\n pointer-events: auto;\n z-index: 10001;\n ",this.debugContainer.appendChild(this.buttonInfoContainer),this.updateButtonInfo([])}setupTouchTracking(){document.addEventListener("touchstart",e=>{this.log(`Touch detected: ${e.touches?e.touches.length:1} points`,"debug"),this.showTouchFeedback(e)},{passive:!0}),document.addEventListener("touchend",e=>{this.log(`Touch ended: ${e.changedTouches?e.changedTouches.length:1} points`,"debug"),this.hideTouchFeedback(e)},{passive:!0}),document.addEventListener("click",e=>{this.log(`Click at: (${e.clientX}, ${e.clientY})`,"debug"),this.showClickFeedback(e)},{passive:!0}),document.addEventListener("mousedown",e=>{this.log(`Mouse down at: (${e.clientX}, ${e.clientY})`,"debug"),this.createTouchIndicator("mouse",e.clientX,e.clientY,"click")},{passive:!0}),this.log("Touch tracking initialized","info")}createGridSystem(){this.gridContainer=document.createElement("div"),this.gridContainer.style.cssText="\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n z-index: 9998;\n ",this.createGrid(),this.debugContainer.appendChild(this.gridContainer)}createGrid(){const e=window.innerWidth,t=window.innerHeight;this.gridContainer.innerHTML="";for(let t=0;t<=e;t+=50){const e=document.createElement("div");if(e.style.cssText=`\n position: absolute;\n left: ${t}px;\n top: 0;\n width: 1px;\n height: 100%;\n background: rgba(0, 255, 0, 0.2);\n pointer-events: none;\n `,this.gridContainer.appendChild(e),t%100==0&&t>0){const e=document.createElement("div");e.textContent=t.toString(),e.style.cssText=`\n position: absolute;\n left: ${t+2}px;\n top: 2px;\n color: rgba(0, 255, 0, 0.7);\n font-size: 10px;\n background: rgba(0, 0, 0, 0.5);\n padding: 1px 3px;\n border-radius: 2px;\n `,this.gridContainer.appendChild(e)}}for(let e=0;e<=t;e+=50){const t=document.createElement("div");if(t.style.cssText=`\n position: absolute;\n left: 0;\n top: ${e}px;\n width: 100%;\n height: 1px;\n background: rgba(0, 255, 0, 0.2);\n pointer-events: none;\n `,this.gridContainer.appendChild(t),e%100==0&&e>0){const t=document.createElement("div");t.textContent=e.toString(),t.style.cssText=`\n position: absolute;\n left: 2px;\n top: ${e+2}px;\n color: rgba(0, 255, 0, 0.7);\n font-size: 10px;\n background: rgba(0, 0, 0, 0.5);\n padding: 1px 3px;\n border-radius: 2px;\n `,this.gridContainer.appendChild(t)}}const i=e/2,o=t/2,n=document.createElement("div");n.style.cssText=`\n position: absolute;\n left: 0;\n top: ${o}px;\n width: 100%;\n height: 2px;\n background: rgba(255, 0, 0, 0.5);\n pointer-events: none;\n `,this.gridContainer.appendChild(n);const s=document.createElement("div");s.style.cssText=`\n position: absolute;\n left: ${i}px;\n top: 0;\n width: 2px;\n height: 100%;\n background: rgba(255, 0, 0, 0.5);\n pointer-events: none;\n `,this.gridContainer.appendChild(s);const a=document.createElement("div");a.textContent=`CENTER (${i.toFixed(0)}, ${o.toFixed(0)})`,a.style.cssText=`\n position: absolute;\n left: ${i+5}px;\n top: ${o+5}px;\n color: rgba(255, 0, 0, 0.8);\n font-size: 11px;\n font-weight: bold;\n background: rgba(0, 0, 0, 0.7);\n padding: 2px 4px;\n border-radius: 3px;\n `,this.gridContainer.appendChild(a)}createControlPanel(){const e=document.createElement("div"),t=this.checkIfShouldEnable(),i=window.innerWidth<=768;e.style.cssText=`\n position: absolute;\n top: ${t||i?"60px":"10px"};\n ${t||i?"left: 10px;":"right: 10px;"}\n background: rgba(0, 0, 0, 0.85);\n color: white;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #333;\n pointer-events: auto;\n z-index: 10001;\n max-width: 200px;\n `,this.log(`Control panel positioned on ${t||i?"left":"right"} side`,"debug");const o=document.createElement("button");o.textContent="Toggle Grid",o.style.cssText="\n display: block;\n width: 100%;\n margin: 2px 0;\n padding: 4px 8px;\n background: #333;\n color: white;\n border: 1px solid #666;\n border-radius: 3px;\n font-size: 11px;\n cursor: pointer;\n ",o.onclick=()=>this.toggleGrid(),e.appendChild(o);const n=document.createElement("button");n.textContent="Clear Log",n.style.cssText=o.style.cssText,n.onclick=()=>this.clearMessages(),e.appendChild(n);const s=document.createElement("button");s.textContent="Show Stats",s.style.cssText=o.style.cssText,s.onclick=()=>this.showSystemStats(),e.appendChild(s);const a=document.createElement("button");a.textContent="Toggle System",a.style.cssText=o.style.cssText,a.onclick=()=>this.app.fire("mobile:toggleSystem"),e.appendChild(a);const r=document.createElement("button");r.textContent="Test Coords",r.style.cssText=o.style.cssText,r.onclick=()=>this.app.fire("mobile:testCoordinates"),e.appendChild(r);const l=document.createElement("button");l.textContent="Show Bounds",l.style.cssText=o.style.cssText,l.onclick=()=>this.app.fire("mobile:showCoordinateBounds"),e.appendChild(l),this.debugContainer.appendChild(e)}log(e,t="debug"){if(!this.isEnabled)return;const i={info:"#00ff00",warn:"#ffff00",error:"#ff0000",debug:"#00ffff",positioning:"#ff00ff"},o={message:e,type:t,timestamp:(new Date).toLocaleTimeString(),color:i[t]||i.debug};this.messages.push(o),this.messages.length>this.maxMessages&&this.messages.shift(),this.updateMessageDisplay()}updateMessageDisplay(){if(!this.messageContainer)return;let e='
🔍 Debug Log
';this.messages.forEach(t=>{e+=`
\n [${t.timestamp}] ${t.message}\n
`}),this.messageContainer.innerHTML=e}showPositioningInfo(e,t){if(!this.isEnabled)return;const i=t.worldPos,o=t.screenPos,n=t.confidence;this.log(`${e}: W(${i.x.toFixed(1)}, ${i.y.toFixed(1)}, ${i.z.toFixed(1)}) -> S(${o.x.toFixed(0)}, ${o.y.toFixed(0)}) conf:${n.toFixed(2)}`,"positioning"),this.createPositioningIndicator(e,o.x,o.y,n)}createPositioningIndicator(e,t,i,o){if(this.positioningIndicators.has(e)){const t=this.positioningIndicators.get(e);t.parentNode&&t.parentNode.removeChild(t)}const n=document.createElement("div"),s=o>=.8?"#00ff00":o>=.6?"#ffff00":"#ff6600";n.style.cssText=`\n position: absolute;\n left: ${t-10}px;\n top: ${i-10}px;\n width: 20px;\n height: 20px;\n border: 2px solid ${s};\n border-radius: 50%;\n background: ${s}33;\n pointer-events: none;\n z-index: 10000;\n animation: pulse 1s ease-in-out infinite alternate;\n `;const a=document.createElement("div");a.textContent=`${e}\n${o.toFixed(2)}`,a.style.cssText=`\n position: absolute;\n left: 25px;\n top: -5px;\n color: ${s};\n font-size: 9px;\n background: rgba(0, 0, 0, 0.7);\n padding: 1px 3px;\n border-radius: 2px;\n white-space: pre;\n `,n.appendChild(a),this.debugContainer.appendChild(n),this.positioningIndicators.set(e,n),setTimeout(()=>{this.positioningIndicators.get(e)===n&&(this.positioningIndicators.delete(e),n.parentNode&&n.parentNode.removeChild(n))},3e3)}showTouchFeedback(e){for(let t=0;t{this.removeTouchIndicator(e)},"click"===o?600:2e3)}removeTouchIndicator(e){const t=this.touchIndicators.get(e);t&&t.parentNode&&(t.parentNode.removeChild(t),this.touchIndicators.delete(e))}updateButtonInfo(e){if(!this.buttonInfoContainer)return;let t='
🎯 Button Info
';if(t+=`
Total Buttons: ${e.length}
`,0===e.length)t+='
No buttons created yet
';else{const i={};e.forEach((e,t)=>{const o=e.getBoundingClientRect(),n=`${Math.round(o.left)},${Math.round(o.top)}`;i[n]||(i[n]=[]),i[n].push({index:t,element:e,rect:o,text:e.textContent||e.innerText||"Unknown"})}),Object.entries(i).forEach(([e,i])=>{const[o,n]=e.split(",");if(i.length>1)t+=`
⚠️ STACKED (${o}, ${n}): ${i.length} buttons
`,i.forEach(e=>{t+=`
- ${e.text}
`});else{const e=i[0];t+=`
✓ (${o}, ${n}): ${e.text}
`}});const o=e.filter(e=>{const t=window.getComputedStyle(e);return"none"!==t.display&&"hidden"!==t.visibility&&"0"!==t.opacity});t+=`
\n Visible: ${o.length}/${e.length}\n
`}this.buttonInfoContainer.innerHTML=t}toggleGrid(){if(this.gridContainer){const e="none"!==this.gridContainer.style.display;this.gridContainer.style.display=e?"none":"block",this.log("Grid "+(e?"hidden":"shown"),"info")}}clearMessages(){this.messages=[],this.updateMessageDisplay(),this.log("Messages cleared","info")}showSystemStats(){this.log("=== SYSTEM STATS ===","info"),this.log(`Screen: ${window.innerWidth}x${window.innerHeight}`,"info"),this.log(`Pixel Ratio: ${window.devicePixelRatio||1}`,"info"),this.log(`Touch Points: ${navigator.maxTouchPoints||0}`,"info"),this.log(`Orientation: ${screen.orientation?screen.orientation.angle:"unknown"}°`,"info"),this.log(`Memory: ${navigator.deviceMemory||"unknown"}GB`,"info"),this.log("==================","info")}handleResize(){this.gridContainer&&this.createGrid(),this.log(`Screen resized: ${window.innerWidth}x${window.innerHeight}`,"info")}addAnimationStyles(){if(document.getElementById("mobile-debug-animations"))return;const e=document.createElement("style");e.id="mobile-debug-animations",e.textContent="\n @keyframes pulse {\n 0% { transform: scale(1); opacity: 0.8; }\n 100% { transform: scale(1.2); opacity: 0.4; }\n }\n @keyframes touchPulse {\n 0% { transform: scale(0.5); opacity: 1; }\n 50% { transform: scale(1.2); opacity: 0.7; }\n 100% { transform: scale(1); opacity: 0.3; }\n }\n ",document.head.appendChild(e)}initialize(){this.createDebugContainer(),this.createMessageSystem(),this.createGridSystem(),this.createControlPanel(),this.addAnimationStyles(),this.isEnabled=!0,window.addEventListener("resize",()=>this.handleResize()),window.addEventListener("orientationchange",()=>{setTimeout(()=>this.handleResize(),500)}),this.log("MobileDebugOverlay initialized","info"),this.log(`Screen: ${window.innerWidth}x${window.innerHeight}`,"info"),this.log(`User Agent: ${navigator.userAgent.substring(0,50)}...`,"info")}destroy(){this.debugContainer&&this.debugContainer.parentNode&&this.debugContainer.parentNode.removeChild(this.debugContainer);const e=document.getElementById("mobile-debug-animations");e&&e.parentNode&&e.parentNode.removeChild(e),this.isEnabled=!1,this.positioningIndicators.clear()}}class x{constructor(e){this.debugOverlay=e,this.catalogBounds={portrait:{center:{x:192,y:291},north:{x:195,y:158},south:{x:185,y:543},west:null,east:null},landscape:{center:{x:376,y:125},north:null,south:null,west:null,east:null}},this.currentOrientation=this.detectOrientation(),this.currentScreenSize=this.getScreenSize(),this.calculateMissingCoordinates(),this.debugOverlay.log("CatalogCoordinateSystem initialized","info"),this.logCoordinateBounds()}detectOrientation(){return window.innerWidth>window.innerHeight?"landscape":"portrait"}getScreenSize(){return{width:window.innerWidth,height:window.innerHeight}}calculateMissingCoordinates(){const e=this.catalogBounds.portrait,t=this.catalogBounds.landscape,i=(e.south.y,e.north.y,e.center.y);e.west={x:Math.max(20,e.center.x-150),y:i},e.east={x:Math.min(this.currentScreenSize.width-20,e.center.x+150),y:i};const o=t.center;t.north={x:o.x,y:Math.max(20,o.y-100)},t.south={x:o.x,y:Math.min(this.currentScreenSize.height-20,o.y+100)},t.west={x:Math.max(20,o.x-250),y:o.y},t.east={x:Math.min(this.currentScreenSize.width-20,o.x+250),y:o.y},this.debugOverlay.log("Calculated missing coordinates from measured values","info")}logCoordinateBounds(){this.debugOverlay.log("=== CATALOG COORDINATE BOUNDS ===","info");const e=this.catalogBounds.portrait,t=this.catalogBounds.landscape;this.debugOverlay.log("PORTRAIT:","info"),this.debugOverlay.log(` Center: (${e.center.x}, ${e.center.y}) [measured]`,"info"),this.debugOverlay.log(` North: (${e.north.x}, ${e.north.y}) [measured]`,"info"),this.debugOverlay.log(` South: (${e.south.x}, ${e.south.y}) [measured]`,"info"),this.debugOverlay.log(` West: (${e.west.x}, ${e.west.y}) [calculated]`,"info"),this.debugOverlay.log(` East: (${e.east.x}, ${e.east.y}) [calculated]`,"info"),this.debugOverlay.log("LANDSCAPE:","info"),this.debugOverlay.log(` Center: (${t.center.x}, ${t.center.y}) [measured]`,"info"),this.debugOverlay.log(` North: (${t.north.x}, ${t.north.y}) [calculated]`,"info"),this.debugOverlay.log(` South: (${t.south.x}, ${t.south.y}) [calculated]`,"info"),this.debugOverlay.log(` West: (${t.west.x}, ${t.west.y}) [calculated]`,"info"),this.debugOverlay.log(` East: (${t.east.x}, ${t.east.y}) [calculated]`,"info"),this.debugOverlay.log("================================","info")}mapToScreenPosition(e,t,i="catalog"){if("catalog"!==i)return this.guessPositionForOtherScene(e,t,i);const o=this.detectOrientation(),n=this.getScreenSize();let s,a=.8;return"portrait"===o?s=this.mapPortraitPosition(e,t,n):(s=this.mapLandscapePosition(e,t,n),a=.6),this.debugOverlay.log(`Mapped catalog (${e}, ${t}) -> screen (${s.x.toFixed(0)}, ${s.y.toFixed(0)}) [${o}]`,"positioning"),{x:s.x,y:s.y,confidence:a,orientation:o,sceneType:i}}mapPortraitPosition(e,t,i){return this.catalogBounds.portrait,{x:Math.max(20,Math.min(i.width-20,e)),y:Math.max(20,Math.min(i.height-20,t))}}mapLandscapePosition(e,t,i){return this.catalogBounds.landscape,{x:Math.max(20,Math.min(i.width-20,e)),y:Math.max(20,Math.min(i.height-20,t))}}guessPositionForOtherScene(e,t,i){this.debugOverlay.log(`Guessing position for scene type: ${i}`,"warn");const o=this.getScreenSize(),n=e/400*o.width,s=t/600*o.height;return{x:Math.max(40,Math.min(o.width-40,n)),y:Math.max(40,Math.min(o.height-40,s)),confidence:.3,orientation:this.detectOrientation(),sceneType:i}}getButtonPlacementRegions(){const e=this.getScreenSize(),t=[];if("portrait"===this.detectOrientation()){const i=this.catalogBounds.portrait;t.push({name:"left-side",x:20,y:Math.max(60,i.north.y),width:Math.max(60,i.west.x-40),height:i.south.y-Math.max(60,i.north.y),priority:1}),t.push({name:"right-side",x:i.east.x+20,y:Math.max(60,i.north.y),width:Math.max(60,e.width-i.east.x-40),height:i.south.y-Math.max(60,i.north.y),priority:2})}else{const i=this.catalogBounds.landscape;t.push({name:"top-area",x:i.west.x,y:60,width:i.east.x-i.west.x,height:Math.max(40,i.north.y-80),priority:1}),t.push({name:"bottom-area",x:i.west.x,y:i.south.y+20,width:i.east.x-i.west.x,height:Math.max(40,e.height-i.south.y-60),priority:2})}return t}handleScreenChange(){const e=this.detectOrientation(),t=this.getScreenSize();e===this.currentOrientation&&t.width===this.currentScreenSize.width&&t.height===this.currentScreenSize.height||(this.currentOrientation=e,this.currentScreenSize=t,this.calculateMissingCoordinates(),this.debugOverlay.log(`Screen changed: ${t.width}x${t.height} ${e}`,"info"),this.logCoordinateBounds())}detectCurrentScene(){return"catalog"}testCoordinateMapping(){this.debugOverlay.log("=== TESTING COORDINATE MAPPING ===","info");const e=this.detectOrientation(),t="portrait"===e?this.catalogBounds.portrait:this.catalogBounds.landscape;this.debugOverlay.log(`Current orientation: ${e}`,"info"),[{name:"Center Point",x:t.center.x,y:t.center.y},{name:"North Point",x:t.north.x,y:t.north.y},{name:"South Point",x:t.south.x,y:t.south.y},{name:"West Point",x:t.west.x,y:t.west.y},{name:"East Point",x:t.east.x,y:t.east.y}].forEach(t=>{const i=this.mapToScreenPosition(t.x,t.y,"catalog"),o="Center Point"===t.name||"portrait"===e&&(t.name.includes("North")||t.name.includes("South"))||"landscape"===e&&"Center Point"===t.name?"[measured]":"[calculated]";this.debugOverlay.log(`${t.name}: catalog(${t.x}, ${t.y}) -> screen(${i.x.toFixed(0)}, ${i.y.toFixed(0)}) ${o} conf:${i.confidence}`,"positioning")}),this.debugOverlay.log("=== PLACEMENT REGIONS ===","info"),this.getButtonPlacementRegions().forEach(e=>{this.debugOverlay.log(`${e.name}: x:${e.x} y:${e.y} w:${e.width} h:${e.height} priority:${e.priority}`,"info")}),this.debugOverlay.log("=== TEST COMPLETE ===","info")}createCoordinateIndicators(){if(!this.debugOverlay.debugContainer)return;this.debugOverlay.debugContainer.querySelectorAll(".coord-indicator").forEach(e=>e.remove());const e=this.detectOrientation(),t=this.getScreenSize(),i="portrait"===e?this.catalogBounds.portrait:this.catalogBounds.landscape;[{name:"N",x:i.north.x,y:i.north.y,color:"#ff0000"},{name:"S",x:i.south.x,y:i.south.y,color:"#00ff00"},{name:"W",x:i.west.x,y:i.west.y,color:"#0000ff"},{name:"E",x:i.east.x,y:i.east.y,color:"#ffff00"},{name:"C",x:i.center.x,y:i.center.y,color:"#ff00ff"}].forEach(({name:e,x:t,y:i,color:o})=>{const n=document.createElement("div");n.className="coord-indicator",n.style.cssText=`\n position: absolute;\n left: ${t-15}px;\n top: ${i-15}px;\n width: 30px;\n height: 30px;\n background: ${o};\n border: 2px solid white;\n border-radius: 50%;\n z-index: 10003;\n pointer-events: none;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: bold;\n color: white;\n box-shadow: 0 2px 4px rgba(0,0,0,0.5);\n `,n.textContent=e;const s=document.createElement("div");s.style.cssText="\n position: absolute;\n top: 32px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0,0,0,0.8);\n color: white;\n padding: 2px 4px;\n border-radius: 2px;\n font-size: 9px;\n white-space: nowrap;\n ",s.textContent=`(${Math.round(t)},${Math.round(i)})`,n.appendChild(s),this.debugOverlay.debugContainer.appendChild(n)}),this.debugOverlay.log(`Coordinate indicators created for ${e} (${t.width}x${t.height})`,"info")}}class E{constructor(e,t){this.app=e,this.debugOverlay=t,this.coordinateSystem=new x(t),this.buttons=new Map,this.buttonContainer=null,this.gridPositions=[],this.currentButtonIndex=0,this.isMobile=this.detectMobile(),this.isMobile&&this.initialize(),this.app.on("mobile:testCoordinates",()=>{this.coordinateSystem.testCoordinateMapping()}),this.app.on("mobile:showCoordinateBounds",()=>{this.coordinateSystem.createCoordinateIndicators()})}detectMobile(){return/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||navigator.maxTouchPoints>0||screen.width<=768}initialize(){this.createButtonContainer(),this.generateGridPositions(),this.debugOverlay.log("SimpleMobileButtons initialized","info"),this.debugOverlay.log(`Generated ${this.gridPositions.length} grid positions`,"debug")}createButtonContainer(){this.buttonContainer=document.createElement("div"),this.buttonContainer.id="simple-mobile-buttons",this.buttonContainer.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n pointer-events: none;\n z-index: 1999;\n ",document.body.appendChild(this.buttonContainer)}generateGridPositions(){this.coordinateSystem.handleScreenChange();const e=this.coordinateSystem.getButtonPlacementRegions();this.gridPositions=[],e.forEach(e=>{const t=Math.floor(e.width/80),i=Math.floor(e.height/80);for(let o=0;oe.priority-t.priority),this.debugOverlay.log(`Generated ${this.gridPositions.length} total grid positions across ${e.length} regions`,"info")}createButtonForEntity(e){if(this.buttons.has(e.getGuid()))return void this.debugOverlay.log(`Button already exists for ${e.name}`,"warn");let t=this.getPositionFromCatalogCoordinates(e);if(!t&&(t=this.getNextPosition(),!t))return void this.debugOverlay.log(`No available positions for ${e.name}`,"error");const i=document.createElement("button");let o="?",n="#666";if(e.tags.has("catalog_podium")){const t=e.name.match(/Podium_(\d+)/);o=t?`S${t[1]}`:"Scene",n="#4CAF50"}else e.tags.has("npc")?(o="💬",n="#2196F3"):e.tags.has("interactive")&&(o="⚡",n="#FF9800");return i.className="simple-mobile-btn",i.textContent=o,i.style.cssText=`\n position: absolute;\n left: ${t.x}px;\n top: ${t.y}px;\n width: 60px;\n height: 60px;\n background: ${n};\n color: white;\n border: 2px solid rgba(255,255,255,0.3);\n border-radius: 8px;\n font-size: 16px;\n font-weight: bold;\n cursor: pointer;\n pointer-events: auto;\n z-index: 2000;\n box-shadow: 0 4px 8px rgba(0,0,0,0.3);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n `,i.onclick=()=>this.handleButtonClick(e,i),this.buttonContainer.appendChild(i),this.buttons.set(e.getGuid(),{button:i,entity:e,position:t,text:o}),t.occupied=!0,this.debugOverlay.log(`✓ Created button for ${e.name} at (${t.x}, ${t.y})`,"info"),i}getPositionFromCatalogCoordinates(e){if(e.catalogCoordinates){const t=this.coordinateSystem.detectCurrentScene(),i=this.coordinateSystem.mapToScreenPosition(e.catalogCoordinates.x,e.catalogCoordinates.y,t);return this.debugOverlay.log(`Using catalog coordinates for ${e.name}: (${e.catalogCoordinates.x}, ${e.catalogCoordinates.y}) -> (${i.x.toFixed(0)}, ${i.y.toFixed(0)})`,"positioning"),{x:i.x,y:i.y,occupied:!1,region:"catalog-coordinate",priority:0,confidence:i.confidence,source:"catalog"}}return null}getNextPosition(){for(let e of this.gridPositions)if(!e.occupied)return e;return null}handleButtonClick(e,t){if(this.debugOverlay.log(`🎯 Button clicked: ${e.name}`,"info"),t.style.transform="scale(0.9)",setTimeout(()=>{t.style.transform="scale(1)"},100),e.tags.has("catalog_podium")){const t=e.name.match(/Podium_(\d+)/),i=t?parseInt(t[1]):0;this.debugOverlay.log(`Loading scene ${i}`,"info"),this.app.core&&this.app.core.loadContent&&this.app.core.loadContent(i)}else if(e.tags.has("npc")){this.debugOverlay.log(`Interacting with NPC: ${e.name}`,"info");const t=e.npcData?e.npcData.id:e.name;this.app.fire("npc:interact",t)}else e.tags.has("interactive")&&(this.debugOverlay.log(`Interacting with item: ${e.name}`,"info"),this.app.fire("interaction:triggered",{entity:e,interactionType:"use"}))}removeAllButtons(){this.debugOverlay.log("Removing all simple mobile buttons","info"),this.buttons.forEach(({button:e,position:t})=>{e.parentNode&&e.parentNode.removeChild(e),t.occupied=!1}),this.buttons.clear(),this.debugOverlay.log("All buttons removed","info")}createButtonsForScene(){this.debugOverlay.log("Creating simple buttons for scene...","info"),this.removeAllButtons();const e=this.findInteractableEntities();this.debugOverlay.log(`Found ${e.length} interactable entities`,"info");let t=0;e.forEach((e,i)=>{this.createButtonForEntity(e)&&t++}),this.debugOverlay.log(`Created ${t}/${e.length} buttons in grid`,"info"),this.updateDebugInfo()}createButtonsForEntities(e){this.debugOverlay.log(`Creating buttons for ${e.length} specific entities...`,"info"),this.removeAllButtons();let t=0;e.forEach((e,i)=>{this.createButtonForEntity(e)&&t++}),this.debugOverlay.log(`Created ${t}/${e.length} entity buttons in grid`,"info"),this.updateDebugInfo()}findInteractableEntities(){const e=[];if(!this.app.root)return this.debugOverlay.log("No app root found","error"),e;const t=this.app.root.findByTag("catalog_podium");this.debugOverlay.log(`Found ${t.length} podiums`,"debug"),e.push(...t);const i=this.app.root.findByTag("npc");this.debugOverlay.log(`Found ${i.length} NPCs`,"debug"),e.push(...i);const o=this.app.root.findByTag("interactive");return this.debugOverlay.log(`Found ${o.length} interactive items`,"debug"),e.push(...o),e}updateDebugInfo(){const e=[];this.buttons.forEach(({button:t})=>{e.push(t)}),this.debugOverlay.updateButtonInfo(e)}handleResize(){this.debugOverlay.log("Screen resized, regenerating grid...","info"),this.removeAllButtons(),this.generateGridPositions(),setTimeout(()=>{this.createButtonsForScene()},100)}destroy(){this.removeAllButtons(),this.buttonContainer&&this.buttonContainer.parentNode&&this.buttonContainer.parentNode.removeChild(this.buttonContainer),this.debugOverlay.log("SimpleMobileButtons destroyed","info")}}class T{constructor(e,t){this.app=e,this.debugOverlay=t,this.menuContainer=null,this.menuButton=null,this.isMenuOpen=!1,this.touchCoordinateDisplay=null,this.sceneButtons=new Map,this.itemButtons=new Map,this.currentScene=null,this.initialize()}initialize(){this.createMenuContainer(),this.createMenuButton(),this.createTouchCoordinateDisplay(),this.setupEventListeners(),this.debugOverlay.log("MobileMenuSystem initialized","info")}createMenuContainer(){this.menuContainer=document.createElement("div"),this.menuContainer.id="mobile-menu-container",this.menuContainer.style.cssText="\n position: fixed;\n top: 60px;\n right: -300px;\n width: 280px;\n height: calc(100vh - 120px);\n background: rgba(0, 0, 0, 0.95);\n border: 2px solid #333;\n border-radius: 8px 0 0 8px;\n z-index: 3000;\n transition: right 0.3s ease;\n overflow-y: auto;\n padding: 10px;\n box-shadow: -4px 0 8px rgba(0,0,0,0.3);\n ",document.body.appendChild(this.menuContainer)}createMenuButton(){this.menuButton=document.createElement("button"),this.menuButton.textContent="☰",this.menuButton.style.cssText="\n position: fixed;\n top: 70px;\n right: 10px;\n width: 50px;\n height: 50px;\n background: #4CAF50;\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 24px;\n cursor: pointer;\n z-index: 3001;\n box-shadow: 0 4px 8px rgba(0,0,0,0.3);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n ",this.menuButton.onclick=()=>this.toggleMenu(),document.body.appendChild(this.menuButton)}createTouchCoordinateDisplay(){this.touchCoordinateDisplay=document.createElement("div"),this.touchCoordinateDisplay.style.cssText="\n position: fixed;\n top: 130px;\n left: 10px;\n background: rgba(0, 0, 0, 0.8);\n color: #00ff00;\n padding: 8px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 12px;\n z-index: 3001;\n pointer-events: none;\n max-width: 200px;\n ",this.touchCoordinateDisplay.textContent="Touch: (0, 0)",document.body.appendChild(this.touchCoordinateDisplay)}setupEventListeners(){document.addEventListener("touchstart",e=>{if(e.touches.length>0){const t=e.touches[0];this.updateTouchCoordinates(t.clientX,t.clientY),this.debugOverlay.log(`Touch at: (${t.clientX}, ${t.clientY})`,"positioning")}}),document.addEventListener("click",e=>{this.updateTouchCoordinates(e.clientX,e.clientY),this.debugOverlay.log(`Click at: (${e.clientX}, ${e.clientY})`,"positioning")}),this.app.on("content:loaded",this.onSceneChanged,this),this.app.on("content:unloaded",this.onSceneUnloaded,this)}updateTouchCoordinates(e,t){this.touchCoordinateDisplay.textContent=`Touch: (${Math.round(e)}, ${Math.round(t)})`,this.lastTouchCoords={x:Math.round(e),y:Math.round(t)}}toggleMenu(){this.isMenuOpen=!this.isMenuOpen,this.isMenuOpen?(this.menuContainer.style.right="0px",this.menuButton.textContent="✕",this.menuButton.style.background="#f44336",this.refreshMenuContent()):(this.menuContainer.style.right="-300px",this.menuButton.textContent="☰",this.menuButton.style.background="#4CAF50"),this.debugOverlay.log("Menu "+(this.isMenuOpen?"opened":"closed"),"info")}refreshMenuContent(){this.menuContainer.innerHTML="";const e=document.createElement("div");e.innerHTML='

Mobile Menu

',this.menuContainer.appendChild(e),this.createSceneSection(),this.createSceneItemsSection(),this.createCoordinateSection(),this.createSystemSection()}createSceneSection(){const e=document.createElement("div");e.innerHTML='\n

📋 Scene Selection

\n ';const t=this.findSceneEntities();0===t.length?e.innerHTML+='

No scenes found

':t.forEach((t,i)=>{const o=document.createElement("button"),n=t.name.match(/Podium_(\\d+)/),s=n?parseInt(n[1]):i;o.textContent=`Scene ${s}`,o.style.cssText="\n display: block;\n width: 100%;\n margin: 5px 0;\n padding: 10px;\n background: #4CAF50;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n ",o.onclick=()=>{this.debugOverlay.log(`Loading scene ${s}`,"info"),this.app.core&&this.app.core.loadContent&&(this.app.core.loadContent(s),this.toggleMenu())},e.appendChild(o)}),this.menuContainer.appendChild(e)}createSceneItemsSection(){const e=document.createElement("div");e.innerHTML='\n

🎯 Scene Items

\n ';const t=this.findNPCs(),i=this.findInteractiveItems();0===t.length&&0===i.length?e.innerHTML+='

No items in current scene

':(t.forEach(t=>{const i=this.createItemButton(t,"NPC","#2196F3","💬");e.appendChild(i)}),i.forEach(t=>{const i=this.createItemButton(t,"Item","#FF9800","⚡");e.appendChild(i)})),this.menuContainer.appendChild(e)}createItemButton(e,t,i,o){const n=document.createElement("button");return n.textContent=`${o} ${e.name}`,n.style.cssText=`\n display: block;\n width: 100%;\n margin: 3px 0;\n padding: 8px;\n background: ${i};\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 12px;\n text-align: left;\n `,n.onclick=()=>{if(this.debugOverlay.log(`Interacting with ${t}: ${e.name}`,"info"),e.tags.has("npc")){const t=e.npcData?e.npcData.id:e.name;this.app.fire("npc:interact",t)}else e.tags.has("interactive")&&this.app.fire("interaction:triggered",{entity:e,interactionType:"use"});this.toggleMenu()},n}createCoordinateSection(){const e=document.createElement("div");e.innerHTML='\n

📍 Touch Helper

\n

Tap anywhere to see coordinates for hardcoded positioning:

\n
Last touch: None
\n ';const t=document.createElement("button");t.textContent="Copy Last Coords",t.style.cssText="\n width: 100%;\n padding: 5px;\n background: #666;\n color: white;\n border: none;\n border-radius: 3px;\n font-size: 11px;\n cursor: pointer;\n ",t.onclick=()=>{if(this.lastTouchCoords){const e=`{ x: ${this.lastTouchCoords.x}, y: ${this.lastTouchCoords.y} }`;navigator.clipboard?.writeText(e),this.debugOverlay.log(`Copied coordinates: ${e}`,"info")}},e.appendChild(t),this.menuContainer.appendChild(e),setInterval(()=>{const e=document.getElementById("coordinate-display");e&&this.lastTouchCoords&&(e.textContent=`Last: (${this.lastTouchCoords.x}, ${this.lastTouchCoords.y})`)},100)}createSystemSection(){const e=document.createElement("div");e.innerHTML='\n

⚙️ System

\n ';const t=document.createElement("button");t.textContent="Toggle Button System",t.style.cssText="\n width: 100%;\n padding: 8px;\n background: #9C27B0;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 12px;\n margin: 5px 0;\n ",t.onclick=()=>{this.app.fire("mobile:toggleSystem"),this.debugOverlay.log("Toggled button system","info")},e.appendChild(t),this.menuContainer.appendChild(e)}findSceneEntities(){return this.app.root&&this.app.root.findByTag("catalog_podium")||[]}findNPCs(){return this.app.root&&this.app.root.findByTag("npc")||[]}findInteractiveItems(){return this.app.root&&this.app.root.findByTag("interactive")||[]}onSceneChanged(){this.debugOverlay.log("Scene changed - updating menu content","info"),this.isMenuOpen&&setTimeout(()=>this.refreshMenuContent(),500)}onSceneUnloaded(){this.debugOverlay.log("Scene unloaded","info")}destroy(){this.menuContainer&&this.menuContainer.parentNode&&this.menuContainer.parentNode.removeChild(this.menuContainer),this.menuButton&&this.menuButton.parentNode&&this.menuButton.parentNode.removeChild(this.menuButton),this.touchCoordinateDisplay&&this.touchCoordinateDisplay.parentNode&&this.touchCoordinateDisplay.parentNode.removeChild(this.touchCoordinateDisplay),this.app.off("content:loaded",this.onSceneChanged,this),this.app.off("content:unloaded",this.onSceneUnloaded,this),this.debugOverlay.log("MobileMenuSystem destroyed","info")}}class M{constructor(e){this.app=e,this.buttons=new Map,this.isActive=!1,this.updateInterval=null,this.platformInfo=u.detect(),this.isMobile=this.platformInfo.isMobile||this.platformInfo.isTablet,this.debugOverlay=new S(e),this.menuSystem=new T(e,this.debugOverlay),this.simpleButtons=new E(e,this.debugOverlay),this.coordinateSystem=new w(e),this.coordinateSystem.setDebugOverlay(this.debugOverlay),this.useMenuSystem=!0,this.useSimpleSystem=!0,this.isMobile?(console.log("[MobileInteractionOverlay] Mobile/tablet detected - enabling menu + grid system"),this.debugOverlay.log(`${this.platformInfo.deviceType} detected - using menu system`,"info"),this.debugOverlay.log(`Platform details: ${JSON.stringify(this.platformInfo.debug)}`,"debug"),this.initialize()):(console.log("[MobileInteractionOverlay] Desktop device - using raycast system"),this.debugOverlay.log("Desktop device - using raycast system","info")),this.app.on("camera:angleChanged",this.onCameraChanged,this),this.app.on("mobile:toggleSystem",this.toggleSystem,this),this.createTestButton()}detectMobile(){return/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||navigator.maxTouchPoints>0||screen.width<=768}initialize(){this.createButtonStyles(),this.startEntityMonitoring(),this.tryInitialButtonCreation(),this.isActive=!0,console.log("[MobileInteractionOverlay] Mobile interaction overlay initialized")}createButtonStyles(){const e=document.createElement("style");e.textContent="\n .mobile-interaction-btn {\n position: fixed;\n z-index: 2000;\n border: none;\n border-radius: 8px;\n color: white;\n font-family: Arial, sans-serif;\n font-size: 14px;\n font-weight: bold;\n cursor: pointer;\n transition: all 0.3s ease;\n box-shadow: 0 4px 8px rgba(0,0,0,0.3);\n pointer-events: auto;\n text-align: center;\n line-height: 1.2;\n transform-origin: center;\n }\n \n .mobile-interaction-btn:active {\n transform: scale(0.9) !important;\n box-shadow: 0 2px 4px rgba(0,0,0,0.3);\n }\n \n /* Confidence-based visual indicators */\n .mobile-interaction-btn.confidence-high {\n box-shadow: 0 4px 12px rgba(0,255,0,0.2), 0 4px 8px rgba(0,0,0,0.3);\n border: 2px solid rgba(255,255,255,0.4);\n }\n \n .mobile-interaction-btn.confidence-medium {\n box-shadow: 0 3px 8px rgba(255,255,0,0.15), 0 3px 6px rgba(0,0,0,0.3);\n border: 2px solid rgba(255,255,255,0.3);\n }\n \n .mobile-interaction-btn.confidence-low {\n box-shadow: 0 2px 6px rgba(255,100,0,0.1), 0 2px 4px rgba(0,0,0,0.3);\n border: 2px solid rgba(255,255,255,0.2);\n filter: brightness(0.8);\n }\n \n .mobile-podium-btn {\n background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);\n padding: 8px 12px;\n min-width: 80px;\n border: 2px solid rgba(255,255,255,0.3);\n }\n \n .mobile-npc-btn {\n background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);\n padding: 10px;\n width: 50px;\n height: 50px;\n border-radius: 50%;\n font-size: 20px;\n border: 2px solid rgba(255,255,255,0.3);\n }\n \n .mobile-item-btn {\n background: linear-gradient(135deg, #FF9800 0%, #F57C00 100%);\n padding: 6px 10px;\n min-width: 60px;\n font-size: 12px;\n border: 2px solid rgba(255,255,255,0.3);\n }\n \n /* Enhanced hover effects for better interaction feedback */\n .mobile-interaction-btn:hover {\n transform: scale(1.05) !important;\n box-shadow: 0 6px 12px rgba(0,0,0,0.4) !important;\n }\n ",document.head.appendChild(e)}startEntityMonitoring(){this.updateInterval=setInterval(()=>{this.updateButtonPositions()},100),this.app.on("content:loaded",this.onSceneLoaded,this),this.app.on("content:unloaded",this.onSceneUnloaded,this),this.app.on("catalog:loaded",this.onSceneLoaded,this),this.startPeriodicCheck()}onSceneLoaded(){console.log("[MobileInteractionOverlay] Scene loaded event received"),this.initializeCoordinateSystem(),setTimeout(()=>{this.createButtonsForScene()},500),this.createButtonsForScene()}onCameraChanged(e){if(console.log("[MobileInteractionOverlay] Camera changed:",e),this.debugOverlay.log(`Camera changed: ${e.angle?e.angle.name:"unknown"}`,"info"),e.angle){this.coordinateSystem.updateCameraConfig({position:e.angle.position,rotation:e.angle.rotation,height:e.angle.position.y,angle:e.angle.rotation.x,name:e.angle.name});const t=e.angle.position,i=e.angle.rotation;this.debugOverlay.log(`Cam pos: (${t.x.toFixed(1)}, ${t.y.toFixed(1)}, ${t.z.toFixed(1)})`,"debug"),this.debugOverlay.log(`Cam rot: (${i.x.toFixed(1)}, ${i.y.toFixed(1)}, ${i.z.toFixed(1)})`,"debug")}this.updateButtonPositions(),this.updateDebugButtonInfo()}toggleSystem(){this.useSimpleSystem=!this.useSimpleSystem;const e=this.useSimpleSystem?"Simple Grid":"Complex Coordinate";this.debugOverlay.log(`Switched to ${e} system`,"info"),this.removeAllButtons(),setTimeout(()=>{this.createButtonsForScene()},100)}initializeCoordinateSystem(){let e=null;if(this.app.platformCamera&&this.app.platformCamera.currentController){const t=this.app.platformCamera.currentController;t.currentAngle&&(e={position:t.currentAngle.position,rotation:t.currentAngle.rotation,height:t.currentAngle.position.y,angle:t.currentAngle.rotation.x,name:t.currentAngle.name})}this.coordinateSystem.initialize(e,null),console.log("[MobileInteractionOverlay] Coordinate system initialized")}onSceneUnloaded(){this.removeAllButtons()}createButtonsForScene(){if(console.log("[MobileInteractionOverlay] Creating buttons for new scene..."),this.debugOverlay.log("Creating buttons for new scene...","info"),this.useMenuSystem&&this.menuSystem){this.debugOverlay.log("Using mobile menu system (scenes via menu, items via grid)","info");const e=this.findInteractableEntities().filter(e=>!e.tags.has("catalog_podium"));return void(e.length>0&&this.simpleButtons&&(this.debugOverlay.log(`Creating grid buttons for ${e.length} scene items`,"info"),this.simpleButtons.createButtonsForEntities(e)))}if(this.useSimpleSystem&&this.simpleButtons)return this.debugOverlay.log("Using simple grid button system (all items)","info"),void this.simpleButtons.createButtonsForScene();this.debugOverlay.log("Using complex coordinate system (fallback)","warn"),console.log("[MobileInteractionOverlay] Mobile check:",this.isMobile),console.log("[MobileInteractionOverlay] App root available:",!!this.app.root),this.debugOverlay.log(`Mobile check: ${this.isMobile}`,"debug");const e=this.findInteractableEntities();console.log("[MobileInteractionOverlay] Found interactables:",e.length),this.debugOverlay.log(`Found ${e.length} interactable entities`,"info"),e.forEach((t,i)=>{console.log(`[MobileInteractionOverlay] Creating button ${i} for entity:`,t.name),this.debugOverlay.log(`Creating button ${i+1}/${e.length}: ${t.name}`,"debug"),this.createButtonForEntity(t)}),console.log(`[MobileInteractionOverlay] Created ${e.length} interaction buttons`),this.debugOverlay.log(`Created ${e.length} interaction buttons`,"info"),this.updateDebugButtonInfo()}findInteractableEntities(){const e=[],t=this.app.root.findByTag("catalog_podium");console.log("[MobileInteractionOverlay] Found podiums:",t.length),e.push(...t);const i=this.app.root.findByTag("npc");console.log("[MobileInteractionOverlay] Found NPCs:",i.length),e.push(...i);const o=this.app.root.findByTag("interactive");return console.log("[MobileInteractionOverlay] Found interactive items:",o.length),e.push(...o),console.log("[MobileInteractionOverlay] Total interactables found:",e.length),e}createButtonForEntity(e){if(this.buttons.has(e.getGuid()))return;const t=document.createElement("button"),i=e.getGuid(),o=e.getPosition();this.debugOverlay.log(`🎯 Creating button for ${e.name}`,"info"),this.debugOverlay.log(` World pos: (${o.x.toFixed(1)}, ${o.y.toFixed(1)}, ${o.z.toFixed(1)})`,"debug"),this.debugOverlay.log(` Tags: ${Array.from(e.tags.list()).join(", ")}`,"debug"),e.tags.has("catalog_podium")?this.setupPodiumButton(t,e):e.tags.has("npc")?this.setupNPCButton(t,e):e.tags.has("interactive")&&this.setupItemButton(t,e),this.positionButtonOverEntity(t,e);const n=this.buttons.size;this.buttons.set(i,{button:t,entity:e,index:n}),document.body.appendChild(t)}setupPodiumButton(e,t){e.className="mobile-interaction-btn mobile-podium-btn";const i=t.name.match(/Podium_(\\d+)/),o=i?parseInt(i[1]):0;e.textContent=`Scene ${o}`,e.onclick=()=>{console.log(`[MobileInteractionOverlay] Loading scene ${o}`),this.app.core&&this.app.core.loadContent&&this.app.core.loadContent(o)}}setupNPCButton(e,t){e.className="mobile-interaction-btn mobile-npc-btn",e.textContent="💬",e.onclick=()=>{console.log(`[MobileInteractionOverlay] Interacting with NPC: ${t.name}`);const e=t.npcData?t.npcData.id:t.name;this.app.fire("npc:interact",e)}}setupItemButton(e,t){e.className="mobile-interaction-btn mobile-item-btn",e.textContent="⚡",e.onclick=()=>{console.log(`[MobileInteractionOverlay] Interacting with item: ${t.name}`),this.app.fire("interaction:triggered",{entity:t,interactionType:"use"})}}positionButtonOverEntity(e,t){if(t&&t.getPosition)try{let i=null,o="none";if(this.app.platformCamera&&this.app.platformCamera.currentController&&this.app.platformCamera.currentController.camera)i=this.app.platformCamera.currentController.camera,o="platformCamera";else if(this.app.cameraController&&this.app.cameraController.camera)i=this.app.cameraController.camera,o="cameraController";else if(this.app.systems.camera&&this.app.systems.camera.cameras&&this.app.systems.camera.cameras.length>0)i=this.app.systems.camera.cameras[0],o="cameraSystem";else{const e=this.app.root.findByTag("camera");e.length>0&&(i=e[0],o="rootSearch")}if(!i)return console.warn("[MobileInteractionOverlay] No camera found for positioning"),void(e.style.display="none");console.log(`[MobileInteractionOverlay] Using camera from: ${o} for entity: ${t.name}`);const n=this.coordinateSystem.calculateButtonPosition(t,i);if(!n.valid){console.log(`[MobileInteractionOverlay] Entity ${t.name} positioning invalid: ${n.reason}`),this.debugOverlay.log(`❌ ${t.name}: ${n.reason}`,"error");const o=i.camera||i;if(o&&o.worldToScreen){const i=t.getPosition(),n=o.worldToScreen(i);if(n.z>0){const o={screenPos:n,worldPos:i,valid:!0,confidence:.3,adjustments:{fallback:!0}};return e.style.display="block",e.style.left=o.screenPos.x-40+"px",e.style.top=o.screenPos.y-20+"px",this.applyConfidenceVisuals(e,o.confidence),this.debugOverlay.showPositioningInfo(t.name+" (fallback)",o),void this.debugOverlay.log(`⚠️ ${t.name}: Using fallback positioning`,"warn")}}return void(e.style.display="none")}e.style.display="block";const s=80,a=40;let r=0;this.buttons.forEach((e,i)=>{e.entity===t&&(r=e.index||0)});let l=n.screenPos.x-s/2,c=n.screenPos.y-a/2;const h=[];this.buttons.forEach(({button:t})=>{if(t!==e&&"none"!==t.style.display){const e=t.getBoundingClientRect();h.push({x:e.left,y:e.top})}});let d=!1;for(const e of h)if(Math.abs(e.x-l)<50&&Math.abs(e.y-c)<30){c+=50*(r+1),l+=20*r,d=!0;break}e.style.left=l+"px",e.style.top=c+"px",d&&this.debugOverlay.log(`📍 ${t.name}: Anti-stack positioning applied`,"warn"),this.applyConfidenceVisuals(e,n.confidence),this.debugOverlay.showPositioningInfo(t.name,n),console.log(`[MobileInteractionOverlay] Positioned ${t.name} button at screen: ${n.screenPos.x.toFixed(1)}, ${n.screenPos.y.toFixed(1)} (confidence: ${n.confidence.toFixed(2)})`)}catch(i){console.error("[MobileInteractionOverlay] Error positioning button:",i),this.debugOverlay.log(`Error positioning ${t.name}: ${i.message}`,"error"),e.style.display="none"}else console.warn("[MobileInteractionOverlay] Invalid entity for positioning")}updateButtonPositions(){this.isMobile&&0!==this.buttons.size&&this.buttons.forEach(({button:e,entity:t})=>{t.enabled?this.positionButtonOverEntityQuiet(e,t):e.style.display="none"})}positionButtonOverEntityQuiet(e,t){if(t&&t.getPosition)try{let i=null;if(this.app.platformCamera&&this.app.platformCamera.currentController&&this.app.platformCamera.currentController.camera?i=this.app.platformCamera.currentController.camera:this.app.cameraController&&this.app.cameraController.camera?i=this.app.cameraController.camera:this.app.systems.camera&&this.app.systems.camera.cameras&&this.app.systems.camera.cameras.length>0&&(i=this.app.systems.camera.cameras[0]),!i)return;const o=this.coordinateSystem.calculateButtonPosition(t,i);if(!o.valid)return void(e.style.display="none");e.style.display="block";const n=80,s=40;e.style.left=o.screenPos.x-n/2+"px",e.style.top=o.screenPos.y-s/2+"px",this.applyConfidenceVisuals(e,o.confidence)}catch(t){e.style.display="none"}}applyConfidenceVisuals(e,t){t>=.8?(e.style.opacity="1.0",e.style.transform="scale(1.0)"):t>=.6?(e.style.opacity="0.85",e.style.transform="scale(0.95)"):t>=.4?(e.style.opacity="0.7",e.style.transform="scale(0.9)"):(e.style.opacity="0.5",e.style.transform="scale(0.85)"),e.className=e.className.replace(/\s*confidence-\w+/g,""),e.className+=t>=.8?" confidence-high":t>=.6?" confidence-medium":" confidence-low"}updateDebugButtonInfo(){if(this.useSimpleSystem&&this.simpleButtons)return;const e=[];this.buttons.forEach(({button:t})=>{e.push(t)}),this.debugOverlay.updateButtonInfo(e)}removeAllButtons(){this.useSimpleSystem&&this.simpleButtons?this.simpleButtons.removeAllButtons():(this.buttons.forEach(({button:e})=>{e.parentNode&&e.parentNode.removeChild(e)}),this.buttons.clear(),console.log("[MobileInteractionOverlay] Removed all interaction buttons"),this.updateDebugButtonInfo())}startPeriodicCheck(){this.entityCheckInterval=setInterval(()=>{0===this.buttons.size&&this.findInteractableEntities().length>0&&(console.log("[MobileInteractionOverlay] Periodic check found entities, creating buttons"),this.createButtonsForScene())},2e3)}tryInitialButtonCreation(){console.log("[MobileInteractionOverlay] Trying initial button creation..."),this.createButtonsForScene(),setTimeout(()=>this.createButtonsForScene(),100),setTimeout(()=>this.createButtonsForScene(),500),setTimeout(()=>this.createButtonsForScene(),1e3),setTimeout(()=>this.createButtonsForScene(),2e3)}destroy(){this.updateInterval&&(clearInterval(this.updateInterval),this.updateInterval=null),this.entityCheckInterval&&(clearInterval(this.entityCheckInterval),this.entityCheckInterval=null),this.removeAllButtons(),this.app.off("content:loaded",this.onSceneLoaded,this),this.app.off("content:unloaded",this.onSceneUnloaded,this),this.app.off("catalog:loaded",this.onSceneLoaded,this),this.app.off("camera:angleChanged",this.onCameraChanged,this),this.debugOverlay&&this.debugOverlay.destroy(),this.isActive=!1,console.log("[MobileInteractionOverlay] Mobile interaction overlay destroyed")}createTestButton(){const e=document.createElement("button");e.textContent="Mobile Overlay Test",e.style.cssText="\n position: fixed;\n top: 10px;\n left: 10px;\n background: #ff0000;\n color: white;\n border: none;\n padding: 10px;\n border-radius: 5px;\n z-index: 99999;\n font-size: 14px;\n ",e.onclick=()=>{console.log("[MobileInteractionOverlay] Test button clicked"),console.log("Mobile detected:",this.isMobile),console.log("System active:",this.isActive),console.log("Buttons count:",this.buttons.size);const e=this.coordinateSystem.getPositioningStats();console.log("Coordinate System Stats:",e),this.debugOverlay.log("Test button clicked!","info"),this.debugOverlay.log(`Mobile: ${this.isMobile}, Active: ${this.isActive}`,"debug"),this.debugOverlay.log(`Buttons: ${this.buttons.size}`,"debug"),this.debugOverlay.showSystemStats(),this.createButtonsForScene()},document.body.appendChild(e),console.log("[MobileInteractionOverlay] Test button created")}}class I{constructor(e){this.app=e,this.transitionScreen=null,this.isTransitioning=!1,this.transitionType="fade",this.transitionDuration=1e3,this.initialize()}initialize(){this.createTransitionScreen(),this.setupEventListeners(),console.log("[SceneTransition] Manager initialized")}createTransitionScreen(){this.transitionScreen=new pc.Entity("TransitionScreen"),this.transitionScreen.addComponent("screen",{referenceResolution:new pc.Vec2(1280,720),scaleMode:pc.SCALEMODE_BLEND,scaleBlend:.5,screenSpace:!0}),this.transitionScreen.screen.priority=1e3;const e=new pc.Entity("TransitionOverlay");e.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.5,.5],color:new pc.Color(0,0,0,0),useInput:!0}),this.transitionScreen.addChild(e),this.overlay=e;const t=new pc.Entity("LoadingSpinner");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[.5,.5,.5,.5],pivot:[.5,.5],width:64,height:64,color:new pc.Color(1,1,1,0),opacity:0}),this.createSpinnerTexture(t),e.addChild(t),this.spinner=t;const i=new pc.Entity("LoadingText");i.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,text:"Loading...",fontSize:24,color:new pc.Color(1,1,1,0),fontAsset:this.app.fontManager?this.app.fontManager.getFontAssetId():null,anchor:[.5,.4,.5,.4],pivot:[.5,.5]}),e.addChild(i),this.loadingText=i;const o=new pc.Entity("ProgressContainer");o.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[.5,.3,.5,.3],pivot:[.5,.5],width:300,height:20}),e.addChild(o);const n=new pc.Entity("ProgressBg");n.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.5,.5],color:new pc.Color(.2,.2,.2,.8)}),o.addChild(n);const s=new pc.Entity("ProgressFill");s.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,0,1],pivot:[0,.5],width:0,color:new pc.Color(.2,.8,.3)}),o.addChild(s),this.progressFill=s,this.progressContainer=o,this.transitionScreen.enabled=!1,this.app.root.addChild(this.transitionScreen)}createSpinnerTexture(e){const t=document.createElement("canvas");t.width=64,t.height=64;const i=t.getContext("2d");i.strokeStyle="white",i.lineWidth=4,i.lineCap="round",i.beginPath(),i.arc(32,32,24,0,1.5*Math.PI),i.stroke();const o=new pc.Texture(this.app.graphicsDevice,{width:64,height:64,format:pc.PIXELFORMAT_R8_G8_B8_A8});o.setSource(t),e.element.texture=o}setupEventListeners(){this.app.on("content:loading",this.onContentLoading,this),this.app.on("content:loaded",this.onContentLoaded,this),this.app.on("content:unloading",this.onContentUnloading,this),this.app.on("content:loadError",this.onContentLoadError,this),this.app.on("transition:start",this.startTransition,this),this.app.on("transition:end",this.endTransition,this)}async onContentLoading(e){console.log(`[SceneTransition] Starting transition for content ${e}`),await this.startTransition({type:this.transitionType,message:`Loading content ${e}...`,showProgress:!0})}async onContentLoaded(e,t){console.log(`[SceneTransition] Content loaded: ${t?.name||e}`),await this.endTransition()}async onContentUnloading(){console.log("[SceneTransition] Content unloading"),await this.startTransition({type:"fade",message:"Switching scenes...",showProgress:!1,duration:500})}async onContentLoadError(e){console.error("[SceneTransition] Content load error:",e),this.showError(`Failed to load content: ${e.message}`),setTimeout(()=>this.endTransition(),3e3)}async startTransition(e={}){if(this.isTransitioning)return void console.warn("[SceneTransition] Already transitioning");this.isTransitioning=!0;const{type:t="fade",message:i="Loading...",showProgress:o=!1,duration:n=this.transitionDuration}=e;switch(this.transitionScreen.enabled=!0,this.loadingText.element.text=i,this.progressContainer.enabled=o,o&&(this.progressFill.element.width=0),t){case"fade":await this.fadeTransition(!0,n/2);break;case"slide":await this.slideTransition(!0,n/2);break;case"circle":await this.circleTransition(!0,n/2)}this.startSpinnerAnimation(),console.log(`[SceneTransition] Transition started (${t})`)}async endTransition(){this.isTransitioning&&(console.log("[SceneTransition] Ending transition"),this.stopSpinnerAnimation(),this.progressContainer.enabled&&await this.animateProgress(100),await this.fadeTransition(!1,this.transitionDuration/2),this.transitionScreen.enabled=!1,this.isTransitioning=!1)}async fadeTransition(e,t){return new Promise(i=>{const o=e?0:1,n=e?1:0,s=Date.now(),a=()=>{const e=Date.now()-s,r=Math.min(e/t,1),l=o+(n-o)*this.easeInOut(r);this.overlay.element.color.a=l,this.loadingText.element.color.a=l,this.spinner.element.opacity=l,r<1?requestAnimationFrame(a):i()};a()})}async slideTransition(e,t){return new Promise(i=>{const o=e?-720:0,n=e?0:-720,s=Date.now();this.overlay.element.color.a=1,this.loadingText.element.color.a=1,this.spinner.element.opacity=1;const a=()=>{const e=Date.now()-s,r=Math.min(e/t,1),l=o+(n-o)*this.easeInOut(r);this.overlay.setLocalPosition(0,l,0),r<1?requestAnimationFrame(a):(this.overlay.setLocalPosition(0,0,0),i())};a()})}async circleTransition(e,t){return new Promise(i=>{this.fadeTransition(e,t).then(i)})}startSpinnerAnimation(){let e=0;const t=()=>{this.isTransitioning&&(e+=5,this.spinner.setLocalEulerAngles(0,0,e),requestAnimationFrame(t))};t()}stopSpinnerAnimation(){}async animateProgress(e){return new Promise(t=>{const i=this.progressFill.element.width,o=e/100*300,n=Date.now(),s=()=>{const e=Date.now()-n,a=Math.min(e/500,1),r=i+(o-i)*this.easeInOut(a);this.progressFill.element.width=r,a<1?requestAnimationFrame(s):t()};s()})}updateProgress(e){this.progressContainer.enabled&&this.animateProgress(e)}showError(e){this.loadingText.element.text=e,this.loadingText.element.color=new pc.Color(1,.3,.3,1),this.spinner.enabled=!1,this.progressContainer.enabled=!1}easeInOut(e){return e<.5?2*e*e:(4-2*e)*e-1}setTransitionType(e){this.transitionType=e}setTransitionDuration(e){this.transitionDuration=e}destroy(){this.app.off("content:loading",this.onContentLoading,this),this.app.off("content:loaded",this.onContentLoaded,this),this.app.off("content:unloading",this.onContentUnloading,this),this.app.off("content:loadError",this.onContentLoadError,this),this.app.off("transition:start",this.startTransition,this),this.app.off("transition:end",this.endTransition,this),this.transitionScreen&&this.transitionScreen.destroy()}}class k{constructor(e){this.app=e,this.sceneStates=new Map,this.loadedScenes=new Map,this.preloadedScenes=new Set,this.maxPreloadedScenes=3,this.cleanupQueue=[],this.memoryStats={totalEntities:0,totalTextures:0,totalMaterials:0,totalMeshes:0},this.initialize()}initialize(){this.setupEventListeners(),console.log("[SceneMemory] Memory manager initialized")}setupEventListeners(){this.app.on("content:loading",this.onContentLoading,this),this.app.on("content:loaded",this.onContentLoaded,this),this.app.on("content:unloading",this.onContentUnloading,this),this.app.on("scene:saveState",this.saveSceneState,this),this.app.on("scene:restoreState",this.restoreSceneState,this)}onContentLoading(e){console.log(`[SceneMemory] Preparing to load content ${e}`),this.app.core&&this.app.core.state.catalogIndex>=0&&this.saveCurrentSceneState(),this.checkMemoryUsage(),this.cleanupExcessScenes()}onContentLoaded(e,t){console.log(`[SceneMemory] Content ${e} loaded into memory`),this.loadedScenes.set(e,{content:t,loadTime:Date.now(),entityCount:this.countSceneEntities(e),memoryUsage:this.estimateMemoryUsage(e)}),this.restoreSceneState(e),this.updateMemoryStats()}onContentUnloading(){console.log("[SceneMemory] Content unloading, saving state"),this.saveCurrentSceneState()}saveCurrentSceneState(){if(!this.app.core||this.app.core.state.catalogIndex<0)return;const e=this.app.core.state.catalogIndex;console.log(`[SceneMemory] Saving state for scene ${e}`);const t={catalogIndex:e,timestamp:Date.now(),playerState:this.capturePlayerState(),entityStates:this.captureEntityStates(e),systemStates:this.captureSystemStates(),objectives:this.captureObjectiveStates(),inventory:this.captureInventoryState()};this.sceneStates.set(e,t),this.app.fire("scene:stateSaved",e,t)}saveSceneState(e,t={}){if(void 0===e)return void this.saveCurrentSceneState();const i={...this.sceneStates.get(e)||{},...t,timestamp:Date.now()};this.sceneStates.set(e,i)}restoreSceneState(e){const t=this.sceneStates.get(e);t?(console.log(`[SceneMemory] Restoring state for scene ${e}`),this.restorePlayerState(t.playerState),this.restoreEntityStates(t.entityStates),this.restoreSystemStates(t.systemStates),this.restoreObjectiveStates(t.objectives),this.restoreInventoryState(t.inventory),this.app.fire("scene:stateRestored",e,t)):console.log(`[SceneMemory] No saved state for scene ${e}`)}capturePlayerState(){const e=this.app.root.findByTag("player"),t=e&&e.length>0?e[0]:null;return t?{position:t.getPosition().clone(),rotation:t.getRotation().clone(),velocity:t.rigidbody?t.rigidbody.linearVelocity.clone():new pc.Vec3,health:t.health||100}:null}restorePlayerState(e){if(!e)return;const t=this.app.root.findByTag("player"),i=t&&t.length>0?t[0]:null;i&&(i.setPosition(e.position),i.setRotation(e.rotation),i.rigidbody&&(i.rigidbody.linearVelocity.copy(e.velocity),i.rigidbody.angularVelocity.set(0,0,0)),void 0!==e.health&&this.app.fire("player:healthChanged",{current:e.health,max:100}))}captureEntityStates(e){const t=this.app.root.findByTag(`catalog_${e}`),i=new Map;return t.forEach(e=>{e.name&&void 0!==e.enabled&&i.set(e.name,{enabled:e.enabled,position:e.getPosition().clone(),rotation:e.getRotation().clone(),scale:e.getLocalScale().clone(),tags:Array.from(e.tags._list),componentStates:this.captureComponentStates(e)})}),i}restoreEntityStates(e){e&&e.forEach((e,t)=>{const i=this.app.root.findByName(t);i&&(i.enabled=e.enabled,i.setPosition(e.position),i.setRotation(e.rotation),i.setLocalScale(e.scale),this.restoreComponentStates(i,e.componentStates))})}captureComponentStates(e){const t={};return e.animation&&(t.animation={currentClip:e.animation.currAnim,speed:e.animation.speed,playing:e.animation.playing}),e.render&&e.render.meshInstances&&(t.materials=e.render.meshInstances.map(e=>e.material?{emissive:e.material.emissive.clone(),opacity:e.material.opacity,visible:e.visible}:null).filter(Boolean)),t}restoreComponentStates(e,t){t&&(t.animation&&e.animation&&t.animation.playing&&(e.animation.play(t.animation.currentClip),e.animation.speed=t.animation.speed),t.materials&&e.render&&e.render.meshInstances&&t.materials.forEach((t,i)=>{const o=e.render.meshInstances[i];o&&o.material&&t&&(o.material.emissive.copy(t.emissive),o.material.opacity=t.opacity,o.visible=t.visible,o.material.update())}))}captureSystemStates(){const e={};if(this.app.core&&this.app.core.systems.objectivemanager){const t=this.app.core.systems.objectivemanager;t.getActiveObjectives&&(e.objectives=t.getActiveObjectives())}if(this.app.core&&this.app.core.systems.statsmanager){const t=this.app.core.systems.statsmanager;t.getStats&&(e.stats=t.getStats())}return e}restoreSystemStates(e){e&&(e.objectives&&this.app.core&&this.app.core.systems.objectivemanager,e.stats&&this.app.core&&this.app.core.systems.statsmanager)}captureObjectiveStates(){return null}restoreObjectiveStates(e){}captureInventoryState(){return null}restoreInventoryState(e){}countSceneEntities(e){return this.app.root.findByTag(`catalog_${e}`).length}estimateMemoryUsage(e){let t=0;return this.app.root.findByTag(`catalog_${e}`).forEach(e=>{t+=1,e.model&&(t+=5),e.render&&(t+=3),e.animation&&(t+=10),e.sound&&(t+=2),e.collision&&(t+=2)}),t}checkMemoryUsage(){const e=this.getMemoryStats();return console.log("[SceneMemory] Memory stats:",e),e.totalEntities>1e3&&console.warn("[SceneMemory] High entity count detected:",e.totalEntities),e}cleanupExcessScenes(){if(this.loadedScenes.size<=this.maxPreloadedScenes)return;const e=Array.from(this.loadedScenes.entries()).sort((e,t)=>e[1].loadTime-t[1].loadTime);e.slice(0,e.length-this.maxPreloadedScenes).forEach(([e,t])=>{this.unloadScene(e)})}unloadScene(e){console.log(`[SceneMemory] Unloading scene ${e} from memory`),this.loadedScenes.delete(e),this.preloadedScenes.delete(e),this.updateMemoryStats()}preloadScene(e){this.preloadedScenes.has(e)||this.loadedScenes.has(e)?console.log(`[SceneMemory] Scene ${e} already loaded/preloaded`):(console.log(`[SceneMemory] Preloading scene ${e}`),this.preloadedScenes.add(e))}updateMemoryStats(){this.memoryStats={totalEntities:this.app.root.find(()=>!0).length,loadedScenes:this.loadedScenes.size,preloadedScenes:this.preloadedScenes.size,savedStates:this.sceneStates.size}}getMemoryStats(){return this.updateMemoryStats(),{...this.memoryStats}}clearSceneState(e){this.sceneStates.delete(e),console.log(`[SceneMemory] Cleared state for scene ${e}`)}clearAllStates(){this.sceneStates.clear(),console.log("[SceneMemory] Cleared all scene states")}destroy(){this.app.off("content:loading",this.onContentLoading,this),this.app.off("content:loaded",this.onContentLoaded,this),this.app.off("content:unloading",this.onContentUnloading,this),this.app.off("scene:saveState",this.saveSceneState,this),this.app.off("scene:restoreState",this.restoreSceneState,this),this.clearAllStates()}}class P{constructor(e,t){this.app=e,this.core=t,this.stats={health:100,maxHealth:100,stamina:100,maxStamina:100,experience:0,level:1,skillPoints:0,strength:10,agility:10,intelligence:10,charisma:10},this.modifiers=new Map,this.setupEventHandlers()}initialize(){console.log("[StatsManager] Initialized")}setupEventHandlers(){this.app.on("stats:modify",this.modifyStat,this),this.app.on("stats:addModifier",this.addModifier,this),this.app.on("stats:removeModifier",this.removeModifier,this),this.app.on("stats:levelUp",this.levelUp,this),this.app.on("stats:reset",this.reset,this)}modifyStat(e,t,i=!0){if(!(e in this.stats))return void console.warn(`[StatsManager] Unknown stat: ${e}`);const o=this.stats[e];i?this.stats[e]+=t:this.stats[e]=t,this.applyConstraints(e),o!==this.stats[e]&&(this.app.fire("stats:changed",e,o,this.stats[e]),"experience"===e&&this.checkLevelUp())}applyConstraints(e){"health"===e&&(this.stats.health=Math.max(0,Math.min(this.stats.health,this.stats.maxHealth))),"stamina"===e&&(this.stats.stamina=Math.max(0,Math.min(this.stats.stamina,this.stats.maxStamina))),["strength","agility","intelligence","charisma"].includes(e)&&(this.stats[e]=Math.max(1,this.stats[e]))}getStat(e){if(!(e in this.stats))return console.warn(`[StatsManager] Unknown stat: ${e}`),0;let t=this.stats[e];return this.modifiers.has(e)&&this.modifiers.get(e).forEach(e=>{"add"===e.type?t+=e.value:"multiply"===e.type&&(t*=e.value)}),Math.floor(t)}getStats(){return{...this.stats}}addModifier(e,t,i,o="add"){this.modifiers.has(t)||this.modifiers.set(t,new Map),this.modifiers.get(t).set(e,{value:i,type:o}),this.app.fire("stats:modifierAdded",e,t,i,o)}removeModifier(e,t){this.modifiers.has(t)&&(this.modifiers.get(t).delete(e),this.app.fire("stats:modifierRemoved",e,t))}checkLevelUp(){let e=0;for(;this.stats.experience>=this.getExperienceForLevel(this.stats.level+1)&&e<10;){const t=this.stats.level;if(this.levelUp(),e++,this.stats.level<=t){console.error("[StatsManager] Level up failed to increase level, breaking loop");break}}e>=10&&console.warn("[StatsManager] Hit maximum level ups per experience gain, preventing infinite loop")}levelUp(){this.stats.level++,this.stats.skillPoints+=3,this.stats.maxHealth+=10,this.stats.maxStamina+=10,this.stats.health=this.stats.maxHealth,this.stats.stamina=this.stats.maxStamina,console.log(`[StatsManager] Level up! Now level ${this.stats.level}`),this.app.fire("stats:leveledUp",this.stats.level)}getExperienceForLevel(e){return Math.floor(100*Math.pow(1.5,e-1))}canAffordStatUpgrade(e,t=1){return this.stats.skillPoints>=t}upgradeAttribute(e,t=1){return["strength","agility","intelligence","charisma"].includes(e)?this.canAffordStatUpgrade(e,t)?(this.stats.skillPoints-=t,this.modifyStat(e,1),!0):(console.warn("[StatsManager] Not enough skill points"),!1):(console.warn(`[StatsManager] Invalid attribute: ${e}`),!1)}reset(){this.stats={health:100,maxHealth:100,stamina:100,maxStamina:100,experience:0,level:1,skillPoints:0,strength:10,agility:10,intelligence:10,charisma:10},this.modifiers.clear(),this.app.fire("stats:reset")}onContentLoaded(e,t){(-1===e||t&&"catalog"===t.type)&&this.reset()}onContentUnloaded(){}getSaveData(){return{stats:{...this.stats},modifiers:Array.from(this.modifiers.entries()).map(([e,t])=>({stat:e,modifiers:Array.from(t.entries()).map(([e,t])=>({id:e,...t}))}))}}loadSaveData(e){e.stats&&(this.stats={...e.stats}),e.modifiers&&(this.modifiers.clear(),e.modifiers.forEach(e=>{const t=new Map;e.modifiers.forEach(e=>{t.set(e.id,{value:e.value,type:e.type})}),this.modifiers.set(e.stat,t)}))}destroy(){this.app.off("stats:modify",this.modifyStat,this),this.app.off("stats:addModifier",this.addModifier,this),this.app.off("stats:removeModifier",this.removeModifier,this),this.app.off("stats:levelUp",this.levelUp,this),this.app.off("stats:reset",this.reset,this)}}class A{constructor(e,t){this.app=e,this.core=t,this.maxSlots=20,this.items=new Map,this.itemData=new Map,this.catalogItems=new Map,this.globalItems=new Map,this.currentCatalogIndex=-1,this.categories={CONSUMABLE:"consumable",EQUIPMENT:"equipment",TOOL:"tool",QUEST:"quest",MISC:"misc"},this.setupEventHandlers(),this.loadItemDatabase(),this.setupCatalogTracking()}setupCatalogTracking(){this.app.on("content:loading",this.onCatalogLoading,this),this.app.on("content:loaded",this.onCatalogLoaded,this),this.app.on("content:unloading",this.onCatalogUnloading,this),this.app.on("catalog:exit",this.onCatalogExit,this)}onCatalogLoading(e){console.log(`[InventoryManager] Preparing inventory for catalog ${e}`),this.currentCatalogIndex>=0&&this.saveCatalogInventory(this.currentCatalogIndex),this.currentCatalogIndex=e,this.catalogItems.has(e)||this.catalogItems.set(e,new Map)}onCatalogLoaded(e){this.restoreCatalogInventory(e),console.log(`[InventoryManager] Restored inventory for catalog ${e}`)}onCatalogUnloading(){this.currentCatalogIndex>=0&&this.saveCatalogInventory(this.currentCatalogIndex)}onCatalogExit(){try{this.currentCatalogIndex=-1,this.items.clear(),this.catalogItems.clear(),this.globalItems.clear(),this.app.fire("inventory:cleared"),console.log("[InventoryManager] Inventory fully cleared on catalog return")}catch(e){console.warn("[InventoryManager] Failed to fully clear inventory on catalog return:",e),this.restoreGlobalInventory()}}saveCatalogInventory(e){const t=new Map;for(const[e,i]of this.items.entries()){const o=this.itemData.get(i.id);o&&o.global||t.set(e,{...i})}this.catalogItems.set(e,t),console.log(`[InventoryManager] Saved ${t.size} items for catalog ${e}`)}restoreCatalogInventory(e){this.clearCatalogSpecificItems();const t=this.catalogItems.get(e);if(t)for(const[e,i]of t.entries())this.items.set(e,{...i});for(const[e,t]of this.globalItems.entries())this.items.has(e)||this.items.set(e,{...t});this.app.fire("inventory:restored",e)}restoreGlobalInventory(){this.items.clear();for(const[e,t]of this.globalItems.entries())this.items.set(e,{...t});this.app.fire("inventory:globalRestored")}clearCatalogSpecificItems(){const e=[];for(const[t,i]of this.items.entries()){const o=this.itemData.get(i.id);o&&o.global||e.push(t)}e.forEach(e=>this.items.delete(e))}initialize(){console.log("[InventoryManager] Initialized")}setupEventHandlers(){this.app.on("item:pickup",this.pickupItem,this),this.app.on("item:drop",this.dropItem,this),this.app.on("item:use",this.useItem,this),this.app.on("item:move",this.moveItem,this),this.app.on("inventory:clear",this.clearInventory,this)}loadItemDatabase(){this.defineItem("health_potion",{name:"Health Potion",description:"Restores 50 health",category:this.categories.CONSUMABLE,stackable:!0,maxStack:10,global:!0,onUse:e=>(this.app.fire("stats:modify","health",50),this.app.fire("ui:notification",{text:"Restored 50 health",type:"success"}),!0)}),this.defineItem("stamina_potion",{name:"Stamina Potion",description:"Restores 50 stamina",category:this.categories.CONSUMABLE,stackable:!0,maxStack:10,global:!0,onUse:e=>(this.app.fire("stats:modify","stamina",50),this.app.fire("ui:notification",{text:"Restored 50 stamina",type:"success"}),!0)}),this.defineItem("security_keycard",{name:"Security Keycard",description:"Opens security doors",category:this.categories.QUEST,stackable:!1,questItem:!0,global:!1}),this.defineItem("wrench",{name:"Wrench",description:"A sturdy tool for repairs",category:this.categories.TOOL,stackable:!1,reusable:!0,global:!0}),this.defineItem("annas_package",{name:"Anna's Package",description:"A sealed package for Dr. Smith",category:this.categories.QUEST,stackable:!1,questItem:!0,global:!1}),this.defineItem("test_tool_1",{name:"Diagnostic Scanner",description:"A handheld diagnostic tool for system analysis",category:this.categories.TOOL,stackable:!1,reusable:!0,global:!1}),this.defineItem("test_consumable_1",{name:"Emergency Rations",description:"High-energy rations for emergency situations",category:this.categories.CONSUMABLE,stackable:!0,maxStack:5,global:!1,onUse:e=>!0}),this.defineItem("test_equipment_1",{name:"Environmental Suit",description:"Protective equipment for hazardous environments",category:this.categories.EQUIPMENT,stackable:!1,reusable:!0,global:!1}),this.defineItem("test_resource_water",{name:"Water Canister",description:"Emergency water supply for life support systems",category:this.categories.CONSUMABLE,stackable:!0,maxStack:3,global:!1}),this.defineItem("test_resource_power",{name:"Power Cell",description:"Portable power source for equipment",category:this.categories.CONSUMABLE,stackable:!0,maxStack:3,global:!1}),this.defineItem("test_material_steel",{name:"Steel Components",description:"Fabricated steel parts for construction",category:this.categories.MISC,stackable:!0,maxStack:5,global:!1}),this.defineItem("test_stat_booster",{name:"Attribute Enhancer",description:"Experimental serum that boosts all attributes by 1",category:this.categories.CONSUMABLE,stackable:!0,maxStack:3,global:!1}),this.defineItem("test_health_potion",{name:"Health Restoration Vial",description:"Medical nanobots that fully restore health",category:this.categories.CONSUMABLE,stackable:!0,maxStack:5,global:!1}),this.defineItem("test_stamina_booster",{name:"Endurance Amplifier",description:"Cybernetic enhancement that increases maximum stamina",category:this.categories.CONSUMABLE,stackable:!0,maxStack:3,global:!1}),this.defineItem("test_attribute_tome",{name:"Knowledge Archive",description:"Digital library containing valuable experience and training data",category:this.categories.MISC,stackable:!1,global:!1})}defineItem(e,t){this.itemData.set(e,{id:e,...t})}pickupItem(e,t=1){const i=this.itemData.get(e);if(!i)return console.error(`[InventoryManager] Unknown item: ${e}`),!1;let o=-1;if(i.stackable)for(const[t,n]of this.items.entries())if(n.id===e&&n.quantity=t}removeItem(e,t=1){let i=t;for(const[t,o]of this.items.entries())if(o.id===e&&i>0){const e=Math.min(o.quantity,i);o.quantity-=e,i-=e,o.quantity<=0?(this.items.delete(t),this.app.fire("inventory:itemRemoved",o.id,t)):this.app.fire("inventory:itemUpdated",o.id,t,o.quantity)}return 0===i}getItemAt(e){return this.items.get(e)}getItemInfo(e){return this.itemData.get(e)}clearInventory(){this.items.clear(),this.app.fire("inventory:cleared")}getSaveData(){return{items:Array.from(this.items.entries()).map(([e,t])=>({slot:e,id:t.id,quantity:t.quantity}))}}getGlobalSaveData(){const e=[];for(const[t,i]of this.globalItems.entries())e.push({slot:t,id:i.id,quantity:i.quantity});return{items:e,maxSlots:this.maxSlots}}getCatalogSaveData(){const e=[];if(this.currentCatalogIndex>=0){const t=this.catalogItems.get(this.currentCatalogIndex);if(t)for(const[i,o]of t.entries())e.push({slot:i,id:o.id,quantity:o.quantity})}return{items:e,catalogIndex:this.currentCatalogIndex}}loadSaveData(e){this.items.clear(),e.items&&e.items.forEach(e=>{this.items.set(e.slot,{id:e.id,quantity:e.quantity})})}loadGlobalSaveData(e){this.globalItems.clear(),e.items&&e.items.forEach(e=>{const t={id:e.id,quantity:e.quantity};this.globalItems.set(e.slot,t),this.items.has(e.slot)||this.items.set(e.slot,{...t})}),console.log(`[InventoryManager] Loaded ${e.items?.length||0} global items`)}loadCatalogSaveData(e){if(e.catalogIndex>=0&&e.items){const t=new Map;e.items.forEach(e=>{t.set(e.slot,{id:e.id,quantity:e.quantity})}),this.catalogItems.set(e.catalogIndex,t),e.catalogIndex===this.currentCatalogIndex&&this.restoreCatalogInventory(e.catalogIndex),console.log(`[InventoryManager] Loaded ${e.items.length} items for catalog ${e.catalogIndex}`)}}destroy(){this.app.off("item:pickup",this.pickupItem,this),this.app.off("item:drop",this.dropItem,this),this.app.off("item:use",this.useItem,this),this.app.off("item:move",this.moveItem,this),this.app.off("inventory:clear",this.clearInventory,this)}}class ${constructor(e,t){this.app=e,this.core=t,this.skills=new Map,this.unlockedSkills=new Set,this.categories={COMBAT:"combat",SOCIAL:"social",TECH:"tech",STEALTH:"stealth"},this.setupEventHandlers(),this.defineSkillTree()}initialize(){console.log("[SkillTreeManager] Initialized")}setupEventHandlers(){this.app.on("skill:unlock",this.unlockSkill,this),this.app.on("skill:upgrade",this.upgradeSkill,this),this.app.on("skill:reset",this.resetSkills,this)}defineSkillTree(){this.defineSkill("strength_boost",{name:"Strength Boost",description:"Increases strength by 5",category:this.categories.COMBAT,cost:1,maxLevel:5,prerequisites:[],onUnlock:()=>{this.app.fire("stats:addModifier","skill_strength","strength",5,"add")},onUpgrade:e=>{this.app.fire("stats:removeModifier","skill_strength","strength"),this.app.fire("stats:addModifier","skill_strength","strength",5*e,"add")}}),this.defineSkill("berserker",{name:"Berserker",description:"Deal 20% more damage when health is below 30%",category:this.categories.COMBAT,cost:2,maxLevel:1,prerequisites:["strength_boost"],passive:!0}),this.defineSkill("charisma",{name:"Charisma",description:"Unlock persuasion dialogue options",category:this.categories.SOCIAL,cost:1,maxLevel:1,prerequisites:[],dialogueFlag:"charisma"}),this.defineSkill("persuasion",{name:"Persuasion",description:"Better prices and quest rewards",category:this.categories.SOCIAL,cost:2,maxLevel:3,prerequisites:["charisma"],onUnlock:()=>{this.app.fire("economy:priceModifier",.9)},onUpgrade:e=>{this.app.fire("economy:priceModifier",1-.1*e)}}),this.defineSkill("intimidation",{name:"Intimidation",description:"Intimidate NPCs to get your way",category:this.categories.SOCIAL,cost:2,maxLevel:1,prerequisites:["strength_boost"],dialogueFlag:"intimidation"}),this.defineSkill("hacking",{name:"Hacking",description:"Hack terminals and security systems",category:this.categories.TECH,cost:1,maxLevel:3,prerequisites:[],interactionFlag:"hacking"}),this.defineSkill("lockpicking",{name:"Lockpicking",description:"Pick locks on doors and containers",category:this.categories.TECH,cost:1,maxLevel:3,prerequisites:[],interactionFlag:"lockpicking"}),this.defineSkill("stealth",{name:"Stealth",description:"Move quietly and avoid detection",category:this.categories.STEALTH,cost:1,maxLevel:3,prerequisites:[],onUnlock:()=>{this.app.fire("player:stealthModifier",.7)},onUpgrade:e=>{this.app.fire("player:stealthModifier",1-.3*e/3)}}),this.defineSkill("assassinate",{name:"Assassinate",description:"Instant kill unaware enemies",category:this.categories.STEALTH,cost:3,maxLevel:1,prerequisites:["stealth"],combatAbility:!0})}defineSkill(e,t){this.skills.set(e,{id:e,level:0,...t})}unlockSkill(e){const t=this.skills.get(e);if(!t)return console.error(`[SkillTreeManager] Unknown skill: ${e}`),!1;if(this.unlockedSkills.has(e))return console.warn(`[SkillTreeManager] Skill already unlocked: ${e}`),!1;for(const e of t.prerequisites)if(!this.unlockedSkills.has(e))return this.app.fire("ui:notification",{text:`Requires: ${this.skills.get(e).name}`,type:"error"}),!1;const i=this.core.getSystem("statsmanager");return i&&i.canAffordStatUpgrade(e,t.cost)?(i.stats.skillPoints-=t.cost,this.unlockedSkills.add(e),t.level=1,t.onUnlock&&t.onUnlock(),this.app.fire("skill:unlocked",e),this.app.fire("ui:notification",{text:`Unlocked: ${t.name}`,type:"success"}),!0):(this.app.fire("ui:notification",{text:"Not enough skill points",type:"error"}),!1)}upgradeSkill(e){const t=this.skills.get(e);if(!t||!this.unlockedSkills.has(e))return console.error(`[SkillTreeManager] Skill not unlocked: ${e}`),!1;if(t.level>=t.maxLevel)return this.app.fire("ui:notification",{text:"Skill at max level",type:"warning"}),!1;const i=this.core.getSystem("statsmanager");return i&&i.canAffordStatUpgrade(e,t.cost)?(i.stats.skillPoints-=t.cost,t.level++,t.onUpgrade&&t.onUpgrade(t.level),this.app.fire("skill:upgraded",e,t.level),this.app.fire("ui:notification",{text:`${t.name} upgraded to level ${t.level}`,type:"success"}),!0):(this.app.fire("ui:notification",{text:"Not enough skill points",type:"error"}),!1)}hasSkill(e){return this.unlockedSkills.has(e)}getSkillLevel(e){const t=this.skills.get(e);return t?t.level:0}getSkillsInCategory(e){const t=[];for(const i of this.skills.values())i.category===e&&t.push({...i,unlocked:this.unlockedSkills.has(i.id)});return t}canUnlockSkill(e){const t=this.skills.get(e);if(!t)return!1;if(this.unlockedSkills.has(e))return!1;for(const e of t.prerequisites)if(!this.unlockedSkills.has(e))return!1;const i=this.core.getSystem("statsmanager");return i&&i.canAffordStatUpgrade(e,t.cost)}getSkillTree(){const e={};for(const t of Object.values(this.categories))e[t]=this.getSkillsInCategory(t);return e}resetSkills(){for(const e of this.unlockedSkills){const t=this.skills.get(e);t&&t.onReset&&t.onReset()}this.unlockedSkills.clear();for(const e of this.skills.values())e.level=0;const e=this.getTotalSkillCost(),t=this.core.getSystem("statsmanager");t&&(t.stats.skillPoints+=e),this.app.fire("skills:reset")}getTotalSkillCost(){let e=0;for(const t of this.unlockedSkills){const i=this.skills.get(t);i&&(e+=i.cost*i.level)}return e}getSaveData(){return{unlockedSkills:Array.from(this.unlockedSkills),skillLevels:Array.from(this.skills.entries()).map(([e,t])=>({id:e,level:t.level}))}}loadSaveData(e){this.unlockedSkills.clear(),e.unlockedSkills&&e.unlockedSkills.forEach(e=>{this.unlockedSkills.add(e);const t=this.skills.get(e);t&&t.onUnlock&&t.onUnlock()}),e.skillLevels&&e.skillLevels.forEach(e=>{const t=this.skills.get(e.id);t&&(t.level=e.level,t.level>1&&t.onUpgrade&&t.onUpgrade(t.level))})}destroy(){this.app.off("skill:unlock",this.unlockSkill,this),this.app.off("skill:upgrade",this.upgradeSkill,this),this.app.off("skill:reset",this.resetSkills,this)}}class O{constructor(e,t){this.app=e,this.core=t,this.npcs=new Map,this.npcTemplates=new Map,this.npcStates={IDLE:"idle",PATROL:"patrol",ALERT:"alert",COMBAT:"combat",DIALOGUE:"dialogue",DEAD:"dead"},this.setupEventHandlers(),this.defineNPCTemplates()}initialize(){console.log("[NPCManager] Initialized")}setupEventHandlers(){this.app.on("npc:spawn",this.spawnNPC,this),this.app.on("npc:remove",this.removeNPC,this),this.app.on("npc:interact",this.interactWithNPC,this),this.app.on("npc:setstate",this.setNPCState,this),this.app.on("npc:damage",this.damageNPC,this)}defineNPCTemplates(){this.defineTemplate("merchant",{name:"Merchant",health:100,faction:"neutral",behavior:"stationary",dialogue:"merchant_default",model:{type:"capsule",color:new pc.Color(.2,.6,.2)},interactions:["trade","talk"],interactionDistance:3}),this.defineTemplate("guard",{name:"Guard",health:150,faction:"security",behavior:"patrol",dialogue:"guard_default",model:{type:"capsule",color:new pc.Color(.2,.2,.8)},interactions:["talk"],combatEnabled:!0,alertRadius:10,interactionDistance:3.5,patrolPath:[{offset:[0,0,0],wait:1},{offset:[5,0,0],wait:1},{offset:[5,0,5],wait:1},{offset:[0,0,5],wait:1}]}),this.defineTemplate("questgiver",{name:"Quest Giver",health:100,faction:"neutral",behavior:"stationary",dialogue:"questgiver_default",model:{type:"capsule",color:new pc.Color(.8,.8,.2)},interactions:["talk","quest"],interactionDistance:3}),this.defineTemplate("enemy",{name:"Enemy",health:80,faction:"hostile",behavior:"aggressive",dialogue:null,model:{type:"capsule",color:new pc.Color(.8,.2,.2)},interactions:[],combatEnabled:!0,damage:10,attackSpeed:1.5,aggroRadius:15,interactionDistance:2.5})}defineTemplate(e,t){this.npcTemplates.set(e,t)}spawnNPC(e){const{template:t,id:i,position:o,rotation:n,customData:s}=e,a=this.npcTemplates.get(t);if(!a)return console.error(`[NPCManager] Unknown template: ${t}`),null;const r=i||`${t}_${Date.now()}`,l=o||[0,0,0];0===l[1]&&(l[1]=1);const c=this.core.entityFactory.createNPC(r,{position:l,scale:s?.scale||[1,2,1],displayName:s?.displayName||a.name||r});a.model&&a.model.color&&this._trackNPCTimer(r,setTimeout(()=>{let e=!1;const t=c.model;t&&t.meshInstances&&t.meshInstances.length>0&&t.meshInstances.forEach(t=>{if(t&&t.material){const i=t.material.clone();i.diffuse=a.model.color,i.update(),t.material=i,e=!0}}),!e&&c.children&&c.children.length>0&&c.children.forEach(t=>{t.model&&t.model.meshInstances&&t.model.meshInstances.length>0&&t.model.meshInstances.forEach(t=>{if(t&&t.material){const i=t.material.clone();i.diffuse=a.model.color,i.update(),t.material=i,e=!0}})}),e?console.log(`[NPCManager] Applied color ${a.model.color} to NPC ${r}`):console.warn(`[NPCManager] Could not apply color to NPC ${r} - model not ready`)},0));const h={id:r,entity:c,template:t,state:this.npcStates.IDLE,health:a.health,maxHealth:a.health,faction:a.faction,mood:"neutral",willingness:.5,interactionDistance:a.interactionDistance||3,_timers:[],...a,...s};if("patrol"===h.behavior&&Array.isArray(h.patrolPath)){const e=new pc.Vec3(l[0],l[1],l[2]);h._resolvedPatrolPath=h.patrolPath.map(t=>{if(t&&"object"==typeof t&&(t.offset||t.position||t.pos)){const i=t.offset||t.position||t.pos,o=t.offset?e:new pc.Vec3(0,0,0);return{position:new pc.Vec3(o.x+(i[0]||0),o.y+(i[1]||0),o.z+(i[2]||0)),wait:t.wait||0}}return Array.isArray(t)&&t.length>=3?{position:new pc.Vec3(t[0],t[1],t[2]),wait:0}:t instanceof pc.Vec3?{position:t.clone(),wait:0}:null}).filter(Boolean)}return this.app.root.addChild(c),this.npcs.set(r,h),this.initializeNPCBehavior(h),"stationary"!==h.behavior&&"patrol"!==h.behavior||this._trackNPCTimer(r,setTimeout(()=>{this.core.entityFactory&&this.npcs.has(r)&&this.core.entityFactory.setNPCRandomTarget(c)},1e3+2e3*Math.random())),this._trackNPCTimer(r,setTimeout(()=>{if(!this.npcs.has(r))return;const e=t=>{if(t.model&&t.model.meshInstances&&t.model.meshInstances.length>0)return!0;if(t.children)for(const i of t.children)if(e(i))return!0;return!1};if(!e(c)){const e=new pc.Entity(`${r}_Visual`);e.addComponent("model",{type:"capsule"}),e.setLocalScale(.5,1.8,.5),e.setLocalPosition(0,1,0),c.addChild(e),console.warn(`[NPCManager] Added fallback visual model for NPC ${r}`)}},100)),"patrol"===h.behavior&&(h.state=this.npcStates.PATROL),this.app.fire("npc:spawned",r,h),h}removeNPC(e){const t=this.npcs.get(e);t&&(this._clearNPCTimers(e),t.entity&&t.entity.destroy(),this.npcs.delete(e),this.app.fire("npc:removed",e))}interactWithNPC(e){const t=this.npcs.get(e);if(!t)return void console.error(`[NPCManager] NPC not found: ${e}`);if(!this.isPlayerNearNPC(t))return void this.app.fire("ui:notification",{text:"Too far away",type:"warning"});if(t.state===this.npcStates.DIALOGUE)return;this.setNPCState(e,this.npcStates.DIALOGUE);const i=this.core.getSystem("skilltreemanager"),o=i?{charisma:i.hasSkill("charisma"),intimidation:i.hasSkill("intimidation"),persuasion:i.hasSkill("persuasion")}:{};o.intimidation&&"hostile"!==t.faction&&(t.mood="nervous",t.willingness=Math.min(1,t.willingness+.3)),t.dialogue?this.app.fire("dialogue:start",{npcId:e,dialogueId:t.dialogue,npcData:t,playerSkills:o}):t.interactions.includes("trade")&&this.app.fire("trade:open",e)}setNPCState(e,t){const i=this.npcs.get(e);if(!i)return;const o=i.state;i.state=t,this.app.fire("npc:stateChanged",e,o,t),this.updateNPCBehavior(i)}damageNPC(e,t,i){const o=this.npcs.get(e);o&&o.state!==this.npcStates.DEAD&&(o.health=Math.max(0,o.health-t),this.app.fire("npc:damaged",e,t,o.health),o.health<=0?(this.setNPCState(e,this.npcStates.DEAD),this.app.fire("npc:died",e),o.drops&&this.dropNPCItems(o)):o.combatEnabled&&"neutral"!==o.faction&&(this.setNPCState(e,this.npcStates.COMBAT),o.target=i))}initializeNPCBehavior(e){const t=e.entity;if(!t)return;t.addComponent("script");const i=pc.createScript("npcBehavior");i.prototype.initialize=function(){this.npcManager=this.app.core.getSystem("npcmanager"),this.npcId=e.id},i.prototype.update=function(e){const t=this.npcManager.npcs.get(this.npcId);t&&(this.npcManager.updateNPCBehavior(t,e),this.npcManager.core.entityFactory&&(this.npcManager.core.entityFactory.updateNPCAnimation(t.entity,e),this.npcManager.core.entityFactory.updateNPCMovement(t.entity,e)))},t.script.create("npcBehavior")}updateNPCBehavior(e,t){if(t)switch(e.state){case this.npcStates.IDLE:break;case this.npcStates.PATROL:this.updatePatrolBehavior(e,t);break;case this.npcStates.ALERT:this.updateAlertBehavior(e,t);break;case this.npcStates.COMBAT:this.updateCombatBehavior(e,t);break;case this.npcStates.DIALOGUE:this.facePlayer(e)}}updatePatrolBehavior(e,t){const i=e._resolvedPatrolPath||e.patrolPath;if(!i||0===i.length)return;if(void 0!==e.currentPatrolIndex&&null!==e.currentPatrolIndex||(e.currentPatrolIndex=0),e._patrolWaitUntil&&Date.now()=e._patrolWaitUntil&&(e._patrolWaitUntil=null);const o=i[e.currentPatrolIndex],n=o.position?o.position:o,s=o.wait||0,a=e.entity.getPosition();if(a.distance(n)<.5)return s>0&&(e._patrolWaitUntil=Date.now()+1e3*s),void(e.currentPatrolIndex=(e.currentPatrolIndex+1)%i.length);const r=(new pc.Vec3).sub2(n,a).normalize(),l=e.moveSpeed||2;e.entity.translate(r.x*l*t,0,r.z*l*t)}updateCombatBehavior(e,t){if(!e.target)return void this.setNPCState(e.id,this.npcStates.IDLE);const i=e.target.getPosition?e.target.getPosition():e.target,o=e.entity.getPosition(),n=o.distance(i);if(n>e.aggroRadius)this.setNPCState(e.id,this.npcStates.IDLE),e.target=null;else if(n>2){const n=(new pc.Vec3).sub2(i,o).normalize(),s=4;e.entity.translate(n.x*s*t,0,n.z*s*t)}else(!e.lastAttackTime||Date.now()-e.lastAttackTime>1e3*e.attackSpeed)&&(this.app.fire("npc:attack",e.id,e.target,e.damage),e.lastAttackTime=Date.now())}updateAlertBehavior(e,t){(!e.lastAlertCheck||Date.now()-e.lastAlertCheck>1e3)&&(e.alertStartTime||(e.alertStartTime=Date.now()),Date.now()-e.alertStartTime>5e3&&(this.setNPCState(e.id,this.npcStates.IDLE),e.alertStartTime=null),e.lastAlertCheck=Date.now())}facePlayer(e){const t=this.app.player;if(!t||!e.entity)return;const i=e.entity.getPosition(),o=t.getPosition(),n=(new pc.Vec3).sub2(o,i);n.y=0,n.normalize();const s=Math.atan2(n.x,n.z)*(180/Math.PI);e.entity.setEulerAngles(0,s,0)}isPlayerNearNPC(e){const t=this.app.player;return!(!t||!e.entity)&&t.getPosition().distance(e.entity.getPosition())<(e.interactionDistance||3)}getNPCState(e){const t=this.npcs.get(e);return t?{mood:t.mood,willingness:t.willingness,state:t.state,health:t.health}:null}dropNPCItems(e){if(!e.drops)return;const t=e.entity.getPosition();e.drops.forEach(e=>{Math.random(){const t=this.spawnNPC({template:e.template,id:e.id,position:e.position,rotation:e.rotation});t&&(t.state=e.state,t.health=e.health,t.mood=e.mood,t.willingness=e.willingness)})}destroy(){this.clearAll(),this.app.off("npc:spawn",this.spawnNPC,this),this.app.off("npc:remove",this.removeNPC,this),this.app.off("npc:interact",this.interactWithNPC,this),this.app.off("npc:setstate",this.setNPCState,this),this.app.off("npc:damage",this.damageNPC,this)}clearAll(){for(const e of Array.from(this.npcs.keys()))this.removeNPC(e)}_trackNPCTimer(e,t){const i=this.npcs.get(e);return i&&t&&(i._timers=i._timers||[],i._timers.push(t)),t}_clearNPCTimers(e){const t=this.npcs.get(e);t&&t._timers&&(t._timers.forEach(e=>clearTimeout(e)),t._timers.length=0)}}class L{constructor(e,t){this.app=e,this.core=t,this.objectives=new Map,this.activeObjectives=new Set,this.completedObjectives=new Set,this.activeTimers=new Map,this.isInCatalog=!1,this.states={INACTIVE:"inactive",ACTIVE:"active",COMPLETED:"completed",FAILED:"failed"},this.setupEventHandlers()}clearAllObjectives(){console.log("[ObjectiveManager] Clearing all objectives");for(const e of this.activeObjectives)this.resetObjective(e);this.activeObjectives.clear(),this.completedObjectives.clear(),this.app.fire("objective:started")}initialize(){console.log("[ObjectiveManager] Initialized")}setupEventHandlers(){this.app.on("objective:start",this.startObjective,this),this.app.on("objective:complete",this.completeObjective,this),this.app.on("objective:fail",this.failObjective,this),this.app.on("objective:attempt",this.attemptObjective,this),this.app.on("objective:update",this.updateObjective,this),this.app.on("objective:reset",this.resetObjective,this)}defineObjective(e,t){if(!e||"string"!=typeof e)return console.error("[ObjectiveManager] Invalid objective ID provided to defineObjective"),!1;if(!t||"object"!=typeof t)return console.error(`[ObjectiveManager] Invalid objective data for ${e}`),!1;const i=["name","type","description"];for(const o of i)if(!t[o]||"string"!=typeof t[o])return console.error(`[ObjectiveManager] Missing or invalid required field '${o}' for objective ${e}`),!1;const o=["simple","counter","timer"];if(!o.includes(t.type))return console.error(`[ObjectiveManager] Invalid objective type '${t.type}' for ${e}. Must be: ${o.join(", ")}`),!1;if("counter"===t.type&&("number"!=typeof t.target||t.target<=0))return console.error(`[ObjectiveManager] Counter objective ${e} must have a positive numeric target`),!1;if("timer"===t.type&&("number"!=typeof t.timeLimit||t.timeLimit<=0))return console.error(`[ObjectiveManager] Timer objective ${e} must have a positive numeric timeLimit`),!1;if(void 0!==t.timeLimit&&("number"!=typeof t.timeLimit||t.timeLimit<=0))return console.error(`[ObjectiveManager] Invalid timeLimit for ${e}: must be positive number`),!1;if(t.prerequisites){if(!Array.isArray(t.prerequisites))return console.error(`[ObjectiveManager] Prerequisites for ${e} must be an array`),!1;if(t.prerequisites.includes(e))return console.error(`[ObjectiveManager] Objective ${e} cannot reference itself as prerequisite`),!1}try{return this.objectives.set(e,{id:e,state:this.states.INACTIVE,progress:"counter"===t.type?0:void 0,...t}),console.debug(`[ObjectiveManager] Defined objective: ${e} (${t.type})`),!0}catch(t){return console.error(`[ObjectiveManager] Error defining objective ${e}:`,t),!1}}validateObjectiveDependencies(){const e=new Set,t=new Set,i=(o,n=[])=>{if(t.has(o))return console.error(`[ObjectiveManager] Circular dependency detected: ${[...n,o].join(" -> ")}`),!0;if(e.has(o))return!1;const s=this.objectives.get(o);if(!s||!s.prerequisites)return e.add(o),!1;t.add(o);for(const e of s.prerequisites)if(i(e,[...n,o]))return!0;return t.delete(o),e.add(o),!1};for(const e of this.objectives.keys())if(i(e))return!1;return!0}startObjective(e){if(!e||"string"!=typeof e)return console.error("[ObjectiveManager] Invalid objective ID provided to startObjective"),!1;const t=this.objectives.get(e);if(!t)return console.error(`[ObjectiveManager] Unknown objective: ${e}`),!1;if(t.state!==this.states.INACTIVE)return console.warn(`[ObjectiveManager] Objective already started: ${e}`),!1;if(t.prerequisites){const i=[];for(const o of t.prerequisites){if(!this.objectives.has(o))return console.error(`[ObjectiveManager] Unknown prerequisite '${o}' for objective ${e}`),!1;this.completedObjectives.has(o)||i.push(o)}if(i.length>0)return console.debug(`[ObjectiveManager] Prerequisites not met for ${e}: ${i.join(", ")}`),this.app.fire("ui:notification",{text:`Prerequisites not met: ${i.join(", ")}`,type:"error"}),!1}return t.state=this.states.ACTIVE,t.startTime=Date.now(),this.activeObjectives.add(e),"counter"===t.type&&(t.progress=0),this.app.fire("objective:started",e),this.app.fire("ui:notification",{text:`New Objective: ${t.name}`,type:"info"}),t.timeLimit&&this.startObjectiveTimer(t),!0}attemptObjective(e){if(!e||"string"!=typeof e)return console.error("[ObjectiveManager] Invalid objective ID provided to attemptObjective"),!1;const t=this.objectives.get(e);return t?t.state!==this.states.ACTIVE?(console.warn(`[ObjectiveManager] Cannot attempt objective in state: ${t.state}`),!1):!!this.checkObjectiveRequirements(t)&&(this.completeObjective(e),!0):(console.warn(`[ObjectiveManager] Cannot attempt unknown objective: ${e}`),!1)}checkObjectiveRequirements(e){if(!e||!e.requires)return!0;try{const t=this.core?.getSystem("inventorymanager"),i=this.core?.getSystem("skilltreemanager"),o=this.core?.getSystem("statsmanager");if(e.requires.item){if(!t)return console.warn("[ObjectiveManager] Inventory system not available for requirement check"),!1;try{if(!t.hasItem(e.requires.item,e.requires.quantity||1))return this.app.fire("ui:notification",{text:`Requires: ${e.requires.item}`,type:"warning"}),!1}catch(e){return console.error("[ObjectiveManager] Error checking item requirement:",e),!1}}if(e.requires.tool){if(!t)return console.warn("[ObjectiveManager] Inventory system not available for tool check"),!1;try{if(!t.hasItem(e.requires.tool))return this.app.fire("ui:notification",{text:`Requires tool: ${e.requires.tool}`,type:"warning"}),!1}catch(e){return console.error("[ObjectiveManager] Error checking tool requirement:",e),!1}}if(e.requires.skill){if(!i)return console.warn("[ObjectiveManager] Skill system not available for skill check"),!1;try{if(!i.hasSkill(e.requires.skill))return this.app.fire("ui:notification",{text:`Requires skill: ${e.requires.skill}`,type:"warning"}),!1}catch(e){return console.error("[ObjectiveManager] Error checking skill requirement:",e),!1}}if(e.requires.stat){if(!o)return console.warn("[ObjectiveManager] Stats system not available for stat check"),!1;try{for(const[t,i]of Object.entries(e.requires.stat)){const e=o.getStat(t);if("number"!=typeof e||e= ${i}`,type:"warning"}),!1}}catch(e){return console.error("[ObjectiveManager] Error checking stat requirements:",e),!1}}return!0}catch(e){return console.error("[ObjectiveManager] Critical error in checkObjectiveRequirements:",e),!1}}updateObjective(e,t){if(!e||"string"!=typeof e)return void console.error("[ObjectiveManager] Invalid objective ID provided to updateObjective");const i=this.objectives.get(e);if(i)if(i.state===this.states.ACTIVE)if("counter"===i.type){if("number"!=typeof t||t<0)return void console.error(`[ObjectiveManager] Invalid progress value for ${e}: ${t}`);if("number"!=typeof i.target||i.target<=0)return void console.error(`[ObjectiveManager] Invalid target for counter objective ${e}: ${i.target}`);const o=i.progress||0;i.progress=Math.min(t,i.target),i.progress!==o&&console.debug(`[ObjectiveManager] ${e} progress: ${o} -> ${i.progress}/${i.target}`),i.progress>=i.target?this.completeObjective(e):this.app.fire("objective:progress",e,i.progress,i.target)}else console.warn(`[ObjectiveManager] Cannot update progress for non-counter objective: ${e} (type: ${i.type})`);else console.debug(`[ObjectiveManager] Cannot update objective in state: ${i.state}`);else console.warn(`[ObjectiveManager] Cannot update unknown objective: ${e}`)}completeObjective(e){if(!e||"string"!=typeof e)return console.error("[ObjectiveManager] Invalid objective ID provided to completeObjective"),!1;const t=this.objectives.get(e);if(!t)return console.warn(`[ObjectiveManager] Cannot complete unknown objective: ${e}`),!1;if(t.state!==this.states.ACTIVE)return console.debug(`[ObjectiveManager] Cannot complete objective in state: ${t.state}`),!1;if(t.requires&&t.requires.item){const e=this.core.getSystem("inventorymanager");e&&e.removeItem(t.requires.item,t.requires.quantity||1)}return t.state=this.states.COMPLETED,t.completionTime=Date.now(),this.activeObjectives.delete(e),this.completedObjectives.add(e),t.timerId&&(clearTimeout(t.timerId),this.activeTimers.delete(e),t.timerId=null),t.rewards&&this.grantObjectiveRewards(t.rewards),this.app.fire("objective:completed",e),this.app.fire("ui:notification",{text:`Completed: ${t.name}`,type:"success"}),t.nextObjective&&this.startObjective(t.nextObjective),!0}failObjective(e){const t=this.objectives.get(e);t&&t.state===this.states.ACTIVE&&(t.state=this.states.FAILED,t.failureTime=Date.now(),this.activeObjectives.delete(e),t.timerId&&(clearTimeout(t.timerId),this.activeTimers.delete(e),t.timerId=null),this.app.fire("objective:failed",e),this.app.fire("ui:notification",{text:`Failed: ${t.name}`,type:"error"}),t.onFailure&&t.onFailure())}resetObjective(e){const t=this.objectives.get(e);t&&(t.state=this.states.INACTIVE,t.progress=0,t.startTime=null,t.completionTime=null,t.failureTime=null,this.activeObjectives.delete(e),this.completedObjectives.delete(e),t.timerId&&(clearTimeout(t.timerId),this.activeTimers.delete(e),t.timerId=null))}startObjectiveTimer(e){e.timerId&&(clearTimeout(e.timerId),this.activeTimers.delete(e.id));const t=setTimeout(()=>{this.activeTimers.delete(e.id),e.state===this.states.ACTIVE&&("timer"===e.type?this.completeObjective(e.id):this.failObjective(e.id))},1e3*e.timeLimit);e.timerId=t,this.activeTimers.set(e.id,t)}grantObjectiveRewards(e){const t=this.core.getSystem("inventorymanager"),i=this.core.getSystem("statsmanager"),o=this.core.getSystem("skilltreemanager");e.experience&&i&&i.modifyStat("experience",e.experience),e.items&&t&&e.items.forEach(e=>{t.pickupItem(e.id,e.quantity||1)}),e.skillPoints&&i&&i.modifyStat("skillPoints",e.skillPoints),e.unlockSkills&&o&&e.unlockSkills.forEach(e=>{o.unlockSkill(e)})}hasObjective(e){return this.activeObjectives.has(e)}isComplete(e){return this.completedObjectives.has(e)}isObjectiveActive(e){const t=this.objectives.get(e);return t&&t.state===this.states.ACTIVE}getActiveObjectives(){if(this.isInCatalog)return[];const e=[];for(const t of this.activeObjectives){const i=this.objectives.get(t);i&&e.push({...i,timeRemaining:i.timeLimit?Math.max(0,i.timeLimit-(Date.now()-i.startTime)/1e3):null})}return e}onContentLoaded(e,t){if(console.log(`[ObjectiveManager] Content loaded: ${e}, content type: ${t?.type}`),this.isInCatalog=-1===e,this.isInCatalog)try{for(const[e,t]of this.activeTimers)clearTimeout(t);this.activeTimers.clear(),this.clearAllObjectives(),this.objectives.clear(),this.app.fire("objective:cleared"),console.log("[ObjectiveManager] Reset all objectives for catalog")}catch(e){console.warn("[ObjectiveManager] Failed to reset objectives on catalog:",e)}this.app.fire("objective:started")}onContentUnloaded(){for(const e of this.activeObjectives)this.resetObjective(e)}getSaveData(){try{const e={activeObjectives:Array.from(this.activeObjectives),completedObjectives:Array.from(this.completedObjectives),objectiveStates:[]};for(const[t,i]of this.objectives.entries())try{e.objectiveStates.push({id:t,state:i.state||this.states.INACTIVE,progress:"number"==typeof i.progress?i.progress:0,startTime:i.startTime||null,completionTime:i.completionTime||null,failureTime:i.failureTime||null})}catch(e){console.warn(`[ObjectiveManager] Failed to serialize objective ${t}:`,e)}return console.debug(`[ObjectiveManager] Saved data for ${e.objectiveStates.length} objectives`),e}catch(e){return console.error("[ObjectiveManager] Critical error creating save data:",e),{activeObjectives:[],completedObjectives:[],objectiveStates:[]}}}loadSaveData(e){try{if(!e||"object"!=typeof e)return console.error("[ObjectiveManager] Invalid save data provided"),!1;console.debug("[ObjectiveManager] Loading save data..."),this.activeObjectives.clear(),this.completedObjectives.clear();for(const[e,t]of this.activeTimers)clearTimeout(t);if(this.activeTimers.clear(),Array.isArray(e.activeObjectives))for(const t of e.activeObjectives)"string"==typeof t&&this.objectives.has(t)?this.activeObjectives.add(t):console.warn(`[ObjectiveManager] Skipping invalid active objective: ${t}`);if(Array.isArray(e.completedObjectives))for(const t of e.completedObjectives)"string"==typeof t&&this.objectives.has(t)?this.completedObjectives.add(t):console.warn(`[ObjectiveManager] Skipping invalid completed objective: ${t}`);if(Array.isArray(e.objectiveStates))for(const t of e.objectiveStates)try{if(!t||"string"!=typeof t.id){console.warn("[ObjectiveManager] Skipping invalid objective state");continue}const e=this.objectives.get(t.id);if(!e){console.warn(`[ObjectiveManager] Objective ${t.id} no longer exists, skipping state restore`);continue}if(Object.values(this.states).includes(t.state)&&(e.state=t.state),"number"==typeof t.progress&&(e.progress=t.progress),e.startTime=t.startTime||null,e.completionTime=t.completionTime||null,e.failureTime=t.failureTime||null,e.state===this.states.ACTIVE&&e.timeLimit&&e.startTime){const t=(Date.now()-e.startTime)/1e3,i=e.timeLimit-t;i>0?(e.timeLimit=i,this.startObjectiveTimer(e)):this.failObjective(e.id)}}catch(e){console.warn(`[ObjectiveManager] Error restoring state for objective ${t?.id}:`,e)}return console.debug(`[ObjectiveManager] Loaded save data: ${this.activeObjectives.size} active, ${this.completedObjectives.size} completed`),!0}catch(e){return console.error("[ObjectiveManager] Critical error loading save data:",e),this.activeObjectives.clear(),this.completedObjectives.clear(),!1}}destroy(){for(const[e,t]of this.activeTimers)clearTimeout(t),console.debug(`[ObjectiveManager] Cleared timer for objective: ${e}`);this.activeTimers.clear();for(const e of this.objectives.values())e.timerId&&(clearTimeout(e.timerId),e.timerId=null);this.app.off("objective:start",this.startObjective,this),this.app.off("objective:complete",this.completeObjective,this),this.app.off("objective:fail",this.failObjective,this),this.app.off("objective:attempt",this.attemptObjective,this),this.app.off("objective:update",this.updateObjective,this),this.app.off("objective:reset",this.resetObjective,this),this.objectives.clear(),this.activeObjectives.clear(),this.completedObjectives.clear()}}class D{constructor(e,t){this.app=e,this.core=t,this.isActive=!1,this.currentDialogue=null,this.currentNode=null,this.currentNPC=null,this.dialogues=new Map,this.setupEventHandlers(),this.defineDialogues()}initialize(){console.log("[DialogueSystem] Initialized")}setupEventHandlers(){this.app.on("dialogue:start",this.startDialogue,this),this.app.on("dialogue:choice",this.selectChoice,this),this.app.on("dialogue:end",this.endDialogue,this),this.app.on("dialogue:skip",this.skipDialogue,this)}defineDialogues(){this.defineDialogue("merchant_default",{start:"greeting",nodes:{greeting:{text:"Welcome, traveler! Looking for supplies?",choices:[{text:"Show me your wares",next:"open_shop"},{text:"Just browsing",next:"browsing"},{text:"Goodbye",next:"exit"}]},browsing:{text:"Take your time. Quality goods at fair prices!",choices:[{text:"Actually, show me what you have",next:"open_shop"},{text:"Goodbye",next:"exit"}]},open_shop:{text:"Here's what I have in stock.",action:"trade:open",next:"exit"},exit:{text:"Come back anytime!",end:!0}}}),this.defineDialogue("guard_default",{start:"greeting",nodes:{greeting:{text:"Halt! State your business.",choices:[{text:"I'm just passing through",next:"passing"},{text:"I'm looking for someone",next:"looking"},{text:"[Intimidation] Step aside",next:"intimidated",requiresSkill:"intimidation"}]},passing:{text:"Move along then, and don't cause trouble.",end:!0},looking:{text:"Who are you looking for?",choices:[{text:"Never mind",next:"exit"},{text:"Dr. Smith",next:"doctor_info"}]},doctor_info:{text:"The doctor? Check the medical bay, down the hall to your left.",choices:[{text:"Thanks",next:"exit"}]},intimidated:{text:"I... uh... go ahead. Just don't cause any trouble!",action:"npc:mood:nervous",end:!0},exit:{text:"Stay out of trouble.",end:!0}}}),this.defineDialogue("questgiver_anna",{start:"check_quest",nodes:{check_quest:{condition:()=>!this.core.getSystem("objectivemanager").hasObjective("deliver_package"),true:"greeting",false:"quest_active"},greeting:{text:"Oh, thank goodness someone's here! I need help.",choices:[{text:"What's wrong?",next:"explain"},{text:"I can't help right now",next:"refuse"},{text:"[Charisma] I'll help you",next:"accept_charisma",requiresSkill:"charisma"}]},explain:{text:"I have an urgent package for Dr. Smith in the medical bay, but I can't leave my post.",choices:[{text:"I'll deliver it",next:"accept"},{text:"Find someone else",next:"refuse"}]},accept:{text:"Thank you! Here's the package. Please be careful with it.",action:"quest:start:deliver_package",next:"quest_given"},accept_charisma:{text:"You seem trustworthy. Here's the package, and take this for your trouble.",action:"quest:start:deliver_package:bonus",next:"quest_given"},quest_given:{text:"Please deliver it to Dr. Smith as soon as possible!",end:!0},quest_active:{text:"Have you delivered the package to Dr. Smith yet?",choices:[{text:"Not yet",next:"hurry"},{text:"I lost it",next:"lost_package"}]},hurry:{text:"Please hurry! It's very important.",end:!0},lost_package:{text:"What?! How could you lose it? That was irreplaceable!",action:"quest:fail:deliver_package",end:!0},refuse:{text:"I understand. I'll find someone else.",end:!0}}}),this.defineDialogue("questgiver_default",{start:"greeting",nodes:{greeting:{text:"I could use some help, if you're willing.",choices:[{text:"Tell me more",next:"more"},{text:"Maybe later",next:"exit"}]},more:{text:"Come back soon. I may have a task for you.",end:!0},exit:{text:"Understood. Stay safe.",end:!0}}}),this.defineDialogue("generic_default",{start:"line",nodes:{line:{text:"Hello.",end:!0}}})}defineDialogue(e,t){this.dialogues.set(e,t)}startDialogue(e){const{npcId:t,dialogueId:i,npcData:o,playerSkills:n}=e;let s=i?this.dialogues.get(i):null;if(!s){const e=o?.template,t=e?{merchant:"merchant_default",guard:"guard_default",questgiver:"questgiver_default"}[e]:null;s=t&&this.dialogues.get(t)||this.dialogues.get("generic_default")}s?(this.isActive=!0,this.currentDialogue=s,this.currentNPC={id:t,data:o},this.playerSkills=n||{},this.showNode(s.start),this.app.fire("game:pause"),this.app.fire("ui:showDialogue",!0)):console.error(`[DialogueSystem] No dialogue available for NPC ${t}`)}showNode(e){const t=this.currentDialogue.nodes[e];if(!t)return console.error(`[DialogueSystem] Unknown node: ${e}`),void this.endDialogue();if(this.currentNode=t,t.condition){const e=t.condition()?t.true:t.false;return void this.showNode(e)}t.action&&this.executeAction(t.action);const i=this.getAvailableChoices(t.choices);this.app.fire("ui:dialogueUpdate",{speaker:this.currentNPC.data.name||"NPC",text:t.text,choices:i}),!t.choices&&t.next&&setTimeout(()=>{this.showNode(t.next)},2e3),t.end&&setTimeout(()=>{this.endDialogue()},2e3)}getAvailableChoices(e){return e?e.filter(e=>{if(e.requiresSkill){const t=this.core.getSystem("skilltreemanager");if(!t||!t.hasSkill(e.requiresSkill))return!1}if(e.requiresItem){const t=this.core.getSystem("inventorymanager");if(!t||!t.hasItem(e.requiresItem))return!1}if(e.requiresStat){const t=this.core.getSystem("statsmanager");if(!t)return!1;for(const[i,o]of Object.entries(e.requiresStat))if(t.getStat(i){let t=e.text;if(e.requiresSkill){const i=t.replace(/^\[.*?\]\s*/,"");t=`[${e.requiresSkill}] ${i}`}return{...e,text:t,originalText:e.text}}):[]}selectChoice(e){if(!this.currentNode||!this.currentNode.choices)return;const t=this.getAvailableChoices(this.currentNode.choices)[e];t&&(t.action&&this.executeAction(t.action),t.next?this.showNode(t.next):this.endDialogue())}executeAction(e){const t=e.split(":");switch(t[0]){case"trade":this.app.fire("trade:open",this.currentNPC.id);break;case"quest":if("start"===t[1]){const e=t[2],i=this.core.getSystem("objectivemanager");if(i.objectives.has(e)||this.defineQuest(e),i.startObjective(e),"deliver_package"===e){const e=this.core.getSystem("inventorymanager");e.pickupItem("annas_package",1),"bonus"===t[3]&&e.pickupItem("health_potion",2)}}else if("fail"===t[1]){const e=t[2];this.core.getSystem("objectivemanager").failObjective(e)}break;case"npc":"mood"===t[1]&&(this.currentNPC.data.mood=t[2]);break;default:this.app.fire(e,this.currentNPC)}}defineQuest(e){const t=this.core.getSystem("objectivemanager");"deliver_package"===e&&t.defineObjective("deliver_package",{name:"Deliver Package",description:"Deliver Anna's package to Dr. Smith in the medical bay",type:"simple",rewards:{experience:150,items:[{id:"health_kit",quantity:1}]}})}skipDialogue(){this.isActive&&(this.currentNode&&this.currentNode.next?this.showNode(this.currentNode.next):this.endDialogue())}endDialogue(){if(this.isActive){if(this.isActive=!1,this.currentDialogue=null,this.currentNode=null,this.currentNPC){const e=this.core.getSystem("npcmanager");e&&e.setNPCState(this.currentNPC.id,"idle")}this.currentNPC=null,this.app.fire("ui:showDialogue",!1),this.app.fire("game:resume")}}getCurrentChoices(){return this.currentNode&&this.currentNode.choices?this.getAvailableChoices(this.currentNode.choices):[]}getSaveData(){return{}}loadSaveData(e){}destroy(){this.app.off("dialogue:start",this.startDialogue,this),this.app.off("dialogue:choice",this.selectChoice,this),this.app.off("dialogue:end",this.endDialogue,this),this.app.off("dialogue:skip",this.skipDialogue,this)}}class j{constructor(e,t){this.app=e,this.core=t,this.maxSaveSlots=10,this.autoSaveSlot=0,this.savePrefix="playcanvas_save_",this.autoSaveInterval=null,this.setupEventHandlers()}initialize(){console.log("[SaveLoadManager] Initialized");const e=this.app.config||{};e.game&&e.game.autoSaveInterval&&this.startAutoSave(e.game.autoSaveInterval)}setupEventHandlers(){this.app.on("save:quick",()=>this.save(1),this),this.app.on("load:quick",()=>this.load(1),this),this.app.on("save:list",this.getSaveList,this),this.app.on("save:delete",this.deleteSave,this)}save(e=0){console.log(`[SaveLoadManager] Saving to slot ${e}`);const t={version:"1.0.0",timestamp:Date.now(),slot:e,catalogIndex:this.core.state.catalogIndex,currentContent:this.core.state.currentContent,playerPosition:this.getPlayerPosition(),playerRotation:this.getPlayerRotation(),globalData:{},catalogData:{},systems:{}};for(const[e,i]of Object.entries(this.core.systems))if(i.getSaveData)try{const o=i.getSaveData();i.getCatalogSaveData&&i.getGlobalSaveData?(t.globalData[e]=i.getGlobalSaveData(),this.core.state.catalogIndex>=0&&(t.catalogData[e]=i.getCatalogSaveData())):t.systems[e]=o}catch(t){console.error(`[SaveLoadManager] Error saving ${e}:`,t)}this.validateSaveData(t);try{const i=JSON.stringify(t);return localStorage.setItem(this.savePrefix+e,i),this.updateSaveMetadata(e,t),this.app.fire("save:complete",e),this.app.fire("ui:notification",{text:`Game saved to slot ${e}`,type:"success"}),!0}catch(t){return console.error("[SaveLoadManager] Save failed:",t),this.app.fire("save:failed",e,t),this.app.fire("ui:notification",{text:"Save failed!",type:"error"}),!1}}load(e=0){console.log(`[SaveLoadManager] Loading from slot ${e}`);try{const t=localStorage.getItem(this.savePrefix+e);if(!t)return this.app.fire("ui:notification",{text:"No save found in slot "+e,type:"warning"}),!1;const i=JSON.parse(t);return this.isCompatibleVersion(i.version)?(this.app.fire("load:start",e),this.core.state.catalogIndex>=0&&this.core.exitToVisualCatalog(),setTimeout(()=>{this.applyLoadData(i)},100),!0):(this.app.fire("ui:notification",{text:"Save file version incompatible",type:"error"}),!1)}catch(t){return console.error("[SaveLoadManager] Load failed:",t),this.app.fire("load:failed",e,t),this.app.fire("ui:notification",{text:"Load failed!",type:"error"}),!1}}validateSaveData(e){if(e.catalogIndex>=0&&e.catalogData&&e.catalogData.inventorymanager){const t=e.catalogData.inventorymanager;this.validateInventoryBoundaries(t,"catalog")}if(e.globalData&&e.globalData.inventorymanager){const t=e.globalData.inventorymanager;this.validateInventoryBoundaries(t,"global")}return console.log("[SaveLoadManager] Save data validated successfully"),!0}validateInventoryBoundaries(e,t){if(!e.items)return;const i=this.core.getSystem("inventorymanager");if(i)for(const o of e.items){const e=i.getItemInfo(o.id);if(e){const i=e.global;i!==("global"===t)&&console.warn(`[SaveLoadManager] Item scope mismatch: ${o.id} is ${i?"global":"catalog"} but found in ${t} data`)}}}applyLoadData(e){if(e.globalData)for(const[t,i]of Object.entries(this.core.systems))if(i.loadGlobalSaveData&&e.globalData[t])try{i.loadGlobalSaveData(e.globalData[t])}catch(e){console.error(`[SaveLoadManager] Error loading global data for ${t}:`,e)}if(e.systems)for(const[t,i]of Object.entries(this.core.systems))if(i.loadSaveData&&e.systems[t])try{i.loadSaveData(e.systems[t])}catch(e){console.error(`[SaveLoadManager] Error loading ${t}:`,e)}e.catalogIndex>=0?this.core.loadContent(e.catalogIndex).then(()=>{if(e.catalogData)for(const[t,i]of Object.entries(this.core.systems))if(i.loadCatalogSaveData&&e.catalogData[t])try{i.loadCatalogSaveData(e.catalogData[t])}catch(e){console.error(`[SaveLoadManager] Error loading catalog data for ${t}:`,e)}e.playerPosition&&this.setPlayerPosition(e.playerPosition),e.playerRotation&&this.setPlayerRotation(e.playerRotation),this.app.fire("load:complete",e.slot),this.app.fire("ui:notification",{text:"Game loaded",type:"success"})}):(this.app.fire("load:complete",e.slot),this.app.fire("ui:notification",{text:"Game loaded",type:"success"}))}deleteSave(e){localStorage.removeItem(this.savePrefix+e),localStorage.removeItem(this.savePrefix+"meta_"+e),this.app.fire("save:deleted",e),this.app.fire("ui:notification",{text:`Deleted save in slot ${e}`,type:"info"})}getSaveList(){const e=[];for(let t=0;t<=this.maxSaveSlots;t++){const i=this.getSaveMetadata(t);i&&e.push({slot:t,...i})}return e}getSaveMetadata(e){try{const t=localStorage.getItem(this.savePrefix+"meta_"+e);return t?JSON.parse(t):null}catch(e){return null}}updateSaveMetadata(e,t){const i={timestamp:t.timestamp,contentName:t.currentContent?t.currentContent.name:"Visual Catalog",playtime:this.getPlaytime(),level:this.getPlayerLevel()};localStorage.setItem(this.savePrefix+"meta_"+e,JSON.stringify(i))}startAutoSave(e){this.stopAutoSave(),this.autoSaveInterval=setInterval(()=>{this.save(this.autoSaveSlot)},e),console.log(`[SaveLoadManager] Auto-save enabled (${e}ms)`)}stopAutoSave(){this.autoSaveInterval&&(clearInterval(this.autoSaveInterval),this.autoSaveInterval=null)}isCompatibleVersion(e){return"1.0.0"===e}getPlayerPosition(){const e=this.app.player;return e?e.getPosition().data:[0,0,0]}getPlayerRotation(){const e=this.app.player;return e?e.getEulerAngles().data:[0,0,0]}setPlayerPosition(e){const t=this.app.player;t&&t.setPosition(e[0],e[1],e[2])}setPlayerRotation(e){const t=this.app.player;t&&t.setEulerAngles(e[0],e[1],e[2])}getPlaytime(){return Math.floor((Date.now()-this.app.startTime)/1e3/60)}getPlayerLevel(){const e=this.core.getSystem("statsmanager");return e?e.stats.level:1}exportSave(e){const t=localStorage.getItem(this.savePrefix+e);if(!t)return null;const i=new Blob([t],{type:"application/json"}),o=URL.createObjectURL(i),n=document.createElement("a");return n.href=o,n.download=`save_slot_${e}_${Date.now()}.json`,n.click(),URL.revokeObjectURL(o),!0}importSave(e,t){try{const i=JSON.parse(e);if(!i.version||!i.timestamp)throw new Error("Invalid save file");return localStorage.setItem(this.savePrefix+t,e),this.updateSaveMetadata(t,i),this.app.fire("ui:notification",{text:`Save imported to slot ${t}`,type:"success"}),!0}catch(e){return console.error("[SaveLoadManager] Import failed:",e),this.app.fire("ui:notification",{text:"Failed to import save",type:"error"}),!1}}destroy(){this.stopAutoSave(),this.app.off("save:quick",()=>this.save(1),this),this.app.off("load:quick",()=>this.load(1),this),this.app.off("save:list",this.getSaveList,this),this.app.off("save:delete",this.deleteSave,this)}}class N{constructor(e,t){this.app=e,this.core=t,this.achievements=new Map,this.unlockedAchievements=new Set,this.statistics={totalPlaytime:0,contentCompleted:new Set,objectivesCompleted:0,npcsInteracted:new Set,itemsCollected:0,skillsUnlocked:0,deaths:0},this.sessionStart=Date.now(),this.setupEventHandlers(),this.defineAchievements()}initialize(){console.log("[CompletionRecord] Initialized"),this.loadPersistentData()}setupEventHandlers(){this.app.on("objective:completed",this.onObjectiveCompleted,this),this.app.on("content:loaded",this.onContentLoaded,this),this.app.on("npc:interacted",this.onNPCInteracted,this),this.app.on("item:pickup",this.onItemPickup,this),this.app.on("skill:unlocked",this.onSkillUnlocked,this),this.app.on("player:died",this.onPlayerDied,this),this.app.on("achievement:check",this.checkAchievement,this)}defineAchievements(){this.defineAchievement("first_steps",{name:"First Steps",description:"Complete your first objective",condition:()=>this.statistics.objectivesCompleted>=1}),this.defineAchievement("quest_master",{name:"Quest Master",description:"Complete 10 objectives",condition:()=>this.statistics.objectivesCompleted>=10}),this.defineAchievement("explorer",{name:"Explorer",description:"Visit all content areas",condition:()=>this.statistics.contentCompleted.size>=4}),this.defineAchievement("social_butterfly",{name:"Social Butterfly",description:"Talk to 10 different NPCs",condition:()=>this.statistics.npcsInteracted.size>=10}),this.defineAchievement("collector",{name:"Collector",description:"Collect 50 items",condition:()=>this.statistics.itemsCollected>=50}),this.defineAchievement("skilled",{name:"Skilled",description:"Unlock 5 skills",condition:()=>this.statistics.skillsUnlocked>=5}),this.defineAchievement("master_of_all",{name:"Master of All",description:"Unlock all skills",condition:()=>{const e=this.core.getSystem("skilltreemanager");return e&&e.skills.size===e.unlockedSkills.size}}),this.defineAchievement("survivor",{name:"Survivor",description:"Complete a mission without dying",condition:()=>!1,special:!0}),this.defineAchievement("speedrunner",{name:"Speedrunner",description:"Complete any mission in under 5 minutes",condition:()=>!1,special:!0})}defineAchievement(e,t){this.achievements.set(e,{id:e,unlocked:!1,unlockedAt:null,...t})}checkAchievement(e){const t=this.achievements.get(e);return!(!t||t.unlocked||!t.condition||!t.condition()||(this.unlockAchievement(e),0))}checkAllAchievements(){for(const e of this.achievements.values())e.unlocked||e.special||this.checkAchievement(e.id)}unlockAchievement(e){const t=this.achievements.get(e);t&&!t.unlocked&&(t.unlocked=!0,t.unlockedAt=Date.now(),this.unlockedAchievements.add(e),console.log(`[CompletionRecord] Achievement unlocked: ${t.name}`),this.app.fire("achievement:unlocked",t),this.app.fire("ui:notification",{text:`Achievement: ${t.name}`,type:"achievement",duration:5e3}),this.savePersistentData())}onObjectiveCompleted(e){this.statistics.objectivesCompleted++,this.checkAllAchievements()}onContentLoaded(e,t){this.statistics.contentCompleted.add(e),"mission"===t.type&&(this.missionStartTime=Date.now(),this.missionDeaths=0),this.checkAllAchievements()}onNPCInteracted(e){this.statistics.npcsInteracted.add(e),this.checkAllAchievements()}onItemPickup(e,t){this.statistics.itemsCollected+=t,this.checkAllAchievements()}onSkillUnlocked(e){this.statistics.skillsUnlocked++,this.checkAllAchievements()}onPlayerDied(){this.statistics.deaths++,this.missionDeaths=(this.missionDeaths||0)+1}onMissionComplete(){0===this.missionDeaths&&this.unlockAchievement("survivor"),this.missionStartTime&&Date.now()-this.missionStartTime<3e5&&this.unlockAchievement("speedrunner")}getProgress(){const e=this.achievements.size,t=this.unlockedAchievements.size;return{achievements:{unlocked:t,total:e,percentage:Math.round(t/e*100)},statistics:{...this.statistics},playtime:this.getTotalPlaytime()}}getAchievementList(){const e=[];for(const t of this.achievements.values())e.push({id:t.id,name:t.name,description:t.description,unlocked:t.unlocked,unlockedAt:t.unlockedAt});return e.sort((e,t)=>e.unlocked&&!t.unlocked?-1:!e.unlocked&&t.unlocked?1:e.name.localeCompare(t.name))}getTotalPlaytime(){const e=Date.now()-this.sessionStart;return this.statistics.totalPlaytime+e}formatPlaytime(e){const t=Math.floor(e/1e3),i=Math.floor(t/60),o=Math.floor(i/60);return o>0?`${o}h ${i%60}m`:`${i}m ${t%60}s`}loadPersistentData(){try{const e=localStorage.getItem("playcanvas_completion_record");if(e){const t=JSON.parse(e);t.statistics&&(Object.assign(this.statistics,t.statistics),this.statistics.contentCompleted=new Set(t.statistics.contentCompleted),this.statistics.npcsInteracted=new Set(t.statistics.npcsInteracted)),t.unlockedAchievements&&t.unlockedAchievements.forEach(e=>{const t=this.achievements.get(e.id);t&&(t.unlocked=!0,t.unlockedAt=e.unlockedAt,this.unlockedAchievements.add(e.id))})}}catch(e){console.error("[CompletionRecord] Failed to load persistent data:",e)}}savePersistentData(){try{const e={statistics:{...this.statistics,totalPlaytime:this.getTotalPlaytime(),contentCompleted:Array.from(this.statistics.contentCompleted),npcsInteracted:Array.from(this.statistics.npcsInteracted)},unlockedAchievements:Array.from(this.unlockedAchievements).map(e=>({id:e,unlockedAt:this.achievements.get(e).unlockedAt}))};localStorage.setItem("playcanvas_completion_record",JSON.stringify(e))}catch(e){console.error("[CompletionRecord] Failed to save persistent data:",e)}}resetProgress(){for(const e of this.achievements.values())e.unlocked=!1,e.unlockedAt=null;this.unlockedAchievements.clear(),this.statistics={totalPlaytime:0,contentCompleted:new Set,objectivesCompleted:0,npcsInteracted:new Set,itemsCollected:0,skillsUnlocked:0,deaths:0},localStorage.removeItem("playcanvas_completion_record"),this.app.fire("completion:reset")}getSaveData(){return{sessionStart:this.sessionStart,missionStartTime:this.missionStartTime,missionDeaths:this.missionDeaths}}loadSaveData(e){e.sessionStart&&(this.sessionStart=e.sessionStart),e.missionStartTime&&(this.missionStartTime=e.missionStartTime),void 0!==e.missionDeaths&&(this.missionDeaths=e.missionDeaths)}destroy(){this.statistics.totalPlaytime=this.getTotalPlaytime(),this.savePersistentData(),this.app.off("objective:completed",this.onObjectiveCompleted,this),this.app.off("content:loaded",this.onContentLoaded,this),this.app.off("npc:interacted",this.onNPCInteracted,this),this.app.off("item:pickup",this.onItemPickup,this),this.app.off("skill:unlocked",this.onSkillUnlocked,this),this.app.off("player:died",this.onPlayerDied,this),this.app.off("achievement:check",this.checkAchievement,this)}}class _{constructor(){this.app=null,this.core=null,this.initialized=!1}async initialize(){console.log("[Game] Initializing PlayCanvas application...");try{this.createApplication(),this.setupGlobalErrorLogging(),this.setupUtilities(),this.core=new a(this.app),await this.app.fontManager.initialize(),this.setupSystemRegistration(),await this.core.initialize(),this.app.ui=new d(this.app),this.app.ui.initialize(),this.app.sceneTransition=new I(this.app),this.app.sceneMemory=new k(this.app),this.app.mobileOverlay=new M(this.app),this.app.fontManager.fixAllTextElements(),await this.setupInitialScene(),e.game.debugMode&&(c.init(this.app),c.createDebugUI(this.app)),this.app.start(),this.initialized=!0,console.log("[Game] Initialization complete!"),this.app.startTime=Date.now(),setTimeout(()=>{console.log("[Game] Welcome! Use debug.help() for debug commands."),this.app.fire("ui:notification",{text:"Welcome to the game! Press H to toggle controls help.",type:"info",duration:5e3}),this.app.gameSystems&&this.app.gameSystems.objectivemanager&&(this.app.gameSystems.objectivemanager.defineObjective("tutorial_move",{name:"Learn to Move",description:"Use WASD keys to move around",type:"simple"}),this.app.gameSystems.objectivemanager.startObjective("tutorial_move"))},1e3)}catch(e){throw console.error("[Game] Initialization failed:",e),e}}setupGlobalErrorLogging(){this._errorLoggingSetup||(this._errorLoggingSetup=!0,window.addEventListener("error",e=>{try{const t={message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno,error:e.error&&e.error.stack?e.error.stack:String(e.error||"")};console.error("[GlobalError]",t),this.app&&this.app.fire&&this.app.fire("ui:notification",{text:`Error: ${t.message||"Unknown"} @ ${t.filename||""}:${t.lineno||""}`,type:"error",duration:4e3})}catch(e){}}),window.addEventListener("unhandledrejection",e=>{try{console.error("[UnhandledRejection]",e.reason),this.app&&this.app.fire&&this.app.fire("ui:notification",{text:`Unhandled promise rejection: ${e.reason?.message||e.reason}`,type:"error",duration:4e3})}catch(e){}}))}createApplication(){const t=document.getElementById("application-canvas");this.app=new pc.Application(t,{mouse:new pc.Mouse(t),touch:new pc.TouchDevice(t),keyboard:new pc.Keyboard(window),gamepads:new pc.GamePads,...e.engine}),this.app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW),this.app.setCanvasResolution(pc.RESOLUTION_AUTO),this.app.systems.rigidbody.gravity.set(0,-9.8,0),window.addEventListener("resize",()=>{this.app.resizeCanvas()}),this.app.config=e,console.log("[Game] PlayCanvas application created")}setupUtilities(){this.app.assetLoader=new r(this.app),this.app.fontManager=new p(this.app),console.log("[Game] Utilities initialized")}setupSystemRegistration(){this.core.registerSystems,this.core.registerSystems=async function(){console.log("[CoreManager] Registering systems with real implementations..."),this.app.sceneBuilder=new l(this.app,this.entityFactory);const e={StatsManager:P,InventoryManager:A,SkillTreeManager:$,NPCManager:O,ObjectiveManager:L,DialogueSystem:D,SaveLoadManager:j,CompletionRecord:N};for(const[t,i]of Object.entries(e))try{const e=new i(this.app,this);this.systems[t.toLowerCase()]=e,e.initialize&&await e.initialize(),e.initialized=!0,console.log(`[CoreManager] Registered system: ${t}`)}catch(e){console.error(`[CoreManager] Failed to load system ${t}:`,e)}this.app.gameSystems=this.systems,this.app.fire("core:systemsReady")}}async setupInitialScene(){this.createBasicLighting(),this.createCamera(),await this.createVisualCatalog(),console.log("[Game] Initial scene created")}createBasicLighting(){this.app.scene.ambientLight=new pc.Color(.4,.4,.4);const e=this.core.entityFactory.createLight("directional",{color:new pc.Color(1,.9,.7),intensity:1,castShadows:!0,position:[5,10,5],rotation:[45,30,0]});e.tags.add("persistent"),e.name="MainDirectionalLight",this.app.root.addChild(e)}createCamera(){const e=this.core.entityFactory.createCamera("MainCamera",{camera:{clearColor:new pc.Color(.5,.5,.6),fov:75},position:[0,1.7,0],rotation:[0,0,0],tags:["main_camera"]});this.app.root.addChild(e),this.app.camera=e,this.app.platformCameraManager=new C(this.app),this.app.platformCameraManager.initializeForScene("catalog",e),this.app.interactionController=new i(e,this.app),this.app.interactionController.initialize(),this.app.cursorSystem=new g(this.app),this.app.cursorSystem.show(),console.log("[Game] Camera created:",e?"SUCCESS":"FAILED"),console.log("[Game] Camera position:",e?.getPosition()),console.log("[Game] Camera controller initialized"),console.log("[Game] Interaction controller initialized"),console.log("[Game] Cursor system initialized")}async createVisualCatalog(){console.log("[Game] Creating visual catalog..."),await this.createDynamicCatalogRoom(),this.createPlayer(),console.log("[Game] Visual catalog created")}createPodiumDisplay(e,t,i){const o=new pc.Entity(`BillboardScreen_${t}`);o.addComponent("screen",{referenceResolution:new pc.Vec2(400,250),scaleMode:pc.SCALEMODE_NONE,screenSpace:!1}),o.setLocalPosition(0,2.5,0);const n=this.calculateNearestPodiumDistance(e,t),s=.008*Math.min(1,n/6*.8);o.setLocalScale(s,s,s);const a={initialize:function(){this.app=o.app,this.entity=o},update:function(e){if(this.app&&this.app.camera){const e=this.app.camera.getPosition(),t=this.entity.getPosition(),i=new pc.Vec3;i.sub2(e,t);const o=i.y;i.y=Math.max(.3*i.y,-.2),i.normalize();const n=Math.atan2(i.x,i.z)*pc.math.RAD_TO_DEG,s=Math.sqrt(i.x*i.x+i.z*i.z),a=Math.atan2(o,s)*pc.math.RAD_TO_DEG*.3;this.entity.setLocalEulerAngles(a,n,0)}}};o.billboardScript=a,o.billboardScript.initialize();const r=e=>{o.billboardScript&&o.enabled&&o.parent&&o.billboardScript.update(e)};this.app.on("update",r),o.updateHandler=r,this.createRichLabel(o,t,i,"billboard"),e.addChild(o),e.billboardScreens||(e.billboardScreens=[]),e.billboardScreens.push(o)}calculateNearestPodiumDistance(e,t){const i=e.getPosition();let o=12;const n=this.app.root.find(t=>t.tags&&t.tags.has("catalog_podium")&&t!==e);for(const e of n){const t=e.getPosition(),n=i.distance(t);n>0&&n{const l=i.split(" "),c=[];let h="";for(const e of l){const t=h?`${h} ${e}`:e;if(t.length<=30?h=t:h?(c.push(h),h=e.length<=30?e:e.substring(0,30)):c.push(e.substring(0,30)),c.length>=10)break}h&&c.length<10&&c.push(h),c.forEach((i,l)=>{const c=new pc.Entity(`${r}_Line${l}_${t}_${o}`);c.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,text:i,fontSize:s,color:a,fontAsset:this.app.fontManager.getFontAssetId(),anchor:[.5,1,.5,1],pivot:[.5,1],width:400,height:s+10,wrapLines:!1,lineHeight:1}),c.setLocalPosition(0,n-60*l,0),e.addChild(c)})};n(i.title,140,64,new pc.Color(1,1,.2),"Title"),n(i.briefing&&i.briefing.setting?i.briefing.setting:i.description,-40,36,new pc.Color(.8,.8,.8),"Brief")}createPlayer(){const e=this.core.entityFactory.create("Player",{components:{model:{type:"capsule"},collision:{type:"capsule",radius:.5,height:2},rigidbody:{type:"dynamic",mass:70,linearDamping:.3,angularDamping:.3}},position:[0,1,0],tags:["player","persistent"]});if(e.model&&e.model.meshInstances){const t=new pc.StandardMaterial;t.diffuse=new pc.Color(.2,.5,.8),t.update(),e.model.meshInstances.forEach(e=>{e&&(e.material=t)})}this.app.root.addChild(e),this.app.player=e,this.setupPlayerInteractions(e)}setupPlayerInteractions(e){this.app.on("update",e=>{this.app.keyboard&&this.app.keyboard.wasPressed&&this.app.keyboard.wasPressed(pc.KEY_H)&&this.app.ui&&this.app.ui.toggleControls(),this.app.keyboard&&this.app.keyboard.wasPressed&&this.app.keyboard.wasPressed(pc.KEY_SPACE)&&this.app.ui&&this.app.ui.dismissCurrentNotification(),this.app.keyboard&&this.app.keyboard.wasPressed&&this.app.keyboard.wasPressed(pc.KEY_ESCAPE)&&this.core&&this.core.state&&this.core.state.catalogIndex>=0&&this.core.exitToVisualCatalog()}),console.log("[Game] Camera controls ready: WASD to move camera, Q/E to rotate, Arrow keys to look up/down"),console.log("[Game] Interaction controls: Look at objects to highlight them, press F to interact")}async createDynamicCatalogRoom(){console.log("[Game] Creating dynamic catalog room...");try{const e=await fetch("assets/data/catalog.json"),t=await e.json(),i=t.entries.length;console.log(`[Game] Found ${i} catalog entries, calculating room size...`);const o=this.calculatePodiumLayout(i),n=this.calculateRequiredRoomSize(o);console.log(`[Game] Creating room of size ${n} for ${i} podiums`);const s=this.app.sceneBuilder.createRoom({size:[n,8,n],position:[0,0,0],lighting:"standard"});s.name="CatalogRoom",s.tags.add("persistent"),this.app.root.addChild(s);for(let e=0;e=n){i++,o=0;const t=e-s-1,a=12+4*i;n=Math.min(a,t)}}}return console.log(`[Game] Generated ${t.length} podium positions`),t}destroy(){this.core&&this.core.destroy(),this.app&&this.app.destroy(),this.initialized=!1}cleanupBillboardLabels(){this.app.root.findByTag("catalog_podium").forEach(e=>{e.billboardScreens&&(e.billboardScreens.forEach(e=>{e.updateHandler&&(this.app.off("update",e.updateHandler),e.updateHandler=null),e.billboardScript&&(e.billboardScript=null)}),e.billboardScreens=[])}),console.log("[Game] Billboard labels cleaned up")}}let z=null,B=null;document.addEventListener("DOMContentLoaded",async()=>{console.log("[Game] DOM ready, starting game..."),"function"==typeof Ammo?(console.log("[Game] Waiting for Ammo.js to initialize..."),B=Ammo,window.AmmoFactory=Ammo,await new Promise(e=>{Ammo().then(t=>{console.log("[Game] Ammo.js physics engine ready"),z=t,window.AmmoInstance=t,console.log("[Game] Stored Ammo factory and instance for restoration"),e()})})):console.warn("[Game] Ammo.js not found, physics interactions will not work");const e=new _;try{await e.initialize(),window.game=e}catch(e){console.error("[Game] Failed to start:",e),document.body.innerHTML=`\n
\n

Game Failed to Load

\n

Check the console for details.

\n

${e.message}

\n
\n `}})})()})();