mirror of
https://git.rezel.net/LudoTech/traque.git
synced 2026-02-09 10:20:16 +01:00
premier test implémentation d'un composant calculant la zone
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { isInCircle } from "./map_utils.js";
|
import { isInCircle } from "./map_utils.js";
|
||||||
|
import { ZoneManager } from "./zoneManager.js";
|
||||||
|
|
||||||
const GameState = {
|
const GameState = {
|
||||||
SETUP: "setup",
|
SETUP: "setup",
|
||||||
@@ -8,10 +9,10 @@ const GameState = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class Game {
|
export default class Game {
|
||||||
constructor() {
|
constructor(onUpdateZone,onUpdateNewZone) {
|
||||||
this.teams = [];
|
this.teams = [];
|
||||||
this.state = GameState.SETUP;
|
this.state = GameState.SETUP;
|
||||||
this.zone = new ZoneManager(null, null)
|
this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone)
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(newState) {
|
setState(newState) {
|
||||||
@@ -21,6 +22,11 @@ export default class Game {
|
|||||||
//The game has started
|
//The game has started
|
||||||
if(newState == GameState.PLAYING) {
|
if(newState == GameState.PLAYING) {
|
||||||
this.initLastSentLocations();
|
this.initLastSentLocations();
|
||||||
|
this.zone.reset()
|
||||||
|
this.zone.start()
|
||||||
|
}
|
||||||
|
if(newState != GameState.PLAYING) {
|
||||||
|
this.zone.reset();
|
||||||
}
|
}
|
||||||
this.state = newState;
|
this.state = newState;
|
||||||
return true;
|
return true;
|
||||||
@@ -152,9 +158,15 @@ export default class Game {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setZone(newSettings) {
|
/**
|
||||||
|
* Change the settings of the Zone manager
|
||||||
|
* The game should not be in PLAYING or FINISHED state
|
||||||
|
* @param {Object} newSettings The object containing the settings to be changed
|
||||||
|
* @returns false if failed
|
||||||
|
*/
|
||||||
|
setZoneSettings(newSettings) {
|
||||||
//cannot change zones while playing
|
//cannot change zones while playing
|
||||||
if(game.state == GameState.PLAYING || game.state == GameState.FINISHED) {
|
if(this.state == GameState.PLAYING || this.state == GameState.FINISHED) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.zone.udpateSettings(newSettings)
|
this.zone.udpateSettings(newSettings)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const io = new Server(httpsServer, {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message to all logged in sockets
|
* Send a message to all logged in admin sockets
|
||||||
* @param {String} event The event name
|
* @param {String} event The event name
|
||||||
* @param {String} data The data to send
|
* @param {String} data The data to send
|
||||||
*/
|
*/
|
||||||
@@ -39,27 +39,52 @@ function secureBroadcast(event, data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a socket message to all the players of a team
|
||||||
|
* @param {String} teamId The team that will receive the message
|
||||||
|
* @param {String} event Event name
|
||||||
|
* @param {*} data The payload
|
||||||
|
*/
|
||||||
function teamBroadcast(teamId, event, data) {
|
function teamBroadcast(teamId, event, data) {
|
||||||
for (let socketId of game.getTeam(teamId).sockets) {
|
for (let socketId of game.getTeam(teamId).sockets) {
|
||||||
io.of("player").to(socketId).emit(event, data)
|
io.of("player").to(socketId).emit(event, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to all logged in players
|
||||||
|
* @param {String} event Event name
|
||||||
|
* @param {String} data payload
|
||||||
|
*/
|
||||||
function playersBroadcast(event, data) {
|
function playersBroadcast(event, data) {
|
||||||
for (let team of game.teams) {
|
for (let team of game.teams) {
|
||||||
teamBroadcast(team.id, event, data);
|
teamBroadcast(team.id, event, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a player from the list of logged in players
|
||||||
|
* @param {Number} id The id of the player to log out
|
||||||
|
*/
|
||||||
function logoutPlayer(id) {
|
function logoutPlayer(id) {
|
||||||
for (let team of game.teams) {
|
for (let team of game.teams) {
|
||||||
team.sockets = team.sockets.filter((sid) => sid != id);
|
team.sockets = team.sockets.filter((sid) => sid != id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Zone update broadcast function, called by the game object
|
||||||
|
function onUpdateNewZone(newZone) {
|
||||||
|
playersBroadcast("new_zone", newZone)
|
||||||
|
secureBroadcast("new_zone", newZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUpdateZone(zone) {
|
||||||
|
playersBroadcast("zone", zone)
|
||||||
|
secureBroadcast("zone", "zone")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const game = new Game();
|
const game = new Game(onUpdateZone, onUpdateNewZone);
|
||||||
|
|
||||||
//Array of logged in sockets
|
//Array of logged in sockets
|
||||||
let loggedInSockets = [];
|
let loggedInSockets = [];
|
||||||
@@ -95,15 +120,16 @@ io.of("admin").on("connection", (socket) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("set_zone", (zone) => {
|
socket.on("set_zone_settings", (zone) => {
|
||||||
if (!loggedIn) {
|
if (!loggedIn) {
|
||||||
socket.emit("error", "Not logged in");
|
socket.emit("error", "Not logged in");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!game.setZone(zone)) {
|
if(!game.setZoneSettings(zone)) {
|
||||||
socket.emit("error", "Error changing zone");
|
socket.emit("error", "Error changing zone");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
//User is attempting to add a new team
|
//User is attempting to add a new team
|
||||||
|
|||||||
13
traque-back/util.js
Normal file
13
traque-back/util.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Scale a value that is known to be in a range to a new range
|
||||||
|
* for instance map(50,0,100,1000,2000) will return 1500 as 50 is halfway between 0 and 100 and 1500 is halfway through 1000 and 2000
|
||||||
|
* @param {Number} value value to map
|
||||||
|
* @param {Number} oldMin minimum value of the number
|
||||||
|
* @param {Number} oldMax maximum value of the number
|
||||||
|
* @param {Number} newMin minimum value of the output
|
||||||
|
* @param {Number} newMax maximum value of the output
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function map(value, oldMin, oldMax, newMin, newMax) {
|
||||||
|
return ((value - oldMin) / (oldMax - oldMin)) * (newMax - newMin) + newMin;
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import randomLocation, { randomCirclePoint } from 'random-location'
|
import randomLocation, { randomCirclePoint } from 'random-location'
|
||||||
import { isInCircle } from './map_utils';
|
import { isInCircle } from './map_utils';
|
||||||
|
import { map } from './util';
|
||||||
|
|
||||||
class ZoneManager {
|
export class ZoneManager {
|
||||||
constructor(onZoneUpdate, onNextZoneUpdate) {
|
constructor(onZoneUpdate, onNextZoneUpdate) {
|
||||||
//Setings storing where the zone will start, end and how it should evolve
|
//Setings storing where the zone will start, end and how it should evolve
|
||||||
//The zone will start by staying at its mzx value for reductionInterval minutes
|
//The zone will start by staying at its mzx value for reductionInterval minutes
|
||||||
@@ -19,17 +20,20 @@ class ZoneManager {
|
|||||||
}
|
}
|
||||||
this.shrinkFactor = null;
|
this.shrinkFactor = null;
|
||||||
//Live location of the zone
|
//Live location of the zone
|
||||||
this.currentZone = null;
|
this.currentZone = {center: null, radius: null};
|
||||||
this.currentDecrement = null;
|
|
||||||
|
|
||||||
//If the zone is shrinking, this is the target of the current shrinking
|
//If the zone is shrinking, this is the target of the current shrinking
|
||||||
//If the zone is not shrinking, this will be the target of the next shrinking
|
//If the zone is not shrinking, this will be the target of the next shrinking
|
||||||
this.nextZone = null;
|
this.nextZone = {center: null, radius: null};
|
||||||
|
|
||||||
|
//Zone at the begining of the shrinking
|
||||||
|
this.currentStartZone = {center: null, radius: null};
|
||||||
|
|
||||||
this.startDate = null;
|
this.startDate = null;
|
||||||
this.zoneSettings = zoneSettings;
|
this.zoneSettings = zoneSettings;
|
||||||
this.started = false;
|
this.started = false;
|
||||||
this.timeoutId = null;
|
this.updateIntervalId = null;
|
||||||
|
this.nextZoneTimeoutId = null;
|
||||||
|
|
||||||
this.onZoneUpdate = onZoneUpdate;
|
this.onZoneUpdate = onZoneUpdate;
|
||||||
this.onNextZoneUpdate = onNextZoneUpdate
|
this.onNextZoneUpdate = onNextZoneUpdate
|
||||||
@@ -40,14 +44,30 @@ class ZoneManager {
|
|||||||
this.shrinkFactor = Math.pow(this.zoneSettings.min.radius / this.zoneSettings.max.radius, 1/this.zoneSettings.reductionCount)
|
this.shrinkFactor = Math.pow(this.zoneSettings.min.radius / this.zoneSettings.max.radius, 1/this.zoneSettings.reductionCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reinitialize the object and stop all the tasks
|
||||||
|
*/
|
||||||
reset() {
|
reset() {
|
||||||
this.currentZoneCount = 0;
|
this.currentZoneCount = 0;
|
||||||
|
this.started = false;
|
||||||
|
if(this.updateIntervalId != null) {
|
||||||
|
clearInterval(this.updateIntervalId);
|
||||||
|
this.updateIntervalId = null;
|
||||||
|
}
|
||||||
|
if(this.nextZoneTimeoutId != null) {
|
||||||
|
clearTimeout(this.nextZoneTimeoutId);
|
||||||
|
this.nextZoneTimeoutId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the zone reduction sequence
|
||||||
|
*/
|
||||||
start() {
|
start() {
|
||||||
this.started = true;
|
this.started = true;
|
||||||
this.startDate = new Date();
|
this.startDate = new Date();
|
||||||
requestAnimationFrame(tick);
|
//initialize the zone to its max value
|
||||||
|
this.currentStartZone = this.currentZone = JSON.parse(JSON.stringify(this.zoneSettings.max));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,33 +80,60 @@ class ZoneManager {
|
|||||||
getRandomNextCenter(newRadius) {
|
getRandomNextCenter(newRadius) {
|
||||||
let ok = false;
|
let ok = false;
|
||||||
let res = null
|
let res = null
|
||||||
|
//take a random point satisfying both conditions
|
||||||
while(!ok) {
|
while(!ok) {
|
||||||
res = randomCirclePoint({latitude: this.currentZone.lat, longitude: this.currentZone.long}, this.currentZone.radius - newRadius);
|
res = randomCirclePoint([ this.currentZone.lat, this.currentZone.long], this.currentZone.radius - newRadius);
|
||||||
ok = (isInCircle({lat: res.latitude, long: res.longitude}, this.zoneSettings.min.center, newRadius - this.zoneSettings.min.radius))
|
ok = (isInCircle([res.latitude, res.longitude], this.zoneSettings.min.center, newRadius - this.zoneSettings.min.radius))
|
||||||
}
|
|
||||||
return {
|
|
||||||
lat: res.latitude,
|
|
||||||
long: res.longitude
|
|
||||||
}
|
}
|
||||||
|
return [
|
||||||
|
res.latitude,
|
||||||
|
res.longitude
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the next zone satifying the given settings, update the nextZone and currentStartZone
|
||||||
|
* Wait for the appropriate duration before starting a new zone reduction if needed
|
||||||
|
*/
|
||||||
setNextZone() {
|
setNextZone() {
|
||||||
//At this point, nextZone == currentZone, we need to update the next zone, the raidus decrement, and start a timer before the next shrink
|
//At this point, nextZone == currentZone, we need to update the next zone, the raidus decrement, and start a timer before the next shrink
|
||||||
|
|
||||||
//last zone
|
//last zone
|
||||||
if(this.currentZoneCount == this.zoneSettings.currentZoneCount - 1) {
|
if(this.currentZoneCount == this.zoneSettings.currentZoneCount - 1) {
|
||||||
//Copy values
|
//Copy values
|
||||||
this.nextZone = {center: [...this.zoneSettings.min.center],...this.zoneSettings.min}
|
this.nextZone =JSON.parse(JSON.stringify(this.zoneSettings.min))
|
||||||
|
this.currentStartZone =JSON.parse(JSON.stringify(this.zoneSettings.min))
|
||||||
}else {
|
}else {
|
||||||
//TODO : compute next zone
|
//TODO : compute next zone
|
||||||
this.nextZone.center = this.getRandomNextCenter(this.nextZone.radius * this.shrinkFactor)
|
this.nextZone.center = this.getRandomNextCenter(this.nextZone.radius * this.shrinkFactor)
|
||||||
this.nextZone.radius *= this.shrinkFactor;
|
this.nextZone.radius *= this.shrinkFactor;
|
||||||
this.timeoutId = setTimeout(() => this.startShrinking(), 1000 * 60 * this.zoneSettings.reductionIntervalMinutes)
|
this.currentStartZone = JSON.parse(JSON.stringify(this.currentZone))
|
||||||
|
this.onNextZoneUpdate({
|
||||||
|
begin: JSON.parse(JSON.stringify(this.currentStartZone)),
|
||||||
|
end: JSON.parse(JOSN.stringify())
|
||||||
|
})
|
||||||
|
this.nextZoneTimeoutId = setTimeout(() => this.startShrinking(), 1000 * 60 * this.zoneSettings.reductionIntervalMinutes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start a task that will run periodically updatinng the zone size, and calling the onZoneUpdate callback
|
||||||
|
* This will also periodically check if the reduction is over or not
|
||||||
|
* If the reduction is over this function will call setNextZone
|
||||||
|
*/
|
||||||
startShrinking() {
|
startShrinking() {
|
||||||
|
const startTime = new Date();
|
||||||
|
this.updateIntervalId = setInterval(() => {
|
||||||
|
const completed = ((new Date() - startTime) / (1000 * 60)) / this.zoneSettings.reductionDuration;
|
||||||
|
this.currentZone.radius = map(completed, 0, 1, this.currentStartZone.radius, this.nextZone.radius)
|
||||||
|
this.onZoneUpdate(JSON.parse(JSON.stringify(this.currentZone)))
|
||||||
|
//Zone shrinking is over
|
||||||
|
if(completed >= 1) {
|
||||||
|
clearInterval(this.updateIntervalId);
|
||||||
|
this.updateIntervalId = null;
|
||||||
|
this.setNextZone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, this.zoneSettings.updateIntervalSeconds * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user