1import * as THREE from '../assets/vendor/three.module.js'; 2import {EXPLORER_OPTIONS} from './explorer.js?v=0.67.1'; 3 4export function explorerPanel(room){ 5 if(room.plan.annex!==0||room.data.preview)return; 6 const group=new THREE.Group();group.name='Explorer Control Panel';group.userData.explorerPanel=true; 7 group.position.set(-room.width/2+.07,0,0);group.rotation.y=Math.PI/2;room.root.add(group); 8 const metal=new THREE.MeshStandardMaterial({color:0x596670,metalness:.78,roughness:.36}); 9 const deck=new THREE.MeshStandardMaterial({color:0x202c34,metalness:.58,roughness:.50}); 10 const inset=new THREE.MeshStandardMaterial({color:0x0c1319,metalness:.32,roughness:.62}); 11 const cyan=new THREE.MeshBasicMaterial({color:0x74cbda,toneMapped:false}); 12 // Clipped steel housing, recessed instrument bays and exposed fasteners. 13 const shape=new THREE.Shape(),points=[[-.90,-.50],[.90,-.50],[.98,-.42],[.98,.42],[.90,.50],[-.90,.50],[-.98,.42],[-.98,-.42]]; 14 shape.moveTo(...points[0]);for(const point of points.slice(1))shape.lineTo(...point);shape.closePath(); 15 const housing=new THREE.Mesh(new THREE.ExtrudeGeometry(shape,{depth:.10,bevelEnabled:false}),metal);housing.position.set(0,1.63,-.05);group.add(housing); 16 room.box(1.86,.91,.07,0,1.63,.053,deck,group); 17 room.label('EXPLORER CONTROL PANEL',1.65,.14,0,2.005,.092,{size:80,color:'#dcecf2'},group); 18 room.box(1.68,.009,.008,0,1.90,.094,cyan,group); 19 room.label('PRESS A BUTTON TO EXPLORE',1.66,.075,0,1.835,.098,{size:45,color:'#9ec6d1'},group); 20 for(const x of [-.90,.90])for(const y of [1.23,2.04]){ 21 const screw=new THREE.Mesh(new THREE.CylinderGeometry(.027,.027,.015,16),metal);screw.rotation.x=Math.PI/2;screw.position.set(x,y,.10);group.add(screw); 22 room.box(.027,.004,.002,x,y,.109,inset,group); 23 } 24 for(const [i,option]of EXPLORER_OPTIONS.entries()){ 25 const x=i? .47:-.47,y=1.515,isRed=option.id==='exploratorium'; 26 room.box(.85,.48,.018,x,1.525,.099,inset,group); 27 room.label(option.title,.82,.115,x,1.705,.112,{size:92,color:'#e1ebef'},group); 28 const hardware=new THREE.Group();hardware.scale.setScalar(.25);hardware.position.set(x,y,.10);group.add(hardware); 29 const base=new THREE.Mesh(new THREE.CylinderGeometry(.335,.335,.04,48),metal);base.rotation.x=Math.PI/2;base.position.set(0,0,.01);hardware.add(base); 30 const light=new THREE.MeshBasicMaterial({color:isRed?0xe64e45:0xe7b44f,toneMapped:false}); 31 const ring=new THREE.Mesh(new THREE.TorusGeometry(.302,.009,8,48),light);ring.position.set(0,0,.034);hardware.add(ring); 32 const capMaterial=new THREE.MeshPhysicalMaterial({color:isRed?0xc21d22:0xc58416,emissive:isRed?0x630509:0x542c03,emissiveIntensity:.15,metalness:.25,roughness:.29,clearcoat:.7,clearcoatRoughness:.19}); 33 const cap=new THREE.Mesh(new THREE.CylinderGeometry(.257,.283,.065,48),capMaterial);cap.rotation.x=Math.PI/2;cap.position.set(0,0,.046);cap.userData.explorerButton=option.id;cap.userData.buttonColor=isRed?'red':'amber';hardware.add(cap); 34 const pressLabel=room.label('PRESS',.11,.045,x,y,.121,{size:200,color:isRed?'#fff2ed':'#241907'},group); 35 const press=()=>{ 36 if(room.categoryTransition||room.graphicsLost)return; 37 cap.position.z=.025;pressLabel.position.z=.116;room.requestRender(); 38 setTimeout(()=>{cap.position.z=.046;pressLabel.position.z=.121;room.requestRender();},160); 39 room.callbacks.explorer?.(option.id); 40 }; 41 const hint=option.title+' · '+option.prompt; 42 room.clickable(cap,press,hint);room.clickable(pressLabel,press,hint);room.clickable(base,press,hint); 43 room.label(option.prompt,.82,.085,x,1.345,.112,{size:65,color:isRed?'#ffb1a6':'#e5c996'},group); 44 } 45 // Small instruments and a status lamp make this a technical control assembly. 46 room.label('TRANSPORT SYSTEM',.91,.055,-.38,1.205,.10,{size:55,color:'#8ba3ae'},group); 47 room.label('ONLINE',.26,.055,.69,1.205,.10,{size:80,color:'#9de4b6'},group); 48 const online=new THREE.Mesh(new THREE.CircleGeometry(.023,16),new THREE.MeshBasicMaterial({color:0x7ee8a9,toneMapped:false}));online.position.set(.50,1.205,.101);group.add(online); 49 for(let i=0;i<5;i++)room.box(.014,.04,.008,.07+i*.035,1.205,.10,i<4?cyan:inset,group); 50 // Button faces stay inside the existing shallow housing collision envelope. 51 room.obstacles.push({x:-room.width/2+.13,z:0,w:.26,d:2.02}); 52}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.