200 lines
6.1 KiB
JavaScript
200 lines
6.1 KiB
JavaScript
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
// 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;
|
|
}
|