diff --git "a/bundle.js" "b/bundle.js" new file mode 100644--- /dev/null +++ "b/bundle.js" @@ -0,0 +1 @@ +(()=>{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/GarageScene.js":[585,585],"./scenes/IndustrialFacility.js":[386,96,386],"./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/QuantumLab.js":[420,96,420],"./scenes/ResearchFacility.js":[260,96,260],"./scenes/SecurityComplex.js":[364,96,364],"./scenes/TavernScene.js":[576,576],"./scenes/UndergroundBunker.js":[614,96,614]};function s(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],s=t[0];return Promise.all(t.slice(1).map(i.e)).then(()=>i(s))}s.keys=()=>Object.keys(o),s.id=949,e.exports=s}},o={};function s(e){var t=o[e];if(void 0!==t)return t.exports;var a=o[e]={exports:{}};return i[e](a,a.exports,s),a.exports}s.m=i,s.d=(e,t)=>{for(var i in t)s.o(t,i)&&!s.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},s.f={},s.e=e=>Promise.all(Object.keys(s.f).reduce((t,i)=>(s.f[i](e,t),t),[])),s.u=e=>e+".bundle.js",s.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e={},t="playcanvas-game:",s.l=(i,o,a,n)=>{if(e[i])e[i].push(o);else{var r,l;if(void 0!==a)for(var c=document.getElementsByTagName("script"),h=0;h{r.onerror=r.onload=null,clearTimeout(m);var s=e[i];if(delete e[i],r.parentNode&&r.parentNode.removeChild(r),s&&s.forEach(e=>e(o)),t)return t(o)},m=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)}},s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;s.g.importScripts&&(e=s.g.location+"");var t=s.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(/\/[^\/]+$/,"/"),s.p=e})(),(()=>{var e={792:0};s.f.j=(t,i)=>{var o=s.o(e,t)?e[t]:void 0;if(0!==o)if(o)i.push(o[2]);else{var a=new Promise((i,s)=>o=e[t]=[i,s]);i.push(o[2]=a);var n=s.p+s.u(t),r=new Error;s.l(n,i=>{if(s.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var a=i&&("load"===i.type?"missing":i.type),n=i&&i.target&&i.target.src;r.message="Loading chunk "+t+" failed.\n("+a+": "+n+")",r.name="ChunkLoadError",r.type=a,r.request=n,o[1](r)}},"chunk-"+t,t)}};var t=(t,i)=>{var o,a,[n,r,l]=i,c=0;if(n.some(t=>0!==e[t])){for(o in r)s.o(r,o)&&(s.m[o]=r[o]);l&&l(s)}for(t&&t(i);c{"use strict";const e={engine:{preferWebGl2:!0,antialias:!0,alpha:!1,preserveDrawingBuffer:!1},game:{catalogPodiums:9999,autoSaveInterval:6e4,debugMode:!0},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 s={handler:t,scope:i};if(this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(s),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(s),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.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 s=o?o.entity:null;if(s&&s.tags&&s.tags.has("npc_body_part")){console.log("[Interaction] Hit NPC body part:",s.name,"looking for parent NPC");let e=s.parent;for(;e;){if(e.tags&&e.tags.has("npc")){console.log("[Interaction] Found parent NPC:",e.name),s=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)),s!==this.highlightedEntity&&(this.highlightedEntity&&(this.setHighlight(this.highlightedEntity,!1),this.app.fire("interaction:unhover")),s&&(this.setHighlight(s,!0),this.app.fire("interaction:hover",s),this.checkCursorInteraction(s)),this.highlightedEntity=s),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()}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.app.fire("ui:notification",{text:`Loading content ${e}...`,type:"info"}),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 s=i.material,a=e._originalMaterials.get(o);t?(s.emissive.set(this.highlightColor.r,this.highlightColor.g,this.highlightColor.b),s.emissiveIntensity=.5):a&&(s.emissive.copy(a.emissive),s.emissiveIntensity=a.emissiveIntensity),s.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,s=this.interactionDistance;i.forEach(i=>{if(!i.enabled)return;const a=i.getPosition(),n=(new pc.Vec3).sub2(a,e),r=n.length();r>s||(n.normalize(),t.dot(n)>.7&&(s=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 ===")}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.initialized=!1}}class o{constructor(e,t={}){this.entity=e,this.onInteract=t.onInteract||(()=>{}),console.log(`[InteractionComponent] Entity ${e.name} set up for interactions`)}destroy(){}}class a{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]});this.createNPCBody(i,t),i.interaction=new o(i,{interactionDistance:3,onInteract:t=>{if(i._interacting)return void 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);const o=this.core.getSystem("dialoguesystem");o&&o.startDialogue(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 s=()=>{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(),s=new pc.Vec3;s.sub2(i,t),s.y=0,s.normalize();const a=Math.atan2(s.x,s.z)*pc.math.RAD_TO_DEG;o.setEulerAngles(0,a,0)}catch(e){o.setEulerAngles(0,0,0)}}};this.app.on("update",s),e._labelUpdateHandler=s,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],s=t.armColor||[.1,.1,.1],a=t.legColor||[.2,.2,.6],n=new pc.Entity(`${e.name}_Body`);n.addComponent("model",{type:"capsule"}),n.setLocalScale(.5,1,.5),n.setLocalPosition(0,1.5,0),n.tags.add("npc_body_part");const r=new pc.StandardMaterial;r.diffuse.set(i[0],i[1],i[2]),r.update(),setTimeout(()=>{n.model&&n.model.model&&n.model.model.meshInstances&&(n.model.model.meshInstances[0].material=r)},0),e.addChild(n),e._body=n,this.createNPCHead(e,o),this.createNPCLimbs(e,s,a),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 s=new pc.Entity(`${e.name}_LeftArm`);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._leftArm=s;const a=new pc.Entity(`${e.name}_RightArm`);a.addComponent("model",{type:"cylinder"}),a.setLocalScale(.1,.5,.1),a.setLocalPosition(.4,1.5,0),a.tags.add("npc_body_part"),setTimeout(()=>{a.model&&a.model.model&&a.model.model.meshInstances&&(a.model.model.meshInstances[0].material=o)},0),e.addChild(a),e._rightArm=a;const n=new pc.StandardMaterial;n.diffuse.set(i[0],i[1],i[2]),n.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=n)},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=n)},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,s=e._head.getLocalPosition();e._head.setLocalPosition(s.x,e._headOriginalY+o,s.z)}if(e._leftArm&&e._rightArm){const t=.15,i=2.5,o=Math.sin(e._animationTime*i)*t,s=Math.sin((e._animationTime+Math.PI)*i)*t,a=e._leftArm.getLocalPosition(),n=e._rightArm.getLocalPosition();e._leftArm.setLocalPosition(e._leftArmOriginalX+o,a.y,a.z),e._rightArm.setLocalPosition(e._rightArmOriginalX+s,n.y,n.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 s=Math.atan2(o.x,o.z)*pc.math.RAD_TO_DEG;e.setEulerAngles(0,s,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 s=null;this.app.fontManager&&this.app.fontManager.initialized&&(s=this.app.fontManager.getFontAssetId());const a=this.createUI(`PodiumTitle_${t}`,{components:{element:{type:pc.ELEMENTTYPE_TEXT,text:o.title,fontSize:24,color:new pc.Color(1,1,.8,1),fontAsset:s,anchor:[.5,.5,.5,.5],pivot:[.5,.5],width:300,height:40,autoWidth:!1,autoHeight:!1},screen:{screenSpace:!1,resolution:new pc.Vec2(300,40),referenceResolution:new pc.Vec2(300,40)}},position:[0,2,0],tags:["ui","podium_label","persistent"]}),n=this.createUI(`PodiumDesc_${t}`,{components:{element:{type:pc.ELEMENTTYPE_TEXT,text:o.description,fontSize:16,color:new pc.Color(.8,.8,.8,1),fontAsset:s,anchor:[.5,.5,.5,.5],pivot:[.5,.5],width:350,height:30,autoWidth:!1,autoHeight:!1,wrapLines:!0},screen:{screenSpace:!1,resolution:new pc.Vec2(350,30),referenceResolution:new pc.Vec2(350,30)}},position:[0,1.5,0],tags:["ui","podium_label","persistent"]}),r="mission"===o.type?new pc.Color(1,.3,.3,1):new pc.Color(.3,.7,1,1),l=this.createUI(`PodiumType_${t}`,{components:{element:{type:pc.ELEMENTTYPE_TEXT,text:o.type.toUpperCase(),fontSize:14,color:r,fontAsset:s,anchor:[.5,.5,.5,.5],pivot:[.5,.5],width:100,height:25,autoWidth:!1,autoHeight:!1},screen:{screenSpace:!1,resolution:new pc.Vec2(100,25),referenceResolution:new pc.Vec2(100,25)}},position:[0,1.1,0],tags:["ui","podium_label","persistent"]});[a,n,l].forEach(i=>{i.screen&&(i.screen.screenSpace=!1),e.addChild(i),console.log(`[EntityFactory] Added label ${i.name} to podium ${t}`)});const c=()=>{[a,n,l].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(),s=new pc.Vec3;s.sub2(o,i).normalize();const a=new pc.Mat4;a.setFromAxisAngle(pc.Vec3.UP,Math.atan2(s.x,s.z)),e.setRotation(a.getEulerAngles())}catch(i){try{e.lookAt(t.getPosition()),e.rotateLocal(0,180,0)}catch(e){}}}})};this.app.on("update",c),e._podiumLabelUpdateHandler=c}catch(e){console.error(`[EntityFactory] Failed to add label to podium ${t}:`,e)}}}class n{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 a(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 s(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 a=this.app.root.findByName("SpawnPoint"),n=this.app.root.findByTag("player"),r=n&&n.length>0?n[0]:null;a&&r&&a.getPosition&&r.setPosition&&(r.setPosition(a.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,this.rebuildPhysicsComponents(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),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"})})}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,"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){if(e.collision&&(e.collision.enabled=!0),e.rigidbody&&(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){const i=t.getCollisionObjectArray();let o=!1;for(let t=0;t{this.rebuildPhysicsComponents(e)})}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,"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"),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,s)=>{const a=new pc.Texture(this.app.graphicsDevice,{name:t.name||e,...t}),n=new Image;n.crossOrigin="anonymous",n.onload=()=>{a.setSource(n),this.trackAsset(i,a),o(a)},n.onerror=()=>{s(new Error(`Failed to load texture: ${e}`))},n.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",(s,a)=>{s?o(new Error(`Failed to load model: ${e} - ${s}`)):(this.trackAsset(i,a),t(a))})})}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",(s,a)=>{s?o(new Error(`Failed to load font: ${e} - ${s}`)):(this.trackAsset(i,a),t(a))})})}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",(s,a)=>{s?o(new Error(`Failed to load sound: ${e} - ${s}`)):(this.trackAsset(i,a),t(a))})})}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[s,a]of this.loadedAssets.entries())if(s.startsWith("texture_")){if(t++,a.getSource&&a.getSource()){const t=a.getSource();e+=t.width*t.height*4}}else s.startsWith("model_")?i++:s.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:s="standard"}=e,a=this.factory.create("Room",{position:i}),n=this.buildFloor({size:[t[0],1,t[2]],position:[0,0,0]});a.addChild(n),this.createRoomWalls(a,t,o);const r=this.buildCeiling({size:[t[0],1,t[2]],height:t[1]});return a.addChild(r),"standard"===s&&this.addStandardRoomLighting(a,t),a}createRoomWalls(e,t,i){const o=t[1],s=.5;[{pos:[t[0]/2,o/2,0],rot:[0,0,0],scale:[s,o,t[2]]},{pos:[-t[0]/2,o/2,0],rot:[0,0,0],scale:[s,o,t[2]]},{pos:[0,o/2,t[2]/2],rot:[0,0,0],scale:[t[0],o,s]},{pos:[0,o/2,-t[2]/2],rot:[0,0,0],scale:[t[0],o,s]}].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:s=[0,0,0],direction:a="z"}=e,n=this.factory.create("Corridor",{position:s}),r={size:"x"===a?[t,o,i]:[i,o,t],position:[0,0,0],lighting:"corridor"},l=this.createRoom(r);return n.addChild(l),n}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"),s=t.state?.catalogIndex||-1,a=s>=0?e.root.findByTag(`catalog_${s}`):[];console.log(`Entities - Total: ${i.length}, Persistent: ${o.length}, Content: ${a.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"))},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 s=new pc.StandardMaterial;return s.diffuse=i,s.emissive=i,s.emissiveIntensity=.3,s.update(),o.model.meshInstances[0].material=s,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 s=Math.round(i/((o-t)/1e3));window.debugStats.fps=s,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){this.app=e,this.uiRoot=null,this.elements=new Map,this.initialized=!1,this.activeNotifications=[],this.persistentNotifications=new Map}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.createHUD(),this.createObjectiveDisplay(),this.createControlsHelp(),this.createNotificationSystem(),this.createPersistentNotificationArea(),this.createInventoryDisplay(),this.setupEventListeners(),this.initialized=!0,console.log("[UIManager] Initialized")}createHUD(){const e=new pc.Entity("HUD");e.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[0,1,1,1],pivot:[.5,1],width:1280,height:100}),this.uiRoot.addChild(e),this.elements.set("hud",e);const t=new pc.Entity("HealthBarBg");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,.5,0,.5],pivot:[0,.5],x:20,width:200,height:30,color:new pc.Color(.2,.2,.2),opacity:.8}),e.addChild(t);const i=new pc.Entity("HealthBarFill");i.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(.8,.2,.2)}),t.addChild(i),this.elements.set("healthFill",i);const o=new pc.Entity("HealthText");o.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]}),t.addChild(o),this.elements.set("healthText",o);const s=new pc.Entity("StatsDisplay");s.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,anchor:[0,.5,0,.5],pivot:[0,.5],x:20,y:-40,fontSize:14,text:"Level: 1 | XP: 0 / 100",color:new pc.Color(1,1,1),fontAsset:this.app.fontManager.getFontAssetId(),autoWidth:!1,width:300,height:30,alignment:[0,.5]}),e.addChild(s),this.elements.set("statsText",s)}createObjectiveDisplay(){const e=new pc.Entity("ObjectivesContainer");e.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[1,1,1,1],pivot:[1,1],x:-20,y:-120,width:300,height:200,useInput:!1}),this.uiRoot.addChild(e),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:-30,height:170,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=new pc.Entity("ControlsHelp");e.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[1,0,1,0],pivot:[1,0],x:-15,y:15,width:220,height:140}),this.uiRoot.addChild(e),this.elements.set("controls",e);const t=new pc.Entity("ControlsBg");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.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\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=new pc.Entity("NotificationContainer");e.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[.25,.7,.75,.95],pivot:[.5,.5],margin:[10,10,10,10]}),this.uiRoot.addChild(e),this.elements.set("notifications",e)}createPersistentNotificationArea(){const e=new pc.Entity("PersistentNotificationContainer");e.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[.3,.85,.7,1],pivot:[.5,1],y:-10,width:300,height:60}),this.uiRoot.addChild(e),this.elements.set("persistentNotifications",e);const t=new pc.Entity("PersistentNotifBg");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,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("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)}showNotification(e){const{text:t,type:i="info",duration:o=3e3}=e,s=new pc.Entity("Notification");s.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[0,0,1,0],pivot:[.5,0],height:60,margin:[5,5,5,5]});const a=new pc.Entity("NotifBg");a.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.5,.5],color:this.getNotificationColor(i),opacity:.9}),s.addChild(a);const n=new pc.Entity("NotifText");n.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]}),a.addChild(n);const r=this.elements.get("notifications");if(!r)return void console.warn("[UIManager] Notifications container not found");r.addChild(s);const l={entity:s,timeoutId:null};this.activeNotifications.push(l),s.setLocalScale(.8,.8,1);let c=.8;const h=()=>{c+=.04,c<=1&&(s.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:s=null}=e;if(!t)return void console.warn("[UIManager] Persistent notification requires an id");this.removePersistentNotification({id:t});const a=this.elements.get("persistentNotifications"),n=this.elements.get("persistentBg");if(!a||!n)return;0===this.persistentNotifications.size&&(n.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:s||this.getNotificationColor(o),fontAsset:this.app.fontManager.getFontAssetId(),autoHeight:!1,wrapLines:!1,alignment:[.5,.5]}),a.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 s=this.persistentNotifications.get(t);s&&s.element&&(s.element.text=i,o&&(s.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),s=Math.min(400,Math.max(110,o+50));t.element&&(t.element.height=s),i&&i.element&&(i.element.height=s-30)}updateStats(e){const t=this.elements.get("statsText");if(t&&t.element&&e){const i=e.level||1,o=e.experience||0,s=e.experienceNext||100;t.element.text=`Level: ${i} | XP: ${o} / ${s}`}}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=new pc.Entity("InventoryContainer");e.addComponent("element",{type:pc.ELEMENTTYPE_GROUP,anchor:[0,0,0,0],pivot:[0,0],x:20,y:200,width:220,height:160}),this.uiRoot.addChild(e),this.elements.set("inventory",e);const t=new pc.Entity("InventoryBackground");t.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.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",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}updateInventoryDisplay(){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 t=e.getItemInfo(i.id);if(t){const e=i.quantity>1?` x${i.quantity}`:"";o.push(`• ${t.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)}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("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.uiRoot&&this.uiRoot.destroy()}}class d{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 p{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=s=>{if(i+=s/.3,i>=1)return e++,this.cursorRing.element.width=32,this.cursorRing.element.height=32,void(e<3&&setTimeout(t,100));const a=1+.5*Math.sin(i*Math.PI);this.cursorRing.element.width=32*a,this.cursorRing.element.height=32*a,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 s=this.entity.forward.clone();s.y=0,s.normalize();const a=this.entity.right.clone();a.y=0,a.normalize(),this.moveDirection.set(0,0,0),this.moveDirection.add(s.mulScalar(t)),this.moveDirection.add(a.mulScalar(e)),this.moveDirection.lengthSq()>0&&(this.moveDirection.normalize().mulScalar(i*o),this.entity.translate(this.moveDirection))}processPhysicsMovement(e,t,i,o){const s=this.entity.forward.clone();s.y=0,s.normalize();const a=this.entity.right.clone();if(a.y=0,a.normalize(),this.moveDirection.set(0,0,0),this.moveDirection.add(s.mulScalar(t)),this.moveDirection.add(a.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 g{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,s=e.rotation;let a=0;const n=1/this.transitionSpeed;this.app.fire("camera:transitionStart");const r=()=>{a+=this.app.dt/n,a=Math.min(a,1);const e=this.easeInOutQuad(a),l=(new pc.Vec3).lerp(t,o,e);this.entity.setPosition(l);const c=(new pc.Vec3).lerp(i,s,e);this.entity.setEulerAngles(c),a<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.isTouchStarted||0===e.changedTouches.length)return;const t=e.changedTouches[0],i=new pc.Vec2(t.x,t.y),o=this.touchStartPos.distance(i),s=Date.now();this.touchStartTime,o{console.error("Failed to load content:",e)})}}}else console.log(`[TouchInteractionController] Non-interactive entity tapped: ${e.name}`)}handleEmptySpaceTap(e,t,i){"double"===i&&this.app.fire("camera:requestAngleMenu"),console.log(`[TouchInteractionController] ${i} tap on empty space`)}showTouchFeedback(e,t){if(e.model){const t=e.getLocalScale().clone();e.setLocalScale(t.mulScalar(1.1)),setTimeout(()=>{e&&e.setLocalScale&&e.setLocalScale(t)},150)}this.triggerHapticFeedback(),this.showRippleEffect(t),this.playTouchSound()}triggerHapticFeedback(){navigator.vibrate&&navigator.vibrate(50)}showRippleEffect(e){this.app.fire("ui:showRipple",{worldPosition:e,duration:500,color:new pc.Color(1,1,1,.5)})}playTouchSound(){this.app.fire("audio:playEffect",{sound:"touch_feedback",volume:.3})}testRaycastAtScreenCenter(){const e=window.innerWidth/2,t=window.innerHeight/2;console.log("[TouchInteractionController] Testing raycast at screen center"),this.performTouchRaycast(e,t,"test")}setEnabled(e){this.enabled=e,e&&!this.initialized&&this.initialize()}destroy(){this.initialized=!1,this.app.touch&&(this.app.touch.off(pc.EVENT_TOUCHSTART,this.onTouchStart,this),this.app.touch.off(pc.EVENT_TOUCHEND,this.onTouchEnd,this),this.app.touch.off(pc.EVENT_TOUCHMOVE,this.onTouchMove,this)),console.log("[TouchInteractionController] Destroyed")}}class y{constructor(e,t){this.entity=e,this.app=t,this.movementSpeed=5,this.lookSensitivity=.3,this.pitchLimit=89,this.eulers=new pc.Vec3,this.pitch=0,this.yaw=0,this.moveDirection=new pc.Vec3,this.joystickActive=!1,this.joystickCenter=new pc.Vec2,this.joystickCurrent=new pc.Vec2,this.joystickRadius=60,this.lookTouchActive=!1,this.lastLookTouchPos=new pc.Vec2,this.initialized=!1}initialize(){console.log("[MobileCameraController] Initializing mobile camera controls...");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.setupTouchControls(),this.setupVirtualJoystick(),this.app.on("update",this.update,this),this.initialized=!0,console.log("[MobileCameraController] Mobile camera controls active"),console.log("- Left side: Virtual joystick for movement"),console.log("- Right side: Touch and drag to look around"),console.log("- Center: Tap to interact with objects")}setupTouchControls(){this.app.touch&&(this.app.touch.on(pc.EVENT_TOUCHSTART,this.onTouchStart,this),this.app.touch.on(pc.EVENT_TOUCHMOVE,this.onTouchMove,this),this.app.touch.on(pc.EVENT_TOUCHEND,this.onTouchEnd,this))}setupVirtualJoystick(){this.app.fire("ui:createVirtualJoystick",{position:{x:80,y:window.innerHeight-80},radius:this.joystickRadius,onMove:e=>this.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,s=this.entity.forward.clone();s.y=0,s.normalize();const a=this.entity.right.clone();a.y=0,a.normalize(),this.moveDirection.set(0,0,0),this.moveDirection.add(s.mulScalar(i)),this.moveDirection.add(a.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 s=0;this.app.keyboard.isPressed(pc.KEY_UP)&&(s=-1),this.app.keyboard.isPressed(pc.KEY_DOWN)&&(s=1),this.pitch+=90*s*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 f={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"]}]}}};class v{constructor(e){this.app=e,this.platform=this.detectPlatform(),this.currentController=null,this.touchController=null,this.userPreference=this.loadUserPreference(),console.log("[PlatformAdaptiveCameraManager] Initialized for platform:",this.platform.type)}detectPlatform(){const e=/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||"ontouchstart"in window,t=/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(navigator.userAgent);return{type:e?t?"tablet":"mobile":"desktop",capabilities:{touch:e||t,keyboard:!e,mouse:!e&&!t,gamepad:!1},screenSize:{width:window.innerWidth,height:window.innerHeight,ratio:window.innerWidth/window.innerHeight}}}initializeForScene(e,t){const i=f[e];if(!i)return console.log(`[PlatformAdaptiveCameraManager] No config for scene "${e}", using standard controller`),void this.initializeStandardController(t);const o=i.designPriority,s=this.checkUserOverride();"mobile-first"===o?"mobile"!==this.platform.type||s?this.initializeFallbackController(t,i):this.initializeMobileFirstController(t,i):"mobile"===this.platform.type?this.initializeMobileFallbackController(t,i):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 g(e,this.app,t),this.currentController.initialize(),this.touchController=new u(e,this.app),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 y(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 g(e,this.app,i),this.currentController.initialize(),this.touchController=new u(e,this.app),this.app.fire("ui:showMobileControls",{showAngleButtons:!1,showTouchHints:!0,simplified:!0}),console.log("[PlatformAdaptiveCameraManager] Initialized mobile fallback controller")}setupControlSwitching(e,t){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 b{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 s=new pc.Entity("ProgressBg");s.addComponent("element",{type:pc.ELEMENTTYPE_IMAGE,anchor:[0,0,1,1],pivot:[.5,.5],color:new pc.Color(.2,.2,.2,.8)}),o.addChild(s);const a=new pc.Entity("ProgressFill");a.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(a),this.progressFill=a,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:s=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,s/2);break;case"slide":await this.slideTransition(!0,s/2);break;case"circle":await this.circleTransition(!0,s/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,s=e?1:0,a=Date.now(),n=()=>{const e=Date.now()-a,r=Math.min(e/t,1),l=o+(s-o)*this.easeInOut(r);this.overlay.element.color.a=l,this.loadingText.element.color.a=l,this.spinner.element.opacity=l,r<1?requestAnimationFrame(n):i()};n()})}async slideTransition(e,t){return new Promise(i=>{const o=e?-720:0,s=e?0:-720,a=Date.now();this.overlay.element.color.a=1,this.loadingText.element.color.a=1,this.spinner.element.opacity=1;const n=()=>{const e=Date.now()-a,r=Math.min(e/t,1),l=o+(s-o)*this.easeInOut(r);this.overlay.setLocalPosition(0,l,0),r<1?requestAnimationFrame(n):(this.overlay.setLocalPosition(0,0,0),i())};n()})}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,s=Date.now(),a=()=>{const e=Date.now()-s,n=Math.min(e/500,1),r=i+(o-i)*this.easeInOut(n);this.progressFill.element.width=r,n<1?requestAnimationFrame(a):t()};a()})}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 C{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 w{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)}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")}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 S{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(){this.currentCatalogIndex=-1,this.restoreGlobalInventory(),console.log("[InventoryManager] Returned to global inventory")}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})}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,s]of this.items.entries())if(s.id===e&&s.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 E{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 T{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"]}),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,patrolPath:[]}),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"]}),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})}defineTemplate(e,t){this.npcTemplates.set(e,t)}spawnNPC(e){const{template:t,id:i,position:o,rotation:s,customData:a}=e,n=this.npcTemplates.get(t);if(!n)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:a?.scale||[1,2,1],displayName:a?.displayName||n.name||r});n.model&&n.model.color&&setTimeout(()=>{const e=c.model;e&&e.meshInstances&&e.meshInstances.length>0?(e.meshInstances.forEach(e=>{if(e&&e.material){const t=e.material.clone();t.diffuse=n.model.color,t.update(),e.material=t}}),console.log(`[NPCManager] Applied color ${n.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:n.health,maxHealth:n.health,faction:n.faction,mood:"neutral",willingness:.5,...n,...a};return this.app.root.addChild(c),this.npcs.set(r,h),this.initializeNPCBehavior(h),"stationary"!==h.behavior&&"patrol"!==h.behavior||setTimeout(()=>{this.core.entityFactory&&this.npcs.has(r)&&this.core.entityFactory.setNPCRandomTarget(c)},1e3+2e3*Math.random()),this.app.fire("npc:spawned",r,h),h}removeNPC(e){const t=this.npcs.get(e);t&&(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){if(!e.patrolPath||0===e.patrolPath.length)return;e.currentPatrolIndex||(e.currentPatrolIndex=0);const i=e.patrolPath[e.currentPatrolIndex],o=e.entity.getPosition();if(o.distance(i)<1)e.currentPatrolIndex=(e.currentPatrolIndex+1)%e.patrolPath.length;else{const s=(new pc.Vec3).sub2(i,o).normalize(),a=2;e.entity.translate(s.x*a*t,0,s.z*a*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(),s=o.distance(i);if(s>e.aggroRadius)this.setNPCState(e.id,this.npcStates.IDLE),e.target=null;else if(s>2){const s=(new pc.Vec3).sub2(i,o).normalize(),a=4;e.entity.translate(s.x*a*t,0,s.z*a*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(),s=(new pc.Vec3).sub2(o,i);s.y=0,s.normalize();const a=Math.atan2(s.x,s.z)*(180/Math.PI);e.entity.setEulerAngles(0,a,0)}isPlayerNearNPC(e){const t=this.app.player;return!(!t||!e.entity)&&t.getPosition().distance(e.entity.getPosition())<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(){for(const e of this.npcs.keys())this.removeNPC(e);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)}}class k{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,s=[])=>{if(t.has(o))return console.error(`[ObjectiveManager] Circular dependency detected: ${[...s,o].join(" -> ")}`),!0;if(e.has(o))return!1;const a=this.objectives.get(o);if(!a||!a.prerequisites)return e.add(o),!1;t.add(o);for(const e of a.prerequisites)if(i(e,[...s,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){console.log(`[ObjectiveManager] Content loaded: ${e}, content type: ${t?.type}`),this.isInCatalog=-1===e,-1===e&&console.log("[ObjectiveManager] Returning to catalog room, preserving mission progress"),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 M{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}}})}defineDialogue(e,t){this.dialogues.set(e,t)}startDialogue(e){const{npcId:t,dialogueId:i,npcData:o,playerSkills:s}=e,a=this.dialogues.get(i);a?(this.isActive=!0,this.currentDialogue=a,this.currentNPC={id:t,data:o},this.playerSkills=s||{},this.showNode(a.start),this.app.fire("game:pause"),this.app.fire("ui:showDialogue",!0)):console.error(`[DialogueSystem] Unknown dialogue: ${i}`)}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 I{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),s=document.createElement("a");return s.href=o,s.download=`save_slot_${e}_${Date.now()}.json`,s.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 A{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 P{constructor(){this.app=null,this.core=null,this.initialized=!1}async initialize(){console.log("[Game] Initializing PlayCanvas application...");try{this.createApplication(),this.setupUtilities(),this.core=new n(this.app),await this.app.fontManager.initialize(),this.setupSystemRegistration(),await this.core.initialize(),this.app.ui=new h(this.app),this.app.ui.initialize(),this.app.sceneTransition=new b(this.app),this.app.sceneMemory=new C(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}}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 d(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:w,InventoryManager:S,SkillTreeManager:E,NPCManager:T,ObjectiveManager:k,DialogueSystem:M,SaveLoadManager:I,CompletionRecord:A};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 v(this.app),this.app.platformCameraManager.initializeForScene("catalog",e),this.app.interactionController=new i(e,this.app),this.app.interactionController.initialize(),this.app.cursorSystem=new p(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=i?i.title:`Content ${t}`;[{name:"Front",rotation:[0,0,0]},{name:"Back",rotation:[0,180,0]},{name:"Left",rotation:[0,-90,0]},{name:"Right",rotation:[0,90,0]}].forEach(i=>{const s=new pc.Entity(`TextScreen_${i.name}`);s.addComponent("screen",{referenceResolution:new pc.Vec2(200,100),scaleMode:pc.SCALEMODE_NONE,screenSpace:!1}),s.setLocalPosition(0,2.5,0),s.setLocalScale(.01,.01,.01),s.setLocalEulerAngles(...i.rotation);const a=new pc.Entity(`PodiumLabel_${t}_${i.name}`);a.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,text:o,fontSize:24,color:new pc.Color(1,1,.2),fontAsset:this.app.fontManager.getFontAssetId(),anchor:[.5,.5,.5,.5],pivot:[.5,.5],width:200,height:100,wrapLines:!0}),s.addChild(a),e.addChild(s)})}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),s=this.calculateRequiredRoomSize(o);console.log(`[Game] Creating room of size ${s} for ${i} podiums`);const a=this.app.sceneBuilder.createRoom({size:[s,8,s],position:[0,0,0],lighting:"standard"});a.name="CatalogRoom",a.tags.add("persistent"),this.app.root.addChild(a);for(let e=0;e=s){i++,o=0;const t=e-a-1,n=12+4*i;s=Math.min(n,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}}let x=null,L=null;document.addEventListener("DOMContentLoaded",async()=>{console.log("[Game] DOM ready, starting game..."),"function"==typeof Ammo?(console.log("[Game] Waiting for Ammo.js to initialize..."),L=Ammo,window.AmmoFactory=Ammo,await new Promise(e=>{Ammo().then(t=>{console.log("[Game] Ammo.js physics engine ready"),x=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 P;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 `}})})()})(); \ No newline at end of file