forked from Timmy_Foundation/the-nexus
feat: add portal status field with visual health indicators
Add `status` field to portals.json (online/offline/maintenance). Portal ring emissive intensity dims for non-online portals (1.5→0.3). Activation overlay shows color-coded status dot and descriptive message: - online (teal): shows countdown redirect or 'destination not yet linked' - offline (red): blocks entry, shows 'destination unreachable' - maintenance (gold): blocks entry, shows 'portal temporarily closed' Refs #5
This commit is contained in:
28
app.js
28
app.js
@@ -560,6 +560,9 @@ function createPortalMesh(cfg) {
|
||||
const ringEmit = new THREE.Color(cfg.ringEmissive);
|
||||
const pCol = new THREE.Color(cfg.particleColor);
|
||||
|
||||
const isOnline = !cfg.status || cfg.status === 'online';
|
||||
const emissiveStrength = isOnline ? 1.5 : 0.3;
|
||||
|
||||
const portalGroup = new THREE.Group();
|
||||
portalGroup.position.set(...cfg.position);
|
||||
portalGroup.rotation.y = cfg.rotationY;
|
||||
@@ -570,7 +573,7 @@ function createPortalMesh(cfg) {
|
||||
const torusMat = new THREE.MeshStandardMaterial({
|
||||
color: ringCol,
|
||||
emissive: ringEmit,
|
||||
emissiveIntensity: 1.5,
|
||||
emissiveIntensity: emissiveStrength,
|
||||
roughness: 0.2,
|
||||
metalness: 0.8,
|
||||
});
|
||||
@@ -1039,23 +1042,38 @@ function activatePortal(entry) {
|
||||
overlay.style.display = 'flex';
|
||||
overlay.classList.remove('fade-out');
|
||||
|
||||
const portalStatus = entry.config.status || 'online';
|
||||
const statusColors = { online: '#4af0c0', offline: '#ff4466', maintenance: '#ffd700' };
|
||||
const statusColor = statusColors[portalStatus] || entry.config.ringColor;
|
||||
status.style.color = statusColor;
|
||||
|
||||
if (portalStatus === 'offline') {
|
||||
status.textContent = '● OFFLINE — destination unreachable';
|
||||
return;
|
||||
}
|
||||
if (portalStatus === 'maintenance') {
|
||||
status.textContent = '● MAINTENANCE — portal temporarily closed';
|
||||
return;
|
||||
}
|
||||
|
||||
const url = entry.config.destination?.url;
|
||||
if (url) {
|
||||
status.textContent = '● ONLINE';
|
||||
let countdown = 3;
|
||||
status.textContent = `Opening portal in ${countdown}s…`;
|
||||
status.textContent = `● ONLINE — Opening portal in ${countdown}s…`;
|
||||
const iv = setInterval(() => {
|
||||
countdown--;
|
||||
if (countdown <= 0) {
|
||||
clearInterval(iv);
|
||||
status.textContent = 'Entering…';
|
||||
status.textContent = '● ONLINE — Entering…';
|
||||
window.location.href = url;
|
||||
} else {
|
||||
status.textContent = `Opening portal in ${countdown}s…`;
|
||||
status.textContent = `● ONLINE — Opening portal in ${countdown}s…`;
|
||||
}
|
||||
}, 1000);
|
||||
btn.dataset.intervalId = iv;
|
||||
} else {
|
||||
status.textContent = 'Portal active — destination not yet linked';
|
||||
status.textContent = '● ONLINE — destination not yet linked';
|
||||
}
|
||||
|
||||
btn.onclick = () => {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "morrowind",
|
||||
"label": "◈ MORROWIND",
|
||||
"description": "The Elder Scrolls III — Vvardenfell awaits",
|
||||
"status": "online",
|
||||
"position": [15, 0, -10],
|
||||
"rotationY": -0.5,
|
||||
"ringColor": "#ff6600",
|
||||
@@ -22,6 +23,7 @@
|
||||
"id": "bannerlord",
|
||||
"label": "⚔ BANNERLORD",
|
||||
"description": "Mount & Blade II — The Calradic Empire calls",
|
||||
"status": "online",
|
||||
"position": [-15, 0, -10],
|
||||
"rotationY": 0.5,
|
||||
"ringColor": "#cc7700",
|
||||
@@ -40,6 +42,7 @@
|
||||
"id": "workshop",
|
||||
"label": "⚙ WORKSHOP",
|
||||
"description": "The Forge — Build, create, iterate without limits",
|
||||
"status": "online",
|
||||
"position": [0, 0, 18],
|
||||
"rotationY": 3.14159,
|
||||
"ringColor": "#00ccaa",
|
||||
|
||||
Reference in New Issue
Block a user