refactored frontend

This commit is contained in:
Wenszel
2023-12-08 17:25:51 +01:00
parent f318afe071
commit f87938ae08
26 changed files with 407 additions and 313 deletions
+38
View File
@@ -0,0 +1,38 @@
import React, { useEffect, useContext } from 'react';
import { SocketContext } from '../../../App';
import one from '../../../images/dice/1.png';
import two from '../../../images/dice/2.png';
import three from '../../../images/dice/3.png';
import four from '../../../images/dice/4.png';
import five from '../../../images/dice/5.png';
import six from '../../../images/dice/6.png';
import roll from '../../../images/dice/roll.png';
const Dice = ({ rolledNumberCallback, rolledNumber, nowMoving, color, movingPlayer }) => {
const socket = useContext(SocketContext);
const images = [one, two, three, four, five, six, roll];
const handleRoll = () => {
socket.emit('game:roll');
};
useEffect(() => {
socket.on('game:roll', number => {
rolledNumberCallback(number);
});
}, []);
return (
<div className={`dice-container dice-${color}`}>
{movingPlayer === color ? (
rolledNumber ? (
<img src={images[rolledNumber - 1]} alt={rolledNumber} width='100' height='100' />
) : nowMoving ? (
<img src={images[6]} className='roll' alt='roll' width='100' height='100' onClick={handleRoll} />
) : null
) : null}
</div>
);
};
export default Dice;
+92
View File
@@ -0,0 +1,92 @@
import React, { useState, useEffect, useContext } from 'react';
import ReactLoading from 'react-loading';
import { PlayerDataContext, SocketContext } from '../../App';
import Map from './Map/Map';
import Navbar from '../Navbar/Navbar';
const Gameboard = () => {
const socket = useContext(SocketContext);
const context = useContext(PlayerDataContext);
const [pawns, setPawns] = useState([]);
const [players, setPlayers] = useState([]);
const [rolledNumber, setRolledNumber] = useState(null);
const [time, setTime] = useState();
const [isReady, setIsReady] = useState();
const [nowMoving, setNowMoving] = useState(false);
const [started, setStarted] = useState(false);
const [movingPlayer, setMovingPlayer] = useState('red');
const checkWin = () => {
// Player wins when all pawns with same color are inside end base
if (pawns.filter(pawn => pawn.color === 'red' && pawn.position === 73).length === 4) {
alert('Red Won');
} else if (pawns.filter(pawn => pawn.color === 'blue' && pawn.position === 79).length === 4) {
alert('Blue Won');
} else if (pawns.filter(pawn => pawn.color === 'green' && pawn.position === 85).length === 4) {
alert('Green Won');
} else if (pawns.filter(pawn => pawn.color === 'yellow' && pawn.position === 91).length === 4) {
alert('Yellow Won');
}
};
useEffect(() => {
socket.emit('room:data', context.roomId);
socket.on('room:data', data => {
data = JSON.parse(data);
if (data.players == null) return;
// Filling navbar with empty player nick container
while (data.players.length !== 4) {
data.players.push({ name: '...' });
}
// Checks if client is currently moving player by session ID
const nowMovingPlayer = data.players.find(player => player.nowMoving === true);
if (nowMovingPlayer) {
if (nowMovingPlayer._id === context.playerId) {
setNowMoving(true);
} else {
setNowMoving(false);
}
setMovingPlayer(nowMovingPlayer.color);
}
const currentPlayer = data.players.find(player => player._id === context.playerId);
checkWin();
setIsReady(currentPlayer.ready);
setRolledNumber(data.rolledNumber);
setPlayers(data.players);
setPawns(data.pawns);
setTime(data.nextMoveTime);
setStarted(data.started);
});
}, [socket]);
const rolledNumberCallback = number => {
setRolledNumber(number);
};
return (
<>
{(players[0] && !started) || (time && started) ? (
<div className='container'>
<Navbar
players={players}
started={started}
time={time}
isReady={isReady}
movingPlayer={movingPlayer}
rolledNumber={rolledNumber}
nowMoving={nowMoving}
rolledNumberCallback={rolledNumberCallback}
/>
<Map pawns={pawns} nowMoving={nowMoving} rolledNumber={rolledNumber} />
</div>
) : (
<ReactLoading type='spinningBubbles' color='white' height={667} width={375} />
)}
</>
);
};
export default Gameboard;
+111
View File
@@ -0,0 +1,111 @@
import React, { useEffect, useRef, useState, useContext } from 'react';
import { PlayerDataContext, SocketContext } from '../../../App';
import mapImage from '../../../images/map.jpg';
import positions from '../positions';
import pawnImages from '../../../constants/pawnImages';
import canPawnMove from './canPawnMove';
import getPositionAfterMove from './getPositionAfterMove';
const Map = ({ pawns, nowMoving, rolledNumber }) => {
const player = useContext(PlayerDataContext);
const socket = useContext(SocketContext);
const canvasRef = useRef(null);
const [hintPawn, setHintPawn] = useState();
const paintPawn = (context, x, y, color) => {
const touchableArea = new Path2D();
touchableArea.arc(x, y, 12, 0, 2 * Math.PI);
const image = new Image();
image.src = pawnImages[color];
image.onload = function () {
context.drawImage(image, x - 17, y - 14, 35, 30);
};
return touchableArea;
};
const handleCanvasClick = event => {
if (hintPawn) {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect(),
cursorX = event.clientX - rect.left,
cursorY = event.clientY - rect.top;
for (const pawn of pawns) {
if (ctx.isPointInPath(pawn.touchableArea, cursorX, cursorY)) {
socket.emit('game:move', pawn._id);
}
}
setHintPawn(null);
}
};
const handleMouseMove = event => {
if (nowMoving && rolledNumber) {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect(),
x = event.clientX - rect.left,
y = event.clientY - rect.top;
canvas.style.cursor = 'default';
for (const pawn of pawns) {
if (pawn.touchableArea) {
if (
ctx.isPointInPath(pawn.touchableArea, x, y) &&
player.color === pawn.color &&
canPawnMove(pawn, rolledNumber)
) {
const pawnPosition = getPositionAfterMove(pawn, rolledNumber);
if (pawnPosition) {
canvas.style.cursor = 'pointer';
setHintPawn({ id: pawn._id, position: pawnPosition, color: 'grey' });
break;
}
} else {
setHintPawn(null);
}
} else {
setHintPawn(null);
}
}
} else {
setHintPawn(null);
}
};
const rerenderCanvas = () => {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
const image = new Image();
image.src = mapImage;
image.onload = function () {
ctx.drawImage(image, 0, 0);
pawns.forEach((pawn, index) => {
pawns[index].touchableArea = paintPawn(
ctx,
positions[pawn.position].x,
positions[pawn.position].y,
pawn.color
);
});
if (hintPawn) {
paintPawn(ctx, positions[hintPawn.position].x, positions[hintPawn.position].y, hintPawn.color);
}
};
};
useEffect(() => {
rerenderCanvas();
}, [hintPawn, pawns, rerenderCanvas]);
return (
<canvas
className='canvas-container'
width={460}
height={460}
ref={canvasRef}
onClick={handleCanvasClick}
onMouseMove={handleMouseMove}
/>
);
};
export default Map;
@@ -0,0 +1,26 @@
export default (pawn, rolledNumber) => {
// If is in base
if ((rolledNumber === 1 || rolledNumber === 6) && pawn.position === pawn.basePos) {
return true;
// Other situations: pawn is on map or pawn is in end positions
} else if (pawn.position !== pawn.basePos) {
switch (pawn.color) {
case 'red':
if (pawn.position + rolledNumber <= 73) return true;
break;
case 'blue':
if (pawn.position + rolledNumber <= 79) return true;
break;
case 'green':
if (pawn.position + rolledNumber <= 85) return true;
break;
case 'yellow':
if (pawn.position + rolledNumber <= 91) return true;
break;
default:
return false;
}
} else {
return false;
}
};
@@ -0,0 +1,59 @@
export default (pawn, rolledNumber) => {
const { position, color } = pawn;
switch (color) {
case 'red':
if (pawn.position + rolledNumber <= 73) {
if (position >= 0 && position <= 3) {
return 16;
} else if (position <= 66 && position + rolledNumber >= 67) {
return position + rolledNumber + 1;
} else {
return position + rolledNumber;
}
} else {
return position;
}
case 'blue':
if (pawn.position + rolledNumber <= 79) {
if (position >= 4 && position <= 7) {
return 55;
} else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52;
} else if (position <= 53 && position + rolledNumber >= 54) {
return position + rolledNumber + 20;
} else {
return position + rolledNumber;
}
} else {
return position;
}
case 'green':
if (pawn.position + rolledNumber <= 85) {
if (position >= 8 && position <= 11) {
return 42;
} else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52;
} else if (position <= 40 && position + rolledNumber >= 41) {
return position + rolledNumber + 39;
} else {
return position + rolledNumber;
}
} else {
return position;
}
case 'yellow':
if (pawn.position + rolledNumber <= 85) {
if (position >= 12 && position <= 15) {
return 29;
} else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52;
} else if (position <= 27 && position + rolledNumber >= 28) {
return position + rolledNumber + 58;
} else {
return position + rolledNumber;
}
} else {
return position;
}
}
};
+117
View File
@@ -0,0 +1,117 @@
const positions = [
// Red base
{x: 67, y: 67}, // 0
{x: 67, y: 116},
{x: 117, y: 67},
{x: 117, y: 116},
// Blue base
{x: 67, y: 343},
{x: 67, y: 392},
{x: 117, y: 343},
{x: 117, y: 392},
// Green base
{x: 343, y: 343},
{x: 392, y: 392},
{x: 392, y: 343}, // 10
{x: 343, y: 392},
// Yellow base
{x: 343, y: 67},
{x: 392, y: 116},
{x: 392, y: 67},
{x: 343, y: 116},
// Map - starting from red field
{x: 45, y: 200},
{x: 76, y: 200},
{x: 107, y: 200},
{x: 138, y: 200},
{x: 169, y: 200}, // 20
{x: 200, y: 169},
{x: 200, y: 138},
{x: 200, y: 107},
{x: 200, y: 76},
{x: 200, y: 45},
{x: 200, y: 14},
// Top
{x: 230, y: 14},
{x: 261, y: 14},
{x: 261, y: 45},
{x: 261, y: 76}, // 30
{x: 261, y: 107},
{x: 261, y: 138},
{x: 261, y: 169},
{x: 291, y: 200},
{x: 321, y: 200},
{x: 352, y: 200},
{x: 383, y: 200},
{x: 414, y: 200},
{x: 445, y: 200},
// Right
{x: 445, y: 230}, // 40
{x: 445, y: 261},
{x: 414, y: 261},
{x: 383, y: 261},
{x: 352, y: 261},
{x: 321, y: 261},
{x: 291, y: 261},
{x: 261, y: 291},
{x: 261, y: 322},
{x: 261, y: 353},
{x: 261, y: 384}, // 50
{x: 261, y: 414},
{x: 261, y: 445},
// Bottom
{x: 230, y: 445},
{x: 200, y: 445},
{x: 200, y: 414},
{x: 200, y: 384},
{x: 200, y: 353},
{x: 200, y: 322},
{x: 200, y: 291},
{x: 169, y: 261}, // 60
{x: 138, y: 261},
{x: 107, y: 261},
{x: 76, y: 261},
{x: 45, y: 261},
{x: 15, y: 261},
// Left
{x: 15, y: 231}, // 66
// One behind red base
{x: 15, y: 200}, //67
// Red end
{x: 45, y: 231}, // 68
{x: 76, y: 231},
{x: 107, y: 231},
{x: 138, y: 231},
{x: 169, y: 231},
{x: 200, y: 231}, // 73
// Blue end
{x: 231, y: 414}, // 74
{x: 231, y: 384},
{x: 231, y: 353},
{x: 231, y: 322},
{x: 231, y: 291},
{x: 231, y: 260}, // 79
// Green end
{x: 414, y: 231}, // 80
{x: 383, y: 231},
{x: 352, y: 231},
{x: 321, y: 231},
{x: 290, y: 231},
{x: 259, y: 231}, // 85
// Yellow base
{x: 230, y: 45}, // 86
{x: 230, y: 76},
{x: 230, y: 107},
{x: 230, y: 138},
{x: 230, y: 169},
{x: 230, y: 200}, // 91
];
export default positions;