From 7b9e05b122d2111cf601a5b68241080937c12dce Mon Sep 17 00:00:00 2001 From: pb-coding Date: Mon, 2 Oct 2023 14:47:13 +0200 Subject: [PATCH] refactors cards to nested array --- .env | 4 +- index.html | 4 +- public/card-back.svg | 1631 +++++++++++++++++++++++++++++ src/App.tsx | 2 - src/components/Footer.tsx | 4 +- src/components/PlayArea.tsx | 4 +- src/components/PlayerCard.tsx | 32 +- src/components/PlayerCards.tsx | 81 -- src/components/PlayerDecks.tsx | 86 ++ src/components/SessionManager.tsx | 16 +- src/components/ThreeScene.tsx | 198 ---- src/objects/cards.ts | 68 +- src/objects/gameObjects.ts | 0 src/types/gameTypes.ts | 27 +- 14 files changed, 1801 insertions(+), 356 deletions(-) create mode 100644 public/card-back.svg delete mode 100644 src/components/PlayerCards.tsx create mode 100644 src/components/PlayerDecks.tsx delete mode 100644 src/components/ThreeScene.tsx delete mode 100644 src/objects/gameObjects.ts diff --git a/.env b/.env index 7751623..b35bba8 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ VITE_ENVIRONMENT=dev VITE_BACKEND_URL=https://skyjo-backend.voltvector.org -#VITE_ENVIRONMENT=local -#VITE_BACKEND_URL=http://localhost:3001 \ No newline at end of file +VITE_ENVIRONMENT=local +VITE_BACKEND_URL=http://localhost:3001 \ No newline at end of file diff --git a/index.html b/index.html index 05f0bce..994d2b6 100644 --- a/index.html +++ b/index.html @@ -7,9 +7,9 @@ rel="stylesheet" as="style" /> - + - Play Skyjo! + Play Skylo!
diff --git a/public/card-back.svg b/public/card-back.svg new file mode 100644 index 0000000..c44421d --- /dev/null +++ b/public/card-back.svg @@ -0,0 +1,1631 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 4a5475b..845c36b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -62,8 +62,6 @@ export default function App() { }; }, []); - console.log(messageDispaly); - return (
{!gameData && ( diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 2509957..afaf92f 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -60,8 +60,8 @@ export const Footer: FC
= ({
- {gameData.players.map((player) => ( -
+ {gameData.players.map((player, index) => ( +
{player?.name} {player.socketId == socket.id && "👤"}{" "} diff --git a/src/components/PlayArea.tsx b/src/components/PlayArea.tsx index dac45a2..c66521c 100644 --- a/src/components/PlayArea.tsx +++ b/src/components/PlayArea.tsx @@ -1,6 +1,6 @@ import { FC } from "react"; -import PlayerCards from "../components/PlayerCards"; +import PlayerDecks from "./PlayerDecks"; import { Game } from "../types/gameTypes"; import CardStackStaple from "./CardStackStaple"; import DiscardPile from "./DiscardPile"; @@ -14,7 +14,7 @@ const PlayArea: FC = ({ gameData }) => { return ( <> - + diff --git a/src/components/PlayerCard.tsx b/src/components/PlayerCard.tsx index 852af43..99ddbec 100644 --- a/src/components/PlayerCard.tsx +++ b/src/components/PlayerCard.tsx @@ -2,22 +2,25 @@ 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"; +import { PlayerVisualDeck } from "../types/gameTypes"; type PlayerCardProps = { card: Object3D; - index: number; + columnIndex: number; + cardIndex: number; isCurrentPlayer: boolean; - playerWithCards: PlayerWithVisualCards; + visualPlayerDeck: PlayerVisualDeck; }; const PlayerCard: FC = ({ card, - index, + columnIndex, + cardIndex, isCurrentPlayer, - playerWithCards, + visualPlayerDeck, }) => { - const isCardRevealed = playerWithCards.player.knownCardPositions[index]; + const isCardRevealed = + visualPlayerDeck.player.knownCardPositions[columnIndex][cardIndex]; const [cardObject, setCardObject] = useState>(card); @@ -54,16 +57,15 @@ const PlayerCard: FC = ({ 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); - }*/ - }); + socket.emit("click-card", [columnIndex, cardIndex]); }; - return clickCard()} />; + return ( + clickCard()} + /> + ); }; export default PlayerCard; diff --git a/src/components/PlayerCards.tsx b/src/components/PlayerCards.tsx deleted file mode 100644 index b27403e..0000000 --- a/src/components/PlayerCards.tsx +++ /dev/null @@ -1,81 +0,0 @@ -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"; -import CardCache from "./CardCache"; - -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 = 14; - 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; -// ich (0) --> 4 -// andere (1) --> -8 diff --git a/src/components/PlayerDecks.tsx b/src/components/PlayerDecks.tsx new file mode 100644 index 0000000..8eb89a6 --- /dev/null +++ b/src/components/PlayerDecks.tsx @@ -0,0 +1,86 @@ +import { FC, useState, useEffect } from "react"; +import { socket } from "../socket"; +import { Vector3 } from "three"; + +import { createPlayerCards as createPlayerDeck } from "../objects/cards"; +import PlayerCard from "./PlayerCard"; +import { PlayerVisualDeck, Player } from "../types/gameTypes"; +import CardCache from "./CardCache"; + +type PlayerDecksProps = { + playersData: Player[]; +}; + +const PlayerDecks: FC = ({ playersData }) => { + const [visualPlayerDecks, setVisualPlayerDecks] = useState< + PlayerVisualDeck[] + >([]); + + const updatePlayerCards = (playersData: Player[]) => { + const visualPlayerDecks: PlayerVisualDeck[] = []; + const currentVisualPlayerDeck: PlayerVisualDeck[] = []; + let nonCurrentPlayerIndex = 1; + playersData.forEach((player) => { + const positionOffset = 14; + const playerVisualDeck: PlayerVisualDeck = { + player, + visualDeck: [], + }; + if (player.socketId === socket.id) { + playerVisualDeck.visualDeck = createPlayerDeck( + player.deck, + new Vector3(0, 20, positionOffset) + ); + currentVisualPlayerDeck.push(playerVisualDeck); + } else { + const playerOffset = positionOffset + nonCurrentPlayerIndex * -20; + playerVisualDeck.visualDeck = createPlayerDeck( + player.deck, + new Vector3(0, 20, playerOffset) + ); + visualPlayerDecks.push(playerVisualDeck); + nonCurrentPlayerIndex++; + } + }); + setVisualPlayerDecks([...currentVisualPlayerDeck, ...visualPlayerDecks]); + }; + + useEffect(() => { + if (!playersData) return; + updatePlayerCards(playersData); + }, [playersData]); + + if (!visualPlayerDecks) return null; + + return ( + <> + {visualPlayerDecks.map((visualPlayerDeck, playerIndex) => ( + <> + {visualPlayerDeck.visualDeck.map((column, columnIndex) => ( + <> + {column.map((card, cardIndex) => ( + + ))} + + ))} + + + ))} + + ); +}; + +export default PlayerDecks; +// ich (0) --> 4 +// andere (1) --> -8 diff --git a/src/components/SessionManager.tsx b/src/components/SessionManager.tsx index 83ed160..5056c99 100644 --- a/src/components/SessionManager.tsx +++ b/src/components/SessionManager.tsx @@ -13,6 +13,8 @@ type SessionManagerProps = { showStartGameButton: boolean; }; +type SessionResponse = "success" | "error:full" | "error:running"; + export const SessionManager: FC = ({ isConnected, clientsInRoom, @@ -25,9 +27,11 @@ export const SessionManager: FC = ({ function joinSession(event: React.FormEvent) { event.preventDefault(); - socket.emit("join-session", sessionField); - setSession(sessionField); - setSessionField(""); + socket.emit("join-session", sessionField, (response: SessionResponse) => { + if (response !== "success") return; + setSession(sessionField); + setSessionField(""); + }); } function leaveSession(sessionName: string) { @@ -52,10 +56,10 @@ export const SessionManager: FC = ({ >

- Skyjo + Skylo

- Play Skyjo online with your friends! + Play Skylo online with your friends!

{!isActiveSession && ( @@ -64,7 +68,7 @@ export const SessionManager: FC = ({ htmlFor="first_name" className="block mb-2 text-sm font-medium text-white" > - Join Skyjo Session + Join Skylo Session
= ({ gameData }) => { - const containerRef = useRef(null); - const cardsRef = useRef([]); - const tableModel = useGLTF("/models/table.glb"); - const heightProportion = 1.25; - - function extractCurrentPlayer(gameData: Game | null): Player | undefined { - if (!gameData) return undefined; - return gameData.players.find((player) => player.socketId === socket.id); - } - - function disposeMesh(mesh: THREE.Mesh) { - if (mesh.material instanceof Material) { - mesh.material.dispose(); - } else if (Array.isArray(mesh.material)) { - for (const material of mesh.material) { - material.dispose(); - } - } - - mesh.geometry.dispose(); - } - - useEffect(() => { - const container = containerRef.current; - const scene = new Scene(); - const camera = new PerspectiveCamera( - 75, - window.innerWidth / (window.innerHeight / heightProportion), - 0.1, - 1000 - ); - - const renderer = new WebGLRenderer(); - - renderer.setSize(window.innerWidth, window.innerHeight / heightProportion); - container && container.appendChild(renderer.domElement); - - // Objects - const cubeCenter = createCube(new Vector3(1, 1, 1), 0x00ff00); - cubeCenter.position.set(0, 20, 0); - - const cubeTL = createCube(new Vector3(1, 1, 1), 0x00ff00); - cubeTL.position.set(-10, 20, -15); - - const cubeTR = createCube(new Vector3(1, 1, 1), 0x00ff00); - cubeTR.position.set(10, 20, -15); - - const cubeBL = createCube(new Vector3(1, 1, 1), 0x00ff00); - cubeBL.position.set(-10, 20, 15); - - const cubeBR = createCube(new Vector3(1, 1, 1), 0x00ff00); - cubeBR.position.set(10, 20, 15); - - scene.add(cubeCenter, cubeBL, cubeBR, cubeTL, cubeTR); - - // Cards - /*const initialCards: Card[] = []; - for (let i = 1; i < 13; i++) { - initialCards.push({ id: i, name: `${i} Card`, value: i as CardValue }); - } - - const playerCards = createPlayerCards(initialCards, new Vector3(0, 20, 0)); - - scene.add(...playerCards);*/ - - const currentPlayer = extractCurrentPlayer(gameData); - if (currentPlayer && !cardsRef.current.length) { - cardsRef.current = createPlayerCards( - currentPlayer.cards, - new Vector3(0, 20, 0) - ); - scene.add(...cardsRef.current); - } - - // Camera - camera.position.set(0, 45, 10); - camera.lookAt(0, 0, 0); - - // Orbit Controls - const orbit = new OrbitControls(camera, renderer.domElement); - orbit.update(); - - // Lights - const directionalLight = new DirectionalLight(0xffffff, 0.8); - directionalLight.position.set(0, 50, 20); - scene.add(directionalLight); - - directionalLight.castShadow = true; - directionalLight.shadow.mapSize.width = 1024; - directionalLight.shadow.mapSize.height = 1024; - - const ambientLight = new AmbientLight(0xa3a3a3, 0.3); - scene.add(ambientLight); - - // Import models - const table = tableModel.scene; - scene.add(table); - // table.rotateY(Math.PI / 2); - table.scale.set(2, 2, 2); - table.position.set(0, 1.8, 0); - - table.traverse(function (node: Object3D) { - if (node instanceof Mesh) { - // node.castShadow = true; - node.receiveShadow = true; - } - }); - - // Helpers - const gridHelper = new GridHelper(100, 100); - scene.add(gridHelper); - - const axesHelper = new AxesHelper(5); - scene.add(axesHelper); - - const directionalLightHelper = new DirectionalLightHelper( - directionalLight, - 5 - ); - scene.add(directionalLightHelper); - - // Animation - const animate = () => { - requestAnimationFrame(animate); - // cube.rotation.x += 0.01; - // cube.rotation.y += 0.01; - const currentPlayer = extractCurrentPlayer(gameData); - if (currentPlayer) { - if (cardsRef.current.length === 0) { - cardsRef.current = createPlayerCards( - currentPlayer.cards, - new Vector3(0, 20, 0) - ); - scene.add(...cardsRef.current); - } else { - cardsRef.current.forEach((card, index) => { - if (card.name !== currentPlayer.cards[index]?.name) { - disposeMesh(card); - scene.remove(card); - cardsRef.current[index] = createCard( - currentPlayer.cards[index], - card.position - ); - scene.add(cardsRef.current[index]); - } - }); - } - //const cards = createPlayerCards(currentPlayer.cards); - //scene.add(...cards); - } - - renderer.render(scene, camera); - }; - - console.log("Animate"); - animate(); - - return () => { - // Clean up on unmount - renderer.dispose(); - // scene.dispose(); - // material.dispose(); - // geometry.dispose(); - container && container.removeChild(renderer.domElement); - }; - }, [gameData]); - - return
; -}; - -export default ThreeScene; diff --git a/src/objects/cards.ts b/src/objects/cards.ts index 3bbe5b0..21fcc4c 100644 --- a/src/objects/cards.ts +++ b/src/objects/cards.ts @@ -6,9 +6,10 @@ import { Vector3, Mesh, Object3DEventMap, + Object3D, } from "three"; -import { Card } from "../types/gameTypes"; +import { Card, Deck, VisualColumn, VisualDeck } from "../types/gameTypes"; const textureLoader = new TextureLoader(); const cardSize = 5; @@ -18,7 +19,7 @@ const cardGeometry = new BoxGeometry( cardSize * 0.6 ); -const getCardTexture = (value: number | string) => { +const getCardTexture = (value: number | null) => { let cardTexture; switch (value) { case -2: @@ -66,7 +67,7 @@ const getCardTexture = (value: number | string) => { case 12: cardTexture = textureLoader.load("/textures/card-12.png"); break; - case "X": + case null: cardTexture = textureLoader.load("/textures/card-back.png"); break; default: @@ -78,54 +79,53 @@ const getCardTexture = (value: number | string) => { }; export const createCard = ( - cardData: Card, + card: Card, position: Vector3, faceUp: boolean = false ) => { const cardMaterial = [ new MeshBasicMaterial(), new MeshBasicMaterial(), - new MeshBasicMaterial({ map: getCardTexture("X") }), // X = backside - new MeshBasicMaterial({ map: getCardTexture(cardData.value) }), + new MeshBasicMaterial({ map: getCardTexture(null) }), // X = backside + new MeshBasicMaterial({ map: getCardTexture(card) }), new MeshBasicMaterial(), new MeshBasicMaterial(), ]; - const card = new Mesh(cardGeometry, cardMaterial); - card.name = cardData.name; - card.position.copy(position); + const visualCard = new Mesh(cardGeometry, cardMaterial); + visualCard.name = card !== null ? card.toString() : "Facedown card"; + visualCard.position.copy(position); if (faceUp) { - card.rotation.x = Math.PI; - console.log("faceUp"); + visualCard.rotation.x = Math.PI; } - return card; + return visualCard; }; -export const createPlayerCards = ( - cards: Card[], - positionReference: Vector3 -) => { - const playerCards: Mesh< - BoxGeometry, - MeshBasicMaterial[], - Object3DEventMap - >[] = []; - cards.forEach((card, index) => { - const cardPositionX = positionReference.x + (index % 4) * 4 - 6; - const cardPositionY = positionReference.y; - const cardPositionZ = - positionReference.z + (Math.ceil((index + 1) / 4) - 1) * 4 - 8; +export const createPlayerCards = (deck: Deck, positionReference: Vector3) => { + const playerDeck: Object3D[][] = []; - const cardPosition = new Vector3( - cardPositionX, - cardPositionY, - cardPositionZ - ); - const playerCard = createCard(card, cardPosition); - playerCards.push(playerCard); + let visualColumn: Object3D[] = []; + deck.forEach((column, columnIndex) => { + column.forEach((card, cardIndex) => { + const cardPositionX = positionReference.x + columnIndex * 4 - 6; + const cardPositionY = positionReference.y; + const cardPositionZ = positionReference.z + cardIndex * 4 - 8; + + const cardPosition = new Vector3( + cardPositionX, + cardPositionY, + cardPositionZ + ); + const playerCard = createCard(card, cardPosition); + visualColumn.push(playerCard); + if (cardIndex === 2) { + playerDeck.push(visualColumn as VisualColumn); + visualColumn = []; + } + }); }); - return playerCards; + return playerDeck as VisualDeck; }; export const createCardStaple = ( diff --git a/src/objects/gameObjects.ts b/src/objects/gameObjects.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/types/gameTypes.ts b/src/types/gameTypes.ts index 8336f2c..71cb4bd 100644 --- a/src/types/gameTypes.ts +++ b/src/types/gameTypes.ts @@ -4,8 +4,8 @@ export type Player = { id: number; socketId: string; name: string; - cards: Card[]; - knownCardPositions: boolean[]; + deck: Deck; + knownCardPositions: KnownCardsColumn[]; playersTurn: boolean; cardCache: Card | null; tookDispiledCard: boolean; @@ -15,12 +15,21 @@ export type Player = { place: number; }; -export type PlayerWithVisualCards = { +export type PlayerVisualDeck = { player: Player; - cards: Object3D[]; + visualDeck: VisualDeck; }; -export type CardValue = +export type VisualColumn = [Object3D, Object3D, Object3D]; + +export type VisualDeck = VisualColumn[]; + +export type Column = [Card, Card, Card]; +export type KnownCardsColumn = [boolean, boolean, boolean]; + +export type Deck = Column[]; + +export type Card = | -2 | -1 | 0 @@ -36,13 +45,7 @@ export type CardValue = | 10 | 11 | 12 - | "X"; - -export type Card = { - id: number; - name: string; - value: CardValue; -}; + | null; export type CardStack = { cards: Card[];