137 lines
6.9 KiB
TypeScript
137 lines
6.9 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { useGame } from '../hooks/GameContext';
|
|
import { motion } from 'framer-motion';
|
|
|
|
const Game: React.FC = () => {
|
|
const { room, currentPlayer, sendGameAction } = useGame();
|
|
const [timeLeft, setTimeLeft] = useState(0);
|
|
|
|
if (!room || !currentPlayer) return null;
|
|
|
|
const currentTeam = room.teams[room.currentTeamIndex];
|
|
// Determine who is describing:
|
|
// The `nextDescriberIndex` points to the NEXT player to describe, or the CURRENT one if the round is active?
|
|
// In `finishRound` (backend), we increment it. So during the round, it points to the CURRENT describer.
|
|
const describerId = currentTeam.playerIds[currentTeam.nextDescriberIndex];
|
|
const isMyTurn = currentPlayer.sessionId === describerId;
|
|
const isMyTeam = currentPlayer.teamId === currentTeam.id;
|
|
|
|
useEffect(() => {
|
|
if (room.roundEndTime) {
|
|
const interval = setInterval(() => {
|
|
const remaining = Math.max(0, Math.ceil((room.roundEndTime! - Date.now()) / 1000));
|
|
setTimeLeft(remaining);
|
|
|
|
// If time is up, we could trigger something, but backend controls the flow usually.
|
|
// However, to be responsive, we can show "Time's up" or similar.
|
|
}, 500);
|
|
return () => clearInterval(interval);
|
|
}
|
|
}, [room.roundEndTime]);
|
|
|
|
const handleAction = (action: 'GUESS' | 'SKIP') => {
|
|
sendGameAction(action);
|
|
// Add haptic feedback here if using Telegram SDK
|
|
};
|
|
|
|
return (
|
|
<div className="flex flex-col h-screen bg-gray-900 text-white p-4 overflow-hidden">
|
|
{/* Header Info */}
|
|
<div className="flex justify-between items-start mb-4">
|
|
<div className="flex flex-col">
|
|
<span className="text-sm text-gray-400">Ход команды:</span>
|
|
<span className="text-xl font-bold text-blue-400">{currentTeam.name}</span>
|
|
</div>
|
|
<div className="flex flex-col items-end">
|
|
<span className="text-sm text-gray-400">Время:</span>
|
|
<span className={`text-3xl font-mono font-bold ${timeLeft <= 10 ? 'text-red-500' : 'text-white'}`}>
|
|
{timeLeft}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Game Area */}
|
|
<div className="flex-1 flex flex-col items-center justify-center relative">
|
|
|
|
{isMyTurn ? (
|
|
// Explainer View
|
|
<div className="w-full max-w-xs">
|
|
<div className="text-center mb-6">
|
|
<p className="text-gray-400 text-sm uppercase tracking-widest">Объясняй</p>
|
|
</div>
|
|
|
|
<motion.div
|
|
key={room.currentWord} // Animate when word changes
|
|
initial={{ scale: 0.8, opacity: 0 }}
|
|
animate={{ scale: 1, opacity: 1 }}
|
|
className="bg-white text-gray-900 rounded-2xl p-10 shadow-2xl flex items-center justify-center mb-12 aspect-square"
|
|
>
|
|
<h2 className="text-4xl font-black text-center break-words">{room.currentWord}</h2>
|
|
</motion.div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<button
|
|
onClick={() => handleAction('SKIP')}
|
|
className="bg-gray-700 hover:bg-gray-600 text-white font-bold py-4 rounded-xl transition-colors flex flex-col items-center"
|
|
>
|
|
<span className="text-2xl mb-1">👎</span>
|
|
<span>Пропустить</span>
|
|
</button>
|
|
<button
|
|
onClick={() => handleAction('GUESS')}
|
|
className="bg-green-600 hover:bg-green-500 text-white font-bold py-4 rounded-xl transition-colors flex flex-col items-center"
|
|
>
|
|
<span className="text-2xl mb-1">👍</span>
|
|
<span>Угадали</span>
|
|
</button>
|
|
</div>
|
|
<p className="text-center text-xs text-gray-500 mt-6">Свайпы скоро будут доступны</p>
|
|
</div>
|
|
) : isMyTeam ? (
|
|
// Guesser (My Team) View
|
|
<div className="text-center">
|
|
<div className="mb-8">
|
|
<div className="w-20 h-20 bg-blue-500 rounded-full mx-auto flex items-center justify-center text-3xl font-bold mb-4 animate-pulse">
|
|
👂
|
|
</div>
|
|
<h3 className="text-2xl font-bold mb-2">Слушайте внимательно!</h3>
|
|
<p className="text-gray-400">
|
|
<span className="text-white font-bold">{room.players[describerId]?.name || 'Игрок'}</span> объясняет слово.
|
|
</p>
|
|
</div>
|
|
<div className="p-6 bg-gray-800/50 rounded-xl border border-gray-700">
|
|
<p className="text-sm text-gray-400">Текущий счет за раунд</p>
|
|
<p className="text-4xl font-bold text-green-400">???</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
// Spectator (Other Team) View
|
|
<div className="text-center opacity-75">
|
|
<div className="mb-8">
|
|
<div className="w-20 h-20 bg-gray-700 rounded-full mx-auto flex items-center justify-center text-3xl font-bold mb-4">
|
|
🤫
|
|
</div>
|
|
<h3 className="text-xl font-bold mb-2">Тихо!</h3>
|
|
<p className="text-gray-400">
|
|
Сейчас играет команда противников.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Footer Score */}
|
|
<div className="mt-auto pt-6 border-t border-gray-800 flex justify-around">
|
|
{room.teams.map(t => (
|
|
<div key={t.id} className={`text-center ${t.id === currentTeam.id ? 'opacity-100' : 'opacity-50'}`}>
|
|
<p className="text-xs text-gray-500">{t.name}</p>
|
|
<p className="text-xl font-bold">{t.score}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Game;
|