/** * energy-beam.js — Vertical energy beam above the Batcave terminal * * Category: DATA-TETHERED AESTHETIC * Data source: state.activeAgentCount (0 = faint, 3+ = full intensity) * * A glowing cyan cylinder rising from the Batcave area. * Intensity and pulse amplitude are driven by the number of active agents. */ import * as THREE from 'three'; const BEAM_RADIUS = 0.2; const BEAM_HEIGHT = 50; const BEAM_X = -10; const BEAM_Y = 0; const BEAM_Z = -10; let _state = null; let _beamMaterial = null; let _pulse = 0; /** * @param {THREE.Scene} scene * @param {object} state Shared state bus (reads state.activeAgentCount) * @param {object} theme Theme bus (reads theme.colors.accent) */ export function init(scene, state, theme) { _state = state; const accentColor = theme?.colors?.accent ?? 0x4488ff; const geo = new THREE.CylinderGeometry(BEAM_RADIUS, BEAM_RADIUS * 2.5, BEAM_HEIGHT, 32, 16, true); _beamMaterial = new THREE.MeshBasicMaterial({ color: accentColor, transparent: true, opacity: 0.6, blending: THREE.AdditiveBlending, side: THREE.DoubleSide, depthWrite: false, }); const beam = new THREE.Mesh(geo, _beamMaterial); beam.position.set(BEAM_X, BEAM_Y + BEAM_HEIGHT / 2, BEAM_Z); scene.add(beam); } export function update(_elapsed, _delta) { if (!_beamMaterial) return; _pulse += 0.02; const agentCount = _state?.activeAgentCount ?? 0; const agentIntensity = agentCount === 0 ? 0.1 : Math.min(0.1 + agentCount * 0.3, 1.0); const pulseEffect = Math.sin(_pulse) * 0.15 * agentIntensity; _beamMaterial.opacity = agentIntensity * 0.6 + pulseEffect; }