|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
|
|
const roomForm = document.getElementById('roomForm'); |
|
|
const usernameInput = document.getElementById('username'); |
|
|
const roomInput = document.getElementById('room'); |
|
|
const joinBtn = document.getElementById('joinBtn'); |
|
|
const readyBtn = document.getElementById('readyBtn'); |
|
|
const gameContainer = document.getElementById('gameContainer'); |
|
|
const textDisplay = document.getElementById('textDisplay'); |
|
|
const inputArea = document.getElementById('inputArea'); |
|
|
const playersList = document.getElementById('playersList'); |
|
|
const countdown = document.getElementById('countdown'); |
|
|
|
|
|
|
|
|
let socket; |
|
|
let username = ''; |
|
|
let room = ''; |
|
|
let text = ''; |
|
|
let startTime = null; |
|
|
let currentPosition = 0; |
|
|
let mistakes = 0; |
|
|
let totalChars = 0; |
|
|
let gameActive = false; |
|
|
let playerReady = false; |
|
|
|
|
|
|
|
|
function initSocket() { |
|
|
socket = io(); |
|
|
|
|
|
|
|
|
socket.on('connect', () => { |
|
|
console.log('Connected to server'); |
|
|
}); |
|
|
|
|
|
socket.on('room_update', (data) => { |
|
|
updatePlayersList(data.players); |
|
|
|
|
|
|
|
|
if (data.started && !gameActive) { |
|
|
startGame(data.text); |
|
|
} |
|
|
}); |
|
|
|
|
|
socket.on('countdown_start', () => { |
|
|
|
|
|
startCountdown(); |
|
|
}); |
|
|
|
|
|
socket.on('game_started', (data) => { |
|
|
startGame(data.text); |
|
|
}); |
|
|
|
|
|
socket.on('progress_update', (players) => { |
|
|
updatePlayersProgress(players); |
|
|
|
|
|
|
|
|
let allFinished = true; |
|
|
for (const id in players) { |
|
|
if (!players[id].finished) { |
|
|
allFinished = false; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (allFinished) { |
|
|
showResults(players); |
|
|
} |
|
|
}); |
|
|
|
|
|
socket.on('disconnect', () => { |
|
|
console.log('Disconnected from server'); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function joinRoom() { |
|
|
username = usernameInput.value.trim(); |
|
|
room = roomInput.value.trim(); |
|
|
|
|
|
if (!username || !room) { |
|
|
alert('Пожалуйста, введите имя пользователя и название комнаты'); |
|
|
return; |
|
|
} |
|
|
|
|
|
socket.emit('join', { |
|
|
username: username, |
|
|
room: room |
|
|
}); |
|
|
|
|
|
|
|
|
roomForm.style.display = 'none'; |
|
|
gameContainer.style.display = 'block'; |
|
|
} |
|
|
|
|
|
|
|
|
function updatePlayersList(players) { |
|
|
playersList.innerHTML = ''; |
|
|
|
|
|
for (const id in players) { |
|
|
const player = players[id]; |
|
|
const playerItem = document.createElement('div'); |
|
|
playerItem.className = 'player-item'; |
|
|
playerItem.id = 'player-' + id; |
|
|
|
|
|
const playerName = document.createElement('div'); |
|
|
playerName.className = 'player-name'; |
|
|
playerName.textContent = player.username; |
|
|
|
|
|
|
|
|
if (player.ready) { |
|
|
playerName.textContent += ' ✓'; |
|
|
playerName.classList.add('player-ready'); |
|
|
} |
|
|
|
|
|
const playerProgress = document.createElement('div'); |
|
|
playerProgress.className = 'player-progress'; |
|
|
|
|
|
const progressBar = document.createElement('div'); |
|
|
progressBar.className = 'progress-bar'; |
|
|
progressBar.style.width = player.progress + '%'; |
|
|
|
|
|
const playerStats = document.createElement('div'); |
|
|
playerStats.className = 'player-stats'; |
|
|
playerStats.innerHTML = ` |
|
|
<span>WPM: ${player.wpm || 0}</span> |
|
|
<span>Точность: ${player.accuracy || 0}%</span> |
|
|
`; |
|
|
|
|
|
playerProgress.appendChild(progressBar); |
|
|
playerItem.appendChild(playerName); |
|
|
playerItem.appendChild(playerProgress); |
|
|
playerItem.appendChild(playerStats); |
|
|
playersList.appendChild(playerItem); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function updatePlayersProgress(players) { |
|
|
for (const id in players) { |
|
|
const player = players[id]; |
|
|
const playerItem = document.getElementById('player-' + id); |
|
|
|
|
|
if (playerItem) { |
|
|
const progressBar = playerItem.querySelector('.progress-bar'); |
|
|
progressBar.style.width = player.progress + '%'; |
|
|
|
|
|
const playerStats = playerItem.querySelector('.player-stats'); |
|
|
playerStats.innerHTML = ` |
|
|
<span>WPM: ${player.wpm || 0}</span> |
|
|
<span>Точность: ${player.accuracy || 0}%</span> |
|
|
`; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function startCountdown() { |
|
|
readyBtn.disabled = true; |
|
|
countdown.style.display = 'block'; |
|
|
let count = 3; |
|
|
countdown.textContent = count; |
|
|
|
|
|
const countdownInterval = setInterval(() => { |
|
|
count--; |
|
|
countdown.textContent = count; |
|
|
|
|
|
if (count <= 0) { |
|
|
clearInterval(countdownInterval); |
|
|
countdown.style.display = 'none'; |
|
|
socket.emit('start_game', { room: room }); |
|
|
} |
|
|
}, 1000); |
|
|
} |
|
|
|
|
|
|
|
|
function startGame(textContent) { |
|
|
text = textContent; |
|
|
displayText(); |
|
|
|
|
|
inputArea.value = ''; |
|
|
inputArea.disabled = false; |
|
|
inputArea.focus(); |
|
|
|
|
|
mistakes = 0; |
|
|
totalChars = 0; |
|
|
currentPosition = 0; |
|
|
|
|
|
startTime = new Date(); |
|
|
gameActive = true; |
|
|
} |
|
|
|
|
|
|
|
|
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 showResults(players) { |
|
|
let winner = null; |
|
|
let maxWpm = 0; |
|
|
|
|
|
for (const id in players) { |
|
|
const player = players[id]; |
|
|
if (player.wpm > maxWpm) { |
|
|
maxWpm = player.wpm; |
|
|
winner = player; |
|
|
} |
|
|
} |
|
|
|
|
|
if (winner) { |
|
|
alert(`Игра окончена! Победитель: ${winner.username} со скоростью ${winner.wpm} WPM и точностью ${winner.accuracy}%`); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
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 { |
|
|
|
|
|
gameActive = false; |
|
|
inputArea.disabled = true; |
|
|
} |
|
|
} else { |
|
|
|
|
|
mistakes++; |
|
|
totalChars++; |
|
|
} |
|
|
|
|
|
|
|
|
updateStats(); |
|
|
}); |
|
|
|
|
|
|
|
|
function updateStats() { |
|
|
if (!gameActive && currentPosition < text.length) return; |
|
|
|
|
|
const currentTime = new Date(); |
|
|
const timeElapsed = (currentTime - startTime) / 1000; |
|
|
|
|
|
|
|
|
|
|
|
const wpm = Math.round((currentPosition / 5) / (timeElapsed / 60)); |
|
|
|
|
|
|
|
|
const accuracy = totalChars > 0 ? Math.round(((totalChars - mistakes) / totalChars) * 100) : 100; |
|
|
|
|
|
|
|
|
const progress = Math.round((currentPosition / text.length) * 100); |
|
|
|
|
|
|
|
|
socket.emit('update_progress', { |
|
|
room: room, |
|
|
progress: progress, |
|
|
wpm: wpm, |
|
|
accuracy: accuracy |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
joinBtn.addEventListener('click', function(e) { |
|
|
e.preventDefault(); |
|
|
joinRoom(); |
|
|
}); |
|
|
|
|
|
readyBtn.addEventListener('click', function() { |
|
|
if (!playerReady) { |
|
|
playerReady = true; |
|
|
readyBtn.textContent = 'Готов!'; |
|
|
readyBtn.classList.add('ready'); |
|
|
|
|
|
socket.emit('player_ready', { |
|
|
room: room |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
initSocket(); |
|
|
}); |