refactors cards to nested array
This commit is contained in:
parent
ab1bad8842
commit
7b9e05b122
14 changed files with 1801 additions and 356 deletions
4
.env
4
.env
|
|
@ -1,5 +1,5 @@
|
||||||
VITE_ENVIRONMENT=dev
|
VITE_ENVIRONMENT=dev
|
||||||
VITE_BACKEND_URL=https://skyjo-backend.voltvector.org
|
VITE_BACKEND_URL=https://skyjo-backend.voltvector.org
|
||||||
|
|
||||||
#VITE_ENVIRONMENT=local
|
VITE_ENVIRONMENT=local
|
||||||
#VITE_BACKEND_URL=http://localhost:3001
|
VITE_BACKEND_URL=http://localhost:3001
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
as="style"
|
as="style"
|
||||||
/>
|
/>
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/card-back.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Play Skyjo!</title>
|
<title>Play Skylo!</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
1631
public/card-back.svg
Normal file
1631
public/card-back.svg
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 93 KiB |
|
|
@ -62,8 +62,6 @@ export default function App() {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
console.log(messageDispaly);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-900 w-screen h-screen">
|
<div className="bg-gray-900 w-screen h-screen">
|
||||||
{!gameData && (
|
{!gameData && (
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ export const Footer: FC<Footer> = ({
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{gameData.players.map((player) => (
|
{gameData.players.map((player, index) => (
|
||||||
<div className="mb-3 pt-2 mt-2 border-t border-gray-600">
|
<div key={index} className="mb-3 pt-2 mt-2 border-t border-gray-600">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<Text>
|
<Text>
|
||||||
{player?.name} {player.socketId == socket.id && "👤"}{" "}
|
{player?.name} {player.socketId == socket.id && "👤"}{" "}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
|
||||||
import PlayerCards from "../components/PlayerCards";
|
import PlayerDecks from "./PlayerDecks";
|
||||||
import { Game } from "../types/gameTypes";
|
import { Game } from "../types/gameTypes";
|
||||||
import CardStackStaple from "./CardStackStaple";
|
import CardStackStaple from "./CardStackStaple";
|
||||||
import DiscardPile from "./DiscardPile";
|
import DiscardPile from "./DiscardPile";
|
||||||
|
|
@ -14,7 +14,7 @@ const PlayArea: FC<PlayAreaProps> = ({ gameData }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PlayerCards playersData={gameData.players} />
|
<PlayerDecks playersData={gameData.players} />
|
||||||
<CardStackStaple cardStackData={gameData.cardStack} />
|
<CardStackStaple cardStackData={gameData.cardStack} />
|
||||||
<DiscardPile discardPileData={gameData.discardPile} />
|
<DiscardPile discardPileData={gameData.discardPile} />
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,25 @@ import { FC, useState, useEffect } from "react";
|
||||||
import { Object3D, Object3DEventMap } from "three";
|
import { Object3D, Object3DEventMap } from "three";
|
||||||
// import { useFrame } from "@react-three/fiber";
|
// import { useFrame } from "@react-three/fiber";
|
||||||
import { socket } from "../socket";
|
import { socket } from "../socket";
|
||||||
import { PlayerWithVisualCards } from "../types/gameTypes";
|
import { PlayerVisualDeck } from "../types/gameTypes";
|
||||||
|
|
||||||
type PlayerCardProps = {
|
type PlayerCardProps = {
|
||||||
card: Object3D<Object3DEventMap>;
|
card: Object3D<Object3DEventMap>;
|
||||||
index: number;
|
columnIndex: number;
|
||||||
|
cardIndex: number;
|
||||||
isCurrentPlayer: boolean;
|
isCurrentPlayer: boolean;
|
||||||
playerWithCards: PlayerWithVisualCards;
|
visualPlayerDeck: PlayerVisualDeck;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlayerCard: FC<PlayerCardProps> = ({
|
const PlayerCard: FC<PlayerCardProps> = ({
|
||||||
card,
|
card,
|
||||||
index,
|
columnIndex,
|
||||||
|
cardIndex,
|
||||||
isCurrentPlayer,
|
isCurrentPlayer,
|
||||||
playerWithCards,
|
visualPlayerDeck,
|
||||||
}) => {
|
}) => {
|
||||||
const isCardRevealed = playerWithCards.player.knownCardPositions[index];
|
const isCardRevealed =
|
||||||
|
visualPlayerDeck.player.knownCardPositions[columnIndex][cardIndex];
|
||||||
const [cardObject, setCardObject] =
|
const [cardObject, setCardObject] =
|
||||||
useState<Object3D<Object3DEventMap>>(card);
|
useState<Object3D<Object3DEventMap>>(card);
|
||||||
|
|
||||||
|
|
@ -54,16 +57,15 @@ const PlayerCard: FC<PlayerCardProps> = ({
|
||||||
const clickCard = () => {
|
const clickCard = () => {
|
||||||
if (!isCurrentPlayer) return;
|
if (!isCurrentPlayer) return;
|
||||||
console.log("Clicked on one of my cards");
|
console.log("Clicked on one of my cards");
|
||||||
socket.emit("click-card", index, (response: string) => {
|
socket.emit("click-card", [columnIndex, cardIndex]);
|
||||||
console.log("Response:", response);
|
|
||||||
/*if (rotationGoal === 0) {
|
|
||||||
setRotationGoal(Math.PI);
|
|
||||||
} else {
|
|
||||||
setRotationGoal(0);
|
|
||||||
}*/
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
return <primitive object={cardObject} onClick={() => clickCard()} />;
|
return (
|
||||||
|
<primitive
|
||||||
|
key={columnIndex + cardIndex * 4}
|
||||||
|
object={cardObject}
|
||||||
|
onClick={() => clickCard()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PlayerCard;
|
export default PlayerCard;
|
||||||
|
|
|
||||||
|
|
@ -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<PlayerCardsProps> = ({ 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) => (
|
|
||||||
<PlayerCard
|
|
||||||
key={index}
|
|
||||||
card={card}
|
|
||||||
playerWithCards={playerWithCards}
|
|
||||||
index={index}
|
|
||||||
isCurrentPlayer={playerIndex === 0}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<CardCache
|
|
||||||
playerData={playerWithCards.player}
|
|
||||||
// current player is always at index 0
|
|
||||||
position={new Vector3(9, 20, 6 - playerIndex * 12)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PlayerCards;
|
|
||||||
// ich (0) --> 4
|
|
||||||
// andere (1) --> -8
|
|
||||||
86
src/components/PlayerDecks.tsx
Normal file
86
src/components/PlayerDecks.tsx
Normal file
|
|
@ -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<PlayerDecksProps> = ({ 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) => (
|
||||||
|
<PlayerCard
|
||||||
|
key={columnIndex + cardIndex * 4}
|
||||||
|
card={card}
|
||||||
|
visualPlayerDeck={visualPlayerDeck}
|
||||||
|
columnIndex={columnIndex}
|
||||||
|
cardIndex={cardIndex}
|
||||||
|
isCurrentPlayer={playerIndex === 0}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
<CardCache
|
||||||
|
playerData={visualPlayerDeck.player}
|
||||||
|
// current player is always at index 0
|
||||||
|
position={new Vector3(9, 20, 6 - playerIndex * 12)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlayerDecks;
|
||||||
|
// ich (0) --> 4
|
||||||
|
// andere (1) --> -8
|
||||||
|
|
@ -13,6 +13,8 @@ type SessionManagerProps = {
|
||||||
showStartGameButton: boolean;
|
showStartGameButton: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SessionResponse = "success" | "error:full" | "error:running";
|
||||||
|
|
||||||
export const SessionManager: FC<SessionManagerProps> = ({
|
export const SessionManager: FC<SessionManagerProps> = ({
|
||||||
isConnected,
|
isConnected,
|
||||||
clientsInRoom,
|
clientsInRoom,
|
||||||
|
|
@ -25,9 +27,11 @@ export const SessionManager: FC<SessionManagerProps> = ({
|
||||||
|
|
||||||
function joinSession(event: React.FormEvent<HTMLFormElement>) {
|
function joinSession(event: React.FormEvent<HTMLFormElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
socket.emit("join-session", sessionField);
|
socket.emit("join-session", sessionField, (response: SessionResponse) => {
|
||||||
setSession(sessionField);
|
if (response !== "success") return;
|
||||||
setSessionField("");
|
setSession(sessionField);
|
||||||
|
setSessionField("");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function leaveSession(sessionName: string) {
|
function leaveSession(sessionName: string) {
|
||||||
|
|
@ -52,10 +56,10 @@ export const SessionManager: FC<SessionManagerProps> = ({
|
||||||
>
|
>
|
||||||
<div className="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16 z-10 relative">
|
<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">
|
<h1 className="mb-4 text-4xl font-extrabold tracking-tight leading-none md:text-5xl lg:text-6xl text-white">
|
||||||
Skyjo
|
Skylo
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mb-8 text-lg font-normal lg:text-xl sm:px-16 lg:px-48 text-gray-200">
|
<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!
|
Play Skylo online with your friends!
|
||||||
</p>
|
</p>
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
{!isActiveSession && (
|
{!isActiveSession && (
|
||||||
|
|
@ -64,7 +68,7 @@ export const SessionManager: FC<SessionManagerProps> = ({
|
||||||
htmlFor="first_name"
|
htmlFor="first_name"
|
||||||
className="block mb-2 text-sm font-medium text-white"
|
className="block mb-2 text-sm font-medium text-white"
|
||||||
>
|
>
|
||||||
Join Skyjo Session
|
Join Skylo Session
|
||||||
</label>
|
</label>
|
||||||
<div className="flex space-x-1 items-center">
|
<div className="flex space-x-1 items-center">
|
||||||
<input
|
<input
|
||||||
|
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
import { useRef, useEffect, FC } from "react";
|
|
||||||
import {
|
|
||||||
Scene,
|
|
||||||
PerspectiveCamera,
|
|
||||||
WebGLRenderer,
|
|
||||||
Vector3,
|
|
||||||
DirectionalLight,
|
|
||||||
DirectionalLightHelper,
|
|
||||||
AmbientLight,
|
|
||||||
Object3D,
|
|
||||||
Mesh,
|
|
||||||
GridHelper,
|
|
||||||
AxesHelper,
|
|
||||||
Material,
|
|
||||||
} from "three";
|
|
||||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
||||||
import { useGLTF } from "@react-three/drei";
|
|
||||||
import { createCube } from "../objects/cube";
|
|
||||||
import { createPlayerCards, createCard } from "../objects/cards";
|
|
||||||
import { Game, Player } from "../types/gameTypes";
|
|
||||||
import { socket } from "../socket";
|
|
||||||
|
|
||||||
type ThreeSceneProps = {
|
|
||||||
gameData: Game | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ThreeScene: FC<ThreeSceneProps> = ({ gameData }) => {
|
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
||||||
const cardsRef = useRef<Mesh[]>([]);
|
|
||||||
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 <div ref={containerRef}></div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ThreeScene;
|
|
||||||
|
|
@ -6,9 +6,10 @@ import {
|
||||||
Vector3,
|
Vector3,
|
||||||
Mesh,
|
Mesh,
|
||||||
Object3DEventMap,
|
Object3DEventMap,
|
||||||
|
Object3D,
|
||||||
} from "three";
|
} from "three";
|
||||||
|
|
||||||
import { Card } from "../types/gameTypes";
|
import { Card, Deck, VisualColumn, VisualDeck } from "../types/gameTypes";
|
||||||
|
|
||||||
const textureLoader = new TextureLoader();
|
const textureLoader = new TextureLoader();
|
||||||
const cardSize = 5;
|
const cardSize = 5;
|
||||||
|
|
@ -18,7 +19,7 @@ const cardGeometry = new BoxGeometry(
|
||||||
cardSize * 0.6
|
cardSize * 0.6
|
||||||
);
|
);
|
||||||
|
|
||||||
const getCardTexture = (value: number | string) => {
|
const getCardTexture = (value: number | null) => {
|
||||||
let cardTexture;
|
let cardTexture;
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case -2:
|
case -2:
|
||||||
|
|
@ -66,7 +67,7 @@ const getCardTexture = (value: number | string) => {
|
||||||
case 12:
|
case 12:
|
||||||
cardTexture = textureLoader.load("/textures/card-12.png");
|
cardTexture = textureLoader.load("/textures/card-12.png");
|
||||||
break;
|
break;
|
||||||
case "X":
|
case null:
|
||||||
cardTexture = textureLoader.load("/textures/card-back.png");
|
cardTexture = textureLoader.load("/textures/card-back.png");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -78,54 +79,53 @@ const getCardTexture = (value: number | string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createCard = (
|
export const createCard = (
|
||||||
cardData: Card,
|
card: Card,
|
||||||
position: Vector3,
|
position: Vector3,
|
||||||
faceUp: boolean = false
|
faceUp: boolean = false
|
||||||
) => {
|
) => {
|
||||||
const cardMaterial = [
|
const cardMaterial = [
|
||||||
new MeshBasicMaterial(),
|
new MeshBasicMaterial(),
|
||||||
new MeshBasicMaterial(),
|
new MeshBasicMaterial(),
|
||||||
new MeshBasicMaterial({ map: getCardTexture("X") }), // X = backside
|
new MeshBasicMaterial({ map: getCardTexture(null) }), // X = backside
|
||||||
new MeshBasicMaterial({ map: getCardTexture(cardData.value) }),
|
new MeshBasicMaterial({ map: getCardTexture(card) }),
|
||||||
new MeshBasicMaterial(),
|
new MeshBasicMaterial(),
|
||||||
new MeshBasicMaterial(),
|
new MeshBasicMaterial(),
|
||||||
];
|
];
|
||||||
const card = new Mesh(cardGeometry, cardMaterial);
|
const visualCard = new Mesh(cardGeometry, cardMaterial);
|
||||||
card.name = cardData.name;
|
visualCard.name = card !== null ? card.toString() : "Facedown card";
|
||||||
card.position.copy(position);
|
visualCard.position.copy(position);
|
||||||
|
|
||||||
if (faceUp) {
|
if (faceUp) {
|
||||||
card.rotation.x = Math.PI;
|
visualCard.rotation.x = Math.PI;
|
||||||
console.log("faceUp");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return card;
|
return visualCard;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createPlayerCards = (
|
export const createPlayerCards = (deck: Deck, positionReference: Vector3) => {
|
||||||
cards: Card[],
|
const playerDeck: Object3D[][] = [];
|
||||||
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;
|
|
||||||
|
|
||||||
const cardPosition = new Vector3(
|
let visualColumn: Object3D[] = [];
|
||||||
cardPositionX,
|
deck.forEach((column, columnIndex) => {
|
||||||
cardPositionY,
|
column.forEach((card, cardIndex) => {
|
||||||
cardPositionZ
|
const cardPositionX = positionReference.x + columnIndex * 4 - 6;
|
||||||
);
|
const cardPositionY = positionReference.y;
|
||||||
const playerCard = createCard(card, cardPosition);
|
const cardPositionZ = positionReference.z + cardIndex * 4 - 8;
|
||||||
playerCards.push(playerCard);
|
|
||||||
|
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 = (
|
export const createCardStaple = (
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ export type Player = {
|
||||||
id: number;
|
id: number;
|
||||||
socketId: string;
|
socketId: string;
|
||||||
name: string;
|
name: string;
|
||||||
cards: Card[];
|
deck: Deck;
|
||||||
knownCardPositions: boolean[];
|
knownCardPositions: KnownCardsColumn[];
|
||||||
playersTurn: boolean;
|
playersTurn: boolean;
|
||||||
cardCache: Card | null;
|
cardCache: Card | null;
|
||||||
tookDispiledCard: boolean;
|
tookDispiledCard: boolean;
|
||||||
|
|
@ -15,12 +15,21 @@ export type Player = {
|
||||||
place: number;
|
place: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PlayerWithVisualCards = {
|
export type PlayerVisualDeck = {
|
||||||
player: Player;
|
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
|
| -2
|
||||||
| -1
|
| -1
|
||||||
| 0
|
| 0
|
||||||
|
|
@ -36,13 +45,7 @@ export type CardValue =
|
||||||
| 10
|
| 10
|
||||||
| 11
|
| 11
|
||||||
| 12
|
| 12
|
||||||
| "X";
|
| null;
|
||||||
|
|
||||||
export type Card = {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
value: CardValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CardStack = {
|
export type CardStack = {
|
||||||
cards: Card[];
|
cards: Card[];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue