wip
This commit is contained in:
parent
93f207cf12
commit
85c508d02d
10 changed files with 218 additions and 193 deletions
167
src/App.tsx
167
src/App.tsx
|
|
@ -1,25 +1,16 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { socket } from "./socket";
|
||||
import { Object3D, Mesh } from "three";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { OrbitControls, useGLTF, PerspectiveCamera } from "@react-three/drei";
|
||||
import { ConnectionState } from "./components/ConnectionState";
|
||||
import { ConnectionManager } from "./components/ConnectionManager";
|
||||
|
||||
import GameCanvas from "./components/GameCanvas";
|
||||
import { Footer } from "./components/Footer";
|
||||
import { JoinSession } from "./components/JoinSession";
|
||||
import { Events } from "./components/Events";
|
||||
import { Game } from "./types/gameTypes";
|
||||
import Action from "./components/Action";
|
||||
import CardStack from "./components/CardStackOld";
|
||||
import DepositCards from "./components/DepositCardsOld";
|
||||
import CardCache from "./components/CardCacheOld";
|
||||
import { extractCurrentPlayer } from "./helpers";
|
||||
import PlayArea from "./components/PlayArea";
|
||||
import Text from "./global/Text";
|
||||
import Button from "./global/Button";
|
||||
import MessageDisplay from "./components/MessageDisplay";
|
||||
|
||||
export default function App() {
|
||||
const [isConnected, setIsConnected] = useState(socket.connected);
|
||||
const [messageEvents, setMessageEvents] = useState<string[]>([]);
|
||||
// const [messageEvents, setMessageEvents] = useState<string[]>([]);
|
||||
const [session, setSession] = useState("");
|
||||
const [clientsInRoom, setClientsInRoom] = useState(0);
|
||||
const [gameData, setGameData] = useState<Game | null>(null);
|
||||
|
|
@ -28,20 +19,7 @@ export default function App() {
|
|||
const showStartGameButton = session !== "" && clientsInRoom >= 2;
|
||||
const showNextGameButton = gameData?.phase === "new round";
|
||||
|
||||
const playersData = extractCurrentPlayer(gameData);
|
||||
|
||||
function startGame() {
|
||||
socket.emit("new-game", { sessionId: session });
|
||||
}
|
||||
|
||||
function nextGame() {
|
||||
socket.emit("next-round", { sessionId: session });
|
||||
}
|
||||
|
||||
function clickCard(cardPosition: number) {
|
||||
console.log("Clicked card", cardPosition);
|
||||
socket.emit("click-card", cardPosition);
|
||||
}
|
||||
const currentPlayerData = extractCurrentPlayer(gameData);
|
||||
|
||||
function setTempMessage(message: string) {
|
||||
setMessageDisplay(message);
|
||||
|
|
@ -65,7 +43,7 @@ export default function App() {
|
|||
|
||||
function onMessageEvent(message: string) {
|
||||
setTempMessage(message);
|
||||
setMessageEvents((previous) => [...previous, message]);
|
||||
// setMessageEvents((previous) => [...previous, message]);
|
||||
}
|
||||
|
||||
function onGameUpdate(gameData: Game) {
|
||||
|
|
@ -87,130 +65,27 @@ export default function App() {
|
|||
};
|
||||
}, []);
|
||||
|
||||
// Three-Fiber
|
||||
const tableModel = useGLTF("/models/table.glb");
|
||||
const heightProportion = 1.25;
|
||||
|
||||
return (
|
||||
<div className="bg-gray-700">
|
||||
<div
|
||||
style={{
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight / heightProportion,
|
||||
}}
|
||||
>
|
||||
<Canvas>
|
||||
<PerspectiveCamera
|
||||
makeDefault
|
||||
manual
|
||||
fov={75}
|
||||
aspect={window.innerWidth / (window.innerHeight / heightProportion)}
|
||||
near={0.1}
|
||||
far={1000}
|
||||
position={[0, 45, 10]}
|
||||
// lookAt={() => new Vector3(0, 0, 0)}
|
||||
<div className="bg-gray-900 w-screen h-screen">
|
||||
{!gameData && (
|
||||
<JoinSession
|
||||
clientsInRoom={clientsInRoom}
|
||||
session={session}
|
||||
setSession={setSession}
|
||||
showStartGameButton={showStartGameButton}
|
||||
/>
|
||||
<ambientLight color={0xa3a3a3} intensity={0.1} />
|
||||
<directionalLight
|
||||
color={0xffffff}
|
||||
position={[0, 50, 20]}
|
||||
castShadow
|
||||
shadow-mapSize={[1024, 1024]}
|
||||
/>
|
||||
<mesh>
|
||||
<boxGeometry args={[2, 2, 2]} />
|
||||
<meshStandardMaterial color={0x00ff00} />
|
||||
</mesh>
|
||||
<primitive
|
||||
object={tableModel.scene}
|
||||
position={[0, 1.8, 0]}
|
||||
scale={[2, 2, 2]}
|
||||
traverse={(node: Object3D) => {
|
||||
if (node instanceof Mesh) {
|
||||
// node.castShadow = true;
|
||||
node.receiveShadow = true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<gridHelper args={[100, 100]} />
|
||||
<axesHelper args={[5]} />
|
||||
<OrbitControls />
|
||||
<PlayArea gameData={gameData} />
|
||||
</Canvas>
|
||||
</div>
|
||||
{messageDispaly && messageDispaly !== "" && <p>{messageDispaly}</p>}
|
||||
<JoinSession session={session} setSession={setSession} />
|
||||
<Text>Player ID: {playersData?.id}</Text>
|
||||
<Text>Player Name: {playersData?.name}</Text>
|
||||
<Text>Player Round Points: {playersData?.roundPoints}</Text>
|
||||
<Text>Player Total Points: {playersData?.totalPoints}</Text>
|
||||
<ConnectionState
|
||||
)}
|
||||
<GameCanvas session={session} gameData={gameData} />
|
||||
<MessageDisplay message={messageDispaly} />
|
||||
{session !== "" && (
|
||||
<Footer
|
||||
isConnected={isConnected}
|
||||
session={session}
|
||||
clientsInRoom={clientsInRoom}
|
||||
currentPlayerData={currentPlayerData}
|
||||
showNextGameButton={showNextGameButton}
|
||||
/>
|
||||
<ConnectionManager />
|
||||
{showStartGameButton && <Button onClick={startGame}>Start Game</Button>}
|
||||
{showNextGameButton && <Button onClick={nextGame}>Next Game</Button>}
|
||||
<div className="bg-white">
|
||||
<p> ############### OLD INTERFACE ############### </p>
|
||||
|
||||
{gameData &&
|
||||
gameData.players.map((playerData, index) => (
|
||||
<div key={index}>
|
||||
<p>Player ID: {playerData.id}</p>
|
||||
<p>Player Name: {playerData.name}</p>
|
||||
<p>Player Round Points: {playerData.roundPoints}</p>
|
||||
<p>Player Total Points: {playerData.totalPoints}</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Column 1</th>
|
||||
<th>Column 2</th>
|
||||
<th>Column 3</th>
|
||||
<th>Column 4</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Array(Math.ceil(playerData.cards.length / 4))
|
||||
.fill(null)
|
||||
.map((_, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
{Array(4)
|
||||
.fill(null)
|
||||
.map((_, colIndex) => {
|
||||
const card =
|
||||
playerData.cards[rowIndex * 4 + colIndex];
|
||||
return (
|
||||
<td key={colIndex}>
|
||||
{card && (
|
||||
<Action
|
||||
data={playerData}
|
||||
action={() =>
|
||||
clickCard(rowIndex * 4 + colIndex)
|
||||
}
|
||||
>
|
||||
{card.value}
|
||||
</Action>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<CardStack gameData={gameData} playerData={playerData} />
|
||||
<DepositCards gameData={gameData} playerData={playerData} />
|
||||
<CardCache playersData={playersData} />
|
||||
<br />
|
||||
</div>
|
||||
))}
|
||||
<br />
|
||||
<br />
|
||||
<Events events={messageEvents} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { socket } from "../socket";
|
||||
|
||||
import Text from "../global/Text";
|
||||
|
||||
type ConnectionStateProps = {
|
||||
isConnected: boolean;
|
||||
session: string;
|
||||
clientsInRoom: number;
|
||||
};
|
||||
|
||||
export const ConnectionState: FC<ConnectionStateProps> = ({
|
||||
isConnected,
|
||||
session,
|
||||
clientsInRoom,
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<Text>
|
||||
State: {"" + isConnected} <br />
|
||||
Session: {session} <br />
|
||||
Players: {clientsInRoom} <br />
|
||||
SocketId: {socket.id}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
40
src/components/Footer.tsx
Normal file
40
src/components/Footer.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { FC } from "react";
|
||||
import { socket } from "../socket";
|
||||
|
||||
import Text from "../global/Text";
|
||||
import Button from "../global/Button";
|
||||
import { Player } from "../types/gameTypes";
|
||||
|
||||
type Footer = {
|
||||
isConnected: boolean;
|
||||
session: string;
|
||||
clientsInRoom: number;
|
||||
currentPlayerData: Player | undefined;
|
||||
showNextGameButton: boolean;
|
||||
};
|
||||
|
||||
export const Footer: FC<Footer> = ({
|
||||
isConnected,
|
||||
session,
|
||||
clientsInRoom,
|
||||
currentPlayerData,
|
||||
showNextGameButton,
|
||||
}) => {
|
||||
function nextGame() {
|
||||
socket.emit("next-round", { sessionId: session });
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Text>Player Name: {currentPlayerData?.name}</Text>
|
||||
<Text>Round Points: {currentPlayerData?.roundPoints}</Text>
|
||||
<Text>Total Points: {currentPlayerData?.totalPoints}</Text>
|
||||
<br />
|
||||
<Text>State: {"" + isConnected}</Text>
|
||||
<Text>SocketId: {socket.id}</Text>
|
||||
<Text>Session: {session}</Text>
|
||||
<Text>Players: {clientsInRoom}</Text>
|
||||
{showNextGameButton && <Button onClick={nextGame}>Next Game</Button>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
69
src/components/GameCanvas.tsx
Normal file
69
src/components/GameCanvas.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { FC } from "react";
|
||||
import { Object3D, Mesh } from "three";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { OrbitControls, useGLTF, PerspectiveCamera } from "@react-three/drei";
|
||||
|
||||
import PlayArea from "../components/PlayArea";
|
||||
import { Game } from "../types/gameTypes";
|
||||
|
||||
type GameCanvasProps = {
|
||||
session: string;
|
||||
gameData: Game | null;
|
||||
};
|
||||
|
||||
const GameCanvas: FC<GameCanvasProps> = ({ gameData }) => {
|
||||
const tableModel = useGLTF("/models/table.glb");
|
||||
const heightProportion = 1.4;
|
||||
|
||||
if (!gameData) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight / heightProportion,
|
||||
}}
|
||||
>
|
||||
<Canvas>
|
||||
<PerspectiveCamera
|
||||
makeDefault
|
||||
manual
|
||||
fov={75}
|
||||
aspect={window.innerWidth / (window.innerHeight / heightProportion)}
|
||||
near={0.1}
|
||||
far={1000}
|
||||
position={[0, 43, 10]}
|
||||
// lookAt={() => new Vector3(0, 20, 0)}
|
||||
/>
|
||||
<ambientLight color={0xa3a3a3} intensity={0.1} />
|
||||
<directionalLight
|
||||
color={0xffffff}
|
||||
position={[0, 50, 20]}
|
||||
castShadow
|
||||
shadow-mapSize={[1024, 1024]}
|
||||
/>
|
||||
<mesh>
|
||||
<boxGeometry args={[2, 2, 2]} />
|
||||
<meshStandardMaterial color={0x00ff00} />
|
||||
</mesh>
|
||||
<primitive
|
||||
object={tableModel.scene}
|
||||
position={[0, 1.8, 2]}
|
||||
scale={[2, 2, 2]}
|
||||
traverse={(node: Object3D) => {
|
||||
if (node instanceof Mesh) {
|
||||
// node.castShadow = true;
|
||||
node.receiveShadow = true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<gridHelper args={[100, 100]} />
|
||||
<axesHelper args={[5]} />
|
||||
<OrbitControls />
|
||||
<PlayArea gameData={gameData} />
|
||||
</Canvas>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GameCanvas;
|
||||
|
|
@ -4,11 +4,18 @@ import { socket } from "../socket";
|
|||
import Button from "../global/Button";
|
||||
|
||||
type JoinSessionProps = {
|
||||
clientsInRoom: number;
|
||||
session: string;
|
||||
setSession: Dispatch<SetStateAction<string>>;
|
||||
showStartGameButton: boolean;
|
||||
};
|
||||
|
||||
export const JoinSession: FC<JoinSessionProps> = ({ session, setSession }) => {
|
||||
export const JoinSession: FC<JoinSessionProps> = ({
|
||||
clientsInRoom,
|
||||
session,
|
||||
setSession,
|
||||
showStartGameButton,
|
||||
}) => {
|
||||
const [sessionField, setSessionField] = useState("");
|
||||
|
||||
function joinSession(event: React.FormEvent<HTMLFormElement>) {
|
||||
|
|
@ -18,15 +25,59 @@ export const JoinSession: FC<JoinSessionProps> = ({ session, setSession }) => {
|
|||
setSessionField("");
|
||||
}
|
||||
|
||||
if (session !== "") {
|
||||
return null;
|
||||
function startGame() {
|
||||
socket.emit("new-game", { sessionId: session });
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={joinSession}>
|
||||
<input onChange={(e) => setSessionField(e.target.value)} />
|
||||
const isActiveSession = session !== "";
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div
|
||||
className="w-full h-full absolute top-0 left-0 z-0"
|
||||
style={{
|
||||
backgroundImage: "linear-gradient(to bottom, #233876, #111827)",
|
||||
}}
|
||||
>
|
||||
<div className="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16 z-10 relative">
|
||||
<h1 className="mb-4 text-4xl font-extrabold tracking-tight leading-none md:text-5xl lg:text-6xl text-white">
|
||||
Skyjo
|
||||
</h1>
|
||||
<p className="mb-8 text-lg font-normal lg:text-xl sm:px-16 lg:px-48 text-gray-200">
|
||||
Play Skyjo online with your friends!
|
||||
</p>
|
||||
<div className="p-4">
|
||||
{!isActiveSession && (
|
||||
<form onSubmit={joinSession}>
|
||||
<label
|
||||
htmlFor="first_name"
|
||||
className="block mb-2 text-sm font-medium text-white"
|
||||
>
|
||||
Join Skyjo Session
|
||||
</label>
|
||||
<div className="flex space-x-1 items-center">
|
||||
<input
|
||||
className="border text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Session name"
|
||||
required
|
||||
onChange={(e) => setSessionField(e.target.value)}
|
||||
/>
|
||||
<Button>Join</Button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-gray-200 text-3xl my-2">Session: {session}</p>
|
||||
<p className="text-gray-200 text-3xl my-4">
|
||||
Players: {clientsInRoom}
|
||||
</p>
|
||||
<br />
|
||||
{showStartGameButton && (
|
||||
<Button onClick={startGame}>Start Game</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
19
src/components/MessageDisplay.tsx
Normal file
19
src/components/MessageDisplay.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { FC } from "react";
|
||||
|
||||
type MessageDisplayProps = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
const MessageDisplay: FC<MessageDisplayProps> = ({ message }) => {
|
||||
if (!message || message == "") return null;
|
||||
|
||||
return (
|
||||
<div className="absolute top-0 left-0 w-full h-full flex justify-center items-center">
|
||||
<div className="bg-gray-800 p-4 rounded-lg">
|
||||
<p className="text-white text-xl opacity-100">{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MessageDisplay;
|
||||
|
|
@ -4,7 +4,6 @@ import PlayerCards from "../components/PlayerCards";
|
|||
import { Game } from "../types/gameTypes";
|
||||
import CardStackStaple from "./CardStackStaple";
|
||||
import DiscardPile from "./DiscardPile";
|
||||
import CardCache from "./CardCache";
|
||||
|
||||
type PlayAreaProps = {
|
||||
gameData: Game | null;
|
||||
|
|
@ -18,7 +17,6 @@ const PlayArea: FC<PlayAreaProps> = ({ gameData }) => {
|
|||
<PlayerCards playersData={gameData.players} />
|
||||
<CardStackStaple cardStackData={gameData.cardStack} />
|
||||
<DiscardPile discardPileData={gameData.discardPile} />
|
||||
<CardCache playersData={gameData.players} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const PlayerCards: FC<PlayerCardsProps> = ({ playersData }) => {
|
|||
const currentPlayerWithCards: PlayerWithVisualCards[] = [];
|
||||
let nonCurrentPlayerIndex = 1;
|
||||
playersData.forEach((player) => {
|
||||
const positionOffset = 12;
|
||||
const positionOffset = 14;
|
||||
const playerWithCards: PlayerWithVisualCards = {
|
||||
player,
|
||||
cards: [],
|
||||
|
|
@ -68,7 +68,7 @@ const PlayerCards: FC<PlayerCardsProps> = ({ playersData }) => {
|
|||
<CardCache
|
||||
playerData={playerWithCards.player}
|
||||
// current player is always at index 0
|
||||
position={new Vector3(9, 20, 4 - playerIndex * 12)}
|
||||
position={new Vector3(9, 20, 6 - playerIndex * 12)}
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|||
const Button: FC<ButtonProps> = ({ children, ...rest }) => {
|
||||
return (
|
||||
<button
|
||||
className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800"
|
||||
className="text-white focus:ring-4 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-blue-800"
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ type TextNormalProps = {
|
|||
};
|
||||
|
||||
const Text: FC<TextNormalProps> = ({ children }) => {
|
||||
return <p className="text-white">{children}</p>;
|
||||
return <p className="mb-2 text-sm font-medium text-white">{children}</p>;
|
||||
};
|
||||
|
||||
export default Text;
|
||||
|
|
|
|||
Loading…
Reference in a new issue