// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // DONAU2SPACE // DEV ENTITY — Visual Effects // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ import { $, sleep, random, bus } from './utils.js'; // ── CSS class-based effects ──────────────────── export function glitch(ms = 850) { document.documentElement.classList.add('fx-glitch'); setTimeout(() => document.documentElement.classList.remove('fx-glitch'), ms); } export function shake(ms = 520) { const card = $('.terminal-card'); if (!card) return; card.classList.add('fx-shake'); setTimeout(() => card.classList.remove('fx-shake'), ms); } export function blinkRed(ms = 2800) { document.body.classList.add('fx-blink-red'); setTimeout(() => document.body.classList.remove('fx-blink-red'), ms); } export function scanPulse() { const sl = $('#scanlines'); if (!sl) return; sl.classList.add('fx-scan-pulse'); setTimeout(() => sl.classList.remove('fx-scan-pulse'), 1200); } // ── Screen tear (built with DOM, no innerHTML) ─ export async function screenTear(duration = 1500) { const tearEl = $('#screen-tear'); if (!tearEl) return; tearEl.style.display = 'block'; tearEl.replaceChildren(); const strips = 12; for (let i = 0; i < strips; i++) { const offset = (Math.random() * 40 - 20) | 0; const h = (100 / strips).toFixed(2); const top = (i * 100 / strips).toFixed(2); const strip = document.createElement('div'); strip.style.cssText = `position:absolute;top:${top}%;left:${offset}px;right:${-offset}px;height:${h}%;` + `background:rgba(${Math.random() > 0.5 ? '255,0,0' : '0,255,128'},0.04);` + `border-top:1px solid rgba(255,255,255,0.06);`; tearEl.appendChild(strip); } await sleep(duration); tearEl.replaceChildren(); tearEl.style.display = 'none'; } // ── Color shift ──────────────────────────────── const originalColors = { '--green': '#7ee787', '--blue': '#6cb6ff', '--pink': '#ff7ad9', '--yellow': '#ffcc66', '--red': '#ff4d4d', '--text': '#cfe3ff', '--bg': '#05070b', '--muted': '#7ea3c7', }; export function shiftColors(intensity = 0.3) { const hueShift = random(-30, 30) * intensity; document.documentElement.style.filter = `hue-rotate(${hueShift}deg) saturate(${1 + intensity * 0.5})`; } export function resetColors() { document.documentElement.style.filter = ''; const root = document.documentElement; for (const [k, v] of Object.entries(originalColors)) { root.style.setProperty(k, v); } } export function setTheme(name) { const root = document.documentElement; const themes = { neon: { '--green': '#7ee787', '--blue': '#6cb6ff', '--pink': '#ff7ad9' }, calm: { '--green': '#9ad7ff', '--blue': '#b4f0c2', '--pink': '#ffd1a6' }, doom: { '--green': '#ffcc66', '--blue': '#ff4d4d', '--pink': '#ff4d4d' }, void: { '--green': '#4a6670', '--blue': '#3d5a6e', '--pink': '#6e3d5a', '--text': '#8ea8b8' }, matrix: { '--green': '#00ff41', '--blue': '#00ff41', '--pink': '#00ff41', '--text': '#00ff41', '--muted': '#008f11' }, }; const t = themes[name]; if (!t) return false; for (const [k, v] of Object.entries(t)) root.style.setProperty(k, v); return true; } // ── Reality Shift sequence ───────────────────── export async function realityShift() { bus.emit('reality-shift-start'); // Phase 1: Color distortion shiftColors(0.8); await sleep(800); // Phase 2: Typography chaos document.body.classList.add('fx-typo-shift'); await sleep(600); // Phase 3: Screen tear screenTear(1200); glitch(1200); shake(400); await sleep(1400); // Phase 4: Fake error flash const overlay = $('#reality-overlay'); if (overlay) { overlay.style.display = 'flex'; overlay.replaceChildren(); const errors = [ 'SEGFAULT at 0x00000DEV', 'kernel panic - not syncing: VFS unable to mount root fs', 'ERROR: reality.sys corrupted', 'WARNING: timeline integrity at 12%', 'FATAL: narrative overflow in dev_entity.consciousness', ]; for (const err of errors) { const div = document.createElement('div'); div.textContent = err; overlay.appendChild(div); await sleep(200); } await sleep(1000); overlay.style.display = 'none'; } // Phase 5: Brief blackout document.body.classList.add('fx-blackout'); await sleep(400); document.body.classList.remove('fx-blackout'); // Phase 6: Rebuild document.body.classList.remove('fx-typo-shift'); resetColors(); await sleep(300); bus.emit('reality-shift-end'); } // ── Overlay management ───────────────────────── export function showOverlay(text) { const overlay = $('#overlay'); const overlayText = $('#overlay-text'); if (!overlay || !overlayText) return; overlayText.textContent = text; overlay.style.display = 'flex'; } export function hideOverlay() { const overlay = $('#overlay'); if (overlay) overlay.style.display = 'none'; } export function isOverlayVisible() { const overlay = $('#overlay'); return overlay && overlay.style.display === 'flex'; } // ── Matrix rain (CSS-based, lightweight) ─────── let matrixInterval = null; export function startMatrixRain() { const el = $('#matrix-rain'); if (!el) return; const charset = '01$#@*+^%&()[]{}<>/\\|;:,.=ABCDEFGHIJKLMNOPQRSTUVWXYZ'; matrixInterval = setInterval(() => { let out = ''; for (let i = 0; i < 300; i++) { out += charset[(Math.random() * charset.length) | 0]; if (i % 55 === 0) out += '\n'; } el.textContent = out; }, 180); } export function stopMatrixRain() { if (matrixInterval) clearInterval(matrixInterval); const el = $('#matrix-rain'); if (el) el.textContent = ''; } export function setMatrixOpacity(val) { const el = $('#matrix-rain'); if (el) el.style.opacity = val; }