Testhostplaycanvasmobile / 96.bundle.js
kwabs22
Added latest updates
3463977
"use strict";(self.webpackChunkplaycanvas_game=self.webpackChunkplaycanvas_game||[]).push([[96],{96:(t,e,o)=>{o.d(e,{n:()=>r});class i{constructor(t,e){this.app=t,this.id=e.id,this.state="locked",this.requirement=e.requirement||{type:"none"},this.fromRoom=e.fromRoom,this.toRoom=e.toRoom,this.animation=e.animation||"slide",this.entity=null,this.playerSpawnPosition=e.playerSpawnPosition||[0,0,0],this.onTransition=e.onTransition||null}createEntity(t,e,o=[0,0,0]){return this.entity=t.create(this.id,{components:{model:{type:"box"},collision:{type:"box",halfExtents:[.1,1.5,1]},rigidbody:{type:"kinematic"}},position:e,rotation:o,scale:[.2,3,2],tags:["door","interactive"]}),this.entity.name=this.id,this.updateDoorAppearance(),this.entity}updateDoorAppearance(){if(!this.entity||!this.entity.model)return;const t=new pc.StandardMaterial;switch(this.state){case"locked":t.diffuse=new pc.Color(.7,.3,.3),t.emissive=new pc.Color(.2,0,0);break;case"unlocked":case"open":t.diffuse=new pc.Color(.3,.7,.3),t.emissive=new pc.Color(0,.2,0);break;default:t.diffuse=new pc.Color(.5,.5,.5)}t.metalness=.8,t.update(),this.entity.model.meshInstances.forEach(e=>e.material=t)}canUnlock(t){if(!this.requirement||!t)return!0;switch(this.requirement.type){case"keycard":return t.inventory?.has(this.requirement.item)||!1;case"objective":try{const e=t.core?.getSystem("objectivemanager");return e?.isComplete(this.requirement.id)||!1}catch(t){return console.warn(`[DoorEntity] Error checking objective: ${t.message}`),!1}case"skill":return t.skills?.hasLevel?.(this.requirement.skill,this.requirement.level||1)||!1;case"sequence":return this.checkInteractionSequence(t.lastInteractions);case"none":return!0;default:return!1}}checkInteractionSequence(t){if(!this.requirement.sequence||!t)return!1;const e=this.requirement.sequence,o=t.slice(-e.length);return e.every((t,e)=>o[e]===t)}async unlock(t){if(!t)return console.error("[DoorEntity] No gameState provided to unlock()"),!1;if(!this.canUnlock(t)){const e=this.getUnlockFailureReason(t);return this.app.fire("ui:notification",{text:`Door locked: ${e}`,type:"warning",duration:3e3}),!1}return this.state="opening",this.updateDoorAppearance(),await this.playAnimation("open"),this.state="open",this.updateDoorAppearance(),this.app.fire("ui:notification",{text:`Access granted to ${this.toRoom.replace("_"," ")}`,type:"success",duration:2e3}),!0}getUnlockFailureReason(t){switch(this.requirement.type){case"keycard":return`Requires ${this.requirement.item.replace("_"," ")}`;case"objective":return`Complete "${this.requirement.id}" first`;case"skill":return`Requires ${this.requirement.skill} level ${this.requirement.level}`;case"sequence":return"Requires specific interaction sequence";default:return"Unknown requirement"}}async playAnimation(t){if(!this.entity)return;const e=this.entity.getLocalPosition().clone(),o=this.entity.getLocalScale().clone(),i=this.entity.getLocalEulerAngles().clone();let n,s;switch(this.createDoorEffects(t),this.animation){case"slide":n="open"===t?new pc.Vec3(e.x,e.y+3.2,e.z):new pc.Vec3(e.x,e.y-3.2,e.z),s=i.clone();break;case"swing":n=e.clone(),s="open"===t?new pc.Vec3(i.x,i.y+90,i.z):new pc.Vec3(i.x,i.y-90,i.z);break;default:n=e.clone(),s=i.clone()}return new Promise(r=>{const a=Date.now(),c=()=>{const l=Date.now()-a;let m=Math.min(l/1500,1);m=1-Math.pow(1-m,3);const h=new pc.Vec3;h.lerp(e,n,m),this.entity.setLocalPosition(h);const d=new pc.Vec3;if(d.lerp(i,s,m),this.entity.setLocalEulerAngles(d),"iris"===this.animation){const e="open"===t?1-.8*m:.2+.8*m;this.entity.setLocalScale(o.x*e,o.y,o.z*e)}if(m>.8){const t=(m-.8)/.2,e=.05*Math.sin(t*Math.PI*3)*(1-t);h.y+=e,this.entity.setLocalPosition(h)}m>=1?r():requestAnimationFrame(c)};c()})}createDoorEffects(t){this.playDoorSound(t),this.createLightFlash(),"slide"===this.animation&&this.createScreenShake()}playDoorSound(t){this.app.fire("audio:play",{sound:"open"===t?"door_open":"door_close",volume:.3,pitch:"slide"===this.animation?.8:1})}createLightFlash(){if(!this.entity)return;const t=new pc.Entity("door_flash");t.addComponent("light",{type:"point",color:new pc.Color(1,1,.8),intensity:2,range:8,castShadows:!1});const e=this.entity.getPosition();t.setPosition(e.x,e.y+1,e.z+1),this.entity.parent.addChild(t);let o=2;const i=()=>{o*=.9,t.light&&(t.light.intensity=o),o>.1?requestAnimationFrame(i):t.destroy()};setTimeout(i,100)}createScreenShake(){this.app.fire("camera:shake",{intensity:.5,duration:300})}async close(){"locked"!==this.state&&"closing"!==this.state&&(this.state="closing",await this.playAnimation("close"),this.state="locked",this.updateDoorAppearance())}async onInteract(t){"locked"===this.state?await this.unlock(t)&&this.onTransition&&setTimeout(()=>{this.onTransition(this.toRoom,this.playerSpawnPosition)},1e3):"open"===this.state&&this.onTransition&&this.onTransition(this.toRoom,this.playerSpawnPosition)}}class n{constructor(t,e){this.app=t,this.multiRoom=e,this.element=null,this.roomElements=new Map,this.doorElements=new Map,this.playerDot=null,this.scale=.15,this.init()}init(){this.element=document.createElement("div"),this.element.id="mini-map",this.element.style.cssText="\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 220px;\n height: 160px;\n background: rgba(0, 0, 0, 0.8);\n border: 2px solid #00ff88;\n border-radius: 8px;\n padding: 10px;\n z-index: 900;\n font-family: 'Courier New', monospace;\n color: #00ff88;\n box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);\n ";const t=document.createElement("div");t.textContent="FACILITY MAP",t.style.cssText="\n font-size: 10px;\n font-weight: bold;\n text-align: center;\n margin-bottom: 8px;\n color: #00ff88;\n border-bottom: 1px solid #00ff88;\n padding-bottom: 4px;\n ",this.element.appendChild(t),this.mapContainer=document.createElement("div"),this.mapContainer.style.cssText="\n position: relative;\n width: 200px;\n height: 120px;\n background: rgba(0, 20, 10, 0.5);\n border: 1px solid #004422;\n border-radius: 4px;\n overflow: hidden;\n ",this.element.appendChild(this.mapContainer),document.body.appendChild(this.element),this.buildMap(),this.app.on("room:changed",this.onRoomChanged,this)}buildMap(){if(!this.multiRoom||!this.multiRoom.rooms)return;const t=this.calculateRoomLayout();for(const[e,o]of this.multiRoom.rooms)this.createRoomElement(e,o,t[e]);for(const[e,o]of this.multiRoom.doors)this.createDoorElement(e,o,t);this.createPlayerDot(),this.updateCurrentRoom()}calculateRoomLayout(){const t={},e=Array.from(this.multiRoom.rooms.keys());return e.includes("reception")&&e.includes("emergency_ward")?(t.reception={x:30,y:60},t.emergency_ward={x:100,y:60},t.surgery_suite={x:170,y:60}):e.includes("entry_hall")&&e.includes("control_room")?(t.entry_hall={x:30,y:80},t.control_room={x:100,y:80},t.generator_bay={x:100,y:40},t.maintenance_tunnel={x:170,y:40}):e.includes("security_office")&&e.includes("main_corridor")?(t.security_office={x:30,y:90},t.main_corridor={x:100,y:90},t.camera_control={x:100,y:60},t.basement_storage={x:170,y:90},t.server_room={x:170,y:60}):e.forEach((e,o)=>{t[e]={x:30+50*o,y:60}}),t}createRoomElement(t,e,o){if(!o)return;const i=document.createElement("div");i.className="minimap-room",i.id=`minimap-${t}`,i.style.cssText=`\n position: absolute;\n left: ${o.x-15}px;\n top: ${o.y-10}px;\n width: 30px;\n height: 20px;\n border: 1px solid #006644;\n background: rgba(0, 100, 50, 0.3);\n border-radius: 3px;\n cursor: pointer;\n transition: all 0.3s ease;\n `;const n=document.createElement("div");n.textContent=t.replace("_"," ").substring(0,4).toUpperCase(),n.style.cssText="\n font-size: 7px;\n text-align: center;\n line-height: 20px;\n color: #00ff88;\n pointer-events: none;\n ",i.appendChild(n),i.onclick=()=>this.onRoomClick(t),this.mapContainer.appendChild(i),this.roomElements.set(t,i)}createDoorElement(t,e,o){const i=o[e.fromRoom],n=o[e.toRoom];if(!i||!n)return;const s=document.createElement("div");s.className="minimap-door",s.id=`minimap-${t}`;const r=(i.x+n.x)/2,a=(i.y+n.y)/2,c=180*Math.atan2(n.y-i.y,n.x-i.x)/Math.PI,l=Math.sqrt(Math.pow(n.x-i.x,2)+Math.pow(n.y-i.y,2))-30;s.style.cssText=`\n position: absolute;\n left: ${r-l/2}px;\n top: ${a-1}px;\n width: ${l}px;\n height: 2px;\n background: #ff6600;\n transform: rotate(${c}deg);\n transform-origin: center;\n transition: all 0.3s ease;\n `;const m=document.createElement("div");m.style.cssText="\n position: absolute;\n left: 50%;\n top: -4px;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #ff3333;\n transform: translateX(-50%);\n transition: background 0.3s ease;\n ",s.appendChild(m),this.mapContainer.appendChild(s),this.doorElements.set(t,{element:s,status:m,door:e})}createPlayerDot(){this.playerDot=document.createElement("div"),this.playerDot.style.cssText="\n position: absolute;\n width: 6px;\n height: 6px;\n background: #00ffff;\n border-radius: 50%;\n box-shadow: 0 0 8px #00ffff;\n transition: all 0.5s ease;\n z-index: 10;\n ",this.mapContainer.appendChild(this.playerDot)}updateCurrentRoom(){this.multiRoom.currentRoom&&(this.roomElements.forEach((t,e)=>{e===this.multiRoom.currentRoom?(t.style.background="rgba(0, 255, 136, 0.6)",t.style.borderColor="#00ff88",t.style.boxShadow="0 0 10px rgba(0, 255, 136, 0.5)",t.getBoundingClientRect(),this.mapContainer.getBoundingClientRect(),this.playerDot.style.left=t.offsetLeft+15-3+"px",this.playerDot.style.top=t.offsetTop+10-3+"px"):(t.style.background="rgba(0, 100, 50, 0.3)",t.style.borderColor="#006644",t.style.boxShadow="none")}),this.updateDoorStates())}updateDoorStates(){this.doorElements.forEach((t,e)=>{try{const{element:e,status:o,door:i}=t,n=!(!i.canUnlock||!this.multiRoom.gameState)&&i.canUnlock(this.multiRoom.gameState);"open"===i.state?(o.style.background="#00ff00",e.style.background="#00ff88"):n?(o.style.background="#ffff00",e.style.background="#ffaa00"):(o.style.background="#ff3333",e.style.background="#ff6600")}catch(t){console.warn(`[MiniMap] Error updating door state ${e}:`,t.message)}})}onRoomChanged(t){this.updateCurrentRoom()}onRoomClick(t){const e=this.multiRoom.currentRoom;if(t!==e){for(const[o,i]of this.multiRoom.doors)if(i.fromRoom===e&&i.toRoom===t)return void(i.canUnlock(this.multiRoom.gameState)?this.app.fire("ui:notification",{text:`Navigate to ${t.replace("_"," ")}`,type:"info",duration:2e3}):this.app.fire("ui:notification",{text:`${t.replace("_"," ")} is locked`,type:"warning",duration:2e3}));this.app.fire("ui:notification",{text:`No direct access to ${t.replace("_"," ")}`,type:"warning",duration:2e3})}}show(){this.element&&(this.element.style.display="block")}hide(){this.element&&(this.element.style.display="none")}destroy(){this.element&&(document.body.removeChild(this.element),this.element=null),this.app.off("room:changed",this.onRoomChanged,this),this.roomElements.clear(),this.doorElements.clear()}}class s{constructor(t,e){this.app=t,this.multiRoom=e,this.doorItems=new Map,this.setupDoorItems()}setupDoorItems(){this.itemConfigs={keycard:{model:"card",color:"#00ff88",emissive:"#004422",animation:"glow",scale:[.1,.05,.15],hoverOffset:.3,description:"Access Card Required"},objective:{model:"screen",color:"#ff8800",emissive:"#442200",animation:"pulse",scale:[.2,.3,.05],hoverOffset:.2,description:"Objective Completion Required"},skill:{model:"tool",color:"#8800ff",emissive:"#220044",animation:"rotate",scale:[.15,.15,.15],hoverOffset:.25,description:"Skill Level Required"},sequence:{model:"complex",color:"#ff0088",emissive:"#440022",animation:"complex",scale:[.12,.2,.12],hoverOffset:.4,description:"Sequence Completion Required"},none:{model:"simple",color:"#888888",emissive:"#222222",animation:"none",scale:[.08,.08,.08],hoverOffset:.1,description:"Access Granted"}}}createDoorItem(t,e,o){const i=this.itemConfigs[t.requirement?.type||"none"];if(!i)return null;const n=o.create(`${t.id}_item`,{components:{model:{type:"box"},collision:{type:"box",halfExtents:[.1,.1,.1]}},position:[e[0]+.3,e[1]+1.5,e[2]],scale:i.scale,tags:["door_item","interactive"]});return this.applyDoorItemMaterial(n,i,t),this.addFloatingAnimation(n,i),this.doorItems.set(t.id,{entity:n,config:i,door:t,basePosition:n.getPosition().clone()}),n}applyDoorItemMaterial(t,e,o){if(!t.model)return;const i=new pc.StandardMaterial;i.diffuse=(new pc.Color).fromString(e.color),i.emissive=(new pc.Color).fromString(e.emissive),i.metalness=.7,i.shininess=80,"open"===o.state?(i.emissive=new pc.Color(.1,.8,.1),i.diffuse=new pc.Color(.3,1,.3)):o.canUnlock&&o.canUnlock(this.multiRoom.gameState)&&(i.emissive=(new pc.Color).fromString(e.emissive).mulScalar(2)),i.update(),t.model.meshInstances.forEach(t=>t.material=i)}addFloatingAnimation(t,e){const o=Date.now(),i=t.getPosition().clone();switch(e.animation){case"glow":this.addGlowAnimation(t,o);break;case"pulse":this.addPulseAnimation(t,o);break;case"rotate":this.addRotateAnimation(t,o);break;case"complex":this.addComplexAnimation(t,o);break;default:this.addFloatAnimation(t,i,e.hoverOffset)}}addFloatAnimation(t,e,o){const i=()=>{if(!t||!t.enabled)return;const n=.001*Date.now(),s=e.clone();s.y+=Math.sin(2*n)*o,t.setPosition(s),requestAnimationFrame(i)};i()}addGlowAnimation(t,e){const o=t.getPosition().clone(),i=()=>{if(!t||!t.enabled)return;const n=.001*(Date.now()-e),s=o.clone();s.y+=.2*Math.sin(1.5*n),t.setPosition(s);const r=1+.1*Math.sin(3*n),a=t.getLocalScale();t.setLocalScale(a.x*r,a.y*r,a.z*r),requestAnimationFrame(i)};i()}addPulseAnimation(t,e){const o=t.getPosition().clone(),i=t.getLocalScale().clone(),n=()=>{if(!t||!t.enabled)return;const s=.001*(Date.now()-e),r=1+.2*Math.sin(4*s);t.setLocalScale(i.x*r,i.y*r,i.z*r);const a=o.clone();a.y+=.15*Math.sin(2*s),t.setPosition(a),requestAnimationFrame(n)};n()}addRotateAnimation(t,e){const o=t.getPosition().clone(),i=()=>{if(!t||!t.enabled)return;const n=.001*(Date.now()-e);t.setEulerAngles(0,45*n,0);const s=o.clone();s.y+=.25*Math.sin(1.8*n),t.setPosition(s),requestAnimationFrame(i)};i()}addComplexAnimation(t,e){const o=t.getPosition().clone(),i=t.getLocalScale().clone(),n=()=>{if(!t||!t.enabled)return;const s=.001*(Date.now()-e),r=o.clone();r.y+=.3*Math.sin(2*s),r.x+=.1*Math.sin(1.5*s),r.z+=.1*Math.cos(1.2*s),t.setPosition(r),t.setEulerAngles(15*Math.sin(s),30*s,10*Math.cos(1.5*s));const a=1+.15*Math.sin(3*s);t.setLocalScale(i.x*a,i.y*a,i.z*a),requestAnimationFrame(n)};n()}updateDoorItemState(t){const e=this.doorItems.get(t);if(!e)return;const{entity:o,config:i,door:n}=e;this.applyDoorItemMaterial(o,i,n);try{n.canUnlock&&this.multiRoom.gameState&&n.canUnlock(this.multiRoom.gameState)?this.showInteractionPrompt(o,i.description):this.hideInteractionPrompt(o)}catch(e){console.warn(`[DoorItems] Error updating door item ${t}:`,e.message)}}showInteractionPrompt(t,e){let o=t.findByName("interaction_prompt");o||(o=new pc.Entity("interaction_prompt"),o.addComponent("element",{type:pc.ELEMENTTYPE_TEXT,text:`[F] ${e}`,fontSize:12,color:new pc.Color(1,1,1),outlineColor:new pc.Color(0,0,0),outlineThickness:.3,fontAsset:null,autoWidth:!0,autoHeight:!0,pivot:[.5,.5],anchor:[.5,.5]}),o.setLocalPosition(0,.8,0),o.setLocalEulerAngles(0,0,0),t.addChild(o));const i=this.app.root.findByTag("MainCamera")[0];i&&o.lookAt(i.getPosition())}hideInteractionPrompt(t){const e=t.findByName("interaction_prompt");e&&e.destroy()}onDoorInteraction(t){const e=this.doorItems.get(t);if(!e)return;const{entity:o,door:i,config:n}=e;this.playInteractionEffect(o),"locked"!==i.state||i.canUnlock(this.multiRoom.gameState)||this.showRequirementMessage(i,n)}playInteractionEffect(t){const e=t.getLocalScale().clone(),o=e.clone().mulScalar(1.3);t.setLocalScale(o),setTimeout(()=>{t&&t.enabled&&t.setLocalScale(e)},200)}showRequirementMessage(t,e){let o=e.description;switch(t.requirement?.type){case"keycard":o=`Requires: ${t.requirement.item.replace("_"," ")}`;break;case"objective":o=`Complete: ${t.requirement.id.replace("_"," ")}`;break;case"skill":o=`Requires: ${t.requirement.skill} level ${t.requirement.level}`;break;case"sequence":o="Complete interaction sequence first"}this.app.fire("ui:notification",{text:o,type:"warning",duration:3e3})}updateAllDoorItems(){this.doorItems.forEach((t,e)=>{this.updateDoorItemState(e)})}cleanup(){this.doorItems.forEach(t=>{t.entity&&t.entity.destroy()}),this.doorItems.clear()}}class r{constructor(t,e){this.app=t,this.core=e,this.DOOR_HALF_HEIGHT=1.5,this.rooms=new Map,this.doors=new Map,this.roomEntities=new Map,this.currentRoom=null,this.sceneRoot=null,this.miniMap=null,this.doorItems=null,this.gameState={inventory:new Set,lastInteractions:[],skills:new Map,core:e},this.gameState.skills.hasLevel=(t,e)=>(this.gameState.skills.get(t)||0)>=e,this.onDoorInteract=this.onDoorInteract.bind(this),this.transitionToRoom=this.transitionToRoom.bind(this)}async build(t,e,o){if(this.sceneBuilder=e,this.entityFactory=o,this.sceneRoot=o.create("MultiRoomRoot",{position:[0,0,0],tags:["scene_root"]}),this.rooms.size>0){const t=this.rooms.keys().next().value;this.currentRoom=t,await this.buildRoom(t,e,o),this.activateRoom(this.rooms.get(t))}return this.doorItems=new s(t,this),this.sceneRoot}deactivateRoom(t){console.log(`[MultiRoom] Deactivating room: ${t.entity.name}`);for(const[e,o]of this.doors)if(o.fromRoom===t.id&&o.entity&&("open"===o.state||"opening"===o.state)){if(o.state="locked",o.updateDoorAppearance(),o.entity){const e=this.computeDoorPlacement(t,o);o.entity.setLocalPosition(e),o.entity.setLocalRotation(0,0,0),o.entity.setLocalScale(.2,3,2)}console.log(`[MultiRoom] Reset door ${e} state and position`)}t.entity.enabled=!1,t.active=!1,this.disableEntityAndChildren(t.entity),setTimeout(()=>{this.enforceEntityVisibility(t.entity,!1)},0)}disableEntityAndChildren(t){t.enabled=!1,t.children.forEach(t=>{this.disableEntityAndChildren(t)})}enforceEntityVisibility(t,e){t.enabled=e,t.render&&(t.render.enabled=e),t.model&&(t.model.enabled=e),t.children.forEach(t=>{this.enforceEntityVisibility(t,e)})}activateRoom(t){console.log(`[MultiRoom] Activating room: ${t.entity.name}`),t.entity.enabled=!0,t.active=!0,this.enforceEntityVisibility(t.entity,!0)}createRoom(t,e){const o={id:t,entity:null,lighting:e.lighting||"normal",size:e.size||[20,4,15],position:e.position||[0,0,0],active:!1,buildFunction:e.buildFunction||null,npcs:e.npcs||[],interactables:e.interactables||[],objectives:e.objectives||[]};return this.rooms.set(t,o),o}createDoor(t,e,o,n={}){const s=`${t}_to_${e}`,r=new i(this.app,{id:s,fromRoom:t,toRoom:e,requirement:o,animation:n.animation||"slide",playerSpawnPosition:n.playerSpawnPosition||[0,0,5],onTransition:this.transitionToRoom});this.doors.set(s,r);const a=this.rooms.get(t);if(a&&a.entity&&this.entityFactory){const e=this.computeDoorPlacement(a,r),o=r.createEntity(this.entityFactory,e);if(a.entity.addChild(o),o.doorInstance=r,this.doorItems){const t=this.doorItems.createDoorItem(r,e,this.entityFactory);t&&a.entity.addChild(t)}console.log(`[MultiRoom] Added door ${s} to existing room ${t}`)}return r}async buildRoom(t,e,o){const i=this.rooms.get(t);if(!i||i.entity)return;console.log(`[MultiRoom] Building room: ${t}`);const n=e.createRoom({size:i.size,position:i.position,lighting:i.lighting});n.name=`${t}_Room`,i.entity=n,i.buildFunction&&await i.buildFunction(n,e,o,this);for(const[e,s]of this.doors)if(s.fromRoom===t){const t=this.computeDoorPlacement(i,s),e=s.createEntity(o,t);if(n.addChild(e),e.doorInstance=s,this.doorItems){const e=this.doorItems.createDoorItem(s,t,o);e&&n.addChild(e)}}return n.enabled=t===this.currentRoom,this.sceneRoot.addChild(n),n}getDoorPosition(t,e,o){const[i,n,s]=o;return"student_practice"===e?[.35*i,.5*n,.15*s]:"or_suite"===e?[.35*-i,.5*n,.15*s]:[.3*i,.5*n,.35*s]}computeDoorPlacement(t,e){const o=e.requirement&&e.requirement.position?e.requirement.position.slice():null;let i=o||this.getDoorPosition(e.fromRoom,e.toRoom,t.size);if((!o||"number"==typeof o[1]&&o[1]<=.05)&&(i[1]=Math.max(i[1]||0,this.DOOR_HALF_HEIGHT)),!o){const o=Array.from(this.doors.values()).filter(e=>e.fromRoom===t.id&&!(e.requirement&&e.requirement.position)).sort((t,e)=>t.id.localeCompare(e.id)),n=Math.max(o.length,1),s=Math.max(0,o.findIndex(t=>t.id===e.id));if(n>1){const[e,,o]=t.size,r=Math.abs(i[0])>Math.abs(i[2]),a=n-1,c=(a>0?(s-a/2)/a:0)*(r?.4*o:.4*e);r?i[2]+=c:i[0]+=c}}return i}addEntityToRoom(t,e){this.roomEntities.has(e)||this.roomEntities.set(e,new Set),this.roomEntities.get(e).add(t),this.sceneRoot.addChild(t),t.enabled=e===this.currentRoom,console.log(`[MultiRoom] Added entity ${t.name} to room ${e}, enabled: ${t.enabled}`)}updateEntityVisibilityForRoom(t){console.log(`[MultiRoom] Updating entity visibility for room: ${t}`);for(const[e,o]of this.roomEntities)e!==t&&o.forEach(t=>{t.enabled=!1,this.enforceEntityVisibility(t,!1)});this.roomEntities.has(t)&&this.roomEntities.get(t).forEach(t=>{t.enabled=!0,this.enforceEntityVisibility(t,!0)})}async transitionToRoom(t,e){if(!this.rooms.has(t))return console.error(`[MultiRoom] Room ${t} not found`),!1;if(t===this.currentRoom)return console.log(`[MultiRoom] Already in room ${t}`),!0;console.log(`[MultiRoom] Transitioning from ${this.currentRoom} to ${t}`),this.app.fire("ui:notification",{text:`Entering ${t.replace("_"," ")}...`,type:"info",duration:2e3});try{const o=this.rooms.get(t);if(!o.entity){if(!this.sceneBuilder||!this.entityFactory)return console.error("[MultiRoom] Missing sceneBuilder or entityFactory for room building"),!1;await this.buildRoom(t,this.sceneBuilder,this.entityFactory)}if(this.currentRoom){const t=this.rooms.get(this.currentRoom);t.entity&&this.deactivateRoom(t)}this.activateRoom(o),this.currentRoom=t,this.updateEntityVisibilityForRoom(t);const i=this.app.root.findByTag("player")[0];return i&&e&&i.setPosition(e),this.updateRoomObjectives(t),this.app.fire("room:changed",{roomId:t,playerPosition:e,roomConfig:o}),console.log(`[MultiRoom] Successfully transitioned to ${t}`),!0}catch(e){return console.error(`[MultiRoom] Error transitioning to room ${t}:`,e),!1}}updateRoomObjectives(t){const e=this.rooms.get(t);if(!e||!e.objectives)return void console.log(`[MultiRoom] No objectives found for room: ${t}`);const o=this.core.getSystem("objectivemanager");o.clearAllObjectives(),console.log(`[MultiRoom] Updating objectives for room: ${t} (${e.objectives.length} objectives)`),e.objectives.forEach(t=>{console.log(`[MultiRoom] Processing objective: ${t.id} (type: ${t.type})`),o.objectives.has(t.id)||(o.defineObjective(t.id,t),console.log(`[MultiRoom] Defined objective: ${t.id}`));const e=t.prerequisites&&t.prerequisites.length>0,i=!e||t.prerequisites.every(t=>o.isComplete(t)),n="guidance"===t.type;!t.autoStart&&!n||!i||o.isObjectiveActive(t.id)||o.isComplete(t.id)?t.autoStart||n?o.isObjectiveActive(t.id)?console.log(`[MultiRoom] Objective ${t.id} already active`):o.isComplete(t.id)?console.log(`[MultiRoom] Objective ${t.id} already completed`):e&&!i&&(console.log(`[MultiRoom] Prerequisites not met for ${t.id}: ${t.prerequisites}`),t.prerequisites.forEach(t=>{console.log(`[MultiRoom] Prerequisite ${t} complete: ${o.isComplete(t)}`)})):console.log(`[MultiRoom] Objective ${t.id} is manual start - skipping`):(console.log(`[MultiRoom] Starting ${n?"guidance":"auto-start"} objective: ${t.id}`),o.startObjective(t.id),console.log(`[MultiRoom] Successfully started objective: ${t.id}`))}),this.checkAndActivateGuidanceObjectives(t)}checkAndActivateGuidanceObjectives(t){const e=this.rooms.get(t);if(!e||!e.objectives)return;const o=this.core.getSystem("objectivemanager");o&&e.objectives.forEach(t=>{"guidance"!==t.type||o.isObjectiveActive(t.id)||o.isComplete(t.id)||(!(t.prerequisites&&t.prerequisites.length>0)||t.prerequisites.every(t=>o.isComplete(t)))&&(console.log(`[MultiRoom] Activating guidance objective: ${t.id}`),o.startObjective(t.id),setTimeout(()=>{o.isObjectiveActive(t.id)&&(o.completeObjective(t.id),console.log(`[MultiRoom] Auto-completed guidance objective: ${t.id}`))},100))})}addItem(t){this.gameState.inventory.add(t);const e=this.core?this.core.getSystem("inventorymanager"):null;e?e.pickupItem(t,1):this.app.fire("ui:notification",{text:`Acquired: ${t.replace("_"," ")}`,type:"success"}),this.doorItems&&this.doorItems.updateAllDoorItems()}hasItem(t){if(this.gameState.inventory.has(t))return!0;const e=this.core?this.core.getSystem("inventorymanager"):null;return!!e&&e.hasItem(t)}recordInteraction(t){this.gameState.lastInteractions.push(t),this.gameState.lastInteractions.length>10&&this.gameState.lastInteractions.shift()}onDoorInteract(t){for(const[e,o]of this.doors)if(e===t||o.entity&&o.entity.name===t)return void o.onInteract(this.gameState);console.warn(`[MultiRoom] Door not found: ${t}`)}setupInteractions(t,e){this.interactionHandler=t=>{if(this.recordInteraction(t),t.includes("_to_")){if(this.onDoorInteract(t),this.doorItems){const e=Array.from(this.doors.values()).find(e=>e.id===t);e&&this.doorItems.onDoorInteraction(e.id)}}else this.handleRoomInteraction(t)},this.npcInteractionHandler=t=>{console.log(`[MultiRoom] NPC interaction with ${t}`),this.recordInteraction(t),this.sceneInteractionHandler?this.sceneInteractionHandler(t):console.log(`[MultiRoom] No scene interaction handler registered for NPC: ${t}`)},t.on("interaction:triggered",this.interactionHandler),t.on("npc:interact",this.npcInteractionHandler)}handleRoomInteraction(t){console.log(`[MultiRoom] Room interaction: ${t} in room ${this.currentRoom}`),this.sceneInteractionHandler?(console.log("[MultiRoom] Calling scene interaction handler"),this.sceneInteractionHandler(t)):console.log("[MultiRoom] No scene interaction handler registered")}setSceneInteractionHandler(t){this.sceneInteractionHandler=t}syncWithGlobalInventory(t){const e=t?t.getSystem("inventorymanager"):null;if(e){for(const[t,o]of e.items.entries())o&&o.id&&this.gameState.inventory.add(o.id);console.log(`[MultiRoom] Synced ${this.gameState.inventory.size} items from global inventory`)}else console.warn("[MultiRoom] No InventoryManager available for sync")}onLoad(t,e){this.setupInteractions(t,e),this.app=t,this.core=e,this.gameState&&e&&(this.gameState.core=e),this.syncWithGlobalInventory(e),this.setupObjectives(t,e),this.miniMap=new n(t,this),setTimeout(()=>{t.fire("ui:notification",{text:`Multi-room scene loaded: ${this.currentRoom}. All interactions available.`,type:"info",duration:3e3})},500),console.log(`[MultiRoom] Multi-room scene loaded, starting in ${this.currentRoom}`),console.log("[MultiRoom] GameState inventory:",this.gameState.inventory.size,"items"),setTimeout(()=>{const t=this.sceneRoot.find(t=>t.tags&&t.tags.has("interactive"));console.log(`[MultiRoom] Found ${t.length} interactive items:`),t.forEach(t=>{console.log(` - ${t.name}: enabled=${t.enabled}, position=${t.getPosition().toString()}`)})},1e3)}setupObjectives(t,e){const o=e.getSystem("objectivemanager");o?(console.log("[MultiRoom] Setting up objectives for multi-room scene"),o.clearAllObjectives(),this.currentRoom&&(console.log(`[MultiRoom] Starting objectives for initial room: ${this.currentRoom}`),this.updateRoomObjectives(this.currentRoom)),console.log(`[MultiRoom] Objective setup complete for current room: ${this.currentRoom}`)):console.warn("[MultiRoom] ObjectiveManager not available")}onUnload(t,e){console.log("[MultiRoom] Cleaning up multi-room scene..."),this.interactionHandler&&(t.off("interaction:triggered",this.interactionHandler),this.interactionHandler=null),this.npcInteractionHandler&&(t.off("npc:interact",this.npcInteractionHandler),this.npcInteractionHandler=null),this.miniMap&&(this.miniMap.destroy(),this.miniMap=null),this.doorItems&&(this.doorItems.cleanup(),this.doorItems=null);for(const[t,e]of this.roomEntities)e.forEach(t=>{t&&t.destroy&&t.destroy()});this.roomEntities.clear();for(const[t,e]of this.rooms)e.entity&&e.entity.findByTag("npc").forEach(t=>{t.cleanupLabel&&t.cleanupLabel()}),e.active=!1;this.gameState.inventory.clear(),this.gameState.lastInteractions=[],this.gameState.skills.clear(),this.currentRoom=null}}}}]);