document.addEventListener('DOMContentLoaded', function() { // Элементы DOM const textDisplay = document.getElementById('textDisplay'); const inputArea = document.getElementById('inputArea'); const wpmDisplay = document.getElementById('wpm'); const accuracyDisplay = document.getElementById('accuracy'); const timeDisplay = document.getElementById('time'); const startBtn = document.getElementById('startBtn'); const resultContainer = document.getElementById('resultContainer'); const finalWpm = document.getElementById('finalWpm'); const finalAccuracy = document.getElementById('finalAccuracy'); const finalTime = document.getElementById('finalTime'); const restartBtn = document.getElementById('restartBtn'); const overseer = document.getElementById('overseer'); const gunshot = document.getElementById('gunshot'); const usernameInput = document.getElementById('username'); // Переменные для игры let text = ''; let startTime = null; let endTime = null; let timer = null; let mistakes = 0; let totalChars = 0; let currentPosition = 0; let gameActive = false; let timeLimit = 60; // Время в секундах let timeRemaining = timeLimit; // Получение текста с сервера function fetchText() { fetch('/get_text') .then(response => response.json()) .then(data => { text = data.text; displayText(); }) .catch(error => console.error('Error fetching text:', error)); } // Отображение текста function displayText() { textDisplay.innerHTML = ''; for (let i = 0; i < text.length; i++) { const span = document.createElement('span'); span.textContent = text[i]; textDisplay.appendChild(span); } // Выделяем первый символ как текущий if (textDisplay.firstChild) { textDisplay.firstChild.classList.add('current'); } } // Начало игры function startGame() { if (gameActive) return; // Скрываем кнопку старта во время обратного отсчёта startBtn.disabled = true; // Запускаем обратный отсчёт startCountdown(); } // Функция обратного отсчёта function startCountdown() { const countdown = document.getElementById('countdown'); countdown.style.display = 'block'; let count = 3; countdown.textContent = count; countdown.classList.add('pulse'); const countdownTimer = setInterval(() => { count--; if (count > 0) { countdown.textContent = count; countdown.classList.remove('pulse'); void countdown.offsetWidth; // Перезапуск анимации countdown.classList.add('pulse'); } else if (count === 0) { countdown.textContent = 'Старт!'; countdown.classList.remove('pulse'); void countdown.offsetWidth; countdown.classList.add('pulse'); } else { clearInterval(countdownTimer); countdown.style.display = 'none'; initializeGame(); } }, 1000); } // Инициализация игры после обратного отсчёта function initializeGame() { fetchText(); inputArea.value = ''; inputArea.disabled = false; inputArea.focus(); mistakes = 0; totalChars = 0; currentPosition = 0; timeRemaining = timeLimit; startTime = new Date(); gameActive = true; // Запускаем таймер timer = setInterval(updateTimer, 1000); // Скрываем результаты и надсмотрщика resultContainer.style.display = 'none'; overseer.classList.remove('show'); // Разблокируем кнопку старта startBtn.disabled = false; // Обновляем статистику updateStats(); } // Обновление таймера function updateTimer() { timeRemaining--; timeDisplay.textContent = timeRemaining; if (timeRemaining <= 0) { endGame(false); } } // Обновление статистики function updateStats() { if (!gameActive) return; const currentTime = new Date(); const timeElapsed = (currentTime - startTime) / 1000; // в секундах // Расчет WPM (слов в минуту) // Считаем, что среднее слово - 5 символов const wpm = Math.round((currentPosition / 5) / (timeElapsed / 60)); // Расчет точности const accuracy = totalChars > 0 ? Math.round(((totalChars - mistakes) / totalChars) * 100) : 100; wpmDisplay.textContent = wpm; accuracyDisplay.textContent = accuracy + '%'; } // Завершение игры function endGame(completed = true) { clearInterval(timer); gameActive = false; endTime = new Date(); inputArea.disabled = true; const timeElapsed = Math.round((endTime - startTime) / 1000); const wpm = Math.round((currentPosition / 5) / (timeElapsed / 60)); const accuracy = totalChars > 0 ? Math.round(((totalChars - mistakes) / totalChars) * 100) : 100; finalWpm.textContent = wpm; finalAccuracy.textContent = accuracy + '%'; finalTime.textContent = timeElapsed + 's'; resultContainer.style.display = 'block'; // Если игра не завершена успешно или точность ниже 70%, показываем надсмотрщика if (!completed || accuracy < 70) { showOverseer(); } // Сохраняем результат if (usernameInput && usernameInput.value) { saveResult(usernameInput.value, wpm, accuracy); } } // Показ надсмотрщика и анимация выстрела function showOverseer() { overseer.classList.add('show'); // Создаем капли крови для разбрызгивания createBloodSplatters(); // Через 2 секунды после появления надсмотрщика - "выстрел" setTimeout(() => { overseer.classList.add('firing'); gunshot.style.display = 'block'; bloodSplatter.classList.add('show'); // Показываем сообщение о смерти deathMessage.classList.add('show'); // Звук выстрела const audio = new Audio('/static/sounds/gunshot.mp3'); audio.play().catch(e => console.log('Audio play failed:', e)); // Эффект тряски экрана document.body.classList.add('shake'); setTimeout(() => { document.body.classList.remove('shake'); }, 500); // Скрываем эффект выстрела через 500мс setTimeout(() => { gunshot.style.display = 'none'; }, 500); }, 2000); } // Создание брызг крови function createBloodSplatters() { const bloodSplatter = document.getElementById('bloodSplatter'); bloodSplatter.innerHTML = ''; // Создаем 30 капель крови разного размера for (let i = 0; i < 30; i++) { const drop = document.createElement('div'); drop.classList.add('blood-drop'); // Случайный размер const size = Math.random() * 20 + 5; drop.style.width = `${size}px`; drop.style.height = `${size}px`; // Случайное положение drop.style.left = `${Math.random() * 100}%`; drop.style.top = `${Math.random() * 50}%`; // Случайная задержка анимации drop.style.animationDelay = `${Math.random() * 1.5}s`; bloodSplatter.appendChild(drop); } } // Сохранение результата function saveResult(username, wpm, accuracy) { fetch('/save_result', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username: username, wpm: wpm, accuracy: accuracy }) }) .then(response => response.json()) .then(data => console.log('Result saved:', data)) .catch(error => console.error('Error saving result:', error)); } // Обработка ввода inputArea.addEventListener('input', function(e) { if (!gameActive) return; const inputValue = e.target.value; const currentChar = text[currentPosition]; // Проверяем, правильно ли введен символ if (inputValue.charAt(inputValue.length - 1) === currentChar) { // Правильный символ const spans = textDisplay.querySelectorAll('span'); spans[currentPosition].classList.remove('current'); spans[currentPosition].classList.add('correct'); currentPosition++; totalChars++; // Если есть следующий символ, делаем его текущим if (currentPosition < text.length) { spans[currentPosition].classList.add('current'); } else { // Текст закончился, завершаем игру endGame(true); } } else { // Неправильный символ mistakes++; totalChars++; const spans = textDisplay.querySelectorAll('span'); spans[currentPosition].classList.add('incorrect'); } // Обновляем статистику updateStats(); }); // Обработчики кнопок startBtn.addEventListener('click', startGame); restartBtn.addEventListener('click', startGame); // Инициализация timeDisplay.textContent = timeLimit; });