refactored frontend
This commit is contained in:
parent
f318afe071
commit
f87938ae08
@ -23,14 +23,8 @@ const createNewRoom = data => {
|
||||
return room;
|
||||
};
|
||||
|
||||
const findPlayer = async sessionID => {
|
||||
const player = await Room.findOne({ 'players.sessionID': sessionID }).exec();
|
||||
console.log(player);
|
||||
return await Room.findOne({ 'players.sessionID': sessionID }).exec();
|
||||
};
|
||||
|
||||
Room.watch().on('change', async data => {
|
||||
sendToPlayersData(await getRoom(data.documentKey._id));
|
||||
});
|
||||
|
||||
module.exports = { getRoom, getRooms, updateRoom, getJoinableRoom, createNewRoom, findPlayer };
|
||||
module.exports = { getRoom, getRooms, updateRoom, getJoinableRoom, createNewRoom };
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
const { getRoom, updateRoom } = require('../controllers/roomController');
|
||||
const { sendToPlayersRolledNumber } = require('../socket/emits');
|
||||
const { getPawnPositionAfterMove } = require('../utils/functions');
|
||||
const { rollDice, isMoveValid } = require('./handlersFunctions');
|
||||
|
||||
module.exports = socket => {
|
||||
@ -10,7 +9,7 @@ module.exports = socket => {
|
||||
const room = await getRoom(req.session.roomId);
|
||||
const pawn = room.getPawn(pawnId);
|
||||
if (isMoveValid(req.session, pawn, room)) {
|
||||
const newPositionOfMovedPawn = getPawnPositionAfterMove(room.rolledNumber, pawn);
|
||||
const newPositionOfMovedPawn = pawn.getPositionAfterMove(room.rolledNumber);
|
||||
room.changePositionOfPawn(pawn, newPositionOfMovedPawn);
|
||||
room.beatPawns(newPositionOfMovedPawn, req.session.color);
|
||||
room.changeMovingPlayer();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const { getRooms, getRoom, updateRoom, createNewRoom } = require('../controllers/roomController');
|
||||
const { sendToOnePlayerRooms, sendToOnePlayerData, sendToPlayersData } = require('../socket/emits');
|
||||
const { sendToOnePlayerRooms, sendToOnePlayerData } = require('../socket/emits');
|
||||
|
||||
module.exports = socket => {
|
||||
const req = socket.request;
|
||||
|
||||
@ -2,8 +2,6 @@ const mongoose = require('mongoose');
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
const { getPawnPositionAfterMove } = require('../utils/functions');
|
||||
|
||||
const PawnSchema = new Schema({
|
||||
color: String,
|
||||
basePos: Number,
|
||||
@ -15,10 +13,70 @@ PawnSchema.methods.canMove = function (rolledNumber) {
|
||||
return true;
|
||||
}
|
||||
// (if player's pawn is near finish line) if the move does not go beyond the win line
|
||||
if (this.position !== getPawnPositionAfterMove(rolledNumber, this) && this.position !== this.basePos) {
|
||||
if (this.position !== this.getPositionAfterMove(rolledNumber) && this.position !== this.basePos) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
PawnSchema.methods.getPositionAfterMove = function (rolledNumber) {
|
||||
const { position, color } = this;
|
||||
switch (color) {
|
||||
case 'red':
|
||||
if (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 (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 (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 (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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = PawnSchema;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
const mongoose = require('mongoose');
|
||||
const { colors } = require('../utils/constants');
|
||||
const { getPawnPositionAfterMove, getStartPositions } = require('../utils/functions');
|
||||
const { makeRandomMove } = require('../handlers/handlersFunctions');
|
||||
const PawnSchema = require('./pawn');
|
||||
const PlayerSchema = require('./player');
|
||||
@ -16,7 +15,23 @@ const RoomSchema = new mongoose.Schema({
|
||||
timeoutID: Number,
|
||||
rolledNumber: Number,
|
||||
players: [PlayerSchema],
|
||||
pawns: { type: [PawnSchema], default: getStartPositions() },
|
||||
pawns: {
|
||||
type: [PawnSchema],
|
||||
default: () => {
|
||||
const startPositions = [];
|
||||
for (let i = 0; i < 16; i++) {
|
||||
let pawn = {};
|
||||
pawn.basePos = i;
|
||||
pawn.position = i;
|
||||
if (i < 4) pawn.color = colors[0];
|
||||
else if (i < 8) pawn.color = colors[1];
|
||||
else if (i < 12) pawn.color = colors[2];
|
||||
else if (i < 16) pawn.color = colors[3];
|
||||
startPositions.push(pawn);
|
||||
}
|
||||
return startPositions;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
RoomSchema.methods.beatPawns = function (position, attackingPawnColor) {
|
||||
@ -44,7 +59,7 @@ RoomSchema.methods.changeMovingPlayer = function () {
|
||||
};
|
||||
|
||||
RoomSchema.methods.movePawn = function (pawn) {
|
||||
const newPositionOfMovedPawn = getPawnPositionAfterMove(this.rolledNumber, pawn);
|
||||
const newPositionOfMovedPawn = pawn.getPositionAfterMove(this.rolledNumber);
|
||||
this.changePositionOfPawn(pawn, newPositionOfMovedPawn);
|
||||
this.beatPawns(newPositionOfMovedPawn, pawn.color);
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useEffect, useState, createContext } from 'react';
|
||||
import { io } from 'socket.io-client';
|
||||
import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
|
||||
import ReactLoading from 'react-loading';
|
||||
import Gameboard from './components/Gameboard';
|
||||
import Gameboard from './components/Gameboard/Gameboard';
|
||||
import LoginPage from './components/LoginPage/LoginPage';
|
||||
|
||||
export const PlayerDataContext = createContext();
|
||||
|
||||
@ -1,24 +1,28 @@
|
||||
import React, { useState, 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';
|
||||
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] = useState([one, two, three, four, five, six, roll]);
|
||||
|
||||
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 ? (
|
||||
@ -1,17 +1,16 @@
|
||||
import React, { useState, useEffect, useContext, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import ReactLoading from 'react-loading';
|
||||
import { PlayerDataContext, SocketContext } from '../App';
|
||||
import Map from './game-board-components/Map';
|
||||
import Navbar from './Navbar';
|
||||
import { PlayerDataContext, SocketContext } from '../../App';
|
||||
import Map from './Map/Map';
|
||||
import Navbar from '../Navbar/Navbar';
|
||||
|
||||
const Gameboard = () => {
|
||||
// Context data
|
||||
const socket = useContext(SocketContext);
|
||||
const context = useContext(PlayerDataContext);
|
||||
// Render data
|
||||
|
||||
const [pawns, setPawns] = useState([]);
|
||||
const [players, setPlayers] = useState([]);
|
||||
// Game logic data
|
||||
|
||||
const [rolledNumber, setRolledNumber] = useState(null);
|
||||
const [time, setTime] = useState();
|
||||
const [isReady, setIsReady] = useState();
|
||||
@ -19,7 +18,8 @@ const Gameboard = () => {
|
||||
const [started, setStarted] = useState(false);
|
||||
|
||||
const [movingPlayer, setMovingPlayer] = useState('red');
|
||||
const checkWin = useCallback(() => {
|
||||
|
||||
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');
|
||||
@ -30,7 +30,8 @@ const Gameboard = () => {
|
||||
} else if (pawns.filter(pawn => pawn.color === 'yellow' && pawn.position === 91).length === 4) {
|
||||
alert('Yellow Won');
|
||||
}
|
||||
}, [pawns]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
socket.emit('room:data', context.roomId);
|
||||
socket.on('room:data', data => {
|
||||
@ -59,9 +60,8 @@ const Gameboard = () => {
|
||||
setTime(data.nextMoveTime);
|
||||
setStarted(data.started);
|
||||
});
|
||||
}, []);
|
||||
}, [socket]);
|
||||
|
||||
// Callback to handle dice rolling between dice and map component
|
||||
const rolledNumberCallback = number => {
|
||||
setRolledNumber(number);
|
||||
};
|
||||
111
src/components/Gameboard/Map/Map.jsx
Normal file
111
src/components/Gameboard/Map/Map.jsx
Normal 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;
|
||||
26
src/components/Gameboard/Map/canPawnMove.js
Normal file
26
src/components/Gameboard/Map/canPawnMove.js
Normal file
@ -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;
|
||||
}
|
||||
};
|
||||
@ -1,19 +1,4 @@
|
||||
const { colors } = require('./constants');
|
||||
function getStartPositions() {
|
||||
const startPositions = [];
|
||||
for (let i = 0; i < 16; i++) {
|
||||
let pawn = {};
|
||||
pawn.basePos = i;
|
||||
pawn.position = i;
|
||||
if (i < 4) pawn.color = colors[0];
|
||||
else if (i < 8) pawn.color = colors[1];
|
||||
else if (i < 12) pawn.color = colors[2];
|
||||
else if (i < 16) pawn.color = colors[3];
|
||||
startPositions.push(pawn);
|
||||
}
|
||||
return startPositions;
|
||||
}
|
||||
function getPawnPositionAfterMove(rolledNumber, pawn) {
|
||||
export default (pawn, rolledNumber) => {
|
||||
const { position, color } = pawn;
|
||||
switch (color) {
|
||||
case 'red':
|
||||
@ -71,5 +56,4 @@ function getPawnPositionAfterMove(rolledNumber, pawn) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = { getStartPositions, getPawnPositionAfterMove };
|
||||
};
|
||||
@ -10,7 +10,6 @@ const AddServer = () => {
|
||||
|
||||
useEffect(() => {
|
||||
socket.on('room:created', () => {
|
||||
console.log('ewa');
|
||||
socket.emit('room:rooms');
|
||||
});
|
||||
}, []);
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { SocketContext } from '../../../App';
|
||||
import useInput from '../../../hooks/useInput';
|
||||
import './NameInput.css';
|
||||
const NameInput = ({ isRoomPrivate, roomId }) => {
|
||||
const socket = useContext(SocketContext);
|
||||
const [nickname, setNickname] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const nickname = useInput('');
|
||||
const password = useInput('');
|
||||
const [isPasswordWrong, setIsPasswordWrong] = useState(false);
|
||||
const handleButtonClick = () => {
|
||||
socket.emit('player:login', { name: nickname, password: password, roomId: roomId });
|
||||
socket.emit('player:login', { name: nickname.value, password: password.value, roomId: roomId });
|
||||
};
|
||||
useEffect(() => {
|
||||
socket.on('error:wrongPassword', () => {
|
||||
@ -28,12 +29,12 @@ const NameInput = ({ isRoomPrivate, roomId }) => {
|
||||
return (
|
||||
<div className='name-overlay'>
|
||||
<div className='name-input-container' style={{ height: isRoomPrivate ? '100px' : '50px' }}>
|
||||
<input placeholder='Nickname' type='text' onChange={e => setNickname(e.target.value)} />
|
||||
<input placeholder='Nickname' type='text' onChange={nickname.onChange} />
|
||||
{isRoomPrivate ? (
|
||||
<input
|
||||
placeholder='Room password'
|
||||
type='text'
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
onChange={password.onChange}
|
||||
style={{ backgroundColor: isPasswordWrong ? 'red' : null }}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -12,7 +12,7 @@ const ServerList = () => {
|
||||
const [rooms, setRooms] = useState([]);
|
||||
const [joining, setJoining] = useState(false);
|
||||
const [clickedRoom, setClickedRoom] = useState(null);
|
||||
useEffect(async () => {
|
||||
useEffect(() => {
|
||||
socket.emit('room:rooms');
|
||||
socket.on('room:rooms', data => {
|
||||
data = JSON.parse(data);
|
||||
@ -70,9 +70,7 @@ const ServerList = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{joining ? (
|
||||
<NameInput roomId={clickedRoom._id} isRoomPrivate={clickedRoom.private} />
|
||||
) : null}
|
||||
{joining ? <NameInput roomId={clickedRoom._id} isRoomPrivate={clickedRoom.private} /> : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
import './TimerAnimation.js';
|
||||
|
||||
const AnimatedOverlay = ({ time }) => {
|
||||
const [animationDelay, setAnimationDelay] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
setAnimationDelay(15 - Math.ceil((time - Date.now()) / 1000));
|
||||
}, [time]);
|
||||
const animationDelay = useMemo(() => 15 - Math.ceil((time - Date.now()) / 1000), [time]);
|
||||
|
||||
return (
|
||||
<CSSTransition
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import AnimatedOverlay from './AnimatedOverlay';
|
||||
import AnimatedOverlay from './AnimatedOverlay/AnimatedOverlay';
|
||||
|
||||
const NameContainer = ({ player, time }) => {
|
||||
return (
|
||||
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import Dice from './game-board-components/Dice';
|
||||
import NameContainer from './navbar-components/NameContainer';
|
||||
import ReadyButton from './navbar-components/ReadyButton';
|
||||
import Dice from '../Gameboard/Dice/Dice';
|
||||
import NameContainer from './NameContainer/NameContainer';
|
||||
import ReadyButton from './ReadyButton/ReadyButton';
|
||||
import './Navbar.css';
|
||||
import { useContext } from 'react';
|
||||
import { PlayerDataContext } from '../App';
|
||||
import { PlayerDataContext } from '../../App';
|
||||
const Navbar = ({ players, started, time, isReady, rolledNumber, nowMoving, rolledNumberCallback, movingPlayer }) => {
|
||||
const context = useContext(PlayerDataContext);
|
||||
const colors = ['red', 'blue', 'green', 'yellow'];
|
||||
@ -1,19 +1,17 @@
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { SocketContext } from '../../App';
|
||||
import { SocketContext } from '../../../App';
|
||||
import Switch from '@material-ui/core/Switch';
|
||||
import '../Navbar.css';
|
||||
import './TimerAnimation';
|
||||
import '../NameContainer/AnimatedOverlay/TimerAnimation';
|
||||
|
||||
const ReadyButton = ({ isReady }) => {
|
||||
const socket = useContext(SocketContext);
|
||||
const [checked, setChecked] = useState();
|
||||
const [checked, setChecked] = useState(isReady);
|
||||
|
||||
const handleCheckboxChange = () => {
|
||||
socket.emit('player:ready');
|
||||
setChecked(!checked);
|
||||
};
|
||||
useEffect(() => {
|
||||
setChecked(isReady);
|
||||
});
|
||||
return (
|
||||
<div className='ready-container'>
|
||||
<Switch onChange={handleCheckboxChange} checked={checked || false} />
|
||||
@ -1,230 +0,0 @@
|
||||
import React, { useEffect, useRef, useState, useContext, useCallback } from 'react';
|
||||
import { PlayerDataContext, SocketContext } from '../../App';
|
||||
import positions from './positions';
|
||||
import bluePawn from '../../images/pawns/blue-pawn.png';
|
||||
import greenPawn from '../../images/pawns/green-pawn.png';
|
||||
import yellowPawn from '../../images/pawns/yellow-pawn.png';
|
||||
import redPawn from '../../images/pawns/red-pawn.png';
|
||||
import greyPawn from '../../images/pawns/grey-pawn.png';
|
||||
const Map = ({ pawns, nowMoving, rolledNumber }) => {
|
||||
const context = useContext(PlayerDataContext);
|
||||
const socket = useContext(SocketContext);
|
||||
const [hintPawn, setHintPawn] = useState();
|
||||
const paintPawn = (context, x, y, color) => {
|
||||
const circle = new Path2D();
|
||||
circle.arc(x, y, 12, 0, 2 * Math.PI);
|
||||
const image = new Image();
|
||||
switch (color) {
|
||||
case 'green':
|
||||
image.src = greenPawn;
|
||||
break;
|
||||
case 'blue':
|
||||
image.src = bluePawn;
|
||||
break;
|
||||
case 'red':
|
||||
image.src = redPawn;
|
||||
break;
|
||||
case 'yellow':
|
||||
image.src = yellowPawn;
|
||||
break;
|
||||
case 'grey':
|
||||
image.src = greyPawn;
|
||||
break;
|
||||
}
|
||||
context.drawImage(image, x - 17, y - 14, 35, 30);
|
||||
return circle;
|
||||
};
|
||||
|
||||
const canvasRef = useRef(null);
|
||||
|
||||
// Return true when pawn can move
|
||||
const checkIfPawnCanMove = useCallback(
|
||||
pawn => {
|
||||
// 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;
|
||||
}
|
||||
},
|
||||
[rolledNumber]
|
||||
);
|
||||
|
||||
const handleCanvasClick = event => {
|
||||
// If hint pawn exist it means that pawn can move
|
||||
if (hintPawn) {
|
||||
const canvas = canvasRef.current;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const rect = canvas.getBoundingClientRect(),
|
||||
x = event.clientX - rect.left,
|
||||
y = event.clientY - rect.top;
|
||||
for (const pawn of pawns) {
|
||||
if (ctx.isPointInPath(pawn.circle, x, y)) {
|
||||
socket.emit('game:move', pawn._id);
|
||||
}
|
||||
}
|
||||
setHintPawn(null);
|
||||
}
|
||||
};
|
||||
const getHintPawnPosition = pawn => {
|
||||
// Based on color (because specific color have specific base and end positions)
|
||||
let { position } = pawn;
|
||||
switch (context.color) {
|
||||
case 'red':
|
||||
// When in base
|
||||
if (position >= 0 && position <= 3) {
|
||||
return 16;
|
||||
// Next to end
|
||||
} else if (position <= 66 && position + rolledNumber >= 67) {
|
||||
return position + rolledNumber + 1; // 1 is difference between last position on map and first on end
|
||||
// Normal move
|
||||
} else {
|
||||
return position + rolledNumber;
|
||||
}
|
||||
case 'blue':
|
||||
// When in base
|
||||
if (position >= 4 && position <= 7) {
|
||||
return 55;
|
||||
// Next to red base
|
||||
} else if (position <= 67 && position + rolledNumber > 67) {
|
||||
return position + rolledNumber - 52;
|
||||
// Next to base
|
||||
} else if (position <= 53 && position + rolledNumber >= 54) {
|
||||
return position + rolledNumber + 20;
|
||||
// Normal move
|
||||
} else {
|
||||
return position + rolledNumber;
|
||||
}
|
||||
case 'green':
|
||||
// When in base
|
||||
if (position >= 8 && position <= 11) {
|
||||
return 42;
|
||||
// Next to red base
|
||||
} else if (position <= 67 && position + rolledNumber > 67) {
|
||||
return position + rolledNumber - 52;
|
||||
// Next to base
|
||||
} else if (position <= 40 && position + rolledNumber >= 41) {
|
||||
return position + rolledNumber + 39;
|
||||
// Normal move
|
||||
} else {
|
||||
return position + rolledNumber;
|
||||
}
|
||||
case 'yellow':
|
||||
// When in base
|
||||
if (position >= 12 && position <= 15) {
|
||||
return 29;
|
||||
// Next to red base
|
||||
} else if (position <= 67 && position + rolledNumber > 67) {
|
||||
return position + rolledNumber - 52;
|
||||
// Next to base
|
||||
} else if (position <= 27 && position + rolledNumber >= 28) {
|
||||
return position + rolledNumber + 58;
|
||||
// Normal move
|
||||
} else {
|
||||
return position + rolledNumber;
|
||||
}
|
||||
default:
|
||||
return position;
|
||||
}
|
||||
};
|
||||
const handleMouseMove = event => {
|
||||
if (nowMoving && rolledNumber) {
|
||||
const canvas = canvasRef.current;
|
||||
const ctx = canvas.getContext('2d');
|
||||
// Gets x and y cords of mouse on canvas
|
||||
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.circle) {
|
||||
/*
|
||||
This condition checks if mouse location is:
|
||||
1) on pawn
|
||||
2) is color of pawn same as player's
|
||||
3) if pawn can move
|
||||
And then sets cursor to pointer and paints hint pawn - where will be pawn after click
|
||||
*/
|
||||
if (
|
||||
ctx.isPointInPath(pawn.circle, x, y) &&
|
||||
context.color === pawn.color &&
|
||||
checkIfPawnCanMove(pawn)
|
||||
) {
|
||||
const pawnPosition = getHintPawnPosition(pawn);
|
||||
// Checks if pawn can make a move
|
||||
if (pawnPosition) {
|
||||
canvas.style.cursor = 'pointer';
|
||||
setHintPawn({ id: pawn._id, position: pawnPosition, color: 'grey' });
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
setHintPawn(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const rerenderCanvas = useCallback(() => {
|
||||
const canvas = canvasRef.current;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const image = new Image();
|
||||
image.src = 'https://img-9gag-fun.9cache.com/photo/a8GdpYZ_460s.jpg';
|
||||
image.onload = function () {
|
||||
ctx.drawImage(image, 0, 0);
|
||||
pawns.forEach((pawn, index) => {
|
||||
pawns[index].circle = 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);
|
||||
}
|
||||
};
|
||||
}, [checkIfPawnCanMove, context.color, hintPawn, nowMoving, pawns, rolledNumber]);
|
||||
|
||||
// Rerender canvas when pawns have changed
|
||||
useEffect(() => {
|
||||
rerenderCanvas();
|
||||
}, [hintPawn, pawns, rerenderCanvas]);
|
||||
|
||||
useEffect(() => {
|
||||
socket.on('game:move', () => {
|
||||
setHintPawn(null);
|
||||
});
|
||||
socket.on('game:roll', () => {
|
||||
setHintPawn(null);
|
||||
});
|
||||
}, [socket]);
|
||||
return (
|
||||
<canvas
|
||||
className='canvas-container'
|
||||
width={460}
|
||||
height={460}
|
||||
ref={canvasRef}
|
||||
onClick={handleCanvasClick}
|
||||
onMouseMove={handleMouseMove}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default Map;
|
||||
13
src/constants/pawnImages.js
Normal file
13
src/constants/pawnImages.js
Normal file
@ -0,0 +1,13 @@
|
||||
import bluePawn from '../images/pawns/blue-pawn.png';
|
||||
import greenPawn from '../images/pawns/green-pawn.png';
|
||||
import redPawn from '../images/pawns/red-pawn.png';
|
||||
import yellowPawn from '../images/pawns/yellow-pawn.png';
|
||||
import greyPawn from '../images/pawns/grey-pawn.png';
|
||||
|
||||
export default {
|
||||
green: greenPawn,
|
||||
blue: bluePawn,
|
||||
red: redPawn,
|
||||
yellow: yellowPawn,
|
||||
grey: greyPawn,
|
||||
};
|
||||
117
src/constants/positions.js
Normal file
117
src/constants/positions.js
Normal 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;
|
||||
11
src/hooks/useInput.js
Normal file
11
src/hooks/useInput.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { useState } from 'react';
|
||||
export default function useInput({ initialValue }) {
|
||||
const [value, setValue] = useState(initialValue);
|
||||
const handleChange = e => {
|
||||
setValue(e.target.value);
|
||||
};
|
||||
return {
|
||||
value,
|
||||
onChange: handleChange,
|
||||
};
|
||||
}
|
||||
BIN
src/images/map.jpg
Normal file
BIN
src/images/map.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Loading…
Reference in New Issue
Block a user