diff --git a/SerpentRace_Frontend/src/App.jsx b/SerpentRace_Frontend/src/App.jsx index 8f9be082..f6ee0b0c 100644 --- a/SerpentRace_Frontend/src/App.jsx +++ b/SerpentRace_Frontend/src/App.jsx @@ -10,7 +10,8 @@ import Landingpage from "./pages/Landing/Landingpage" import Home from "./pages/Landing/Home" import DeckManagerPage from "./pages/Decks/DeckManagerPage" import About from "./pages/About/About" -import ScrollToTop from "./components/ScrollToTop"; +import ScrollToTop from "./components/ScrollToTop" +import GameScreen from "./pages/Game/GameScreen" function App() { const [isMobile, setIsMobile] = useState(false) @@ -40,22 +41,20 @@ function App() { return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - {/* Add more routes as needed */} - - - - + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + {/* Add more routes as needed */} + ) } diff --git a/SerpentRace_Frontend/src/pages/Game/GameScreen.jsx b/SerpentRace_Frontend/src/pages/Game/GameScreen.jsx new file mode 100644 index 00000000..364f1af1 --- /dev/null +++ b/SerpentRace_Frontend/src/pages/Game/GameScreen.jsx @@ -0,0 +1,235 @@ +import React, { useState } from "react" +import { getVerticalOffset } from "../../utils/randomUtils" +import Dice from "../../utils/dice/Dice" + +const GameScreen = () => { + const boardRows = 5 + const boardCols = 20 + const totalCells = boardRows * boardCols + const cellSize = 40 + const cellMargin = 2.5 + const rowSpacing = 70 // Extra spacing between rows + const topOffset = rowSpacing * 0.5 // Increase topOffset for more spacing + const bottomOffset = rowSpacing * 0.5 // Increase bottomOffset for more spacing + const boardWidthPx = boardCols * (cellSize + cellMargin * 2) + const boardHeightPx = + boardRows * (cellSize + cellMargin * 2 + rowSpacing) + topOffset + bottomOffset - rowSpacing + + // Generate a snake-like path with vertical spacing and vertical offsets + const generateWindingPath = () => { + const path = [] + let currentNum = 1 + + for (let row = 0; row < boardRows && currentNum <= totalCells; row++) { + // Calculate the y position with extra row spacing + const baseYPosition = topOffset + row * (cellSize + cellMargin * 2 + rowSpacing) + + // If row number is even, go right; if odd, go left + if (row % 2 === 0) { + // Left to right + for (let col = 0; col < boardCols && currentNum <= totalCells; col++) { + path.push({ + number: currentNum++, + x: col * (cellSize + cellMargin * 2), + y: baseYPosition + getVerticalOffset(currentNum - 1), + type: getFieldType(currentNum - 1), + }) + } + } else { + // Right to left + for (let col = boardCols - 1; col >= 0 && currentNum <= totalCells; col--) { + path.push({ + number: currentNum++, + x: col * (cellSize + cellMargin * 2), + y: baseYPosition + getVerticalOffset(currentNum - 1), + type: getFieldType(currentNum - 1), + }) + } + } + } + + return path + } + + const getFieldType = (count) => { + if (count % 17 === 0) return "clover" + if (count % 13 === 0) return "bad" + if ((count + 5) % 13 === 0) return "good" + return "regular" + } + + const [path, setPath] = useState(generateWindingPath()) + const [players, setPlayers] = useState([ + { id: 1, name: "Béla", position: 34, score: 25, color: "bg-blue-600", emoji: "🐍" }, + { id: 2, name: "Juci", position: 50, score: 30, color: "bg-green-600", emoji: "🐢" }, + { id: 3, name: "Kati", position: 70, score: 15, color: "bg-purple-600", emoji: "🐇" }, + { id: 3, name: "Fürtös", position: 68, score: 14, color: "bg-yellow-600", emoji: "😂" }, + ]) + + // Sort players by position in descending order + const sortedPlayers = [...players].sort((a, b) => b.position - a.position) + + // Handle dice roll + const handleDiceRoll = (value) => { + console.log("Rolled:", value) + // You can add logic here to move the current player based on the dice value + } + + console.log("Generated path length:", path.length) + + const getFieldStyle = (type) => { + switch (type) { + case "clover": + return "bg-teal-700 border-teal-500 shadow-teal-800" + case "bad": + return "bg-red-800 border-red-600 shadow-red-900" + case "good": + return "bg-blue-800 border-blue-600 shadow-blue-900" + default: + return "bg-gray-800 border-gray-600 shadow-gray-900" + } + } + + const getPlayerPosition = (playerPosition) => { + const field = path.find((p) => p.number === playerPosition) + return field ? { top: `${field.y}px`, left: `${field.x}px` } : { top: 0, left: 0 } + } + + // Function to get medal style based on rank + const getMedalStyle = (rank) => { + switch (rank) { + case 1: + return "bg-yellow-400 text-yellow-900 border-yellow-500 shadow-yellow-600" + case 2: + return "bg-gray-400 text-gray-900 border-gray-500 shadow-gray-600" + case 3: + return "bg-orange-500 text-orange-900 border-orange-600 shadow-orange-700" + default: + return "bg-gray-700 text-gray-300 border-gray-600 shadow-gray-800" + } + } + + return ( +
+
+
+ {/* Game Board */} +
+ {/* Háttér */} +
+ {[...Array(35)].map((_, i) => ( + // Sajat pulse effect! => node_modules/tailwindcss/index.css: + // --animate-pulse8: pulse 6s cubic-bezier(0.4, 0.2, 0.6, 1) infinite; + +
+ ))} +
+
+ {/* Mezők */} + {path.map((field) => ( +
+ {field.number} +
+ ))} + + {/* Player tokens */} + {players.map((player) => ( +
+ {player.emoji} +
+ ))} +
+ + {/* Game information */} + {/*
+

+ Sima + Lóhere + Rossz + +

+
*/} +
+ + {/* Right sidebar */} +
+
+

Játékosok

+ {sortedPlayers.map((player, index) => ( +
+
+ {player.emoji} +
+
+
+ {player.name} + + {index + 1 === 1 + ? "🥇 1st" + : index + 1 === 2 + ? "🥈 2nd" + : index + 1 === 3 + ? "🥉 3rd" + : `${index + 1}th`} + +
+
+ Pozíció: {player.position} • Pontszám: {player.score} +
+
+
+ ))} +
+ + {/* Dice Container */} +
+

Dobókocka

+

Kattints a kockára dobáshoz!

+ +
+
+
+
+
+ ) +} + +export default GameScreen diff --git a/SerpentRace_Frontend/src/utils/MrandomUtils.js b/SerpentRace_Frontend/src/utils/MrandomUtils.js new file mode 100644 index 00000000..994d8c5d --- /dev/null +++ b/SerpentRace_Frontend/src/utils/MrandomUtils.js @@ -0,0 +1,24 @@ +// Scaling factor for vertical offsets +const offsetScalingFactor = 0.4; + +// Predefined vertical offsets represented as a 5x20 grid of zeros +export const verticalOffsets = [ + 0, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, +]; + +/** + * Gets a vertical offset value from the predefined array + * @param {number} index - Index to retrieve from the offsets array + * @returns {number} Offset value + */ + +export const getVerticalOffset = (index) => { + const normalizedIndex = index % verticalOffsets.length; + const offset = verticalOffsets[normalizedIndex >= 0 ? normalizedIndex : 0]; + return offset * offsetScalingFactor; // Apply scaling factor +}; \ No newline at end of file diff --git a/SerpentRace_Frontend/src/utils/dice/Dice.jsx b/SerpentRace_Frontend/src/utils/dice/Dice.jsx new file mode 100644 index 00000000..756a69cf --- /dev/null +++ b/SerpentRace_Frontend/src/utils/dice/Dice.jsx @@ -0,0 +1,139 @@ +import React, { useState, useEffect, useRef } from 'react'; + +const Dice = ({ onRoll }) => { + const [diceValue, setDiceValue] = useState(1); + const [isRolling, setIsRolling] = useState(false); + const animationRef = useRef(null); + const rollTimeoutRef = useRef(null); + + const diceFaces = [ + [
], + [
,
], + [
,
,
], + [
,
, +
,
], + [
,
, +
, +
,
], + [
,
, +
,
, +
,
] + ]; + + useEffect(() => { + return () => { + if (animationRef.current) cancelAnimationFrame(animationRef.current); + if (rollTimeoutRef.current) clearTimeout(rollTimeoutRef.current); + }; + }, []); + + const rollDice = () => { + if (isRolling) return; + + setIsRolling(true); + + let duration = 0; + const rollInterval = 80; // ms between dice face changes + const maxDuration = 1500; // total animation time + + const rollAnimation = () => { + const randomValue = Math.floor(Math.random() * 6) + 1; + setDiceValue(randomValue); + + duration += rollInterval; + + if (duration < maxDuration) { + // Speed effect: slow down towards the end + const nextInterval = rollInterval * (1 + (duration / maxDuration) * 2); + rollTimeoutRef.current = setTimeout(() => { + animationRef.current = requestAnimationFrame(rollAnimation); + }, nextInterval); + } else { + // Final roll + const finalValue = Math.floor(Math.random() * 6) + 1; + setDiceValue(finalValue); + setIsRolling(false); + + if (onRoll) onRoll(finalValue); + } + }; + + animationRef.current = requestAnimationFrame(rollAnimation); + }; + + return ( +
+
+ {diceFaces[diceValue - 1]} +
+ +
+ ); +}; + +export default Dice; diff --git a/SerpentRace_Frontend/src/utils/randomUtils.js b/SerpentRace_Frontend/src/utils/randomUtils.js new file mode 100644 index 00000000..c9502414 --- /dev/null +++ b/SerpentRace_Frontend/src/utils/randomUtils.js @@ -0,0 +1,20 @@ +// Predefined vertical offsets represented as a 5x20 grid of zeros +export const verticalOffsets = [ + 0, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, + -35, -10, -5, 10, 15, 12, 5, -5, -12, -15, -12, -5, 5, 12, 15, 12, 5, -5, 10, 35, +]; + +/** + * Gets a vertical offset value from the predefined array + * @param {number} index - Index to retrieve from the offsets array + * @returns {number} Offset value + */ + +export const getVerticalOffset = (index) => { + const normalizedIndex = index % verticalOffsets.length; + return verticalOffsets[normalizedIndex >= 0 ? normalizedIndex : 0]; +}; \ No newline at end of file