Nexus Vision: Update app.js
This commit is contained in:
120
app.js
120
app.js
@@ -29,8 +29,11 @@ let keys = {};
|
||||
let mouseDown = false;
|
||||
let batcaveTerminals = [];
|
||||
let portals = []; // Registry of active portals
|
||||
let visionPoints = []; // Registry of vision points
|
||||
let activePortal = null; // Portal currently in proximity
|
||||
let activeVisionPoint = null; // Vision point currently in proximity
|
||||
let portalOverlayActive = false;
|
||||
let visionOverlayActive = false;
|
||||
let particles, dustParticles;
|
||||
let debugOverlay;
|
||||
let frameCount = 0, lastFPSTime = 0, fps = 0;
|
||||
@@ -98,6 +101,15 @@ async function init() {
|
||||
console.error('Failed to load portals.json:', e);
|
||||
addChatMessage('error', 'Portal registry offline. Check logs.');
|
||||
}
|
||||
|
||||
// Load Vision Points
|
||||
try {
|
||||
const response = await fetch('./vision.json');
|
||||
const visionData = await response.json();
|
||||
createVisionPoints(visionData);
|
||||
} catch (e) {
|
||||
console.error('Failed to load vision.json:', e);
|
||||
}
|
||||
|
||||
updateLoad(80);
|
||||
createParticles();
|
||||
@@ -418,6 +430,53 @@ function createTerminalPanel(parent, x, y, rot, title, color, lines) {
|
||||
batcaveTerminals.push({ group, scanMat, borderMat });
|
||||
}
|
||||
|
||||
// ═══ VISION SYSTEM ═══
|
||||
function createVisionPoints(data) {
|
||||
data.forEach(config => {
|
||||
const vp = createVisionPoint(config);
|
||||
visionPoints.push(vp);
|
||||
});
|
||||
}
|
||||
|
||||
function createVisionPoint(config) {
|
||||
const group = new THREE.Group();
|
||||
group.position.set(config.position.x, config.position.y, config.position.z);
|
||||
|
||||
const color = new THREE.Color(config.color);
|
||||
|
||||
// Floating Crystal
|
||||
const crystalGeo = new THREE.OctahedronGeometry(0.6, 0);
|
||||
const crystalMat = new THREE.MeshPhysicalMaterial({
|
||||
color: color,
|
||||
emissive: color,
|
||||
emissiveIntensity: 1,
|
||||
roughness: 0,
|
||||
metalness: 1,
|
||||
transmission: 0.5,
|
||||
thickness: 1,
|
||||
});
|
||||
const crystal = new THREE.Mesh(crystalGeo, crystalMat);
|
||||
crystal.position.y = 2.5;
|
||||
group.add(crystal);
|
||||
|
||||
// Glow Ring
|
||||
const ringGeo = new THREE.TorusGeometry(0.8, 0.02, 16, 64);
|
||||
const ringMat = new THREE.MeshBasicMaterial({ color: color, transparent: true, opacity: 0.5 });
|
||||
const ring = new THREE.Mesh(ringGeo, ringMat);
|
||||
ring.position.y = 2.5;
|
||||
ring.rotation.x = Math.PI / 2;
|
||||
group.add(ring);
|
||||
|
||||
// Light
|
||||
const light = new THREE.PointLight(color, 1, 10);
|
||||
light.position.set(0, 2.5, 0);
|
||||
group.add(light);
|
||||
|
||||
scene.add(group);
|
||||
|
||||
return { config, group, crystal, ring, light };
|
||||
}
|
||||
|
||||
// ═══ PORTAL SYSTEM ═══
|
||||
function createPortals(data) {
|
||||
data.forEach(config => {
|
||||
@@ -762,6 +821,7 @@ function setupControls() {
|
||||
if (e.key === 'Escape') {
|
||||
document.getElementById('chat-input').blur();
|
||||
if (portalOverlayActive) closePortalOverlay();
|
||||
if (visionOverlayActive) closeVisionOverlay();
|
||||
}
|
||||
if (e.key.toLowerCase() === 'v' && document.activeElement !== document.getElementById('chat-input')) {
|
||||
cycleNavMode();
|
||||
@@ -769,6 +829,9 @@ function setupControls() {
|
||||
if (e.key.toLowerCase() === 'f' && activePortal && !portalOverlayActive) {
|
||||
activatePortal(activePortal);
|
||||
}
|
||||
if (e.key.toLowerCase() === 'e' && activeVisionPoint && !visionOverlayActive) {
|
||||
activateVisionPoint(activeVisionPoint);
|
||||
}
|
||||
});
|
||||
document.addEventListener('keyup', (e) => {
|
||||
keys[e.key.toLowerCase()] = false;
|
||||
@@ -830,6 +893,7 @@ function setupControls() {
|
||||
document.getElementById('chat-send').addEventListener('click', sendChatMessage);
|
||||
|
||||
document.getElementById('portal-close-btn').addEventListener('click', closePortalOverlay);
|
||||
document.getElementById('vision-close-btn').addEventListener('click', closeVisionOverlay);
|
||||
}
|
||||
|
||||
function sendChatMessage() {
|
||||
@@ -932,6 +996,51 @@ function closePortalOverlay() {
|
||||
document.getElementById('portal-overlay').style.display = 'none';
|
||||
}
|
||||
|
||||
// ═══ VISION INTERACTION ═══
|
||||
function checkVisionProximity() {
|
||||
if (visionOverlayActive) return;
|
||||
|
||||
let closest = null;
|
||||
let minDist = Infinity;
|
||||
|
||||
visionPoints.forEach(vp => {
|
||||
const dist = playerPos.distanceTo(vp.group.position);
|
||||
if (dist < 3.5 && dist < minDist) {
|
||||
minDist = dist;
|
||||
closest = vp;
|
||||
}
|
||||
});
|
||||
|
||||
activeVisionPoint = closest;
|
||||
const hint = document.getElementById('vision-hint');
|
||||
if (activeVisionPoint) {
|
||||
document.getElementById('vision-hint-title').textContent = activeVisionPoint.config.title;
|
||||
hint.style.display = 'flex';
|
||||
} else {
|
||||
hint.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function activateVisionPoint(vp) {
|
||||
visionOverlayActive = true;
|
||||
const overlay = document.getElementById('vision-overlay');
|
||||
const titleDisplay = document.getElementById('vision-title-display');
|
||||
const contentDisplay = document.getElementById('vision-content-display');
|
||||
const statusDot = document.getElementById('vision-status-dot');
|
||||
|
||||
titleDisplay.textContent = vp.config.title.toUpperCase();
|
||||
contentDisplay.textContent = vp.config.content;
|
||||
statusDot.style.background = vp.config.color;
|
||||
statusDot.style.boxShadow = `0 0 10px ${vp.config.color}`;
|
||||
|
||||
overlay.style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeVisionOverlay() {
|
||||
visionOverlayActive = false;
|
||||
document.getElementById('vision-overlay').style.display = 'none';
|
||||
}
|
||||
|
||||
// ═══ GAME LOOP ═══
|
||||
function gameLoop() {
|
||||
requestAnimationFrame(gameLoop);
|
||||
@@ -1007,6 +1116,7 @@ function gameLoop() {
|
||||
|
||||
// Proximity check
|
||||
checkPortalProximity();
|
||||
checkVisionProximity();
|
||||
|
||||
const sky = scene.getObjectByName('skybox');
|
||||
if (sky) sky.material.uniforms.uTime.value = elapsed;
|
||||
@@ -1032,6 +1142,16 @@ function gameLoop() {
|
||||
portal.pSystem.geometry.attributes.position.needsUpdate = true;
|
||||
});
|
||||
|
||||
// Animate Vision Points
|
||||
visionPoints.forEach(vp => {
|
||||
vp.crystal.rotation.y = elapsed * 0.8;
|
||||
vp.crystal.rotation.x = Math.sin(elapsed * 0.5) * 0.2;
|
||||
vp.crystal.position.y = 2.5 + Math.sin(elapsed * 1.5) * 0.2;
|
||||
vp.ring.rotation.z = elapsed * 0.5;
|
||||
vp.ring.scale.setScalar(1 + Math.sin(elapsed * 2) * 0.05);
|
||||
vp.light.intensity = 1 + Math.sin(elapsed * 3) * 0.3;
|
||||
});
|
||||
|
||||
if (particles?.material?.uniforms) {
|
||||
particles.material.uniforms.uTime.value = elapsed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user