🎮ArcadeLab

Литературный клуб

by NeonPanther57
284 lines11.4 KB
▶ Play
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Литературный клуб</title>
    <style>
        * { margin:0; padding:0; box-sizing:border-box; user-select:none; -webkit-tap-highlight-color:transparent; }
        body {
            background: #0a0a0a;
            height: 100vh;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Courier New', monospace;
            touch-action: none;
        }
        #container {
            position: relative;
            width: 100%;
            height: 100%;
            background: #000;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }
        #message {
            color: #888;
            font-size: 4vmin;
            letter-spacing: 0.5vmin;
            text-align: center;
            z-index: 2;
            animation: pulse 2s infinite alternate;
        }
        @keyframes pulse {
            0% { opacity: 0.3; }
            100% { opacity: 1; }
        }
        /* Моника (скример) */
        #monika {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: cover;
            z-index: 10;
            display: none;
            filter: contrast(1.8) brightness(0.9) saturate(0.3);
            animation: glitch 0.15s infinite alternate;
        }
        #monika.active {
            display: block;
        }
        @keyframes glitch {
            0% { transform: translate(0,0) scale(1.02); }
            25% { transform: translate(-10px,6px) scale(1.06); }
            50% { transform: translate(10px,-6px) scale(0.96); }
            75% { transform: translate(-5px,10px) scale(1.04); }
            100% { transform: translate(0,0) scale(1); }
        }
        /* Вспышка */
        #flash {
            position: fixed;
            top: 0; left: 0;
            width: 100%; height: 100%;
            background: #fff;
            z-index: 5;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.05s;
        }
        #flash.active {
            opacity: 1;
        }
        /* Затемнение */
        #overlay {
            position: fixed;
            top: 0; left: 0;
            width: 100%; height: 100%;
            background: #000;
            z-index: 15;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.5s;
        }
        #overlay.dark {
            opacity: 0.6;
        }
        /* Запасной экран (если картинка не загрузилась) */
        #fallback {
            position: fixed;
            top: 0; left: 0;
            width: 100%; height: 100%;
            background: #8b0000;
            display: none;
            align-items: center;
            justify-content: center;
            z-index: 10;
            color: #fff;
            font-size: 20vmin;
            font-weight: bold;
            text-shadow: 0 0 30px #000;
            animation: glitch 0.2s infinite alternate;
        }
        #fallback.active {
            display: flex;
        }
        /* Скрытый выход */
        #info {
            position: fixed;
            bottom: 2vmin;
            left: 2vmin;
            color: #111;
            font-size: 2vmin;
            opacity: 0.05;
            z-index: 20;
            pointer-events: none;
        }
        @media (orientation: portrait) {
            body::after {
                content: "Поверни телефон горизонтально";
                position: fixed;
                top: 0; left: 0;
                width: 100%; height: 100%;
                background: #000;
                color: #888;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 5vmin;
                z-index: 999;
                font-family: 'Courier New', monospace;
            }
        }
    </style>
