// === SOVEREIGNTY EASTER EGG + SHOCKWAVE + FIREWORKS + MERGE FLASH === import * as THREE from 'three'; import { scene, starMaterial, constellationLines } from './scene-setup.js'; import { S } from './state.js'; import { clock } from './warp.js'; // === SOVEREIGNTY EASTER EGG === const SOVEREIGNTY_WORD = 'sovereignty'; const sovereigntyMsg = document.getElementById('sovereignty-msg'); export function triggerSovereigntyEasterEgg() { const originalLineColor = constellationLines.material.color.getHex(); constellationLines.material.color.setHex(0xffd700); constellationLines.material.opacity = 0.9; const originalStarColor = starMaterial.color.getHex(); const originalStarOpacity = starMaterial.opacity; starMaterial.color.setHex(0xffd700); starMaterial.opacity = 1.0; if (sovereigntyMsg) { sovereigntyMsg.classList.remove('visible'); void sovereigntyMsg.offsetWidth; sovereigntyMsg.classList.add('visible'); } const startTime = performance.now(); const DURATION = 2500; function fadeBack() { const t = Math.min((performance.now() - startTime) / DURATION, 1); const eased = t * t; const goldR = 1.0, goldG = 0.843, goldB = 0; const origColor = new THREE.Color(originalStarColor); starMaterial.color.setRGB( goldR + (origColor.r - goldR) * eased, goldG + (origColor.g - goldG) * eased, goldB + (origColor.b - goldB) * eased ); starMaterial.opacity = 1.0 + (originalStarOpacity - 1.0) * eased; const origLineColor = new THREE.Color(originalLineColor); constellationLines.material.color.setRGB( 1.0 + (origLineColor.r - 1.0) * eased, 0.843 + (origLineColor.g - 0.843) * eased, 0 + origLineColor.b * eased ); if (t < 1) { requestAnimationFrame(fadeBack); } else { starMaterial.color.setHex(originalStarColor); starMaterial.opacity = originalStarOpacity; constellationLines.material.color.setHex(originalLineColor); if (sovereigntyMsg) sovereigntyMsg.classList.remove('visible'); } } requestAnimationFrame(fadeBack); } // === SHOCKWAVE RIPPLE === const SHOCKWAVE_RING_COUNT = 3; const SHOCKWAVE_MAX_RADIUS = 14; export const SHOCKWAVE_DURATION = 2.5; export const shockwaveRings = []; export function triggerShockwave() { const now = clock.getElapsedTime(); for (let i = 0; i < SHOCKWAVE_RING_COUNT; i++) { const mat = new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0, side: THREE.DoubleSide, depthWrite: false, blending: THREE.AdditiveBlending, }); const geo = new THREE.RingGeometry(0.9, 1.0, 64); const mesh = new THREE.Mesh(geo, mat); mesh.rotation.x = -Math.PI / 2; mesh.position.y = 0.02; scene.add(mesh); shockwaveRings.push({ mesh, mat, startTime: now, delay: i * 0.35 }); } } // === FIREWORK CELEBRATION === const FIREWORK_COLORS = [0xff4466, 0xffaa00, 0x00ffaa, 0x4488ff, 0xff44ff, 0xffff44, 0x00ffff]; export const FIREWORK_BURST_PARTICLES = 80; export const FIREWORK_BURST_DURATION = 2.2; export const FIREWORK_GRAVITY = -5.0; export const fireworkBursts = []; function spawnFireworkBurst(origin, color) { const now = clock.getElapsedTime(); const count = FIREWORK_BURST_PARTICLES; const positions = new Float32Array(count * 3); const origins = new Float32Array(count * 3); const velocities = new Float32Array(count * 3); for (let i = 0; i < count; i++) { const theta = Math.random() * Math.PI * 2; const phi = Math.acos(2 * Math.random() - 1); const speed = 2.5 + Math.random() * 3.5; velocities[i * 3] = Math.sin(phi) * Math.cos(theta) * speed; velocities[i * 3 + 1] = Math.sin(phi) * Math.sin(theta) * speed; velocities[i * 3 + 2] = Math.cos(phi) * speed; origins[i * 3] = origin.x; origins[i * 3 + 1] = origin.y; origins[i * 3 + 2] = origin.z; positions[i * 3] = origin.x; positions[i * 3 + 1] = origin.y; positions[i * 3 + 2] = origin.z; } const geo = new THREE.BufferGeometry(); geo.setAttribute('position', new THREE.BufferAttribute(positions, 3)); const mat = new THREE.PointsMaterial({ color, size: 0.35, sizeAttenuation: true, transparent: true, opacity: 1.0, blending: THREE.AdditiveBlending, depthWrite: false, }); const points = new THREE.Points(geo, mat); scene.add(points); fireworkBursts.push({ points, geo, mat, origins, velocities, startTime: now }); } export function triggerFireworks() { const burstCount = 6; for (let i = 0; i < burstCount; i++) { const delay = i * 0.35; setTimeout(() => { const x = (Math.random() - 0.5) * 12; const y = 8 + Math.random() * 6; const z = (Math.random() - 0.5) * 12; const color = FIREWORK_COLORS[Math.floor(Math.random() * FIREWORK_COLORS.length)]; spawnFireworkBurst(new THREE.Vector3(x, y, z), color); }, delay * 1000); } } export function triggerMergeFlash() { triggerShockwave(); const originalLineColor = constellationLines.material.color.getHex(); constellationLines.material.color.setHex(0x00ffff); constellationLines.material.opacity = 1.0; const originalStarColor = starMaterial.color.getHex(); const originalStarOpacity = starMaterial.opacity; starMaterial.color.setHex(0x00ffff); starMaterial.opacity = 1.0; const startTime = performance.now(); const DURATION = 2000; function fadeBack() { const t = Math.min((performance.now() - startTime) / DURATION, 1); const eased = t * t; const mergeR = 0.0, mergeG = 1.0, mergeB = 1.0; const origStarColor = new THREE.Color(originalStarColor); starMaterial.color.setRGB( mergeR + (origStarColor.r - mergeR) * eased, mergeG + (origStarColor.g - mergeG) * eased, mergeB + (origStarColor.b - mergeB) * eased ); starMaterial.opacity = 1.0 + (originalStarOpacity - 1.0) * eased; const origLineColor = new THREE.Color(originalLineColor); constellationLines.material.color.setRGB( mergeR + (origLineColor.r - mergeR) * eased, mergeG + (origLineColor.g - mergeG) * eased, mergeB + (origLineColor.b - mergeB) * eased ); constellationLines.material.opacity = 1.0 + (0.18 - 1.0) * eased; if (t < 1) { requestAnimationFrame(fadeBack); } else { starMaterial.color.setHex(originalStarColor); starMaterial.opacity = originalStarOpacity; constellationLines.material.color.setHex(originalLineColor); constellationLines.material.opacity = 0.18; } } requestAnimationFrame(fadeBack); } export function initSovereigntyEasterEgg() { document.addEventListener('keydown', (e) => { if (e.metaKey || e.ctrlKey || e.altKey) return; if (e.key.length !== 1) { S.sovereigntyBuffer = ''; return; } S.sovereigntyBuffer += e.key.toLowerCase(); if (S.sovereigntyBuffer.length > SOVEREIGNTY_WORD.length) { S.sovereigntyBuffer = S.sovereigntyBuffer.slice(-SOVEREIGNTY_WORD.length); } if (S.sovereigntyBuffer === SOVEREIGNTY_WORD) { S.sovereigntyBuffer = ''; triggerSovereigntyEasterEgg(); } if (S.sovereigntyBufferTimer) clearTimeout(S.sovereigntyBufferTimer); S.sovereigntyBufferTimer = setTimeout(() => { S.sovereigntyBuffer = ''; }, 3000); }); }