144 lines
4.0 KiB
JavaScript
144 lines
4.0 KiB
JavaScript
import React, { useState, useEffect, useCallback } from 'react';
|
|
import socket from './socket';
|
|
import Lobby from './components/Lobby';
|
|
import GameBoard from './components/GameBoard';
|
|
|
|
const SCREENS = { NAME: 'name', LOBBY: 'lobby', ROOM: 'room', GAME: 'game' };
|
|
|
|
export default function App() {
|
|
const [screen, setScreen] = useState(SCREENS.NAME);
|
|
const [playerName, setPlayerName] = useState('');
|
|
const [playerId, setPlayerId] = useState(null);
|
|
const [rooms, setRooms] = useState([]);
|
|
const [currentRoom, setCurrentRoom] = useState(null);
|
|
const [gameState, setGameState] = useState(null);
|
|
const [gameId, setGameId] = useState(null);
|
|
const [error, setError] = useState(null);
|
|
const [connected, setConnected] = useState(false);
|
|
|
|
useEffect(() => {
|
|
socket.connect();
|
|
|
|
socket.on('connect', () => {
|
|
setConnected(true);
|
|
setPlayerId(socket.id);
|
|
});
|
|
|
|
socket.on('disconnect', () => setConnected(false));
|
|
|
|
socket.on('nameSet', (data) => {
|
|
setPlayerId(data.id);
|
|
setScreen(SCREENS.LOBBY);
|
|
});
|
|
|
|
socket.on('roomsUpdated', (data) => setRooms(data));
|
|
socket.on('roomCreated', (room) => {
|
|
setCurrentRoom(room);
|
|
setScreen(SCREENS.ROOM);
|
|
});
|
|
socket.on('roomJoined', (room) => {
|
|
setCurrentRoom(room);
|
|
setScreen(SCREENS.ROOM);
|
|
});
|
|
socket.on('roomUpdated', (room) => setCurrentRoom(room));
|
|
|
|
socket.on('gameStarted', (data) => {
|
|
setGameId(data.gameId);
|
|
setGameState(data.state);
|
|
setScreen(SCREENS.GAME);
|
|
});
|
|
|
|
socket.on('gameStateUpdated', (state) => setGameState(state));
|
|
|
|
socket.on('error', (data) => {
|
|
setError(data.message);
|
|
setTimeout(() => setError(null), 3000);
|
|
});
|
|
|
|
socket.on('actionError', (data) => {
|
|
setError(data.message);
|
|
setTimeout(() => setError(null), 3000);
|
|
});
|
|
|
|
socket.on('playerDisconnected', (data) => {
|
|
setError(`${data.playerName} disconnected`);
|
|
setTimeout(() => setError(null), 3000);
|
|
});
|
|
|
|
socket.on('needsResponse', () => {});
|
|
|
|
return () => {
|
|
socket.removeAllListeners();
|
|
socket.disconnect();
|
|
};
|
|
}, []);
|
|
|
|
const handleSetName = useCallback((e) => {
|
|
e.preventDefault();
|
|
if (!playerName.trim()) return;
|
|
socket.emit('setName', playerName.trim());
|
|
}, [playerName]);
|
|
|
|
const handleBackToLobby = useCallback(() => {
|
|
socket.emit('leaveRoom');
|
|
setCurrentRoom(null);
|
|
setGameState(null);
|
|
setGameId(null);
|
|
setScreen(SCREENS.LOBBY);
|
|
socket.emit('getRooms');
|
|
}, []);
|
|
|
|
if (screen === SCREENS.NAME) {
|
|
return (
|
|
<div className="app">
|
|
<div className="title-screen">
|
|
<h1 className="game-title">Arcane Duels</h1>
|
|
<p className="game-subtitle">A game of strategy, mana, and might</p>
|
|
<form onSubmit={handleSetName} className="name-form">
|
|
<input
|
|
type="text"
|
|
value={playerName}
|
|
onChange={(e) => setPlayerName(e.target.value)}
|
|
placeholder="Enter your name..."
|
|
className="name-input"
|
|
maxLength={20}
|
|
autoFocus
|
|
/>
|
|
<button type="submit" className="btn btn-primary" disabled={!connected}>
|
|
{connected ? 'Enter the Arena' : 'Connecting...'}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
{error && <div className="error-toast">{error}</div>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (screen === SCREENS.GAME && gameState) {
|
|
return (
|
|
<div className="app">
|
|
<GameBoard
|
|
gameState={gameState}
|
|
playerId={playerId}
|
|
onLeave={handleBackToLobby}
|
|
/>
|
|
{error && <div className="error-toast">{error}</div>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="app">
|
|
<Lobby
|
|
playerId={playerId}
|
|
playerName={playerName}
|
|
rooms={rooms}
|
|
currentRoom={currentRoom}
|
|
screen={screen}
|
|
onBackToLobby={handleBackToLobby}
|
|
/>
|
|
{error && <div className="error-toast">{error}</div>}
|
|
</div>
|
|
);
|
|
}
|