diff --git a/traque-front/app/admin/layout.js b/traque-front/app/admin/layout.js index 9ff7fa6..c75d592 100644 --- a/traque-front/app/admin/layout.js +++ b/traque-front/app/admin/layout.js @@ -11,6 +11,7 @@ export default function AdminLayout({ children}) {
diff --git a/traque-front/app/admin/map/page.js b/traque-front/app/admin/map/page.js new file mode 100644 index 0000000..8dfecf8 --- /dev/null +++ b/traque-front/app/admin/map/page.js @@ -0,0 +1,9 @@ +"use client"; +import dynamic from 'next/dynamic'; + +const LiveMap = dynamic(() => import('@/components/admin/maps').then((mod) => mod.LiveMap), { + ssr: false +}); +export default function LiveMapPage() { + return +} \ No newline at end of file diff --git a/traque-front/app/admin/page.js b/traque-front/app/admin/page.js index af4a7d7..6e0ff72 100644 --- a/traque-front/app/admin/page.js +++ b/traque-front/app/admin/page.js @@ -11,7 +11,7 @@ import dynamic from "next/dynamic"; const ZoneSelector = dynamic(() => import('@/components/admin/zoneSelector').then((mod) => mod.ZoneSelector), { ssr: false }); -const LiveMap = dynamic(() => import('@/components/admin/mapPicker').then((mod) => mod.LiveMap), { +const ZoneEditor = dynamic(() => import('@/components/admin/maps').then((mod) => mod.ZoneEditor), { ssr: false }); export default function AdminPage() { @@ -36,7 +36,7 @@ export default function AdminPage() { {(gameState == GameState.SETUP || gameState == GameState.PLACEMENT) && } {(gameState == GameState.SETUP || gameState == GameState.PLACEMENT) && } {gameState == GameState.PLAYING &&
- +
}
) diff --git a/traque-front/components/admin/mapPicker.jsx b/traque-front/components/admin/mapPicker.jsx deleted file mode 100644 index e511c3b..0000000 --- a/traque-front/components/admin/mapPicker.jsx +++ /dev/null @@ -1,125 +0,0 @@ -"use client"; -import { useLocation } from "@/hook/useLocation"; -import { useEffect, useState } from "react"; -import "leaflet/dist/leaflet.css"; -import { Circle, MapContainer, Marker, Popup, TileLayer, useMap } from "react-leaflet"; -import { useMapCircleDraw } from "@/hook/mapDrawing"; -import useAdmin from "@/hook/useAdmin"; - -function MapPan(props) { - const map = useMap(); - const [initialized, setInitialized] = useState(false); - - useEffect(() => { - if (!initialized && props.center) { - map.flyTo(props.center, props.zoom, { animate: false }); - setInitialized(true) - } - }, [props.center]); - return null; -} - -function MapEventListener({ onClick, onMouseMove }) { - const map = useMap(); - useEffect(() => { - map.on('click', onClick); - return () => { - map.off('click', onClick); - } - }, [onClick]); - useEffect(() => { - map.on('mousemove', onMouseMove); - return () => { - map.off('mousemove', onMouseMove); - } - }); - return null; -} - -const DEFAULT_ZOOM = 17; -export function CircularAreaPicker({ area, setArea, markerPosition, ...props }) { - const location = useLocation(Infinity); - const { handleClick, handleMouseMove, center, radius } = useMapCircleDraw(area, setArea); - return ( - - - {center && radius && } - {markerPosition && } - - - ) -} -export const EditMode = { - MIN: 0, - MAX: 1 -} -export function ZonePicker({ minZone, setMinZone, maxZone, setMaxZone, editMode, ...props }) { - const location = useLocation(Infinity); - const { handleClick: maxClick, handleMouseMove: maxHover, center: maxCenter, radius: maxRadius } = useMapCircleDraw(minZone, setMinZone); - const { handleClick: minClick, handleMouseMove: minHover, center: minCenter, radius: minRadius } = useMapCircleDraw(maxZone, setMaxZone); - function handleClick(e) { - if (editMode == EditMode.MAX) { - maxClick(e); - } else { - minClick(e); - } - } - function handleMouseMove(e) { - if (editMode == EditMode.MAX) { - maxHover(e); - } else { - minHover(e); - } - } - return ( - - - {minCenter && minRadius && } - {maxCenter && maxRadius && } - - - - ) -} - -export function LiveMap() { - const location = useLocation(Infinity); - const { zone, zoneExtremities, teams, getTeamName } = useAdmin(); - return ( - - - - {zone && } - {zoneExtremities && } - {zoneExtremities && } - {teams.map((team) => team.currentLocation && !team.captured && - - {team.name} -

Chasing : {getTeamName(team.chasing)}

-

Chased by : {getTeamName(team.chased)}

-
-
)} -
- ) -} \ No newline at end of file diff --git a/traque-front/components/admin/mapZoneSelector.jsx b/traque-front/components/admin/mapZoneSelector.jsx new file mode 100644 index 0000000..cf16fde --- /dev/null +++ b/traque-front/components/admin/mapZoneSelector.jsx @@ -0,0 +1,15 @@ +import { useMapGrid } from '@/hook/mapDrawing'; +import { TileNumber, latLngToTileNumber } from '../util/map'; +import { useMapEvent } from 'react-leaflet'; + +export function MapGridZoneSelector({ tilesColor, onClickTile, tileSize }) { + useMapEvent('click', (e) => { + const fractionalTileNumber = latLngToTileNumber(e.latlng, tileSize); + const tileNumber = new TileNumber(Math.floor(fractionalTileNumber.x), Math.floor(fractionalTileNumber.y)); + onClickTile(tileNumber); + }); + + useMapGrid(tilesColor, tileSize); + + return null; +} diff --git a/traque-front/components/admin/maps.jsx b/traque-front/components/admin/maps.jsx new file mode 100644 index 0000000..1c0aff6 --- /dev/null +++ b/traque-front/components/admin/maps.jsx @@ -0,0 +1,244 @@ +"use client"; +import { useLocation } from "@/hook/useLocation"; +import { use, useEffect, useState } from "react"; +import "leaflet/dist/leaflet.css"; +import L from "leaflet"; +import { Circle, LayerGroup, LayersControl, MapContainer, Marker, Popup, TileLayer, useMap } from "react-leaflet"; +import { useMapCircleDraw, useTilesColor } from "@/hook/mapDrawing"; +import useAdmin from "@/hook/useAdmin"; +import { MapGridZoneSelector } from "./mapZoneSelector.jsx"; +import { useAdminContext } from "@/context/adminContext.jsx"; +import { GreenButton } from "../util/button.jsx"; +import TextInput from "../util/textInput.jsx"; + + +function MapPan(props) { + const map = useMap(); + const [initialized, setInitialized] = useState(false); + + useEffect(() => { + if (!initialized && props.center) { + map.flyTo(props.center, props.zoom, { animate: false }); + setInitialized(true) + } + }, [props.center]); + return null; +} + +function MapEventListener({ onClick, onMouseMove }) { + const map = useMap(); + useEffect(() => { + map.on('click', onClick); + return () => { + map.off('click', onClick); + } + }, [onClick]); + useEffect(() => { + map.on('mousemove', onMouseMove); + return () => { + map.off('mousemove', onMouseMove); + } + }); + return null; +} + +const DEFAULT_ZOOM = 13; +export function CircularAreaPicker({ area, setArea, markerPosition, ...props }) { + const location = useLocation(Infinity); + const { handleClick, handleMouseMove, center, radius } = useMapCircleDraw(area, setArea); + return ( + + + {center && radius && } + {markerPosition && } + + + ) +} + +//https://stackoverflow.com/questions/71231865/show-fixed-100-m-x-100-m-grid-on-lowest-zoom-level + +export function ZoneInitializer({ ...props }) { + const location = useLocation(Infinity); + const { zone } = useAdminContext(); + const { initZone } = useAdmin(); + + const tilesColor = useTilesColor(zone); + + const handleClickTile = (tile) => { + if (zone) { + if (zone.some(t => t.x === tile.x && t.y === tile.y)) { + initZone(zone.filter(t => t.x !== tile.x || t.y !== tile.y)); + } else { + initZone([...zone, tile]); + } + } + } + + return ( + + + + + + + + + + + + ) +} + +export function ZoneEditor() { + const location = useLocation(Infinity); + const { zone, teams, getTeamName, removeZone } = useAdmin(); + const [zonesToDelete, setZonesToDelete] = useState([]); + const [tilesColor, setTilesColor] = useState([]); + const [timeBeforeDeletion, setTimeBeforeDeletion] = useState(null); + + function handleClickTile(tile) { + if (!zone.some(t => t.x === tile.x && t.y === tile.y)) return; + console.log(tile, "click", zonesToDelete); + if (!zonesToDelete.some(t => t.x === tile.x && t.y === tile.y)) { + setZonesToDelete([...zonesToDelete, tile]); + console.log("delete", tile); + } else { + setZonesToDelete(zonesToDelete.filter(t => t.x !== tile.x || t.y !== tile.y)); + } + } + + useEffect(() => { + console.log(zone); + setTilesColor([ + ...zonesToDelete.map(t => ({ x: t.x, y: t.y, color: 'rgba(255, 0, 0, 0.5)' })), + ...zone + .filter(t => !zonesToDelete.some(t2 => t.x == t2.x && t.y == t2.y)) + .map(t => { + console.log(t) + if (t.removeDate == null) { + return { x: t.x, y: t.y, color: 'rgba(0, 0, 255, 0.5)' } + } else { + return { x: t.x, y: t.y, color: 'rgba(255, 255, 0, 0.5)' } + } + }), + ]); + }, [zone, zonesToDelete]); + + const handleSubmit = (e) => { + if (timeBeforeDeletion == null) { + return; + } + e.preventDefault(); + removeZone(zonesToDelete, timeBeforeDeletion); + setZonesToDelete([]); + setTimeBeforeDeletion(null); + } + + + return ( +
+
+ + + + {teams.map((team) => team.currentLocation && !team.captured && + + {team.name} +

Chasing : {getTeamName(team.chasing)}

+

Chased by : {getTeamName(team.chased)}

+
+
)} + + + + + + + +
+
+
+ + setTimeBeforeDeletion(Number(e.target.value))}> + Delete selected zones +
+
+ ) +} + +export function LiveMap({ ...props }) { + const location = useLocation(Infinity); + const { zone, teams, getTeamName } = useAdmin(); + const tilesColor = useTilesColor(zone); + + return ( + + + + + + + { }} tileSize={16} /> + + + + + {teams.map((team) => team.currentLocation && !team.captured && + + {team.name} +

Chasing : {getTeamName(team.chasing)}

+

Chased by : {getTeamName(team.chased)}

+
+
)} +
+
+ + + {teams.map((team) => team.currentLocation && !team.captured && + + Last position of {team.name} + + )} + + +
+
+ ) +} \ No newline at end of file diff --git a/traque-front/components/admin/teamEdit.jsx b/traque-front/components/admin/teamEdit.jsx index e8f845a..bb8ea53 100644 --- a/traque-front/components/admin/teamEdit.jsx +++ b/traque-front/components/admin/teamEdit.jsx @@ -4,7 +4,7 @@ import BlueButton, { RedButton } from '../util/button'; import useAdmin from '@/hook/useAdmin'; import dynamic from 'next/dynamic'; -const CircularAreaPicker = dynamic(() => import('./mapPicker').then((mod) => mod.CircularAreaPicker), { +const CircularAreaPicker = dynamic(() => import('./maps').then((mod) => mod.CircularAreaPicker), { ssr: false }); diff --git a/traque-front/components/admin/zoneSelector.jsx b/traque-front/components/admin/zoneSelector.jsx index 2ef1781..32c7d89 100644 --- a/traque-front/components/admin/zoneSelector.jsx +++ b/traque-front/components/admin/zoneSelector.jsx @@ -1,61 +1,12 @@ -import { useEffect, useState } from "react"; -import BlueButton, { GreenButton, RedButton } from "../util/button"; -import { EditMode, ZonePicker } from "./mapPicker"; -import TextInput from "../util/textInput"; -import useAdmin from "@/hook/useAdmin"; +import { ZoneInitializer } from "./maps"; export function ZoneSelector() { - const [editMode, setEditMode] = useState(EditMode.MIN); - const [minZone, setMinZone] = useState(null); - const [maxZone, setMaxZone] = useState(null); - const [reductionCount, setReductionCount] = useState(""); - const [reductionDuration, setReductionDuration] = useState(""); - const [reductionInterval, setReductionInterval] = useState(""); - const {zoneSettings, changeZoneSettings} = useAdmin(); - - useEffect(() => { - if (zoneSettings) { - setMinZone(zoneSettings.min); - setMaxZone(zoneSettings.max); - setReductionCount(zoneSettings.reductionCount.toString()); - setReductionDuration(zoneSettings.reductionDuration.toString()); - setReductionInterval(zoneSettings.reductionInterval.toString()); - } - }, [zoneSettings]); - - function handleSettingsSubmit() { - changeZoneSettings({min:minZone, max:maxZone, reductionCount: Number(reductionCount), reductionDuration: Number(reductionDuration), reductionInterval: Number(reductionInterval)}); - } //When the user set one zone, switch to the other - useEffect(() => { - if(editMode == EditMode.MIN) { - setEditMode(EditMode.MAX); - }else { - setEditMode(EditMode.MIN); - } - - }, [minZone, maxZone]); - return

Edit zones

- {editMode == EditMode.MIN && setEditMode(EditMode.MAX)}>Edit end zone} - {editMode == EditMode.MAX && setEditMode(EditMode.MIN)}>Edit start zone}
- +
-
-

Number of reductions

- setReductionCount(e.target.value)}> -
-
-

