2025-12-31 16:53:26 +00:00
|
|
|
|
import { useState } from 'react'
|
|
|
|
|
|
import Question from './Question'
|
2025-12-31 17:05:48 +00:00
|
|
|
|
import Players from './Players'
|
|
|
|
|
|
import PlayersModal from './PlayersModal'
|
2025-12-31 17:13:24 +00:00
|
|
|
|
import QuestionsModal from './QuestionsModal'
|
2025-12-31 17:05:48 +00:00
|
|
|
|
import Score from './Score'
|
2025-12-31 17:13:24 +00:00
|
|
|
|
import { questions as initialQuestions } from '../data/questions'
|
2025-12-31 16:53:26 +00:00
|
|
|
|
import './Game.css'
|
|
|
|
|
|
|
|
|
|
|
|
const Game = () => {
|
2025-12-31 17:05:48 +00:00
|
|
|
|
const [players, setPlayers] = useState([])
|
|
|
|
|
|
const [currentPlayerId, setCurrentPlayerId] = useState(null)
|
|
|
|
|
|
const [playerScores, setPlayerScores] = useState({})
|
2025-12-31 17:13:24 +00:00
|
|
|
|
const [questions, setQuestions] = useState(initialQuestions)
|
2025-12-31 16:53:26 +00:00
|
|
|
|
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0)
|
|
|
|
|
|
const [gameOver, setGameOver] = useState(false)
|
|
|
|
|
|
const [revealedAnswers, setRevealedAnswers] = useState([])
|
2025-12-31 17:05:48 +00:00
|
|
|
|
const [isPlayersModalOpen, setIsPlayersModalOpen] = useState(false)
|
2025-12-31 17:13:24 +00:00
|
|
|
|
const [isQuestionsModalOpen, setIsQuestionsModalOpen] = useState(false)
|
2025-12-31 16:53:26 +00:00
|
|
|
|
|
|
|
|
|
|
const currentQuestion = questions[currentQuestionIndex]
|
|
|
|
|
|
const isLastQuestion = currentQuestionIndex === questions.length - 1
|
|
|
|
|
|
|
2025-12-31 17:05:48 +00:00
|
|
|
|
const handleAddPlayer = (name) => {
|
|
|
|
|
|
const newPlayer = {
|
|
|
|
|
|
id: Date.now(),
|
|
|
|
|
|
name: name,
|
|
|
|
|
|
}
|
|
|
|
|
|
const updatedPlayers = [...players, newPlayer]
|
|
|
|
|
|
setPlayers(updatedPlayers)
|
|
|
|
|
|
|
|
|
|
|
|
// Если это первый участник, делаем его текущим
|
|
|
|
|
|
if (updatedPlayers.length === 1) {
|
|
|
|
|
|
setCurrentPlayerId(newPlayer.id)
|
|
|
|
|
|
setPlayerScores({ [newPlayer.id]: 0 })
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setPlayerScores({ ...playerScores, [newPlayer.id]: 0 })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleSelectPlayer = (playerId) => {
|
|
|
|
|
|
setCurrentPlayerId(playerId)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleRemovePlayer = (playerId) => {
|
|
|
|
|
|
const updatedPlayers = players.filter(p => p.id !== playerId)
|
|
|
|
|
|
setPlayers(updatedPlayers)
|
|
|
|
|
|
|
|
|
|
|
|
const updatedScores = { ...playerScores }
|
|
|
|
|
|
delete updatedScores[playerId]
|
|
|
|
|
|
setPlayerScores(updatedScores)
|
|
|
|
|
|
|
|
|
|
|
|
// Если удалили текущего участника, выбираем другого
|
|
|
|
|
|
if (currentPlayerId === playerId) {
|
|
|
|
|
|
if (updatedPlayers.length > 0) {
|
|
|
|
|
|
setCurrentPlayerId(updatedPlayers[0].id)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setCurrentPlayerId(null)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getNextPlayerId = () => {
|
|
|
|
|
|
if (players.length === 0) return null
|
|
|
|
|
|
if (players.length === 1) return currentPlayerId
|
|
|
|
|
|
|
|
|
|
|
|
const currentIndex = players.findIndex(p => p.id === currentPlayerId)
|
|
|
|
|
|
const nextIndex = (currentIndex + 1) % players.length
|
|
|
|
|
|
return players[nextIndex].id
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 16:53:26 +00:00
|
|
|
|
const handleAnswerClick = (answerIndex, points) => {
|
|
|
|
|
|
if (revealedAnswers.includes(answerIndex)) return
|
2025-12-31 17:05:48 +00:00
|
|
|
|
if (!currentPlayerId) return
|
2025-12-31 17:13:24 +00:00
|
|
|
|
if (!currentQuestion) return
|
2025-12-31 16:53:26 +00:00
|
|
|
|
|
2025-12-31 17:05:48 +00:00
|
|
|
|
const isLastAnswer = revealedAnswers.length === currentQuestion.answers.length - 1
|
|
|
|
|
|
|
2025-12-31 16:53:26 +00:00
|
|
|
|
setRevealedAnswers([...revealedAnswers, answerIndex])
|
2025-12-31 17:05:48 +00:00
|
|
|
|
|
|
|
|
|
|
// Добавляем очки текущему участнику
|
|
|
|
|
|
setPlayerScores({
|
|
|
|
|
|
...playerScores,
|
|
|
|
|
|
[currentPlayerId]: (playerScores[currentPlayerId] || 0) + points,
|
|
|
|
|
|
})
|
2025-12-31 16:53:26 +00:00
|
|
|
|
|
2025-12-31 17:05:48 +00:00
|
|
|
|
// Переходим к следующему участнику только если это не последний ответ
|
|
|
|
|
|
if (!isLastAnswer) {
|
|
|
|
|
|
const nextPlayerId = getNextPlayerId()
|
|
|
|
|
|
if (nextPlayerId) {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
setCurrentPlayerId(nextPlayerId)
|
|
|
|
|
|
}, 500)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Если это последний ответ, переходим к следующему участнику перед следующим вопросом
|
2025-12-31 16:53:26 +00:00
|
|
|
|
setTimeout(() => {
|
2025-12-31 17:05:48 +00:00
|
|
|
|
const nextPlayerId = getNextPlayerId()
|
|
|
|
|
|
if (nextPlayerId) {
|
|
|
|
|
|
setCurrentPlayerId(nextPlayerId)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 16:53:26 +00:00
|
|
|
|
if (isLastQuestion) {
|
|
|
|
|
|
setGameOver(true)
|
|
|
|
|
|
} else {
|
2025-12-31 17:05:48 +00:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
nextQuestion()
|
|
|
|
|
|
}, 500)
|
2025-12-31 16:53:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
}, 2000)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const nextQuestion = () => {
|
|
|
|
|
|
setCurrentQuestionIndex(currentQuestionIndex + 1)
|
|
|
|
|
|
setRevealedAnswers([])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const restartGame = () => {
|
|
|
|
|
|
setCurrentQuestionIndex(0)
|
|
|
|
|
|
setGameOver(false)
|
|
|
|
|
|
setRevealedAnswers([])
|
2025-12-31 17:05:48 +00:00
|
|
|
|
const initialScores = {}
|
|
|
|
|
|
players.forEach(player => {
|
|
|
|
|
|
initialScores[player.id] = 0
|
|
|
|
|
|
})
|
|
|
|
|
|
setPlayerScores(initialScores)
|
|
|
|
|
|
if (players.length > 0) {
|
|
|
|
|
|
setCurrentPlayerId(players[0].id)
|
|
|
|
|
|
}
|
2025-12-31 16:53:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:13:24 +00:00
|
|
|
|
const handleUpdateQuestions = (updatedQuestions) => {
|
|
|
|
|
|
setQuestions(updatedQuestions)
|
|
|
|
|
|
// Если текущий вопрос был удален, сбрасываем индекс
|
|
|
|
|
|
if (currentQuestionIndex >= updatedQuestions.length) {
|
|
|
|
|
|
setCurrentQuestionIndex(0)
|
|
|
|
|
|
setRevealedAnswers([])
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 16:53:26 +00:00
|
|
|
|
if (gameOver) {
|
2025-12-31 17:05:48 +00:00
|
|
|
|
// Находим победителя(ей)
|
|
|
|
|
|
const scores = Object.values(playerScores)
|
|
|
|
|
|
const maxScore = scores.length > 0 ? Math.max(...scores) : 0
|
|
|
|
|
|
const winners = players.filter(p => playerScores[p.id] === maxScore)
|
|
|
|
|
|
|
2025-12-31 16:53:26 +00:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="game-over">
|
|
|
|
|
|
<div className="game-over-content">
|
|
|
|
|
|
<h2 className="game-over-title">🎉 Игра окончена! 🎉</h2>
|
2025-12-31 17:05:48 +00:00
|
|
|
|
<div className="final-scores">
|
|
|
|
|
|
<h3 className="final-scores-title">Итоговые результаты:</h3>
|
|
|
|
|
|
{players
|
|
|
|
|
|
.sort((a, b) => (playerScores[b.id] || 0) - (playerScores[a.id] || 0))
|
|
|
|
|
|
.map((player) => (
|
|
|
|
|
|
<div
|
|
|
|
|
|
key={player.id}
|
|
|
|
|
|
className={`final-score-item ${
|
|
|
|
|
|
winners.includes(player) ? 'final-score-winner' : ''
|
|
|
|
|
|
}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span className="final-score-name">{player.name}</span>
|
|
|
|
|
|
<span className="final-score-value">
|
|
|
|
|
|
{playerScores[player.id] || 0} очков
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
2025-12-31 16:53:26 +00:00
|
|
|
|
<button className="restart-button" onClick={restartGame}>
|
|
|
|
|
|
Играть снова
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="game">
|
2025-12-31 17:05:48 +00:00
|
|
|
|
<PlayersModal
|
|
|
|
|
|
isOpen={isPlayersModalOpen}
|
|
|
|
|
|
onClose={() => setIsPlayersModalOpen(false)}
|
|
|
|
|
|
players={players}
|
|
|
|
|
|
onAddPlayer={handleAddPlayer}
|
|
|
|
|
|
onRemovePlayer={handleRemovePlayer}
|
2025-12-31 16:53:26 +00:00
|
|
|
|
/>
|
2025-12-31 17:05:48 +00:00
|
|
|
|
|
2025-12-31 17:13:24 +00:00
|
|
|
|
<QuestionsModal
|
|
|
|
|
|
isOpen={isQuestionsModalOpen}
|
|
|
|
|
|
onClose={() => setIsQuestionsModalOpen(false)}
|
|
|
|
|
|
questions={questions}
|
|
|
|
|
|
onUpdateQuestions={handleUpdateQuestions}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2025-12-31 17:05:48 +00:00
|
|
|
|
<div className="game-header">
|
2025-12-31 17:13:24 +00:00
|
|
|
|
<div className="game-header-buttons">
|
|
|
|
|
|
<button
|
|
|
|
|
|
className="manage-players-button"
|
|
|
|
|
|
onClick={() => setIsPlayersModalOpen(true)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{players.length === 0 ? 'Добавить участников' : 'Управление участниками'}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
className="manage-questions-button"
|
|
|
|
|
|
onClick={() => setIsQuestionsModalOpen(true)}
|
|
|
|
|
|
>
|
|
|
|
|
|
Управление вопросами
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
2025-12-31 17:05:48 +00:00
|
|
|
|
|
|
|
|
|
|
{players.length > 0 && (
|
|
|
|
|
|
<Players
|
|
|
|
|
|
players={players}
|
|
|
|
|
|
currentPlayerId={currentPlayerId}
|
|
|
|
|
|
playerScores={playerScores}
|
|
|
|
|
|
onSelectPlayer={handleSelectPlayer}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{players.length > 0 && currentPlayerId ? (
|
|
|
|
|
|
<>
|
2025-12-31 17:13:24 +00:00
|
|
|
|
{questions.length === 0 ? (
|
|
|
|
|
|
<div className="no-players-message">
|
|
|
|
|
|
<p>Добавьте вопросы, чтобы начать игру</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : currentQuestion ? (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Score
|
|
|
|
|
|
score={playerScores[currentPlayerId] || 0}
|
|
|
|
|
|
questionNumber={currentQuestionIndex + 1}
|
|
|
|
|
|
totalQuestions={questions.length}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Question
|
|
|
|
|
|
question={currentQuestion}
|
|
|
|
|
|
questionNumber={currentQuestionIndex + 1}
|
|
|
|
|
|
onAnswerClick={handleAnswerClick}
|
|
|
|
|
|
revealedAnswers={revealedAnswers}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="no-players-message">
|
|
|
|
|
|
<p>Ошибка: вопрос не найден</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2025-12-31 17:05:48 +00:00
|
|
|
|
</>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="no-players-message">
|
|
|
|
|
|
<p>Добавьте участников, чтобы начать игру</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2025-12-31 16:53:26 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default Game
|
|
|
|
|
|
|