Archived
1
0

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:
Alexander Whitestone
2026-03-23 23:21:55 -04:00
parent 960461436b
commit 401180d71c
2 changed files with 26 additions and 5 deletions

28
app.js
View File

@@ -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 = () => {

View File

@@ -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",