diff --git a/app.js b/app.js index d4d82d7..0cdc417 100644 --- a/app.js +++ b/app.js @@ -6,6 +6,14 @@ import { scene, camera, renderer, composer } from './modules/scene-setup.js'; import { clock, warpPass } from './modules/warp.js'; import { nostr } from './modules/nostr.js'; import { createNostrPanelTexture } from './modules/nostr-panel.js'; +import { state } from './modules/core/state.js'; +import { globalTicker } from './modules/core/ticker.js'; +import * as matrixRain from './modules/effects/matrix-rain.js'; +import * as lightning from './modules/effects/lightning.js'; +import * as energyBeam from './modules/effects/energy-beam.js'; +import * as runeRing from './modules/effects/rune-ring.js'; +import * as gravityZones from './modules/effects/gravity-zones.js'; +import * as shockwave from './modules/effects/shockwave.js'; // === NOSTR INIT === nostr.connect(); @@ -17,11 +25,27 @@ nostrPanel.position.set(-6, 3.5, -7.5); nostrPanel.rotation.y = 0.4; scene.add(nostrPanel); +// === EFFECTS INIT === +matrixRain.init(scene, state, NEXUS); +lightning.init(scene, state, NEXUS); +energyBeam.init(scene, state, NEXUS); +runeRing.init(scene, state, NEXUS); +gravityZones.init(scene, state, NEXUS); +shockwave.init(scene, state, NEXUS, { clock }); + +globalTicker.subscribe((delta, elapsed) => matrixRain.update(elapsed, delta)); +globalTicker.subscribe((delta, elapsed) => lightning.update(elapsed, delta)); +globalTicker.subscribe((delta, elapsed) => energyBeam.update(elapsed, delta)); +globalTicker.subscribe((delta, elapsed) => runeRing.update(elapsed, delta)); +globalTicker.subscribe((delta, elapsed) => gravityZones.update(elapsed, delta)); +globalTicker.subscribe((delta, elapsed) => shockwave.update(elapsed, delta)); + // === MAIN ANIMATION LOOP === function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); const elapsed = clock.elapsedTime; + globalTicker.tick(delta, elapsed); // Update Nostr UI periodically or on event if (Math.random() > 0.95) { diff --git a/modules/core/ticker.js b/modules/core/ticker.js index 333e2c3..b5b3fb8 100644 --- a/modules/core/ticker.js +++ b/modules/core/ticker.js @@ -8,3 +8,4 @@ export class Ticker { } } export const globalTicker = new Ticker(); +export function subscribe(fn) { globalTicker.subscribe(fn); } diff --git a/modules/data/gitea.js b/modules/data/gitea.js index 1eeb4d3..e8edb13 100644 --- a/modules/data/gitea.js +++ b/modules/data/gitea.js @@ -1,6 +1,7 @@ // modules/data/gitea.js — All Gitea API calls // Writes to S: _activeAgentCount, _matrixCommitHashes, agentStatus import { S } from '../state.js'; +import { state } from '../core/state.js'; const GITEA_BASE = 'http://143.198.27.163:3000/api/v1'; const GITEA_TOKEN = 'dc0517a965226b7a0c5ffdd961b1ba26521ac592'; @@ -122,6 +123,7 @@ export async function refreshCommitData() { S._matrixCommitHashes = commits.slice(0, 20) .map(c => (c.sha || '').slice(0, 7)) .filter(h => h.length > 0); + state.commitHashes = S._matrixCommitHashes; return commits; } @@ -129,12 +131,14 @@ export async function refreshAgentData() { try { const data = await fetchAgentStatus(); S._activeAgentCount = data.agents.filter(a => a.status === 'working').length; + state.activeAgentCount = S._activeAgentCount; return data; } catch { const fallback = { agents: AGENT_NAMES.map(n => ({ name: n.toLowerCase(), status: 'unreachable', issue: null, prs_today: 0, local: false, })) }; S._activeAgentCount = 0; + state.activeAgentCount = 0; return fallback; } } diff --git a/modules/heatmap.js b/modules/heatmap.js index 617e46a..e4cb703 100644 --- a/modules/heatmap.js +++ b/modules/heatmap.js @@ -3,6 +3,7 @@ import * as THREE from 'three'; import { scene } from './scene-setup.js'; import { GLASS_RADIUS } from './platform.js'; import { S } from './state.js'; +import { state } from './core/state.js'; import { refreshCommitData } from './data/gitea.js'; const HEATMAP_SIZE = 512; @@ -118,6 +119,7 @@ export async function updateHeatmap() { const MAX_WEIGHT = 8; for (const zone of HEATMAP_ZONES) { zoneIntensity[zone.name] = Math.min(rawWeights[zone.name] / MAX_WEIGHT, 1.0); + state.zoneIntensity[zone.name] = zoneIntensity[zone.name]; } drawHeatmap(); diff --git a/modules/portals.js b/modules/portals.js index abe7453..a67c53c 100644 --- a/modules/portals.js +++ b/modules/portals.js @@ -1,9 +1,10 @@ // === PORTALS === import * as THREE from 'three'; import { scene } from './scene-setup.js'; -import { rebuildRuneRing, setPortalsRef } from './effects.js'; +import { rebuild as rebuildRuneRing } from './effects/rune-ring.js'; import { setPortalsRefAudio, startPortalHums } from './audio.js'; import { S } from './state.js'; +import { state } from './core/state.js'; import { fetchPortals as fetchPortalData } from './data/loaders.js'; export const portalGroup = new THREE.Group(); @@ -50,8 +51,8 @@ export function setRunPortalHealthChecksFn(fn) { _runPortalHealthChecksFn = fn; export async function loadPortals() { try { portals = await fetchPortalData(); + state.portals = portals; console.log('Loaded portals:', portals); - setPortalsRef(portals); setPortalsRefAudio(portals); createPortals(); rebuildRuneRing(); diff --git a/modules/weather.js b/modules/weather.js index 9fcbfd4..54e4c38 100644 --- a/modules/weather.js +++ b/modules/weather.js @@ -2,7 +2,8 @@ import * as THREE from 'three'; import { scene, ambientLight } from './scene-setup.js'; import { cloudMaterial } from './platform.js'; -import { rebuildRuneRing } from './effects.js'; +import { rebuild as rebuildRuneRing } from './effects/rune-ring.js'; +import { rebuildFromPortals } from './effects/gravity-zones.js'; import { S } from './state.js'; import { fetchWeatherData } from './data/weather.js'; @@ -40,7 +41,7 @@ export async function runPortalHealthChecks() { } rebuildRuneRing(); - if (_rebuildGravityZonesFn) _rebuildGravityZonesFn(); + rebuildFromPortals(); if (_portalGroupRef) { for (const child of _portalGroupRef.children) {