Files
the-nexus/modules/portals.js
2026-04-05 13:26:46 -04:00

120 lines
4.3 KiB
JavaScript

// === PORTALS ===
import * as THREE from 'three';
import { scene } from './scene-setup.js';
import { rebuildRuneRing, setPortalsRef } from './effects.js';
import { setPortalsRefAudio, startPortalHums } from './audio.js';
import { S } from './state.js';
import { fetchPortals as fetchPortalData } from './data/loaders.js';
export const portalGroup = new THREE.Group();
scene.add(portalGroup);
export let portals = [];
const atlasOverlay = document.getElementById('portal-atlas-overlay');
const atlasList = document.getElementById('portal-atlas-list');
const atlasToggle = document.getElementById('portal-atlas-toggle');
const atlasClose = document.getElementById('portal-atlas-close');
function renderPortalAtlas() {
if (!atlasList) return;
atlasList.innerHTML = portals.map((portal) => {
const env = portal.environment || 'unknown';
const readiness = portal.readiness_state || portal.status || 'unknown';
const access = portal.access_mode || 'unknown';
const actionLabel = portal.destination?.action_label || 'Inspect';
const url = portal.destination?.url || '#';
return `
<article class="portal-atlas-card" data-portal-id="${portal.id}">
<div class="portal-atlas-meta">
<span class="portal-atlas-name">${portal.name}</span>
<span class="portal-atlas-status status-${portal.status}">${portal.status}</span>
</div>
<p class="portal-atlas-description">${portal.description}</p>
<div class="portal-atlas-tags">
<span>${portal.portal_type || 'portal'}</span>
<span>${portal.world_category || 'world'}</span>
<span>${env}</span>
<span>${access}</span>
<span>${readiness}</span>
</div>
<div class="portal-atlas-footer">
<span class="portal-atlas-owner">Owner: ${portal.owner || 'unknown'}</span>
<a class="portal-atlas-link" href="${url}">${actionLabel}</a>
</div>
</article>
`;
}).join('');
}
function setAtlasOpen(open) {
if (!atlasOverlay) return;
atlasOverlay.classList.toggle('visible', open);
}
function initPortalAtlas() {
atlasToggle?.addEventListener('click', () => setAtlasOpen(!atlasOverlay?.classList.contains('visible')));
atlasClose?.addEventListener('click', () => setAtlasOpen(false));
document.addEventListener('keydown', (event) => {
if (event.key === 'g' || event.key === 'G') setAtlasOpen(!atlasOverlay?.classList.contains('visible'));
if (event.key === 'Escape') setAtlasOpen(false);
});
const params = new URLSearchParams(window.location.search);
if (params.get('atlas') === '1') setAtlasOpen(true); // atlas=1
}
initPortalAtlas();
function createPortals() {
const portalGeo = new THREE.TorusGeometry(3.0, 0.2, 16, 100);
portals.forEach(portal => {
const isOnline = portal.status === 'online';
const portalMat = new THREE.MeshBasicMaterial({
color: new THREE.Color(portal.color).convertSRGBToLinear(),
transparent: true,
opacity: isOnline ? 0.7 : 0.15,
blending: THREE.AdditiveBlending,
side: THREE.DoubleSide,
});
const portalMesh = new THREE.Mesh(portalGeo, portalMat);
portalMesh.position.set(portal.position.x, portal.position.y + 0.5, portal.position.z);
portalMesh.rotation.y = portal.rotation.y;
portalMesh.rotation.x = Math.PI / 2;
portalMesh.name = `portal-${portal.id}`;
portalMesh.userData.destinationUrl = portal.destination?.url || null;
portalMesh.userData.portalColor = new THREE.Color(portal.color).convertSRGBToLinear();
portalGroup.add(portalMesh);
});
}
// rebuildGravityZones forward ref
let _rebuildGravityZonesFn = null;
export function setRebuildGravityZonesFn(fn) { _rebuildGravityZonesFn = fn; }
// runPortalHealthChecks forward ref
let _runPortalHealthChecksFn = null;
export function setRunPortalHealthChecksFn(fn) { _runPortalHealthChecksFn = fn; }
export async function loadPortals() {
try {
portals = await fetchPortalData();
console.log('Loaded portals:', portals);
setPortalsRef(portals);
setPortalsRefAudio(portals);
createPortals();
renderPortalAtlas();
rebuildRuneRing();
if (_rebuildGravityZonesFn) _rebuildGravityZonesFn();
startPortalHums();
if (_runPortalHealthChecksFn) _runPortalHealthChecksFn();
} catch (error) {
console.error('Failed to load portals:', error);
}
}