</head>
<body>
    <div id="container">
        <div id="message">Добро пожаловать в литературный клуб...</div>
        <!-- Музыка (ссылка на 'Your Reality' с freemusicarchive) -->
        <audio id="bgm" src="https://files.freemusicarchive.org/storage-files/music/uncategorized/Your_Reality.mp3" preload="auto" loop></audio>
        <!-- Изображение Моники (ссылка с imgur, но если не загрузится – сработает fallback) -->
        <img id="monika" src="https://i.imgur.com/1nJ4l4j.png" alt="Monika" crossorigin="anonymous" />
        <div id="fallback">MONIKA</div>
        <div id="flash"></div>
        <div id="overlay"></div>
        <div id="info">•</div>
    </div>

    <script>
        (function() {
            const bgm = document.getElementById('bgm');
            const monika = document.getElementById('monika');
            const fallback = document.getElementById('fallback');
            const flash = document.getElementById('flash');
            const overlay = document.getElementById('overlay');
            const message = document.getElementById('message');
            let isTriggered = false;
            let scareTimer = null;

            // Генерация звука скримера (громкий шум + писк)
            function playScareSound() {
                try {
                    const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                    // Белый шум
                    const bufferSize = audioCtx.sampleRate * 0.5;
                    const buffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
                    const data = buffer.getChannelData(0);
                    for (let i = 0; i < bufferSize; i++) {
                        data[i] = (Math.random() * 2 - 1) * 0.9;
                    }
                    const noise = audioCtx.createBufferSource();
                    noise.buffer = buffer;
                    const gain = audioCtx.createGain();
                    gain.gain.setValueAtTime(0, audioCtx.currentTime);
                    gain.gain.linearRampToValueAtTime(0.8, audioCtx.currentTime + 0.02);
                    gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.5);
                    noise.connect(gain);
                    gain.connect(audioCtx.destination);
                    noise.start();
                    noise.stop(audioCtx.currentTime + 0.5);

                    // Писк
                    const osc = audioCtx.createOscillator();
                    osc.type = 'square';
                    osc.frequency.setValueAtTime(4000, audioCtx.currentTime);
                    osc.frequency.exponentialRampToValueAtTime(1000, audioCtx.currentTime + 0.4);
                    const gain2 = audioCtx.createGain();
                    gain2.gain.setValueAtTime(0, audioCtx.currentTime);
                    gain2.gain.linearRampToValueAtTime(0.6, audioCtx.currentTime + 0.02);
                    gain2.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.45);
                    osc.connect(gain2);
                    gain2.connect(audioCtx.destination);
                    osc.start();
                    osc.stop(audioCtx.currentTime + 0.45);
                } catch(e) { /* тихо */ }
            }

            // Запуск скримера
            function triggerScare() {
                if (isTriggered) return;
                isTriggered = true;
                if (scareTimer) { clearTimeout(scareTimer); scareTimer = null; }

                // Останавливаем музыку
                bgm.pause();
                bgm.currentTime = 0;

                // Вспышка
                flash.classList.add('active');
                setTimeout(() => { flash.classList.remove('active'); }, 300);

                // Показываем Монику (или fallback) через 300 мс
                setTimeout(() => {
                    // Если картинка Моники загрузилась – показываем её, иначе fallback уже показан
                    if (monika.complete && monika.naturalWidth > 0) {
                        monika.classList.add('active');
                    } else {
                        fallback.classList.add('active');
                    }
                    overlay.classList.add('dark');
                    // Звук скримера
                    playScareSound();
                    // Вибрация
                    if (navigator.vibrate) {
                        navigator.vibrate([100, 50, 200, 80, 300, 100, 500, 100, 300]);
                    }
                    // Полноэкранный режим
                    try {
                        const el = document.documentElement;
                        if (el.requestFullscreen) el.requestFullscreen().catch(() => {});
                        else if (el.webkitRequestFullscreen) el.webkitRequestFullscreen();
                        else if (el.msRequestFullscreen) el.msRequestFullscreen();
                    } catch(e) {}
                }, 300);
            }

            // Запускаем музыку при загрузке (автоплей с обработкой ошибок)
            bgm.play().catch(() => {
                // Если автоплей заблокирован, запустим по первому касанию
                document.addEventListener('click', function playOnce() {
                    bgm.play().catch(() => {});
                    document.removeEventListener('click', playOnce);
                }, { once: true });
            });

            // Таймер на 7 секунд
            scareTimer = setTimeout(triggerScare, 7000);

            // Если пользователь кликнет до скримера – не мешаем, но музыку запускаем
            document.addEventListener('click', function() {
                if (bgm.paused) {
                    bgm.play().catch(() => {});
                }
            });

            // Если картинка Моники не загрузилась – показываем fallback
            monika.onerror = function() {
                monika.style.display = 'none';
                // fallback уже есть в DOM
            };

            // Скрытый выход: двойное касание после скримера перезагружает страницу
            let tapCount = 0, tapTimer = null;
            document.addEventListener('click', function(e) {
                if (!isTriggered) return;
                tapCount++;
                if (tapCount === 1) {
                    tapTimer = setTimeout(() => { tapCount = 0; }, 500);
                } else if (tapCount >= 2) {
                    location.reload();
                }
            });
            document.addEventListener('touchstart', function(e) {
                if (!isTriggered) return;
                document.click(); // эмуляция для мобильных
            }, { passive: true });
        })();
    </script>
</body>
</html>

Game Source: Литературный клуб

Creator: NeonPanther57

Libraries: none

Complexity: complex (284 lines, 11.4 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-neonpanther57-msrfc4hn" to link back to the original. Then publish at arcadelab.ai/publish.