mirror of
https://git.rezel.net/LudoTech/traque.git
synced 2026-02-09 02:10:18 +01:00
Ajout de stats + corrections
This commit is contained in:
14
traque-back/.vscode/launch.json
vendored
14
traque-back/.vscode/launch.json
vendored
@@ -1,14 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "sudo npm start",
|
||||
"name": "Run npm start",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export function initAdminSocketHandler() {
|
||||
loggedInSockets.push(socket.id);
|
||||
loggedIn = true;
|
||||
// Send the current state
|
||||
socket.emit("game_state", game.state)
|
||||
socket.emit("game_state", {state: game.state, startDate: game.startDate})
|
||||
// Other settings that need initialization
|
||||
socket.emit("penalty_settings", penaltyController.settings)
|
||||
socket.emit("game_settings", game.settings)
|
||||
@@ -61,7 +61,8 @@ export function initAdminSocketHandler() {
|
||||
socket.emit("zone", zone.currentZone)
|
||||
socket.emit("new_zone", {
|
||||
begin: zone.currentStartZone,
|
||||
end: zone.nextZone
|
||||
end: zone.nextZone,
|
||||
endDate: zone.nextZoneDate,
|
||||
})
|
||||
} else {
|
||||
// Attempt unsuccessful
|
||||
@@ -143,10 +144,7 @@ export function initAdminSocketHandler() {
|
||||
socket.emit("error", "Not logged in");
|
||||
return;
|
||||
}
|
||||
if (game.setState(state)) {
|
||||
secureAdminBroadcast("game_state", game.state);
|
||||
playersBroadcast("game_state", game.state)
|
||||
} else {
|
||||
if (!game.setState(state)) {
|
||||
socket.emit("error", "Error setting state");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,11 +3,11 @@ This module manages the main game state, the teams, the settings and the game lo
|
||||
*/
|
||||
import { secureAdminBroadcast } from "./admin_socket.js";
|
||||
import { playersBroadcast, sendUpdatedTeamInformations } from "./team_socket.js";
|
||||
import { isInCircle } from "./map_utils.js";
|
||||
import { isInCircle, getDistanceFromLatLon } from "./map_utils.js";
|
||||
import timeoutHandler from "./timeoutHandler.js";
|
||||
import penaltyController from "./penalty_controller.js";
|
||||
import zoneManager from "./zone_manager.js";
|
||||
import { writePosition, writeCapture, writeSeePosition } from "./trajectory.js";
|
||||
import trajectory from "./trajectory.js";
|
||||
|
||||
/**
|
||||
* The possible states of the game
|
||||
@@ -24,6 +24,8 @@ export default {
|
||||
teams: [],
|
||||
//Current state of the game
|
||||
state: GameState.SETUP,
|
||||
// Date since gameState switched to PLAYING
|
||||
startDate: null,
|
||||
//Settings of the game
|
||||
settings: {
|
||||
loserEndGameMessage: "",
|
||||
@@ -48,41 +50,63 @@ export default {
|
||||
* @returns true if the state has been changed
|
||||
*/
|
||||
setState(newState) {
|
||||
if (Object.values(GameState).indexOf(newState) == -1) {
|
||||
return false;
|
||||
}
|
||||
//The game has started
|
||||
if (newState == GameState.PLAYING) {
|
||||
penaltyController.start();
|
||||
if (!zoneManager.ready()) {
|
||||
return false;
|
||||
}
|
||||
this.initLastSentLocations();
|
||||
zoneManager.reset()
|
||||
//If the zone cannot be setup, reset everything
|
||||
if (!zoneManager.start()) {
|
||||
this.setState(GameState.SETUP);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (newState != GameState.PLAYING) {
|
||||
// Checks is the newState is a Gamestate
|
||||
if (Object.values(GameState).indexOf(newState) == -1) return false;
|
||||
// Match case
|
||||
switch (newState) {
|
||||
case GameState.SETUP:
|
||||
trajectory.stop();
|
||||
zoneManager.reset();
|
||||
penaltyController.stop();
|
||||
timeoutHandler.endAllSendPositionTimeout();
|
||||
}
|
||||
//Game reset
|
||||
if (newState == GameState.SETUP) {
|
||||
for (let team of this.teams) {
|
||||
team.outOfZone = false;
|
||||
team.penalties = 0;
|
||||
team.captured = false;
|
||||
team.enemyLocation = null;
|
||||
team.enemyName = null;
|
||||
team.currentLocation = null;
|
||||
team.lastSentLocation = null;
|
||||
team.distance = null;
|
||||
team.finishDate = null;
|
||||
team.nCaptures = 0;
|
||||
team.nSentLocation = 0;
|
||||
team.nObserved = 0;
|
||||
}
|
||||
this.startDate = null;
|
||||
this.updateTeamChasing();
|
||||
break;
|
||||
case GameState.PLACEMENT:
|
||||
trajectory.stop();
|
||||
zoneManager.reset();
|
||||
penaltyController.stop();
|
||||
timeoutHandler.endAllSendPositionTimeout();
|
||||
this.startDate = null;
|
||||
break;
|
||||
case GameState.PLAYING:
|
||||
if (!zoneManager.start()) {
|
||||
return false;
|
||||
}
|
||||
trajectory.start();
|
||||
penaltyController.start();
|
||||
this.initLastSentLocations();
|
||||
this.startDate = Date.now();
|
||||
break;
|
||||
case GameState.FINISHED:
|
||||
for (const team of this.teams) {
|
||||
if (!team.finishDate) team.finishDate = Date.now();
|
||||
}
|
||||
trajectory.stop();
|
||||
penaltyController.stop();
|
||||
zoneManager.reset();
|
||||
timeoutHandler.endAllSendPositionTimeout();
|
||||
break;
|
||||
}
|
||||
// Update the state
|
||||
this.state = newState;
|
||||
secureAdminBroadcast("game_state", {state: newState, startDate: this.startDate});
|
||||
playersBroadcast("game_state", newState);
|
||||
secureAdminBroadcast("teams", this.teams);
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -128,6 +152,18 @@ export default {
|
||||
ready: false,
|
||||
captured: false,
|
||||
penalties: 0,
|
||||
outOfZone: false,
|
||||
outOfZoneDeadline: null,
|
||||
distance: 0,
|
||||
finishDate: null,
|
||||
nCaptures: 0,
|
||||
nSentLocation: 0,
|
||||
nObserved: 0,
|
||||
phoneModel: null,
|
||||
phoneName: null,
|
||||
battery: null,
|
||||
ping: null,
|
||||
nConnected: 0,
|
||||
});
|
||||
this.updateTeamChasing();
|
||||
return true;
|
||||
@@ -156,7 +192,7 @@ export default {
|
||||
updateTeamChasing() {
|
||||
if (this.playingTeamCount() <= 2) {
|
||||
if (this.state == GameState.PLAYING) {
|
||||
this.finishGame();
|
||||
this.setState(GameState.FINISHED);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -230,8 +266,9 @@ export default {
|
||||
if (!team || !location) {
|
||||
return false;
|
||||
}
|
||||
if (team.currentLocation) team.distance += Math.floor(getDistanceFromLatLon({lat: location[0], lng: location[1]}, {lat: team.currentLocation[0], lng: team.currentLocation[1]}));
|
||||
// Update of events of the game
|
||||
writePosition(Date.now(), teamId, location[0], location[1]);
|
||||
trajectory.writePosition(Date.now(), teamId, location[0], location[1]);
|
||||
// Update of currentLocation
|
||||
team.currentLocation = location;
|
||||
// Update of ready (true if the team is in the starting area)
|
||||
@@ -247,13 +284,14 @@ export default {
|
||||
* Initialize the last sent location of the teams to their starting location
|
||||
*/
|
||||
initLastSentLocations() {
|
||||
// Update of lastSentLocation
|
||||
for (const team of this.teams) {
|
||||
team.lastSentLocation = team.currentLocation;
|
||||
team.locationSendDeadline = Date.now() + penaltyController.settings.allowedTimeBetweenPositionUpdate * 60 * 1000;
|
||||
timeoutHandler.setSendPositionTimeout(team.id, team.locationSendDeadline);
|
||||
this.getTeam(team.chasing).enemyLocation = team.lastSentLocation;
|
||||
sendUpdatedTeamInformations(team.id);
|
||||
}
|
||||
// Update of enemyLocation now we have the lastSentLocation of the enemy
|
||||
for (const team of this.teams) {
|
||||
team.enemyLocation = this.getTeam(team.chasing).lastSentLocation;
|
||||
sendUpdatedTeamInformations(team.id);
|
||||
@@ -267,12 +305,15 @@ export default {
|
||||
*/
|
||||
sendLocation(teamId) {
|
||||
const team = this.getTeam(teamId);
|
||||
const enemyTeam = this.getTeam(team.chasing);
|
||||
if (!team || !team.currentLocation) {
|
||||
return false;
|
||||
}
|
||||
team.nSentLocation++;
|
||||
enemyTeam.nObserved++;
|
||||
const dateNow = Date.now();
|
||||
// Update of events of the game
|
||||
writeSeePosition(dateNow, teamId, team.chasing);
|
||||
trajectory.writeSeePosition(dateNow, teamId, team.chasing);
|
||||
// Update of locationSendDeadline
|
||||
team.locationSendDeadline = dateNow + penaltyController.settings.allowedTimeBetweenPositionUpdate * 60 * 1000;
|
||||
timeoutHandler.setSendPositionTimeout(team.id, team.locationSendDeadline);
|
||||
@@ -283,6 +324,7 @@ export default {
|
||||
if (teamChasing) team.enemyLocation = teamChasing.lastSentLocation;
|
||||
// Sending new infos to the team
|
||||
sendUpdatedTeamInformations(team.id);
|
||||
sendUpdatedTeamInformations(enemyTeam.id);
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -315,8 +357,9 @@ export default {
|
||||
if (!enemyTeam || enemyTeam.captureCode != captureCode) {
|
||||
return false;
|
||||
}
|
||||
team.nCaptures++;
|
||||
// Update of events of the game
|
||||
writeCapture(Date.now(), teamId, enemyTeam.id);
|
||||
trajectory.writeCapture(Date.now(), teamId, enemyTeam.id);
|
||||
// Update of capture and chasing cycle
|
||||
this.capture(enemyTeam.id);
|
||||
// Sending new infos to the teams
|
||||
@@ -330,7 +373,9 @@ export default {
|
||||
* @param {Number} teamId the Id of the captured team
|
||||
*/
|
||||
capture(teamId) {
|
||||
this.getTeam(teamId).captured = true;
|
||||
const team = this.getTeam(teamId);
|
||||
team.captured = true;
|
||||
team.finishDate = Date.now();
|
||||
timeoutHandler.endSendPositionTimeout(teamId);
|
||||
this.updateTeamChasing();
|
||||
},
|
||||
@@ -352,22 +397,10 @@ export default {
|
||||
}
|
||||
zoneManager.udpateSettings(newSettings);
|
||||
if (this.state == GameState.PLAYING || this.state == GameState.FINISHED) {
|
||||
zoneManager.reset()
|
||||
if (!zoneManager.start()) {
|
||||
this.setState(GameState.SETUP);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the game state as finished, as well as resetting the zone manager
|
||||
*/
|
||||
finishGame() {
|
||||
this.setState(GameState.FINISHED);
|
||||
zoneManager.reset();
|
||||
timeoutHandler.endAllSendPositionTimeout();
|
||||
playersBroadcast("game_state", this.state);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import { config } from "dotenv";
|
||||
import { initAdminSocketHandler } from "./admin_socket.js";
|
||||
import { initTeamSocket } from "./team_socket.js";
|
||||
import { initPhotoUpload } from "./photo.js";
|
||||
import { initTrajectories } from "./trajectory.js";
|
||||
|
||||
config();
|
||||
const HOST = process.env.HOST;
|
||||
@@ -29,4 +28,3 @@ export const io = new Server(httpServer, {
|
||||
initAdminSocketHandler();
|
||||
initTeamSocket();
|
||||
initPhotoUpload();
|
||||
initTrajectories();
|
||||
|
||||
@@ -14,7 +14,7 @@ function degToRad(deg) {
|
||||
* @returns the distance between the two positions in meters
|
||||
* @see https://gist.github.com/miguelmota/10076960
|
||||
*/
|
||||
function getDistanceFromLatLon({ lat: lat1, lng: lon1 }, { lat: lat2, lng: lon2 }) {
|
||||
export function getDistanceFromLatLon({ lat: lat1, lng: lon1 }, { lat: lat2, lng: lon2 }) {
|
||||
var R = 6371; // Radius of the earth in km
|
||||
var dLat = degToRad(lat2 - lat1);
|
||||
var dLon = degToRad(lon2 - lon1);
|
||||
|
||||
@@ -121,17 +121,21 @@ export default {
|
||||
if (!isInCircle({ lat: team.currentLocation[0], lng: team.currentLocation[1] }, zone.currentZone.center, zone.currentZone.radius)) {
|
||||
//The team was not previously out of the zone
|
||||
if (!this.outOfBoundsSince[team.id]) {
|
||||
this.outOfBoundsSince[team.id] = new Date();
|
||||
teamBroadcast(team.id, "warning", `You left the zone, you have ${this.settings.allowedTimeOutOfZone} minutes to get back in the marked area.`)
|
||||
} else if (new Date() - this.outOfBoundsSince[team.id] > this.settings.allowedTimeOutOfZone * 60 * 1000) {
|
||||
this.addPenalty(team.id)
|
||||
this.outOfBoundsSince[team.id] = new Date();
|
||||
} else if (Math.abs(new Date() - this.outOfBoundsSince[team.id] - (this.settings.allowedTimeOutOfZone - 1) * 60 * 1000) < 100) {
|
||||
teamBroadcast(team.id, "warning", `You left the zone, you have 1 minutes to get back in the marked area.`)
|
||||
this.outOfBoundsSince[team.id] = Date.now();
|
||||
team.outOfZone = true;
|
||||
team.outOfZoneDeadline = this.outOfBoundsSince[team.id] + this.settings.allowedTimeOutOfZone * 60 * 1000;
|
||||
secureAdminBroadcast("teams", game.teams)
|
||||
} else if (Date.now() - this.outOfBoundsSince[team.id] > this.settings.allowedTimeOutOfZone * 60 * 1000) {
|
||||
this.addPenalty(team.id);
|
||||
this.outOfBoundsSince[team.id] = Date.now();
|
||||
team.outOfZoneDeadline = this.outOfBoundsSince[team.id] + this.settings.allowedTimeOutOfZone * 60 * 1000;
|
||||
secureAdminBroadcast("teams", game.teams)
|
||||
}
|
||||
} else {
|
||||
if (this.outOfBoundsSince[team.id]) {
|
||||
team.outOfZone = false;
|
||||
delete this.outOfBoundsSince[team.id];
|
||||
secureAdminBroadcast("teams", game.teams)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -146,15 +150,15 @@ export default {
|
||||
//If the team has not sent their location for more than the allowed period, automatically send it and add a penalty
|
||||
if (team.captured) { return }
|
||||
if (team.locationSendDeadline == null) {
|
||||
team.locationSendDeadline = Number(new Date()) + this.settings.allowedTimeBetweenPositionUpdate * 60 * 1000;
|
||||
team.locationSendDeadline = Number(Date.now()) + this.settings.allowedTimeBetweenPositionUpdate * 60 * 1000;
|
||||
return;
|
||||
}
|
||||
if (new Date() > team.locationSendDeadline) {
|
||||
if (Date.now() > team.locationSendDeadline) {
|
||||
this.addPenalty(team.id);
|
||||
game.sendLocation(team.id);
|
||||
sendUpdatedTeamInformations(team.id);
|
||||
secureAdminBroadcast("teams", game.teams)
|
||||
} else if (Math.abs(new Date() - team.locationSendDeadline - 60 * 1000) < 100) {
|
||||
} else if (Math.abs(Date.now() - team.locationSendDeadline - 60 * 1000) < 100) {
|
||||
teamBroadcast(team.id, "warning", `You have one minute left to udpate your location.`)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -45,7 +45,6 @@ export function sendUpdatedTeamInformations(teamId) {
|
||||
name: team.name,
|
||||
enemyLocation: team.enemyLocation,
|
||||
enemyName: team.enemyName,
|
||||
currentLocation: team.currentLocation,
|
||||
lastSentLocation: team.lastSentLocation,
|
||||
locationSendDeadline: team.locationSendDeadline,
|
||||
captureCode: team.captureCode,
|
||||
@@ -53,6 +52,13 @@ export function sendUpdatedTeamInformations(teamId) {
|
||||
ready: team.ready,
|
||||
captured: team.captured,
|
||||
penalties: team.penalties,
|
||||
outOfZone: team.outOfZone,
|
||||
outOfZoneDeadline: team.outOfZoneDeadline,
|
||||
distance: team.distance,
|
||||
startDate: game.startDate,
|
||||
finishDate: team.finishDate,
|
||||
nCaptures: team.nCaptures,
|
||||
nSentLocation: team.nSentLocation,
|
||||
})
|
||||
})
|
||||
secureAdminBroadcast("teams", game.teams);
|
||||
@@ -94,7 +100,8 @@ export function initTeamSocket() {
|
||||
socket.emit("zone", zone.currentZone);
|
||||
socket.emit("new_zone", {
|
||||
begin: zone.currentStartZone,
|
||||
end: zone.nextZone
|
||||
end: zone.nextZone,
|
||||
endDate: zone.nextZoneDate,
|
||||
})
|
||||
callback({ isLoggedIn : true, message: "Logged in"});
|
||||
});
|
||||
@@ -133,6 +140,29 @@ export function initTeamSocket() {
|
||||
return;
|
||||
}
|
||||
callback({ hasCaptured : true, message: "Capture successful" });
|
||||
})
|
||||
});
|
||||
|
||||
socket.on("deviceInfo", (infos) => {
|
||||
if (!teamId) {
|
||||
return;
|
||||
}
|
||||
const team = game.getTeam(teamId);
|
||||
// Only the first socket shares its infos since he is the one whose location is tracked
|
||||
if (team.sockets.indexOf(socket.id) == 0) {
|
||||
team.phoneModel = infos.model;
|
||||
team.phoneName = infos.name;
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("batteryUpdate", (batteryLevel) => {
|
||||
if (!teamId) {
|
||||
return;
|
||||
}
|
||||
const team = game.getTeam(teamId);
|
||||
// Only the first socket shares its infos since he is the one whose location is tracked
|
||||
if (team.sockets.indexOf(socket.id) == 0) {
|
||||
team.battery = batteryLevel;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -43,23 +43,48 @@ function addLineToFile(teamID, line) {
|
||||
}
|
||||
}
|
||||
|
||||
// Export functions
|
||||
|
||||
export async function initTrajectories() {
|
||||
function initTrajectories() {
|
||||
const files = fs.readdirSync(UPLOAD_DIR);
|
||||
for (const file of files) fs.unlinkSync(path.join(UPLOAD_DIR, file));
|
||||
}
|
||||
|
||||
export function writePosition(date, teamID, lon, lat) {
|
||||
// Export functions
|
||||
|
||||
export default {
|
||||
isRecording: false,
|
||||
|
||||
start() {
|
||||
initTrajectories();
|
||||
this.isRecording = true;
|
||||
},
|
||||
|
||||
stop() {
|
||||
this.isRecording = false;
|
||||
},
|
||||
|
||||
writePosition(date, teamID, lon, lat) {
|
||||
if (this.isRecording) {
|
||||
addLineToFile(teamID, dataToLine(date, "position", lon, lat));
|
||||
}
|
||||
},
|
||||
|
||||
export function writeCapture(date, teamID, capturedTeamID) {
|
||||
writeCapture(date, teamID, capturedTeamID) {
|
||||
if (this.isRecording) {
|
||||
addLineToFile(teamID, dataToLine(date, "capture", capturedTeamID));
|
||||
addLineToFile(capturedTeamID, dataToLine(date, "captured", teamID));
|
||||
}
|
||||
},
|
||||
|
||||
export function writeSeePosition(date, teamID, seenTeamID) {
|
||||
writeSeePosition(date, teamID, seenTeamID) {
|
||||
if (this.isRecording) {
|
||||
addLineToFile(teamID, dataToLine(date, "see"));
|
||||
addLineToFile(seenTeamID, dataToLine(date, "seen"));
|
||||
}
|
||||
},
|
||||
|
||||
writeOutOfZone(date, teamID, isOutOfZone) {
|
||||
if (this.isRecording) {
|
||||
addLineToFile(teamID, dataToLine(date, "zone", isOutOfZone));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -116,6 +116,8 @@ export default {
|
||||
* Start the zone reduction sequence
|
||||
*/
|
||||
start() {
|
||||
if (!this.ready()) return false;
|
||||
this.reset();
|
||||
this.started = true;
|
||||
this.startDate = new Date();
|
||||
//initialize the zone to its max value
|
||||
|
||||
28
traque-front/.vscode/launch.json
vendored
28
traque-front/.vscode/launch.json
vendored
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Next.js: debug server-side",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "npm run dev"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug client-side",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug full stack",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "npm run dev",
|
||||
"serverReadyAction": {
|
||||
"pattern": "- Local:.+(https?://.+)",
|
||||
"uriFormat": "%s",
|
||||
"action": "debugWithChrome"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import useAdmin from '@/hook/useAdmin';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import { env } from 'next-runtime-env';
|
||||
import { GameState } from '@/util/gameState';
|
||||
|
||||
const CircularAreaPicker = dynamic(() => import('./mapPicker').then((mod) => mod.CircularAreaPicker), {
|
||||
ssr: false
|
||||
@@ -12,8 +13,9 @@ const CircularAreaPicker = dynamic(() => import('./mapPicker').then((mod) => mod
|
||||
|
||||
export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
|
||||
const teamImage = useRef(null);
|
||||
const [avgSpeed, setAvgSpeed] = useState(0); // Speed in m/s
|
||||
const [newTeamName, setNewTeamName] = React.useState('');
|
||||
const { updateTeam, getTeamName, removeTeam, getTeam, teams } = useAdmin();
|
||||
const { updateTeam, getTeamName, removeTeam, getTeam, teams, gameState, startDate } = useAdmin();
|
||||
const [team, setTeam] = useState({});
|
||||
const NEXT_PUBLIC_SOCKET_HOST = env("NEXT_PUBLIC_SOCKET_HOST");
|
||||
var protocol = "https://";
|
||||
@@ -32,6 +34,12 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
|
||||
teamImage.current.src = SERVER_URL + "/photo/my?team=" + selectedTeamId + "&t=" + new Date().getTime();
|
||||
}, [selectedTeamId, teams])
|
||||
|
||||
// Update the average speed
|
||||
useEffect(() => {
|
||||
const time = Math.floor((team.finishDate ? team.finishDate - startDate : Date.now() - startDate) / 1000);
|
||||
setAvgSpeed(team.distance/time);
|
||||
}, [team.distance, team.finishDate]);
|
||||
|
||||
function handleRename(e) {
|
||||
e.preventDefault();
|
||||
updateTeam(team.id, { name: newTeamName });
|
||||
@@ -49,10 +57,19 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
|
||||
updateTeam(team.id, { penalties: newPenalties });
|
||||
}
|
||||
|
||||
function formatTimeHours(time) {
|
||||
// time is in seconds
|
||||
if (!Number.isInteger(time)) return "Inconnue";
|
||||
if (time < 0) time = 0;
|
||||
const hours = Math.floor(time / 3600);
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return String(hours).padStart(2,"0") + ":" + String(minutes).padStart(2,"0") + ":" + String(seconds).padStart(2,"0");
|
||||
}
|
||||
|
||||
return (team &&
|
||||
<div className='flex flex-col w-full h-full'>
|
||||
<div className='flex flex-row gap-2'>
|
||||
<div className='flex w-1/2 flex-col gap-2 h-min self-start'>
|
||||
<div className='flex flex-row w-full h-full gap-2'>
|
||||
<div className='flex w-1/2 flex-col h-1/2 gap-2'>
|
||||
<h2 className='text-2xl text-center'>Actions</h2>
|
||||
<form className='flex flex-row' onSubmit={handleRename}>
|
||||
<div className='w-4/5'>
|
||||
@@ -63,37 +80,47 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
|
||||
</div>
|
||||
</form>
|
||||
<div className='flex flex-row'>
|
||||
<BlueButton onClick={() => updateTeam(team.id, { captured: !team.captured })}>{team.captured ? "Revive" : "Capture"}</BlueButton>
|
||||
<BlueButton onClick={() => {updateTeam(team.id, { captured: !team.captured }); team.finishDate = Date.now()}}>{team.captured ? "Revive" : "Capture"}</BlueButton>
|
||||
<RedButton onClick={handleRemove}>Remove</RedButton>
|
||||
</div>
|
||||
<p className='text-2xl text-center w-full'>Starting zone</p>
|
||||
<CircularAreaPicker area={team.startingArea} setArea={(startingArea) => updateTeam(team.id, { startingArea })} markerPosition={team?.currentLocation} />
|
||||
</div>
|
||||
<div className='flex w-1/2 flex-col space-y-2 h-min self-start'>
|
||||
<div className='flex w-1/2 flex-col h-min gap-2 items-center'>
|
||||
<h2 className='text-2xl text-center'>Team details</h2>
|
||||
<div className='w-3/5'>
|
||||
<img className='self-stretch' ref={teamImage} onError={(e) => {e.target.src = "/images/missing_image.jpg"}} />
|
||||
</div>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<div>
|
||||
<p>Secret : {String(team.id).padStart(6, '0').replace(/(\d{3})(\d{3})/, '$1 $2')}</p>
|
||||
<p>Capture code : {String(team.captureCode).padStart(4, '0')}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Chasing : {getTeamName(team.chasing)}</p>
|
||||
<p>Chased by : {getTeamName(team.chased)}</p>
|
||||
</div>
|
||||
{gameState == GameState.PLAYING &&
|
||||
<div>
|
||||
<p>Distance: { (team.distance / 1000).toFixed(1) }km</p>
|
||||
<p>Time : {formatTimeHours(Math.floor((team.finishDate ? team.finishDate - startDate : Date.now() - startDate) / 1000))}</p>
|
||||
<p>Average speed : {(avgSpeed*3.6).toFixed(1)}km/h</p>
|
||||
<p>Captures : {team.nCaptures}</p>
|
||||
<p>Sent location : {team.nSentLocation}</p>
|
||||
<p>Oberved : {team.nObserved}</p>
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
<p>Phone model : {team.phoneModel ?? "Unknown"}</p>
|
||||
<p>Phone name : {team.phoneName ?? "Unknown"}</p>
|
||||
<p>Battery: {team.battery ? team.battery + "%" : "Unknown"}</p>
|
||||
</div>
|
||||
<div className='flex flex-row'>
|
||||
<p>Penalties :</p>
|
||||
<button className='w-7 h-7 mx-4 bg-blue-600 hover:bg-blue-500 text-md ease-out duration-200 text-white shadow-sm rounded' onClick={() => handleAddPenalty(-1)}>-</button>
|
||||
<p>{team.penalties}</p>
|
||||
<button className='w-7 h-7 mx-4 bg-blue-600 hover:bg-blue-500 text-md ease-out duration-200 text-white shadow-sm rounded' onClick={() => handleAddPenalty(1)}>+</button>
|
||||
</div>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-row'>
|
||||
<p className='text-2xl text-center w-full'>Starting zone</p>
|
||||
<p className='text-2xl text-center w-full'>Profile picture</p>
|
||||
</div>
|
||||
<div className='flex grow flex-row'>
|
||||
<div className='w-1/2'>
|
||||
<CircularAreaPicker area={team.startingArea} setArea={(startingArea) => updateTeam(team.id, { startingArea })} markerPosition={team?.currentLocation} />
|
||||
</div>
|
||||
<div className='w-1/2'>
|
||||
<img className='self-stretch' ref={teamImage} onError={(e) => {e.target.src = "/images/missing_image.jpg"}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,8 +19,9 @@ function AdminProvider({ children }) {
|
||||
const { adminSocket } = useSocket();
|
||||
const { loggedIn } = useAdminConnexion();
|
||||
const [gameState, setGameState] = useState(GameState.SETUP);
|
||||
const [startDate, setStartDate] = useState(null);
|
||||
|
||||
useSocketListener(adminSocket, "game_state", setGameState);
|
||||
useSocketListener(adminSocket, "game_state", (data) => {setGameState(data.state); setStartDate(data.startDate)});
|
||||
//Send a request to get the teams when the user logs in
|
||||
useEffect(() => {
|
||||
adminSocket.emit("get_teams");
|
||||
@@ -46,7 +47,7 @@ function AdminProvider({ children }) {
|
||||
useSocketListener(adminSocket, "zone_start", shrinking);
|
||||
useSocketListener(adminSocket, "new_zone", waiting);
|
||||
|
||||
const value = useMemo(() => ({ zone, zoneExtremities, teams, zoneSettings, penaltySettings, gameSettings, gameState, nextZoneDate, isShrinking }), [zoneSettings, teams, gameState, zone, zoneExtremities, penaltySettings, gameSettings, nextZoneDate, isShrinking]);
|
||||
const value = useMemo(() => ({ zone, zoneExtremities, teams, zoneSettings, penaltySettings, gameSettings, gameState, nextZoneDate, isShrinking, startDate }), [zoneSettings, teams, gameState, zone, zoneExtremities, penaltySettings, gameSettings, nextZoneDate, isShrinking, startDate]);
|
||||
return (
|
||||
<adminContext.Provider value={value}>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user