/
DOLOR3SS
/
LOVEEEE
Обзор
Документация
Войти
/
DOLOR3SS
/
LOVEEEE
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
index.html
842 строки
31 KB
DOLOR3SS
Update: index.html
29 июн 2026, 00:04
Верифицирован
29 июн 2026, 00:04
0c89765
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>открыточкаавв!!</title> <style> /* ========== ОБЩИЕ СТИЛИ ========== */ * { box-sizing: border-box; margin: 0; padding: 0; user-select: none; } body { background: #1a1a2e; display: flex; justify-content: center; align-items: center; min-height: 100vh; font-family: 'Press Start 2P', 'Courier New', monospace; margin: 0; padding: 10px; overflow: hidden; } @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); #gameWrapper { position: relative; width: 900px; max-width: 100%; aspect-ratio: 16 / 9; background: #bfb9a3; border: 6px solid #66281f; box-shadow: 0 0 0 4px #9d8391, 0 10px 30px rgba(0,0,0,0.7); image-rendering: pixelated; overflow: hidden; touch-action: none; } canvas { display: block; width: 100%; height: 100%; image-rendering: pixelated; background: transparent; touch-action: none; } .screen-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; background: rgba(0, 0, 0, 0.75); backdrop-filter: blur(3px); color: #e6e5d5; text-align: center; padding: 20px; z-index: 20; transition: opacity 0.3s; image-rendering: pixelated; } .screen-overlay.hidden { display: none; } .screen-overlay h1 { font-size: clamp(18px, 5vw, 48px); text-shadow: 4px 4px 0 #66281f, 6px 6px 0 #994d47; margin-bottom: 20px; letter-spacing: 2px; line-height: 1.4; color: #e6e5d5; } .pixel-btn { font-family: 'Press Start 2P', monospace; background: #994d47; border: none; padding: 14px 36px; font-size: clamp(14px, 2.5vw, 28px); color: #e6e5d5; box-shadow: 0 6px 0 #66281f, 0 8px 12px rgba(0,0,0,0.3); cursor: pointer; transition: 0.05s linear; text-transform: uppercase; image-rendering: pixelated; letter-spacing: 2px; } .pixel-btn:active { transform: translateY(6px); box-shadow: 0 2px 0 #66281f; } .pixel-btn:hover { background: #bfb9a3; color: #66281f; } /* ========== ЭКРАН ОТКРЫТКИ ========== */ #cardOverlay { background: rgba(0, 0, 0, 0.8); backdrop-filter: blur(6px); justify-content: center; align-items: center; z-index: 30; } .card-content { background: #e6e5d5; padding: 25px 30px; max-width: 500px; width: 90%; border-radius: 20px; border: 4px solid #66281f; box-shadow: 0 0 0 4px #9d8391, 0 10px 30px black; max-height: 90vh; overflow-y: auto; font-family: 'Segoe UI', Roboto, sans-serif; text-align: center; color: #66281f; } .card-content .title { color: #994d47; font-size: 12px; text-transform: uppercase; letter-spacing: 2px; margin-bottom: 8px; font-family: 'Press Start 2P', monospace; } .card-content h2 { font-size: 20px; margin-bottom: 12px; color: #66281f; } .card-content .text-letter { font-size: 15px; line-height: 1.7; text-align: left; max-height: 200px; overflow-y: auto; padding-right: 6px; margin: 10px 0; color: #66281f; } .card-content .text-letter p { margin-bottom: 12px; } .card-continue-btn { font-family: 'Press Start 2P', monospace; background: #994d47; border: none; padding: 12px 30px; font-size: 16px; color: #e6e5d5; box-shadow: 0 5px 0 #66281f; cursor: pointer; margin-top: 12px; transition: 0.05s linear; } .card-continue-btn:active { transform: translateY(5px); box-shadow: 0 1px 0 #66281f; } /* ========== ФИНАЛЬНЫЙ ЭКРАН ========== */ #finalScreen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #000000; z-index: 40; justify-content: center; align-items: center; cursor: default; display: none; } #finalScreen.active { display: flex; } /* ===== МАТРИЧНЫЙ ФОН ===== */ .matrix-bg { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; justify-content: space-between; overflow: hidden; z-index: 1; opacity: 0; transition: opacity 0.2s ease; } .matrix-bg.active { display: flex; opacity: 1; } .matrix-column { display: flex; flex-direction: column; white-space: nowrap; font-size: 13px; line-height: 1.5; color: rgba(120, 20, 20, 0.6); user-select: none; font-family: 'Press Start 2P', monospace; } .matrix-column.up .matrix-move { animation: scrollUp 8s linear infinite; } .matrix-column.down .matrix-move { animation: scrollDown 10s linear infinite; } @keyframes scrollUp { 0% { transform: translateY(0%); } 100% { transform: translateY(-50%); } } @keyframes scrollDown { 0% { transform: translateY(-50%); } 100% { transform: translateY(0%); } } /* ===== НЕОНОВОЕ СЕРДЦЕ ===== */ .neon-final { display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); flex-direction: column; align-items: center; gap: 12px; z-index: 15; user-select: none; pointer-events: none; } .neon-final.active { display: flex; } .neon-final .neon-text { color: #8b0000; font-size: clamp(18px, 4vw, 36px); text-align: center; text-shadow: 0 0 5px #cc0000, 0 0 15px #aa0000, 0 0 30px #880000, 0 0 50px #660000; font-family: 'Press Start 2P', monospace; letter-spacing: 4px; } .neon-final .neon-heart-svg { width: clamp(120px, 22vw, 200px); height: clamp(120px, 22vw, 200px); filter: drop-shadow(0 0 10px #cc0000) drop-shadow(0 0 30px #aa0000) drop-shadow(0 0 60px #880000); animation: pulseHeart 1.2s ease-in-out infinite; } .neon-final .neon-heart-svg path { fill: #8b0000; transition: fill 0.3s; } @keyframes pulseHeart { 0%, 100% { transform: scale(1); } 30% { transform: scale(1.15); } 60% { transform: scale(0.95); } } @media (max-width: 700px) { .card-content { padding: 15px; } .card-content .text-letter { font-size: 13px; } } </style> </head> <body> <div id="gameWrapper"> <canvas id="gameCanvas" width="900" height="506"></canvas> <!-- СТАРТОВЫЙ ЭКРАН --> <div id="startScreen" class="screen-overlay"> <h1>ЛЮБИМОМУУ!! σ(≧ε≦σ) ♡</h1> <button class="pixel-btn" id="startGameBtn">НАЧАТЬ!!! (≧◡≦)</button> </div> <!-- ЭКРАН ОТКРЫТКИ --> <div id="cardOverlay" class="screen-overlay hidden"> <div class="card-content" id="cardContent"></div> </div> <!-- ===== ФИНАЛЬНЫЙ ЭКРАН ===== --> <div id="finalScreen"> <div class="matrix-bg" id="bgContainer"></div> <div class="neon-final" id="neonFinal"> <div class="neon-text">I LOVE YOU</div> <svg class="neon-heart-svg" viewBox="0 0 24 24"> <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/> </svg> </div> </div> </div> <script> // ============================================================ // 1. ИГРОВАЯ ЛОГИКА // ============================================================ const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const W = 900, H = 506; const player = { x: 60, y: 350, w: 32, h: 48, vx: 0, vy: 0, speed: 3.2, jumpPower: -11, gravity: 0.38, onGround: false, frame: 0, frameTimer: 0, facing: 1 }; let platforms = []; let heartItems = []; let gameOver = false; let collectedHearts = 0; const TOTAL_HEARTS = 3; let animationId = null; let keys = {}; const GROUND_Y = 440; const PLATFORM_H = 24; const HEART_SIZE = 32; let cardActive = false; let pendingHeartIndex = -1; // ============================================================ // 2. ПОСТРОЕНИЕ УРОВНЯ // ============================================================ function buildLevel() { platforms = []; heartItems = []; platforms.push({ x: 0, y: GROUND_Y, w: 300, h: PLATFORM_H }); platforms.push({ x: 380, y: GROUND_Y, w: 300, h: PLATFORM_H }); platforms.push({ x: 720, y: GROUND_Y, w: 180, h: PLATFORM_H }); platforms.push({ x: 160, y: 360, w: 80, h: 20 }); platforms.push({ x: 300, y: 310, w: 70, h: 20 }); platforms.push({ x: 460, y: 340, w: 80, h: 20 }); platforms.push({ x: 580, y: 300, w: 70, h: 20 }); platforms.push({ x: 730, y: 350, w: 70, h: 20 }); platforms.push({ x: 810, y: 310, w: 60, h: 20 }); const heartPositions = [ { x: 180, y: 320 }, { x: 480, y: 300 }, { x: 750, y: 310 } ]; heartPositions.forEach((pos, i) => { heartItems.push({ x: pos.x, y: pos.y, w: HEART_SIZE, h: HEART_SIZE, collected: false, index: i }); }); } // ============================================================ // 3. ДЕТАЛИЗИРОВАННАЯ ПИКСЕЛЬНАЯ ОТРИСОВКА // ============================================================ function drawPixelRect(x, y, w, h, fillColor, strokeColor = '#66281f', lineW = 2) { ctx.fillStyle = fillColor; ctx.fillRect(x, y, w, h); ctx.strokeStyle = strokeColor; ctx.lineWidth = lineW; ctx.strokeRect(x, y, w, h); } function drawBackground() { // Небо (градиент в пиксельных цветах) const grad = ctx.createLinearGradient(0, 0, 0, H); grad.addColorStop(0, '#9d8391'); grad.addColorStop(0.5, '#bfb9a3'); grad.addColorStop(1, '#e6e5d5'); ctx.fillStyle = grad; ctx.fillRect(0, 0, W, H); ctx.strokeStyle = '#66281f'; ctx.lineWidth = 2; ctx.strokeRect(0, 0, W, H); // Детализированные облака drawPixelCloud(80, 60, 70, 35); drawPixelCloud(300, 90, 90, 40); drawPixelCloud(600, 50, 80, 35); drawPixelCloud(780, 100, 60, 30); // Детализированные кусты drawPixelBush(50, GROUND_Y - 12, 70, 25); drawPixelBush(400, GROUND_Y - 12, 80, 28); drawPixelBush(750, GROUND_Y - 12, 60, 22); // Декоративные элементы drawPixelFlower(120, GROUND_Y - 8, 8); drawPixelFlower(450, GROUND_Y - 8, 8); drawPixelFlower(780, GROUND_Y - 8, 8); } function drawPixelCloud(x, y, w, h) { ctx.fillStyle = '#e6e5d5'; // Основное облако ctx.fillRect(x, y, w, h); ctx.fillRect(x + 10, y - 12, w - 20, 16); ctx.fillRect(x + 6, y - 6, w - 12, 10); // Детали (пиксельные "прожилки") ctx.fillStyle = '#bfb9a3'; for (let i = 0; i < w; i += 12) { ctx.fillRect(x + i, y + 6, 4, 4); ctx.fillRect(x + i + 4, y + 12, 4, 4); } ctx.strokeStyle = '#66281f'; ctx.lineWidth = 2; ctx.strokeRect(x, y, w, h); ctx.strokeRect(x + 10, y - 12, w - 20, 16); ctx.strokeRect(x + 6, y - 6, w - 12, 10); } function drawPixelBush(x, y, w, h) { ctx.fillStyle = '#66281f'; ctx.fillRect(x, y, w, h); ctx.fillRect(x + 6, y - 10, w - 12, 14); ctx.fillRect(x + 3, y - 6, w - 6, 10); // Детали (листочки) ctx.fillStyle = '#994d47'; for (let i = 4; i < w; i += 10) { ctx.fillRect(x + i, y - 4, 4, 4); ctx.fillRect(x + i + 2, y + 6, 4, 4); } ctx.strokeStyle = '#66281f'; ctx.lineWidth = 2; ctx.strokeRect(x, y, w, h); ctx.strokeRect(x + 6, y - 10, w - 12, 14); ctx.strokeRect(x + 3, y - 6, w - 6, 10); } function drawPixelFlower(x, y, size) { const colors = ['#994d47', '#9d8391', '#bfb9a3']; ctx.fillStyle = colors[Math.floor(Math.random() * colors.length)]; ctx.fillRect(x, y - size, size, size); ctx.fillRect(x - size, y, size, size); ctx.fillRect(x + size, y, size, size); ctx.fillRect(x, y + size, size, size); ctx.fillStyle = '#66281f'; ctx.fillRect(x, y, size/2, size/2); } function drawPlatforms() { platforms.forEach(p => { // Основной блок drawPixelRect(p.x, p.y, p.w, p.h, '#9d8391', '#66281f', 2.5); // Детализация (текстура кирпичей) ctx.fillStyle = '#bfb9a3'; for (let i = 0; i < p.w; i += 16) { ctx.fillRect(p.x + i, p.y + 4, 8, 4); ctx.fillRect(p.x + i + 4, p.y + 12, 8, 4); } ctx.fillStyle = '#994d47'; for (let i = 8; i < p.w; i += 16) { ctx.fillRect(p.x + i, p.y + 4, 8, 4); ctx.fillRect(p.x + i + 4, p.y + 12, 8, 4); } // Контурные линии ctx.strokeStyle = '#66281f'; ctx.lineWidth = 1.5; for (let i = 0; i < p.w; i += 20) { ctx.beginPath(); ctx.moveTo(p.x + i, p.y); ctx.lineTo(p.x + i, p.y + p.h); ctx.stroke(); } }); } function drawHearts() { heartItems.forEach(h => { if (h.collected) return; const cx = h.x + h.w/2, cy = h.y + h.h/2; ctx.fillStyle = '#994d47'; ctx.shadowColor = '#66281f'; ctx.shadowBlur = 10; // Детализированное пиксельное сердце const s = 5; const offsets = [ [-2,-2], [-1,-2], [0,-2], [1,-2], [2,-2], [-3,-1], [-2,-1], [-1,-1], [0,-1], [1,-1], [2,-1], [3,-1], [-3,0], [-2,0], [-1,0], [0,0], [1,0], [2,0], [3,0], [-2,1], [-1,1], [0,1], [1,1], [2,1], [-2,2], [-1,2], [0,2], [1,2], [2,2], [-1,3], [0,3], [1,3], [0,4] ]; offsets.forEach(([dx, dy]) => { ctx.fillRect(cx + dx*s - s/2, cy + dy*s - s/2, s, s); }); ctx.shadowBlur = 0; ctx.strokeStyle = '#66281f'; ctx.lineWidth = 1.5; ctx.strokeRect(cx - 14, cy - 14, 28, 28); }); } function drawPlayer() { const p = player; // Детализированный пиксельный персонаж // Тело (рубашка) ctx.fillStyle = '#994d47'; ctx.fillRect(p.x + 4, p.y + 12, 24, 24); // Голова ctx.fillStyle = '#e6e5d5'; ctx.fillRect(p.x + 6, p.y, 20, 16); // Волосы ctx.fillStyle = '#66281f'; ctx.fillRect(p.x + 4, p.y, 6, 6); ctx.fillRect(p.x + 22, p.y, 6, 6); ctx.fillRect(p.x + 6, p.y - 2, 20, 4); // Глаза ctx.fillStyle = '#66281f'; ctx.fillRect(p.x + 10, p.y + 4, 4, 4); ctx.fillRect(p.x + 18, p.y + 4, 4, 4); // Зрачки (белые) ctx.fillStyle = '#e6e5d5'; ctx.fillRect(p.x + 12, p.y + 6, 2, 2); ctx.fillRect(p.x + 20, p.y + 6, 2, 2); // Рот ctx.fillStyle = '#66281f'; ctx.fillRect(p.x + 12, p.y + 10, 8, 2); // Штаны ctx.fillStyle = '#9d8391'; ctx.fillRect(p.x + 6, p.y + 32, 8, 12); ctx.fillRect(p.x + 18, p.y + 32, 8, 12); // Ботинки ctx.fillStyle = '#66281f'; ctx.fillRect(p.x + 4, p.y + 42, 10, 6); ctx.fillRect(p.x + 18, p.y + 42, 10, 6); // Контур ctx.strokeStyle = '#66281f'; ctx.lineWidth = 2; ctx.strokeRect(p.x + 4, p.y + 12, 24, 24); ctx.strokeRect(p.x + 6, p.y, 20, 16); } // ============================================================ // 4. ФИЗИКА // ============================================================ function updatePlayer() { if (gameOver || cardActive) return; const p = player; p.vx = 0; if (keys['ArrowLeft'] || keys['KeyA']) { p.vx = -p.speed; p.facing = -1; } if (keys['ArrowRight'] || keys['KeyD']) { p.vx = p.speed; p.facing = 1; } if ((keys['ArrowUp'] || keys['KeyW'] || keys['Space']) && p.onGround) { p.vy = p.jumpPower; p.onGround = false; } p.vy += p.gravity; if (p.vy > 12) p.vy = 12; p.x += p.vx; platforms.forEach(plat => { if (p.x < plat.x + plat.w && p.x + p.w > plat.x && p.y < plat.y + plat.h && p.y + p.h > plat.y) { if (p.vx > 0) p.x = plat.x - p.w; else if (p.vx < 0) p.x = plat.x + plat.w; } }); p.y += p.vy; p.onGround = false; platforms.forEach(plat => { if (p.x < plat.x + plat.w && p.x + p.w > plat.x && p.y < plat.y + plat.h && p.y + p.h > plat.y) { if (p.vy > 0) { p.y = plat.y - p.h; p.vy = 0; p.onGround = true; } else if (p.vy < 0) { p.y = plat.y + plat.h; p.vy = 0; } } }); if (p.x < 0) p.x = 0; if (p.x + p.w > W) p.x = W - p.w; if (p.y > H + 50) { gameOver = true; resetPlayer(); } heartItems.forEach(h => { if (h.collected) return; if (p.x < h.x + h.w && p.x + p.w > h.x && p.y < h.y + h.h && p.y + p.h > h.y) { h.collected = true; collectedHearts++; showCard(h.index); } }); if (collectedHearts >= TOTAL_HEARTS && !cardActive) { showFinalScreen(); } } function resetPlayer() { player.x = 60; player.y = 350; player.vx = 0; player.vy = 0; player.onGround = false; gameOver = false; collectedHearts = 0; heartItems.forEach(h => h.collected = false); document.getElementById('cardOverlay').classList.add('hidden'); cardActive = false; document.getElementById('finalScreen').classList.remove('active'); document.getElementById('neonFinal').classList.remove('active'); document.getElementById('bgContainer').classList.remove('active'); document.getElementById('bgContainer').style.opacity = '0'; } // ============================================================ // 5. ОТКРЫТКА // ============================================================ function showCard(heartIndex) { if (cardActive) return; cardActive = true; pendingHeartIndex = heartIndex; const overlay = document.getElementById('cardOverlay'); const container = document.getElementById('cardContent'); const cardTexts = [ { title: 'Первое сердечкаа (´。• ω •。`) ♡', text: `<p>Ты стал для меня по-настоящему близким человеком, и это настоящее чудо. С самого начала нашего общения ты показался мне весёлым, родным и понимающим — и с каждым днём это только укрепляется.</p> <p>С тобой легко и спокойно: я могу быть собой, делиться сокровенным, смеяться до слёз или говорить о серьёзном — и знать, что меня услышат и не осудят. Ты показал мне, какой может быть настоящая любовь и забота — и я это очень ценю.</p>` }, { title: 'Второе сердечкаа (〃^▽^〃)', text: `<p>Спасибо за твоё терпение, доброту и поддержку. За то, что всегда готов выслушать, подбодрить и заставить улыбнуться в хмурый день. Благодаря тебе я знаю, каково это — общаться с человеком, которому можно доверять безоговорочно.</p> <p>Береги себя, пожалуйста. Знай, что я рядом — и всегда готова поддержать, выслушать и порадоваться твоим успехам.</p> <p>Я тебя очень люблю, ты добрый, весёлый, чуткий, надёжный — и при этом остаёшься самим собой. С тобой можно и повеселиться от души, и поговорить по душам спасибо тебее.</p>` }, { title: 'Третье сердечкаа', text: `<p>Мне оочень нравится то, что ты проявляешь внимание ко мне, ты пишешь комплименты, и в тгк и в лс, а ещё прозвища вот эти вот там "кис" "милая" И вот это всë Я ТЕКУ ЕМАЕЕЕЕЕ</p> <p>За всë время нашего общения, ты мне прям в сердце запал, ты запомнился мне больше всего из моих знакомств, поверь, с тобой очень комфортно, приятно, весело и интересно общаться, да и сам ты довольно красивый, милый, пиздатый и ахуенный, очень дорожу тобой, таких людей как ты еще поискать нужно, люблю тебя очеееньь!!</p>` } ]; const data = cardTexts[heartIndex] || cardTexts[0]; container.innerHTML = ` <div class="title">${data.title}</div> <h2>❤️</h2> <div class="text-letter" id="cardLetter">${data.text}</div> <div id="cardContinueArea"> <button class="card-continue-btn" id="continueBtn"> ${heartIndex === TOTAL_HEARTS - 1 ? 'КОНЕЦ!' : 'ПРОДОЛЖИТЬ'} </button> </div> `; overlay.classList.remove('hidden'); document.getElementById('continueBtn').addEventListener('click', function() { closeCard(); }); } function closeCard() { document.getElementById('cardOverlay').classList.add('hidden'); cardActive = false; if (collectedHearts >= TOTAL_HEARTS) { showFinalScreen(); } } // ============================================================ // 6. ФИНАЛ // ============================================================ function showFinalScreen() { const finalScreen = document.getElementById('finalScreen'); finalScreen.classList.add('active'); const bgContainer = document.getElementById('bgContainer'); bgContainer.style.display = 'flex'; buildMatrixBackground(); setTimeout(() => { bgContainer.classList.add('active'); bgContainer.style.opacity = '1'; }, 10); const neonFinal = document.getElementById('neonFinal'); neonFinal.style.display = 'flex'; setTimeout(() => neonFinal.classList.add('active'), 20); } function buildMatrixBackground() { const bgContainer = document.getElementById('bgContainer'); bgContainer.innerHTML = ''; const charWidth = 95; const columnCount = Math.ceil(window.innerWidth / charWidth) + 1; let textBlock = ""; for (let i = 0; i < 60; i++) { textBlock += "I LOVE YOU<br>"; } for (let i = 0; i < columnCount; i++) { const col = document.createElement('div'); col.className = 'matrix-column'; // Чередуем направление if (i % 2 === 0) { col.classList.add('up'); } else { col.classList.add('down'); } const moveDiv = document.createElement('div'); moveDiv.className = 'matrix-move'; moveDiv.innerHTML = textBlock + textBlock; col.style.opacity = (Math.random() * 0.4 + 0.2).toFixed(2); moveDiv.style.animationDelay = (Math.random() * -8) + 's'; col.appendChild(moveDiv); bgContainer.appendChild(col); } } // ============================================================ // 7. ИГРОВОЙ ЦИКЛ // ============================================================ function gameLoop() { if (!document.getElementById('startScreen').classList.contains('hidden')) { animationId = requestAnimationFrame(gameLoop); return; } if (!cardActive && !gameOver) { updatePlayer(); } ctx.clearRect(0, 0, W, H); drawBackground(); drawPlatforms(); drawHearts(); drawPlayer(); if (gameOver) { ctx.fillStyle = 'rgba(0,0,0,0.5)'; ctx.fillRect(0, 0, W, H); ctx.fillStyle = '#e6e5d5'; ctx.font = '20px "Press Start 2P", monospace'; ctx.textAlign = 'center'; ctx.fillText('ПАДЕНИЕ!', W/2, H/2 - 10); ctx.font = '14px "Press Start 2P", monospace'; ctx.fillText('Перезапуск...', W/2, H/2 + 30); } animationId = requestAnimationFrame(gameLoop); } // ============================================================ // 8. ЗАПУСК // ============================================================ function init() { buildLevel(); resetPlayer(); document.getElementById('startGameBtn').addEventListener('click', function() { document.getElementById('startScreen').classList.add('hidden'); resetPlayer(); buildLevel(); collectedHearts = 0; heartItems.forEach(h => h.collected = false); document.getElementById('cardOverlay').classList.add('hidden'); document.getElementById('finalScreen').classList.remove('active'); document.getElementById('neonFinal').classList.remove('active'); document.getElementById('bgContainer').classList.remove('active'); document.getElementById('bgContainer').style.opacity = '0'; cardActive = false; }); document.addEventListener('keydown', (e) => { keys[e.code] = true; if (e.code === 'Space') e.preventDefault(); }); document.addEventListener('keyup', (e) => { keys[e.code] = false; }); canvas.addEventListener('touchstart', (e) => { e.preventDefault(); if (!cardActive && !gameOver) { if (player.onGround) { player.vy = player.jumpPower; player.onGround = false; } } }, {passive: false}); window.addEventListener('resize', () => { if (document.getElementById('bgContainer').classList.contains('active')) { buildMatrixBackground(); } }); gameLoop(); } window.onload = init; </script> </body> </html>