diff --git a/app.js b/app.js index 6e85528..596bac3 100644 --- a/app.js +++ b/app.js @@ -146,11 +146,122 @@ function animate() { // Subtle pulse on constellation opacity constellationLines.material.opacity = 0.12 + Math.sin(elapsed * 0.5) * 0.06; + // Animate core: gentle pulse + const pulse = 0.8 + Math.sin(elapsed * 1.2) * 0.2; + coreMat.emissiveIntensity = pulse; + coreLight.intensity = pulse * 1.5; + + // Animate sentinels: slow orbit around core + sentinels.forEach((s, i) => { + const angle = elapsed * 0.4 + (i * Math.PI * 2) / sentinels.length; + const r = sentinelDefs[i].pos.map(Math.abs).reduce((a, b) => Math.max(a, b)); + s.position.x = Math.cos(angle) * r; + s.position.z = Math.sin(angle) * r; + s.rotation.y = elapsed * 0.8 + i; + }); + renderer.render(scene, camera); } animate(); +// === INSPECTABLE 3D OBJECTS === +// Objects registered here are raycasted on right-click. +const inspectableObjects = []; + +function addInspectable(mesh, name, description) { + mesh.userData.inspectName = name; + mesh.userData.inspectDesc = description; + scene.add(mesh); + inspectableObjects.push(mesh); + return mesh; +} + +// Nexus Core — central glowing orb +const coreGeo = new THREE.SphereGeometry(0.4, 32, 32); +const coreMat = new THREE.MeshStandardMaterial({ + color: NEXUS.colors.accent, + emissive: NEXUS.colors.accent, + emissiveIntensity: 0.8, + roughness: 0.2, + metalness: 0.6, +}); +addInspectable( + new THREE.Mesh(coreGeo, coreMat), + 'Nexus Core', + 'The sovereign heart of this world. All portals radiate from this point.' +); + +// Ambient point light to illuminate the core +const coreLight = new THREE.PointLight(NEXUS.colors.accent, 1.5, 10); +scene.add(coreLight); + +// Portal sentinels — three small cubes orbiting the core +const sentinelDefs = [ + { pos: [2.5, 0, 0], name: 'Portal Alpha', desc: 'A dormant gateway. Awaiting its destination world.' }, + { pos: [-1.5, 2, 0], name: 'Portal Beta', desc: 'Shimmers faintly. Something stirs on the other side.' }, + { pos: [0, -2, 1.5], name: 'Portal Gamma', desc: 'Cold to the touch. Opens only when summoned by name.' }, +]; + +const sentinels = sentinelDefs.map(({ pos, name, desc }) => { + const geo = new THREE.BoxGeometry(0.25, 0.25, 0.25); + const mat = new THREE.MeshStandardMaterial({ + color: 0x88bbff, + emissive: 0x223366, + emissiveIntensity: 0.5, + roughness: 0.4, + metalness: 0.8, + }); + const mesh = new THREE.Mesh(geo, mat); + mesh.position.set(...pos); + return addInspectable(mesh, name, desc); +}); + +// === OBJECT INSPECTION — RIGHT CLICK === +const raycaster = new THREE.Raycaster(); +const inspectTooltip = document.getElementById('inspect-tooltip'); +const inspectName = document.getElementById('inspect-name'); +const inspectDesc = document.getElementById('inspect-desc'); + +function hideInspectTooltip() { + inspectTooltip.classList.remove('visible'); +} + +renderer.domElement.addEventListener('contextmenu', (e) => { + e.preventDefault(); + + const mouse = new THREE.Vector2( + (e.clientX / window.innerWidth) * 2 - 1, + -(e.clientY / window.innerHeight) * 2 + 1 + ); + + raycaster.setFromCamera(mouse, camera); + const hits = raycaster.intersectObjects(inspectableObjects, false); + + if (hits.length > 0) { + const obj = hits[0].object; + inspectName.textContent = obj.userData.inspectName; + inspectDesc.textContent = obj.userData.inspectDesc; + + // Position tooltip near cursor, keep it inside viewport + const pad = 12; + const tw = 240; + let x = e.clientX + pad; + let y = e.clientY + pad; + if (x + tw > window.innerWidth) x = e.clientX - tw - pad; + if (y + 80 > window.innerHeight) y = e.clientY - 80 - pad; + + inspectTooltip.style.left = x + 'px'; + inspectTooltip.style.top = y + 'px'; + inspectTooltip.classList.add('visible'); + } else { + hideInspectTooltip(); + } +}); + +document.addEventListener('click', hideInspectTooltip); +document.addEventListener('keydown', (e) => { if (e.key === 'Escape') hideInspectTooltip(); }); + // === DEBUG MODE === let debugMode = false; diff --git a/index.html b/index.html index 2006413..b265de1 100644 --- a/index.html +++ b/index.html @@ -36,6 +36,12 @@ + +
+
+
+
+ diff --git a/style.css b/style.css index 948c916..ba3a346 100644 --- a/style.css +++ b/style.css @@ -70,3 +70,36 @@ canvas { outline: 2px dashed yellow; outline-offset: 2px; } + +/* === OBJECT INSPECTION TOOLTIP === */ +#inspect-tooltip { + display: none; + position: fixed; + z-index: 100; + background: rgba(0, 0, 20, 0.92); + border: 1px solid var(--color-primary); + border-radius: 6px; + padding: 10px 14px; + max-width: 240px; + pointer-events: none; + box-shadow: 0 0 16px rgba(68, 136, 255, 0.3); +} + +#inspect-tooltip.visible { + display: block; +} + +#inspect-tooltip .inspect-name { + color: var(--color-primary); + font-size: 13px; + font-weight: bold; + margin-bottom: 4px; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +#inspect-tooltip .inspect-desc { + color: var(--color-text); + font-size: 12px; + line-height: 1.5; +}