From 1e549de4fb3d590912ede7f9728323fe20ea9bf2 Mon Sep 17 00:00:00 2001 From: pb-coding <71174645+pb-coding@users.noreply.github.com> Date: Sat, 23 Sep 2023 12:18:34 +0200 Subject: [PATCH] wip --- src/App.tsx | 79 ++++++++++++++----- src/components/CardCache.tsx | 41 +++++++--- src/components/CardCacheOld.tsx | 21 +++++ src/components/CardStackCard.tsx | 27 +++++++ .../{CardStack.tsx => CardStackOld.tsx} | 0 src/components/CardStackStaple.tsx | 45 +++++++++++ .../{DepositCards.tsx => DepositCardsOld.tsx} | 0 src/components/DiscardPile.tsx | 48 +++++++++++ src/components/DiscardPileCard.tsx | 30 +++++++ src/components/PlayArea.tsx | 26 ++++++ src/components/PlayerCard.tsx | 69 ++++++++++++++++ src/components/PlayerCards.tsx | 73 +++++++++++++++++ src/helpers.ts | 7 ++ src/objects/cards.ts | 38 ++++++++- src/types/gameTypes.ts | 7 ++ 15 files changed, 479 insertions(+), 32 deletions(-) create mode 100644 src/components/CardCacheOld.tsx create mode 100644 src/components/CardStackCard.tsx rename src/components/{CardStack.tsx => CardStackOld.tsx} (100%) create mode 100644 src/components/CardStackStaple.tsx rename src/components/{DepositCards.tsx => DepositCardsOld.tsx} (100%) create mode 100644 src/components/DiscardPile.tsx create mode 100644 src/components/DiscardPileCard.tsx create mode 100644 src/components/PlayArea.tsx create mode 100644 src/components/PlayerCard.tsx create mode 100644 src/components/PlayerCards.tsx create mode 100644 src/helpers.ts diff --git a/src/App.tsx b/src/App.tsx index 3cb566e..22a0bfa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,19 @@ import { useState, useEffect } from "react"; import { socket } from "./socket"; -import ThreeScene from "./components/ThreeScene"; +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 { JoinSession } from "./components/JoinSession"; import { Events } from "./components/Events"; import { Game } from "./types/gameTypes"; import Action from "./components/Action"; -import CardStack from "./components/CardStack"; -import DepositCards from "./components/DepositCards"; -import CardCache from "./components/CardCache"; +import CardStack from "./components/CardStackOld"; +import DepositCards from "./components/DepositCardsOld"; +import CardCache from "./components/CardCacheOld"; +import { extractCurrentPlayer } from "./helpers"; +import PlayArea from "./components/PlayArea"; export default function App() { const [isConnected, setIsConnected] = useState(socket.connected); @@ -22,7 +26,7 @@ export default function App() { const showStartGameButton = session !== "" && clientsInRoom >= 2; const showNextGameButton = gameData?.phase === "new round"; - const playersData = extractMyData(gameData); + const playersData = extractCurrentPlayer(gameData); function startGame() { socket.emit("new-game", { sessionId: session }); @@ -37,11 +41,6 @@ export default function App() { socket.emit("click-card", cardPosition); } - function extractMyData(gameData: Game | null) { - if (!gameData) return undefined; - return gameData.players.find((player) => player.socketId === socket.id); - } - function setTempMessage(message: string) { setMessageDisplay(message); setTimeout(() => { @@ -49,16 +48,6 @@ export default function App() { }, 3000); } - useEffect(() => { - if (gameData?.phase === "game-over") { - setTempMessage("Game Over"); - } else if (gameData?.phase === "game-started") { - setTempMessage("Game Started"); - } else if (gameData?.phase === "waiting-for-players") { - setTempMessage("Waiting for players"); - } - }, [gameData]); - useEffect(() => { function onConnect() { setIsConnected(true); @@ -96,9 +85,57 @@ export default function App() { }; }, []); + // Three-Fiber + const tableModel = useGLTF("/models/table.glb"); + const heightProportion = 1.25; + return (
- +
+ + new Vector3(0, 0, 0)} + /> + + + + + + + { + if (node instanceof Mesh) { + // node.castShadow = true; + node.receiveShadow = true; + } + }} + /> + + + + + +
= ({ playersData }) => { - if (!playersData?.cardCache) { - return null; - } + const [cardCacheCard, setCardCacheCard] = useState(null); - return ( -
-

Card Cache: {playersData.cardCache.value}

-
- ); + const updateCardCache = (playerData: Player) => { + if (playerData.cardCache == null) { + setCardCacheCard(null); + return; + } + const showFaceUp = true; + const card = createCard( + playerData.cardCache, + new Vector3(9, 20, 4), + showFaceUp + ); + setCardCacheCard(card); + }; + + useEffect(() => { + const playerData = playersData.find( + (player) => player.socketId === socket.id + ); + if (!playerData) return; + updateCardCache(playerData); + }, [playersData]); + + if (!cardCacheCard) return null; + + return ; }; export default CardCache; diff --git a/src/components/CardCacheOld.tsx b/src/components/CardCacheOld.tsx new file mode 100644 index 0000000..f1f068d --- /dev/null +++ b/src/components/CardCacheOld.tsx @@ -0,0 +1,21 @@ +import { FC } from "react"; + +import { Player } from "../types/gameTypes"; + +type CardCacheProps = { + playersData: Player | undefined; +}; + +const CardCache: FC = ({ playersData }) => { + if (!playersData?.cardCache) { + return null; + } + + return ( +
+

Card Cache: {playersData.cardCache.value}

+
+ ); +}; + +export default CardCache; diff --git a/src/components/CardStackCard.tsx b/src/components/CardStackCard.tsx new file mode 100644 index 0000000..26c7f02 --- /dev/null +++ b/src/components/CardStackCard.tsx @@ -0,0 +1,27 @@ +import { FC, useState, useEffect } from "react"; +import { Object3D, Object3DEventMap } from "three"; +import { socket } from "../socket"; + +type CardStackCardProps = { + card: Object3D; + isUppermostCard: boolean; +}; + +const CardStackCard: FC = ({ card, isUppermostCard }) => { + const [cardObject, setCardObject] = + useState>(card); + + useEffect(() => { + if (cardObject.name === card.name) return; + setCardObject(card); + }, [card, cardObject]); + + const clickCard = () => { + if (!isUppermostCard) return; + console.log("Draw card"); + socket.emit("draw-from-card-stack", "draw card"); + }; + return clickCard()} />; +}; + +export default CardStackCard; diff --git a/src/components/CardStack.tsx b/src/components/CardStackOld.tsx similarity index 100% rename from src/components/CardStack.tsx rename to src/components/CardStackOld.tsx diff --git a/src/components/CardStackStaple.tsx b/src/components/CardStackStaple.tsx new file mode 100644 index 0000000..03f91f3 --- /dev/null +++ b/src/components/CardStackStaple.tsx @@ -0,0 +1,45 @@ +import { FC, useEffect, useState } from "react"; +import { Vector3, Object3D } from "three"; +import { CardStack } from "../types/gameTypes"; +import { createCardStaple } from "../objects/cards"; +import CardStackCard from "./CardStackCard"; + +type CardStackProps = { + cardStackData: CardStack | null; +}; + +const CardStackStaple: FC = ({ cardStackData }) => { + const [cardStack, setCardStack] = useState([]); + + const updateCardStack = (cardStackData: CardStack) => { + const stack = createCardStaple( + cardStackData.cards, + new Vector3(-1.2, 20, 0) + ); + setCardStack(stack); + }; + + const checkIfUppermostCard = (index: number) => { + return index === cardStack.length - 1; + }; + + useEffect(() => { + if (!cardStackData) return; + updateCardStack(cardStackData); + }, [cardStackData]); + if (!cardStackData) return null; + + return ( + <> + {cardStack.map((card, index) => ( + + ))} + + ); +}; + +export default CardStackStaple; diff --git a/src/components/DepositCards.tsx b/src/components/DepositCardsOld.tsx similarity index 100% rename from src/components/DepositCards.tsx rename to src/components/DepositCardsOld.tsx diff --git a/src/components/DiscardPile.tsx b/src/components/DiscardPile.tsx new file mode 100644 index 0000000..5c9bb5d --- /dev/null +++ b/src/components/DiscardPile.tsx @@ -0,0 +1,48 @@ +import { FC, useState, useEffect } from "react"; +import { Vector3, Object3D } from "three"; + +import { Card } from "../types/gameTypes"; +import { createCardStaple } from "../objects/cards"; +import DiscardPileCard from "./DiscardPileCard"; + +type DiscardPileProps = { + discardPileData: Card[] | null; +}; + +const DiscardPile: FC = ({ discardPileData }) => { + const [discardPile, setDiscardPile] = useState([]); + + const updateDiscardPile = (discardPileData: Card[]) => { + const showFaceUp = true; + const pile = createCardStaple( + discardPileData, + new Vector3(1.2, 20, 0), + showFaceUp + ); + setDiscardPile(pile); + }; + + const checkIfUppermostCard = (index: number) => { + return index === discardPile.length - 1; + }; + + useEffect(() => { + if (!discardPileData) return; + updateDiscardPile(discardPileData); + }, [discardPileData]); + + if (!discardPile) return null; + return ( + <> + {discardPile.map((card, index) => ( + + ))} + + ); +}; + +export default DiscardPile; diff --git a/src/components/DiscardPileCard.tsx b/src/components/DiscardPileCard.tsx new file mode 100644 index 0000000..9eaa1c1 --- /dev/null +++ b/src/components/DiscardPileCard.tsx @@ -0,0 +1,30 @@ +import { FC, useState, useEffect } from "react"; +import { Object3D, Object3DEventMap } from "three"; +import { socket } from "../socket"; + +type DiscardPileCardProps = { + card: Object3D; + isUppermostCard: boolean; +}; + +const DiscardPileCard: FC = ({ + card, + isUppermostCard, +}) => { + const [cardObject, setCardObject] = + useState>(card); + + useEffect(() => { + if (cardObject.name === card.name) return; + setCardObject(card); + }, [card, cardObject]); + + const clickCard = () => { + if (!isUppermostCard) return; + console.log("Draw card"); + socket.emit("draw-from-card-stack", "draw card"); + }; + return clickCard()} />; +}; + +export default DiscardPileCard; diff --git a/src/components/PlayArea.tsx b/src/components/PlayArea.tsx new file mode 100644 index 0000000..984baba --- /dev/null +++ b/src/components/PlayArea.tsx @@ -0,0 +1,26 @@ +import { FC } from "react"; + +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; +}; + +const PlayArea: FC = ({ gameData }) => { + if (!gameData) return null; + + return ( + <> + + + + + + ); +}; + +export default PlayArea; diff --git a/src/components/PlayerCard.tsx b/src/components/PlayerCard.tsx new file mode 100644 index 0000000..852af43 --- /dev/null +++ b/src/components/PlayerCard.tsx @@ -0,0 +1,69 @@ +import { FC, useState, useEffect } from "react"; +import { Object3D, Object3DEventMap } from "three"; +// import { useFrame } from "@react-three/fiber"; +import { socket } from "../socket"; +import { PlayerWithVisualCards } from "../types/gameTypes"; + +type PlayerCardProps = { + card: Object3D; + index: number; + isCurrentPlayer: boolean; + playerWithCards: PlayerWithVisualCards; +}; + +const PlayerCard: FC = ({ + card, + index, + isCurrentPlayer, + playerWithCards, +}) => { + const isCardRevealed = playerWithCards.player.knownCardPositions[index]; + const [cardObject, setCardObject] = + useState>(card); + + useEffect(() => { + if (cardObject.name === card.name) return; + setCardObject(card); + }, [card, cardObject]); + + // TODO: Fix stuttering card rotation + /*const [rotationGoal, setRotationGoal] = useState(0); + const rotationSpeed = 0.05; // Adjust for faster/slower flip + const currentRotation = useRef(0); + + useFrame(() => { + if (currentRotation.current < rotationGoal) { + card.rotation.x += rotationSpeed; + currentRotation.current += rotationSpeed; + if (currentRotation.current >= rotationGoal) { + card.rotation.x = rotationGoal; + } + } else if (currentRotation.current > rotationGoal) { + card.rotation.x -= rotationSpeed; + currentRotation.current -= rotationSpeed; + if (currentRotation.current <= rotationGoal) { + card.rotation.x = rotationGoal; + } + } + });*/ + + if (isCardRevealed) { + card.rotation.x = Math.PI; + } + + const clickCard = () => { + if (!isCurrentPlayer) return; + console.log("Clicked on one of my cards"); + socket.emit("click-card", index, (response: string) => { + console.log("Response:", response); + /*if (rotationGoal === 0) { + setRotationGoal(Math.PI); + } else { + setRotationGoal(0); + }*/ + }); + }; + return clickCard()} />; +}; + +export default PlayerCard; diff --git a/src/components/PlayerCards.tsx b/src/components/PlayerCards.tsx new file mode 100644 index 0000000..39b9692 --- /dev/null +++ b/src/components/PlayerCards.tsx @@ -0,0 +1,73 @@ +import { FC, useState, useEffect } from "react"; +import { socket } from "../socket"; +import { Vector3 } from "three"; + +import { createPlayerCards } from "../objects/cards"; +import PlayerCard from "./PlayerCard"; +import { PlayerWithVisualCards, Player } from "../types/gameTypes"; + +type PlayerCardsProps = { + playersData: Player[]; +}; + +const PlayerCards: FC = ({ playersData }) => { + const [playersWithCards, setPlayersWithCards] = useState< + PlayerWithVisualCards[] + >([]); + + const updatePlayerCards = (playersData: Player[]) => { + const playersWithCards: PlayerWithVisualCards[] = []; + const currentPlayerWithCards: PlayerWithVisualCards[] = []; + let nonCurrentPlayerIndex = 1; + playersData.forEach((player) => { + const positionOffset = 12; + const playerWithCards: PlayerWithVisualCards = { + player, + cards: [], + }; + if (player.socketId === socket.id) { + playerWithCards.cards = createPlayerCards( + player.cards, + new Vector3(0, 20, positionOffset) + ); + currentPlayerWithCards.push(playerWithCards); + } else { + const playerOffset = positionOffset + nonCurrentPlayerIndex * -20; + playerWithCards.cards = createPlayerCards( + player.cards, + new Vector3(0, 20, playerOffset) + ); + playersWithCards.push(playerWithCards); + nonCurrentPlayerIndex++; + } + }); + setPlayersWithCards([...currentPlayerWithCards, ...playersWithCards]); + }; + + useEffect(() => { + if (!playersData) return; + updatePlayerCards(playersData); + }, [playersData]); + + if (!playersWithCards) return null; + + return ( + <> + {playersWithCards.map((playerWithCards, playerIndex) => ( + <> + {playerWithCards.cards.map((card, index) => ( + + ))} + + ))} + + ); +}; + +export default PlayerCards; diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 0000000..1aae00f --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,7 @@ +import { Game } from "./types/gameTypes"; +import { socket } from "./socket"; + +export function extractCurrentPlayer(gameData: Game | null) { + if (!gameData) return undefined; + return gameData.players.find((player) => player.socketId === socket.id); +} diff --git a/src/objects/cards.ts b/src/objects/cards.ts index 517e0b5..3bbe5b0 100644 --- a/src/objects/cards.ts +++ b/src/objects/cards.ts @@ -77,7 +77,11 @@ const getCardTexture = (value: number | string) => { return cardTexture; }; -export const createCard = (cardData: Card, position: Vector3) => { +export const createCard = ( + cardData: Card, + position: Vector3, + faceUp: boolean = false +) => { const cardMaterial = [ new MeshBasicMaterial(), new MeshBasicMaterial(), @@ -89,6 +93,12 @@ export const createCard = (cardData: Card, position: Vector3) => { const card = new Mesh(cardGeometry, cardMaterial); card.name = cardData.name; card.position.copy(position); + + if (faceUp) { + card.rotation.x = Math.PI; + console.log("faceUp"); + } + return card; }; @@ -117,3 +127,29 @@ export const createPlayerCards = ( }); return playerCards; }; + +export const createCardStaple = ( + cards: Card[], + positionReference: Vector3, + faceUp: boolean = false +) => { + const cardStackCards: Mesh< + BoxGeometry, + MeshBasicMaterial[], + Object3DEventMap + >[] = []; + cards.forEach((card: Card, index: number) => { + const cardPositionX = positionReference.x; + const cardPositionY = positionReference.y + index * 0.01; + const cardPositionZ = positionReference.z; + + const cardPosition = new Vector3( + cardPositionX, + cardPositionY, + cardPositionZ + ); + const cardStackCard = createCard(card, cardPosition, faceUp); + cardStackCards.push(cardStackCard); + }); + return cardStackCards; +}; diff --git a/src/types/gameTypes.ts b/src/types/gameTypes.ts index 93811b0..1ced9bf 100644 --- a/src/types/gameTypes.ts +++ b/src/types/gameTypes.ts @@ -1,3 +1,5 @@ +import { Object3D } from "three"; + export type Player = { id: number; socketId: string; @@ -12,6 +14,11 @@ export type Player = { closedRound: boolean; }; +export type PlayerWithVisualCards = { + player: Player; + cards: Object3D[]; +}; + export type CardValue = | -2 | -1