refactored frontend
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user