Duration of each reduction

- setReductionDuration(e.target.value)}> -
-
-

Interval between reductions

- setReductionInterval(e.target.value)}> -
- Apply
} \ No newline at end of file diff --git a/traque-front/components/team/actionDrawer.jsx b/traque-front/components/team/actionDrawer.jsx index 0828c00..6354139 100644 --- a/traque-front/components/team/actionDrawer.jsx +++ b/traque-front/components/team/actionDrawer.jsx @@ -16,7 +16,6 @@ export default function ActionDrawer() { useEffect(() => { const interval = setInterval(() => { - console.log(locationSendDeadline) const timeLeft = locationSendDeadline - Date.now(); setTimeLeftBeforePenalty(Math.floor(timeLeft / 1000 / 60)); }, 1000); diff --git a/traque-front/components/team/map.jsx b/traque-front/components/team/map.jsx index e5b505e..b6a11a8 100644 --- a/traque-front/components/team/map.jsx +++ b/traque-front/components/team/map.jsx @@ -1,11 +1,13 @@ 'use client'; import React, { useEffect, useState } from 'react' -import { Circle, MapContainer, Marker, Popup, TileLayer, useMap } from 'react-leaflet' +import { Circle, LayerGroup, LayersControl, MapContainer, Marker, Popup, TileLayer, useMap } from 'react-leaflet' import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css' import "leaflet-defaulticon-compatibility"; import "leaflet/dist/leaflet.css"; import useGame from '@/hook/useGame'; import { useTeamContext } from '@/context/teamContext'; +import { useTilesColor } from '@/hook/mapDrawing'; +import { MapGridZoneSelector } from '../admin/mapZoneSelector'; const DEFAULT_ZOOM = 17; @@ -25,27 +27,11 @@ function MapPan(props) { return null; } -function LiveZone() { - const { zone } = useTeamContext(); - console.log('Zone', zone); - return zone && -} - -function ZoneExtremities() { - const { zoneExtremities } = useTeamContext(); - console.log('Zone extremities', zoneExtremities); - return zoneExtremities?.begin && zoneExtremities?.end && <> - {/* */} - - - -} - export function LiveMap({ ...props }) { + const {zone} = useTeamContext(); + const tilesColor = useTilesColor(zone); const { currentPosition, enemyPosition } = useGame(); - useEffect(() => { - console.log('Current position', currentPosition); - }, [currentPosition]); + return ( } - - + + + + {}} tileSize={16}/> + + + ) } diff --git a/traque-front/components/team/placementOverlay.jsx b/traque-front/components/team/placementOverlay.jsx index 63e6f68..c4bb5a6 100644 --- a/traque-front/components/team/placementOverlay.jsx +++ b/traque-front/components/team/placementOverlay.jsx @@ -1,6 +1,5 @@ import { useTeamConnexion } from "@/context/teamConnexionContext"; import useGame from "@/hook/useGame" -import Image from "next/image"; export default function PlacementOverlay() { const { name, ready } = useGame(); diff --git a/traque-front/components/util/map.jsx b/traque-front/components/util/map.jsx new file mode 100644 index 0000000..bf19533 --- /dev/null +++ b/traque-front/components/util/map.jsx @@ -0,0 +1,21 @@ +export class TileNumber { + constructor(x, y) { + this.x = x; + this.y = y; + this.removeDate = null; + } + equals(other) { + return this.x === other.x && this.y === other.y; + } +} + +export function latLngToTileNumber(latLng, tileSize) { + const numTilesX = 2 ** tileSize; + const numTilesY = 2 ** tileSize; + const lngDegrees = latLng.lng; + const latRadians = latLng.lat * (Math.PI / 180); + return new L.Point( + numTilesX * ((lngDegrees + 180) / 360), + numTilesY * (1 - Math.log(Math.tan(latRadians) + 1 / Math.cos(latRadians)) / Math.PI) / 2 + ); +} \ No newline at end of file diff --git a/traque-front/context/adminContext.jsx b/traque-front/context/adminContext.jsx index 0c94189..25f1e51 100644 --- a/traque-front/context/adminContext.jsx +++ b/traque-front/context/adminContext.jsx @@ -4,15 +4,15 @@ import { useSocket } from "./socketContext"; import { useSocketListener } from "@/hook/useSocketListener"; import { useAdminConnexion } from "./adminConnexionContext"; import { GameState } from "@/util/gameState"; +import { TileNumber } from "@/components/util/map"; const adminContext = createContext(); function AdminProvider({ children }) { const [teams, setTeams] = useState([]); - const [zoneSettings, setZoneSettings] = useState(null) + const [zone, setZone] = useState([]) const [penaltySettings, setPenaltySettings] = useState(null); const [gameSettings, setGameSettings] = useState(null); - const [zone, setZone] = useState(null); const [zoneExtremities, setZoneExtremities] = useState(null); const { adminSocket } = useSocket(); const { loggedIn } = useAdminConnexion(); @@ -26,13 +26,16 @@ function AdminProvider({ children }) { //Bind listeners to update the team list and the game status on socket message useSocketListener(adminSocket, "teams", setTeams); - useSocketListener(adminSocket, "zone_settings", setZoneSettings); useSocketListener(adminSocket, "game_settings", setGameSettings); useSocketListener(adminSocket, "penalty_settings", setPenaltySettings); - useSocketListener(adminSocket, "zone", setZone); + useSocketListener(adminSocket, "zone", (zone) => setZone(zone.map(t => { + let tile = new TileNumber(t.x, t.y); + tile.removeDate = t.removeDate; + return tile; + }))); useSocketListener(adminSocket, "new_zone", setZoneExtremities); - const value = useMemo(() => ({ zone, zoneExtremities, teams, zoneSettings, penaltySettings, gameSettings, gameState }), [zoneSettings, teams, gameState, zone, zoneExtremities, penaltySettings, gameSettings]); + const value = useMemo(() => ({ zone, zoneExtremities, teams, penaltySettings, gameSettings, gameState }), [teams, gameState, zone, zoneExtremities, penaltySettings, gameSettings]); return ( {children} diff --git a/traque-front/context/teamContext.jsx b/traque-front/context/teamContext.jsx index 5d66e9f..e22c13f 100644 --- a/traque-front/context/teamContext.jsx +++ b/traque-front/context/teamContext.jsx @@ -13,7 +13,6 @@ function TeamProvider({children}) { const [gameState, setGameState] = useState(GameState.SETUP); const [gameSettings, setGameSettings] = useState(null); const [zone, setZone] = useState(null); - const [zoneExtremities, setZoneExtremities] = useState(null); const measuredLocation = useLocation(5000); const {teamSocket} = useSocket(); const {loggedIn} = useTeamConnexion(); @@ -27,7 +26,6 @@ function TeamProvider({children}) { useSocketListener(teamSocket, "game_state", setGameState); useSocketListener(teamSocket, "zone", setZone); - useSocketListener(teamSocket, "new_zone", setZoneExtremities); useSocketListener(teamSocket, "game_settings", setGameSettings); @@ -40,7 +38,7 @@ function TeamProvider({children}) { } }, [loggedIn, measuredLocation]); - const value = useMemo(() => ({teamInfos, gameState, zone, zoneExtremities, gameSettings}), [gameSettings, teamInfos, gameState, zone, zoneExtremities]); + const value = useMemo(() => ({teamInfos, gameState, zone, gameSettings}), [gameSettings, teamInfos, gameState, zone]); return ( {children} diff --git a/traque-front/hook/mapDrawing.jsx b/traque-front/hook/mapDrawing.jsx index 804a99a..17dbd97 100644 --- a/traque-front/hook/mapDrawing.jsx +++ b/traque-front/hook/mapDrawing.jsx @@ -1,4 +1,7 @@ +import { TileNumber, latLngToTileNumber } from "@/components/util/map"; import { useEffect, useState } from "react"; +import { useLeafletContext } from '@react-leaflet/core'; +import L from 'leaflet'; export function useMapCircleDraw(area, setArea) { const [drawing, setDrawing] = useState(false); @@ -12,18 +15,18 @@ export function useMapCircleDraw(area, setArea) { }, [area]) function handleClick(e) { - if(!drawing) { + if (!drawing) { setCenter(e.latlng); setRadius(null); setDrawing(true); } else { setDrawing(false); - setArea({center, radius}); + setArea({ center, radius }); } } function handleMouseMove(e) { - if(drawing) { + if (drawing) { setRadius(e.latlng.distanceTo(center)); } } @@ -33,4 +36,91 @@ export function useMapCircleDraw(area, setArea) { center, radius, } +} + +export function useMapGrid(tilesColor, tileSize) { + const { layerContainer, map } = useLeafletContext(); + const [grid, setGrid] = useState(null); + + const Grid = L.GridLayer.extend({ + createTile: function (coords) { + const tile = L.DomUtil.create('canvas', 'leaflet-tile'); + const ctx = tile.getContext('2d'); + const size = this.getTileSize(); + tile.width = size.x + tile.height = size.y + + // calculate projection coordinates of top left tile pixel + const nwPoint = coords.scaleBy(size); + // calculate geographic coordinates of top left tile pixel + const nw = map.unproject(nwPoint, coords.z); + // calculate fraction tile number at top left point + const nwTile = latLngToTileNumber(nw, tileSize) + + // calculate projection coordinates of bottom right tile pixel + const sePoint = new L.Point(nwPoint.x + size.x - 1, nwPoint.y + size.y - 1) + // calculate geographic coordinates of bottom right tile pixel + const se = map.unproject(sePoint, coords.z); + // calculate fractional tile number at bottom right point + const seTile = latLngToTileNumber(se, tileSize) + + const minTileX = nwTile.x + const maxTileX = seTile.x + const minTileY = nwTile.y + const maxTileY = seTile.y + + for (let x = Math.ceil(minTileX) - 1; x <= Math.floor(maxTileX) + 1; x++) { + for (let y = Math.ceil(minTileY) - 1; y <= Math.floor(maxTileY) + 1; y++) { + + let tile = new TileNumber(x, y) + + const xMinPixel = Math.round(size.x * (x - minTileX) / (maxTileX - minTileX)); + const xMaxPixel = Math.round(size.x * (x + 1 - minTileX) / (maxTileX - minTileX)); + const yMinPixel = Math.round(size.y * (y - minTileY) / (maxTileY - minTileY)); + const yMaxPixel = Math.round(size.y * (y + 1 - minTileY) / (maxTileY - minTileY)); + + if (this.tilesColor?.some(t => t.x == tile.x && t.y == tile.y)) { + ctx.fillStyle = this.tilesColor.find(t => t.x == tile.x && t.y == tile.y).color; + } + else { + ctx.fillStyle = 'rgba(255, 255, 255, 0)'; + } + ctx.fillRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, yMaxPixel - yMinPixel); + } + } + return tile; + }, + }); + + useEffect(() => { + let g = new Grid({ + minZoom: 12, + }); + setGrid(g); + layerContainer.addLayer(g); + }, []) + + useEffect(() => { + if (grid) { + grid.tilesColor = tilesColor; + grid.redraw(); + } + }, [tilesColor,grid]); +} + +export function useTilesColor(zone) { + const [tilesColor, setTilesColor] = useState([]); + useEffect(() => { + if (zone) { + setTilesColor(zone.map(t => { + if(t.removeDate == null) { + return { x: t.x, y: t.y, color: 'rgba(0, 0, 255, 0.3'} + }else { + return { x: t.x, y: t.y, color: 'rgba(255, 255, 0, 0.3'} + } + + })); + } + }, [zone]); + return tilesColor; } \ No newline at end of file diff --git a/traque-front/hook/useAdmin.jsx b/traque-front/hook/useAdmin.jsx index 3441641..f031e1d 100644 --- a/traque-front/hook/useAdmin.jsx +++ b/traque-front/hook/useAdmin.jsx @@ -39,8 +39,12 @@ export default function useAdmin(){ adminSocket.emit("change_state", state); } - function changeZoneSettings(zone) { - adminSocket.emit("set_zone_settings", zone); + function initZone(zone) { + adminSocket.emit("set_zone", zone); + } + + function removeZone(zone, time) { + adminSocket.emit("remove_zone", zone, time); } function changePenaltySettings(penalties) { @@ -50,6 +54,6 @@ export default function useAdmin(){ function changeGameSettings(settings) { adminSocket.emit("set_game_settings", settings); } - return {...adminContext,changeGameSettings, changeZoneSettings, changePenaltySettings, pollTeams, getTeam, getTeamName, reorderTeams, addTeam, removeTeam, changeState, updateTeam }; + return {...adminContext,changeGameSettings, removeZone, initZone, changePenaltySettings, pollTeams, getTeam, getTeamName, reorderTeams, addTeam, removeTeam, changeState, updateTeam }; } \ No newline at end of file diff --git a/traque-front/public/icons/clock.png b/traque-front/public/icons/clock.png new file mode 100644 index 0000000..6097257 Binary files /dev/null and b/traque-front/public/icons/clock.png differ