Merge branch 'master' into weeklyDaily

pull/104/head^2
Baptiste MARCEL 1 year ago
commit ae53743188

@ -25,12 +25,18 @@ io.on('connection', (socket) => {
console.log(socket.id); console.log(socket.id);
socket.on('network created', (network, person, indices, room, start) =>{ socket.on('network created', (network, person, indices, room, start) =>{
try{
io.to(room).emit("game created", network, person, indices, start) io.to(room).emit("game created", network, person, indices, start)
map.get(room).started = true map.get(room).started = true
map.get(room).actualPlayer=start map.get(room).actualPlayer=start
const playerArray = Array.from(map.entries()).map(([key, value]) => ({ key, value })) const playerArray = Array.from(map.entries()).map(([key, value]) => ({ key, value }))
const playerJson = JSON.stringify(playerArray); const playerJson = JSON.stringify(playerArray);
io.emit("request lobbies", playerJson) io.emit("request lobbies", playerJson)
}
catch{
console.log("error")
}
}); });
socket.on("give network", (networkPerson, person, indices, start, room, nodes, playerId) => { socket.on("give network", (networkPerson, person, indices, start, room, nodes, playerId) => {
@ -145,6 +151,7 @@ io.on('connection', (socket) => {
}) })
socket.on("who plays", (room) => { socket.on("who plays", (room) => {
try{
if (map.get(room) !== undefined){ if (map.get(room) !== undefined){
let player = map.get(room).actualPlayer let player = map.get(room).actualPlayer
if (map.get(room).tab[player].type != "User"){ if (map.get(room).tab[player].type != "User"){
@ -156,6 +163,11 @@ io.on('connection', (socket) => {
// console.log(player) // console.log(player)
io.to(room).emit("who plays", player, map.get(room).lastWorks) io.to(room).emit("who plays", player, map.get(room).lastWorks)
} }
}
catch{
}
}) })
socket.on("disconnect", () =>{ socket.on("disconnect", () =>{
@ -216,9 +228,14 @@ io.on('connection', (socket) => {
}) })
socket.on("node checked", (id, works, color, room, playerIndex) =>{ socket.on("node checked", (id, works, color, room, playerIndex) =>{
try{
map.get(room).actualPlayer=playerIndex map.get(room).actualPlayer=playerIndex
map.get(room).lastWorks=works map.get(room).lastWorks=works
io.to(room).emit("node checked", id, works, color, playerIndex, socket.id) io.to(room).emit("node checked", id, works, color, playerIndex, socket.id)
}
catch{
console.log("error")
}
}) })
socket.on("put correct background", (id) =>{ socket.on("put correct background", (id) =>{

@ -43,6 +43,7 @@ import ErrorBoundary from './Error/ErrorBoundary';
import ErrorPage from './Error/ErrorPage'; import ErrorPage from './Error/ErrorPage';
import DeducCheck from './Pages/DeducCheck'; import DeducCheck from './Pages/DeducCheck';
import {basePath} from "./AdressSetup" import {basePath} from "./AdressSetup"
import Tutorial from './Pages/Tutorial';
@ -68,6 +69,7 @@ function App() {
//const location = useLocation(); //const location = useLocation();
const hasNavbarVisible = [basePath + "/", basePath + "/login", basePath + "/signup", basePath + "/lobby", basePath + "/endgame", basePath + "/deduc"]//.includes(window.location.pathname); const hasNavbarVisible = [basePath + "/", basePath + "/login", basePath + "/signup", basePath + "/lobby", basePath + "/endgame", basePath + "/deduc"]//.includes(window.location.pathname);
document.title = "Social Graph";
return ( return (
<ErrorBoundary fallback={(error, errorInfo) => <ErrorPage />}> <ErrorBoundary fallback={(error, errorInfo) => <ErrorPage />}>
@ -88,6 +90,7 @@ function App() {
<Route path={`${basePath}/endgame`} element={<EndGame/>} /> <Route path={`${basePath}/endgame`} element={<EndGame/>} />
<Route path={`${basePath}/game`} element={<InGame locale={locale} changeLocale={changeLocale}/>}/> <Route path={`${basePath}/game`} element={<InGame locale={locale} changeLocale={changeLocale}/>}/>
<Route path={`${basePath}/info`} element={<InfoPage locale={locale} changeLocale={changeLocale}/>} /> <Route path={`${basePath}/info`} element={<InfoPage locale={locale} changeLocale={changeLocale}/>} />
<Route path={`${basePath}/tutorial`} element={<Tutorial locale={locale} changeLocale={changeLocale}/>} />
<Route path={`${basePath}/deduc`} element={<DeducCheck/>} /> <Route path={`${basePath}/deduc`} element={<DeducCheck/>} />
<Route path={`${basePath}/TheRealDeduc`} element={<DeducGrid/>} /> <Route path={`${basePath}/TheRealDeduc`} element={<DeducGrid/>} />
<Route path={`${basePath}/profile`} element={<Profile/>} /> <Route path={`${basePath}/profile`} element={<Profile/>} />

@ -19,6 +19,7 @@ const ChoiceBar = () => {
async function askEveryone(){ async function askEveryone(){
if (nodeId !== null){ if (nodeId !== null){
//@ts-ignore
const person = personNetwork?.getPersons().find((p) => p.getId() == nodeId) const person = personNetwork?.getPersons().find((p) => p.getId() == nodeId)
if (person != undefined){ if (person != undefined){
const indiceTester = IndiceTesterFactory.Create(indices[actualPlayerIndex]) const indiceTester = IndiceTesterFactory.Create(indices[actualPlayerIndex])

@ -41,6 +41,15 @@ interface MyGraphComponentProps {
setPlayerIndex: (playerIndex: number) => void setPlayerIndex: (playerIndex: number) => void
importToPdf: boolean importToPdf: boolean
setImportToPdf: (imp: boolean) => void setImportToPdf: (imp: boolean) => void
importToJSON: boolean
setImportToJSON: (imp: boolean) => void
setPutCorrectBackground : (func: () => void) => void
setPutGreyBackground : (func: () => void) => void
setPutImposssibleGrey : (func: () => void) => void
putGreyBackground : () => void
putCorrectBackground : () => void
putImposssibleGrey : () => void
} }
let lastAskingPlayer = 0 let lastAskingPlayer = 0
@ -71,7 +80,7 @@ let testTemps = 0
let testFirst = false let testFirst = false
const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleShowTurnBar, handleTurnBarTextChange, playerTouched, setPlayerTouched, changecptTour, solo, isDaily, isEasy, addToHistory, showLast, setNetwork, setNetworkEnigme, setPlayerIndex, askedWrong, setAskedWrong, importToPdf, setImportToPdf}) => { const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleShowTurnBar, handleTurnBarTextChange, playerTouched, setPlayerTouched, changecptTour, solo, isDaily, isEasy, addToHistory, showLast, setNetwork, setNetworkEnigme, setPlayerIndex, askedWrong, setAskedWrong, importToPdf, setImportToPdf, importToJSON, setImportToJSON, setPutCorrectBackground, setPutGreyBackground, setPutImposssibleGrey, putCorrectBackground, putGreyBackground, putImposssibleGrey}) => {
let cptTour: number = 0 let cptTour: number = 0
//* Gestion du temps : //* Gestion du temps :
@ -127,17 +136,17 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
console.log(playerTouched) console.log(playerTouched)
if (touchedPlayer == -1){ if (touchedPlayer == -1){
if (!askedWrongLocal){ if (!askedWrongLocal){
socket.emit("put correct background", socket.id) putCorrectBackground()
} }
} }
else if (touchedPlayer < players.length && touchedPlayer>=0){ else if (touchedPlayer < players.length && touchedPlayer>=0){
if(!askedWrongLocal){ if(!askedWrongLocal){
socket.emit("put correct background", socket.id) putCorrectBackground()
socket.emit("put grey background", socket.id, touchedPlayer) putGreyBackground()
} }
} }
else if(touchedPlayer == players.length){ else if(touchedPlayer == players.length){
socket.emit("put imossible grey", socket.id) putImposssibleGrey()
} }
}, [playerTouched]) }, [playerTouched])
@ -328,6 +337,25 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
}, [importToPdf]) }, [importToPdf])
function downloadToJSON(jsonData: any, filename: string) {
const jsonBlob = new Blob([JSON.stringify(jsonData)], {type: 'application/json'});
const url = URL.createObjectURL(jsonBlob);
const link = document.createElement('a');
link.download = filename;
link.href = url;
link.click();
URL.revokeObjectURL(url);
}
useEffect(() => {
if (importToJSON){
setImportToJSON(false)
downloadToJSON(personNetwork, "graph.json")
downloadToJSON(JSON.stringify(indices), "indices.json")
}
}, [importToJSON])
useEffect(() => { useEffect(() => {
if (personNetwork == null){ if (personNetwork == null){
return return
@ -457,7 +485,6 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
p.indice=indices[index] p.indice=indices[index]
p.index=index p.index=index
p.initiateMap(personNetwork) p.initiateMap(personNetwork)
console.log(p.indice.ToString("fr"))
} }
}) })
} }
@ -468,7 +495,6 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
bot.indice=indices[i] bot.indice=indices[i]
bot.index=index bot.index=index
bot.initiateMap(personNetwork) bot.initiateMap(personNetwork)
console.log(bot.indice.ToString("fr"))
} }
} }
if (i==playerIndex){ if (i==playerIndex){
@ -500,6 +526,7 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
setPlayerIndex(index) setPlayerIndex(index)
setLastIndex(index) setLastIndex(index)
if (actualPlayerIndex==index){ if (actualPlayerIndex==index){
handleTurnBarTextChange("À vous de jouer")
handleShowTurnBar(true) handleShowTurnBar(true)
} }
}) })
@ -612,7 +639,8 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
askedWrongBot=true askedWrongBot=true
handleShowTurnBar(true) handleShowTurnBar(true)
handleTurnBarTextChange("Mauvais choix, posez un carré !") handleTurnBarTextChange("Mauvais choix, posez un carré !")
socket.emit("put grey background", socket.id, actualPlayerIndex) touchedPlayer = actualPlayerIndex
putGreyBackgroud()
} }
else{ else{
socket.emit("can't put square", actualPlayerIndex, room) socket.emit("can't put square", actualPlayerIndex, room)
@ -637,7 +665,7 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
else{ else{
handleShowTurnBar(false) handleShowTurnBar(false)
socket.emit("put correct background", socket.id) putCorrectBackground()
} }
} }
}) })
@ -698,16 +726,20 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
socket.on("put correct background", () =>{ const putCorrectBackground = () => {
if (personNetwork != null){ if (personNetwork != null){
for(const person of personNetwork.getPersons()){ for(const person of personNetwork.getPersons()){
networkData.nodes.update({id: person.getId(), color: ColorToHexa(person.getColor())}) networkData.nodes.update({id: person.getId(), color: ColorToHexa(person.getColor())})
} }
} }
}) };
socket.on("put grey background", (player) =>{
setPutCorrectBackground(() => putCorrectBackground)
const putGreyBackgroud = () => {
if (personNetwork != null){ if (personNetwork != null){
const player = touchedPlayer
const tab = mapIndexPersons.get(player) const tab = mapIndexPersons.get(player)
if (tab != undefined){ if (tab != undefined){
if (player != actualPlayerIndex){ if (player != actualPlayerIndex){
@ -735,9 +767,11 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
} }
} }
}) };
setPutGreyBackground(() => putGreyBackgroud)
socket.on("put imossible grey", ()=>{ const putGreyImpossible = () => {
if (personNetwork != null && indice!=null){ if (personNetwork != null && indice!=null){
const tabNodes: any = [] const tabNodes: any = []
const tester = IndiceTesterFactory.Create(indice) const tester = IndiceTesterFactory.Create(indice)
@ -758,7 +792,9 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
networkData.nodes.update({id: n.id, color: "#808080"}) networkData.nodes.update({id: n.id, color: "#808080"})
} }
} }
}) }
setPutImposssibleGrey(() => putGreyImpossible)
socket.on("end game", (winnerIndex) =>{ socket.on("end game", (winnerIndex) =>{
if (cptEndgame % 2 == 0){ if (cptEndgame % 2 == 0){
@ -815,9 +851,6 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
socket.off("already asked") socket.off("already asked")
socket.off("asked wrong") socket.off("asked wrong")
socket.off("asked") socket.off("asked")
socket.off("put correct background")
socket.off("put grey background")
socket.off("put imossible grey")
socket.off("who plays") socket.off("who plays")
navigate(`${basePath}/endgame`) navigate(`${basePath}/endgame`)
@ -879,7 +912,7 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
playerIndex = 0 playerIndex = 0
} }
socket.emit("node checked", params.nodes[0], false, actualPlayerIndex, room, playerIndex) socket.emit("node checked", params.nodes[0], false, actualPlayerIndex, room, playerIndex)
socket.emit("put correct background", socket.id) putCorrectBackground()
touchedPlayer=-1 touchedPlayer=-1
askedPersons.push(person) askedPersons.push(person)
askedWrongLocal=false askedWrongLocal=false
@ -917,7 +950,7 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
console.log("CE N'EST PAS UN BOT") console.log("CE N'EST PAS UN BOT")
//@ts-ignore //@ts-ignore
socket.emit("ask player", params.nodes[0], players[touchedPlayer].id, players.find((p) => p.id === socket.id, actualPlayerIndex)) socket.emit("ask player", params.nodes[0], players[touchedPlayer].id, players.find((p) => p.id === socket.id, actualPlayerIndex))
socket.emit("put correct background", socket.id) putCorrectBackground()
touchedPlayer=-1 touchedPlayer=-1
setPlayerTouched(-1) setPlayerTouched(-1)
} }
@ -951,14 +984,14 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
if(!works){ if(!works){
socket.emit("node checked", params.nodes[0], works, playerIndex, room, actualPlayerIndex) socket.emit("node checked", params.nodes[0], works, playerIndex, room, actualPlayerIndex)
socket.emit("put correct background", socket.id) putCorrectBackground()
socket.emit("asked wrong", players[actualPlayerIndex]) socket.emit("asked wrong", players[actualPlayerIndex])
touchedPlayer=-1 touchedPlayer=-1
setPlayerTouched(-1) setPlayerTouched(-1)
return return
} }
if (i == players.length - 1){ if (i == players.length - 1){
socket.emit("put correct background", socket.id) putCorrectBackground()
await delay(1000) await delay(1000)
socket.emit("end game", actualPlayerIndex, room) socket.emit("end game", actualPlayerIndex, room)
return return
@ -968,7 +1001,7 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
touchedPlayer=-1 touchedPlayer=-1
setPlayerTouched(-1) setPlayerTouched(-1)
socket.emit("put correct background", socket.id) putCorrectBackground()
await delay(1000) await delay(1000)
socket.emit("end game", actualPlayerIndex, room) socket.emit("end game", actualPlayerIndex, room)
} }
@ -977,25 +1010,26 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
else{ else{
//@ts-ignore //@ts-ignore
const person = personNetwork?.getPersons().find((p) => p.getId() == params.nodes[0]) //person sélectionnée const personTest = personNetwork?.getPersons().find((p) => p.getId() == params.nodes[0]) //person sélectionnée
if (person != undefined){ const node = nodes.get().find((n: any) => params.nodes[0] == n.id)
if(node == undefined)return;
if (personTest != undefined && !node.label.includes(positionToEmoji(index, true)) && !node.label.includes(positionToEmoji(index, false))){ //si la personne existe et que le noeud n'a pas déjà été cliqué
let index =0 let index =0
let works = true let works = true
const statsTime = elapsedTime; const statsTime = elapsedTime;
for (const i of indices){ for (const i of indices){
const tester = IndiceTesterFactory.Create(i) const tester = IndiceTesterFactory.Create(i)
const test = tester.Works(person) const test = tester.Works(personTest)
//@ts-ignore //@ts-ignore
const node = nodes.get().find((n) => params.nodes[0] == n.id)
if (node!=undefined){ if (node!=undefined){
if (!node.label.includes(positionToEmoji(index, test))){ const nodeNode = nodes.get().find((n: any) => params.nodes[0] == n.id)
networkData.nodes.update({id: params.nodes[0], label: node.label + positionToEmoji(index, test)}) if(nodeNode == undefined)return;
await delay(500) networkData.nodes.update({id: params.nodes[0], label: nodeNode.label + positionToEmoji(index, test)})
if(!test){ await delay(500);
works = false
} }
if (index == indices.length - 1 && works){ index++
}
if (person !== null && person.getId() === params.nodes[0]){
if (user!=null){ if (user!=null){
setWinnerData(user) setWinnerData(user)
setNetworkDataData(networkData) setNetworkDataData(networkData)
@ -1025,21 +1059,24 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
} }
} }
else{
// add stats mastermind
if(user && user.mastermindStats){
manager.userService.addMastermindStats(user.pseudo, cptTour, elapsedTime);
} }
catch(error){
console.log(error);
} }
testFirst = true testFirst = true
setElapsedTime(0) setElapsedTime(0)
endgame = true endgame = true
navigate(`${basePath}/endgame?solo=true&daily=${isDaily}`) navigate(`${basePath}/endgame?solo=true&daily=${isDaily}`)
} }
catch(error){
console.log(error);
} }
navigate(`${basePath}/endgame?solo=true&daily=${isDaily}`)
} }
index++ else{
} addToHistory(personTest.getName() + " n'est pas le coupable !"); //TODO préciser le nombre d'indice qu'il a de juste
addToHistory(person.getName() + " n'est pas le coupable !"); //TODO préciser le nombre d'indice qu'il a de juste
cptTour ++; // On Incrémente le nombre de tour du joueur cptTour ++; // On Incrémente le nombre de tour du joueur
const tour = cptTour+1; const tour = cptTour+1;
addToHistory("<----- [Tour " + tour +"/"+networkData.nodes.length + "] ----->"); addToHistory("<----- [Tour " + tour +"/"+networkData.nodes.length + "] ----->");
@ -1047,6 +1084,7 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
} }
} }
} }
}
// Renvoyer un true pour afficher la choice bar // Renvoyer un true pour afficher la choice bar
else{ else{
// Renvoyer un false pour cacher la choice bar // Renvoyer un false pour cacher la choice bar
@ -1065,12 +1103,6 @@ const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleS
function delay(ms: number): Promise<void> { function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
function putGreyBackgroud(index: number){
/*
*/
}
} }

@ -37,7 +37,7 @@ const PersonStatus: React.FC<PlayerStatusProps> = ({img = Person, state= Person,
img = BotImg img = BotImg
} }
const [buffer, setBuffer] = useState("") const [buffer, setBuffer] = useState('50%')
const [touchedPlayer, setTouchedPlayer] = useState(-2) const [touchedPlayer, setTouchedPlayer] = useState(-2)
useEffect(() =>{ useEffect(() =>{
@ -48,10 +48,10 @@ const PersonStatus: React.FC<PlayerStatusProps> = ({img = Person, state= Person,
useEffect(() => { useEffect(() => {
if (playerIndex===index){ if (playerIndex===index){
setBuffer('solid 3px green') setBuffer('5px')
} }
else{ else{
setBuffer('') setBuffer('50%')
} }
}, [playerIndex]) }, [playerIndex])
@ -70,8 +70,8 @@ const PersonStatus: React.FC<PlayerStatusProps> = ({img = Person, state= Person,
onTouch(); onTouch();
}; };
const circleStyle: React.CSSProperties = { const circleStyle: React.CSSProperties = {
backgroundColor: touchedPlayer == index && showCircle ? 'gold' : positionToColor(index), // Changement de la couleur en fonction de la condition backgroundColor: positionToColor(index), // Changement de la couleur en fonction de la condition
borderRadius: '50%', borderRadius: buffer,
width: '80px', width: '80px',
height: '80px', height: '80px',
display: 'flex', display: 'flex',
@ -83,8 +83,8 @@ const PersonStatus: React.FC<PlayerStatusProps> = ({img = Person, state= Person,
const circleStyleInt: React.CSSProperties = { const circleStyleInt: React.CSSProperties = {
backgroundColor:'white', backgroundColor: touchedPlayer == index && showCircle ? 'lightblue' : 'white',
borderRadius: '50%', borderRadius: buffer,
width: '70px', width: '70px',
height: '70px', height: '70px',
display: 'flex', display: 'flex',
@ -111,8 +111,8 @@ const PersonStatus: React.FC<PlayerStatusProps> = ({img = Person, state= Person,
</div> </div>
{/* </div> */} {/* </div> */}
</div> </div>
<div className='playerNameDisplay' style={{border:buffer}}> <div className='playerNameDisplay'>
<h5>{actualPlayerIndex !== index ? (name.substring(0, name.length - 2).length > 7 ? name.substring(0, name.length - 2).substring(0, 7) + '...' : name) : 'vous'}</h5> <h6>{actualPlayerIndex !== index ? (name.length > 18 ? name.substring(0, 15) + '...' : name) : 'vous'}</h6>
</div> </div>
</div> </div>
); );

@ -5,6 +5,8 @@ import Player from '../model/Player';
import { useTheme } from '../Style/ThemeContext'; import { useTheme } from '../Style/ThemeContext';
import PersonStatus from './PersonStatus'; import PersonStatus from './PersonStatus';
import Person from '../res/img/Person.png' import Person from '../res/img/Person.png'
import BotImg from '../res/img/bot.png'
import { socket } from '../SocketConfig'; import { socket } from '../SocketConfig';
@ -16,13 +18,15 @@ interface PlayerListProps {
setPlayerTouched: (newPlayerTouch: number) => void; setPlayerTouched: (newPlayerTouch: number) => void;
playerIndex: number playerIndex: number
askedWrong: boolean askedWrong: boolean
greyForEveryone: () => void
} }
const PlayerList: React.FC<PlayerListProps> = ({ players, playerTouched, setPlayerTouched, playerIndex, askedWrong}) => { const PlayerList: React.FC<PlayerListProps> = ({ players, playerTouched, setPlayerTouched, playerIndex, askedWrong, greyForEveryone}) => {
const theme = useTheme(); const theme = useTheme();
function askEveryone(){ function askEveryone(){
if (!askedWrong){ if (!askedWrong){
greyForEveryone()
setPlayerTouched(players.length) setPlayerTouched(players.length)
} }
} }
@ -34,12 +38,10 @@ const PlayerList: React.FC<PlayerListProps> = ({ players, playerTouched, setPlay
//@ts-ignore //@ts-ignore
players.map((player, index) => ( players.map((player, index) => (
//player.id!=socket.id && //player.id!=socket.id &&
<PersonStatus img={player.profilePicture} <PersonStatus img={player instanceof Player ? Person : BotImg}
state={Person} state={Person}
key={index} key={index}
name={player.pseudo name={player.pseudo}
+ " " +
positionToEmoji(index, true)}
playerTouched={playerTouched} playerTouched={playerTouched}
setPlayerTouched={setPlayerTouched} setPlayerTouched={setPlayerTouched}
index={index} index={index}

@ -0,0 +1,331 @@
import React, { useEffect, useState } from "react";
import { DataSet, Network} from "vis-network/standalone/esm/vis-network";
import GraphCreator from "../model/Graph/GraphCreator";
import "./GraphContainer.css";
import IndiceTesterFactory from "../model/Factory/IndiceTesterFactory";
import Person from "../model/Person";
import { useNavigate } from "react-router-dom";
import { useGame } from "../Contexts/GameContext";
import { socket } from "../SocketConfig"
import { colorToEmoji, positionToColor, positionToEmoji } from "../ColorHelper";
import { ColorToHexa } from "../model/EnumExtender";
import Bot from "../model/Bot";
import NodePerson from "../model/Graph/NodePerson";
import { useAuth } from "../Contexts/AuthContext";
import Indice from "../model/Indices/Indice";
import Pair from "../model/Pair";
import Player from "../model/Player";
import JSONParser from "../JSONParser";
import User from "../model/User";
import { json } from "body-parser";
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import {basePath} from "../AdressSetup"
import PersonNetwork from "../model/PersonsNetwork";
interface TutorialGraphProps {
setNetwork: (network: Network) => void
showLast: boolean
setPlayerIndex: (playerIndex: number) => void
handleShowTurnBar: (bool: boolean) => void
handleTurnBarTextChange: (text: string) => void
addToHistory: (text: string) => void
setPlayerTouched: (playerIndex: number) => void
playerTouched: number
tutoStep: number
setTutoStep: (step: number) => void
setGreyForEveryone: (func: () => void) => void
displayModalstep: (step: number) => void;
}
let lastNodes: NodePerson[] = []
let firstIndex = true
let first = true
let touchedPlayer = -1
let stepTuto = -1
const TutorialGraph: React.FC<TutorialGraphProps> = ({showLast, setNetwork, setPlayerIndex, handleShowTurnBar, handleTurnBarTextChange, addToHistory, setPlayerTouched, playerTouched, tutoStep, setTutoStep, setGreyForEveryone, displayModalstep}) => {
let cptTour: number = 0
//* Gestion du temps :
let initMtn = 0
const {isLoggedIn, user, manager} = useAuth();
const {setIndiceData, setIndicesData, setActualPlayerIndexData, setWinnerData, setPlayersData, setNetworkDataData, setPersonData} = useGame();
const params = new URLSearchParams(window.location.search);
const navigate = useNavigate();
const [lastIndex, setLastIndex] = useState(-1)
if (first){
first = false
setActualPlayerIndexData(0)
handleTurnBarTextChange("C'est à vous de jouer !")
handleShowTurnBar(true)
}
useEffect(() =>{
touchedPlayer=playerTouched
}, [playerTouched])
useEffect(() => {
stepTuto = tutoStep
if(stepTuto===1){
handleShowTurnBar(true)
}
}, [tutoStep])
useEffect(() => {
const tab: NodePerson[] = []
for(const n of lastNodes.reverse()){
}
lastNodes = tab
if (showLast){
socket.emit("opacity activated", socket.id)
}
else{
socket.emit("opacity deactivated", socket.id)
}
}, [showLast])
let playerIndex: number = 0
if (firstIndex){
firstIndex=false
setPlayerIndex(playerIndex)
}
let index = 0
useEffect(() => {
let jsonGraph = require("../tuto/graph.json")
let jsonIndice = require("../tuto/indices.json")
const personNetwork = JSONParser.JSONToNetwork(JSON.stringify(jsonGraph))
const indices = JSONParser.JSONToIndices(jsonIndice)
setIndiceData(indices[0])
if (personNetwork == null){
return
}
const graph = GraphCreator.CreateGraph(personNetwork)
const nodes = new DataSet(graph.nodesPerson);
setIndicesData(indices)
setPersonData(personNetwork.getPersons()[4])
let n = graph.nodesPerson;
let e = graph.edges;
const graphState = { n, e };
// Sauvegarder l'état dans localStorage
localStorage.setItem('graphState', JSON.stringify(graphState));
const container = document.getElementById('graph-container');
if (!container) {
console.error("Container not found");
return;
}
// Charger les données dans le graph
// Configuration des options du Graphe
const initialOptions = {
layout: {
improvedLayout: true,
hierarchical: {
enabled: false,
direction: 'LR', // LR (Left to Right) ou autre selon votre préférence
sortMethod: 'hubsize'
},
randomSeed: 3
},
physics: {
enabled: true,
barnesHut: {
gravitationalConstant: -1000,
springConstant: 0.001,
springLength: 100
},
solver: "repulsion",
repulsion: {
nodeDistance: 100 // Put more distance between the nodes.
}
}
};
const networkData = { nodes: nodes, edges: graph.edges };
const network = new Network(container, networkData, initialOptions);
network.stabilize();
setNetwork(network)
const myFunctionInsideEffect = () => {
if (stepTuto === 3){
const tabGrey = [0, 1, 2, 3, 5, 6, 7, 8, 9]
for (const i of tabGrey){
nodes.update({id: i, color: "#808080"})
}
}
};
setGreyForEveryone(() => myFunctionInsideEffect);
network.on("click", async (params) => {
if(params.nodes.length > 0){
}
});
network.on("dragging", (params) => {
if (params.nodes.length > 0) {
// Un nœud a été cliqué
initialOptions.physics.enabled = false;
network.setOptions(initialOptions);
setNetwork(network)
}
});
network.on("click", async (params) => {
if(params.nodes.length > 0){
if (stepTuto === 0 && touchedPlayer === 1){
const node = nodes.get().find((n: NodePerson) => n.id === params.nodes[0])
if (node === undefined)return;
if (node.id === 7){
nodes.update({id: node.id, label: node.label + positionToEmoji(1, true)})
handleShowTurnBar(false)
setPlayerIndex(1)
setPlayerTouched(-1)
await delay(500)
const node2 = nodes.get().find((n: NodePerson) => n.id === params.nodes[0])
if (node2 === undefined)return;
nodes.update({id: node.id, label: node2.label + positionToEmoji(2, false)})
await delay(500)
const node3 = nodes.get().find((n: NodePerson) => n.id === 8)
if (node3 === undefined)return;
nodes.update({id: node3.id, label: node3.label + positionToEmoji(1, false)})
setPlayerIndex(2)
await delay(500)
const node4 = nodes.get().find((n: NodePerson) => n.id === 0)
if (node4 === undefined)return;
nodes.update({id: node4.id, label: node4.label + positionToEmoji(1, true)})
setPlayerIndex(0)
setTutoStep(1)
displayModalstep(1);
}
}
else if(stepTuto === 1 && touchedPlayer === 2){
const node = nodes.get().find((n: NodePerson) => n.id === params.nodes[0])
if (node === undefined){
return;
}
if (node.id === 0){
nodes.update({id: node.id, label: node.label + positionToEmoji(2, false)})
setPlayerTouched(-1)
displayModalstep(2);
handleTurnBarTextChange("Mauvais choix, posez un carré !")
const tabGrey = [7, 0, 4, 1, 8]
for (const id of tabGrey){
const node = nodes.get().find((n: NodePerson) => n.id === id)
if (node === undefined)return;
nodes.update({id: node.id, color: "#808080"})
}
setTutoStep(2)
}
}
else if(stepTuto === 2){
const node = nodes.get().find((n: NodePerson) => n.id === params.nodes[0])
if (node === undefined)return;
if (node.id === 9){
const tabColor = [7, 0, 4, 1, 8]
nodes.update({id: node.id, label: node.label + positionToEmoji(0, false)})
for(const id of tabColor){
const pers = personNetwork.getPersons().find((p: Person) => p.getId() === id)
if (pers !== undefined){
nodes.update({id: id, color: ColorToHexa(pers.getColor())})
}
}
handleShowTurnBar(false)
setPlayerIndex(1)
await delay(500)
const node2 = nodes.get().find((n: NodePerson) => n.id === 4)
if (node2 === undefined)return;
nodes.update({id: node2.id, label: node2.label + positionToEmoji(2, true)})
setPlayerIndex(2)
await delay(500)
const node3 = nodes.get().find((n: NodePerson) => n.id === 3)
if (node3 === undefined)return;
nodes.update({id: node3.id, label: node3.label + positionToEmoji(0, false)})
await delay(500)
const node4 = nodes.get().find((n: NodePerson) => n.id === 1)
if (node4 === undefined)return;
nodes.update({id: node4.id, label: node4.label + positionToEmoji(2, false)})
setPlayerIndex(0)
handleTurnBarTextChange("A vous de jouer !")
handleShowTurnBar(true)
setTutoStep(3)
displayModalstep(3);
}
}
else if(stepTuto === 3 && touchedPlayer === 3){
const node = nodes.get().find((n: NodePerson) => n.id === params.nodes[0])
if (node === undefined)return;
if (node.id === 4){
nodes.update({id: node.id, label: node.label + positionToEmoji(0, true)})
setPlayerTouched(-1)
await delay(500)
const node2 = nodes.get().find((n: NodePerson) => n.id === 4)
if (node2 === undefined)return;
nodes.update({id: node2.id, label: node2.label + positionToEmoji(1, true)})
await delay(500)
for(const person of personNetwork.getPersons()){
nodes.update({id: person.getId(), color: ColorToHexa(person.getColor())})
}
if (user != null){
const winner = user;
setNetworkDataData(networkData)
setActualPlayerIndexData(-1)
setLastIndex(-1)
setPlayerTouched(-1)
setWinnerData(winner)
first = true
navigate(`${basePath}/endgame`)
}
}
}
}
else{
setPlayerTouched(-1)
}
});
}, []); // Le tableau vide signifie que cela ne s'exécutera qu'une fois après le premier rendu
return (
<>
<div id="graph-container"/>
</>
);
function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
export default TutorialGraph;

@ -146,15 +146,13 @@ function EndGame() {
<BigButtonNav dest="/play" img={Leave}/> <BigButtonNav dest="/play" img={Leave}/>
</div> */} </div> */}
<div className="losingPlayersContainer"> <div className="losingPlayersContainer">
{losingPlayers.map((player, index) => ( {players.map((player, index) => (
player.id !== winner?.id && (
<div className="playerContainer" key={index}> <div className="playerContainer" key={index}>
{player.id !== winner?.id && (
<div>
<PersonStatus img={Person} state={Person} key={index} name={player.pseudo} playerTouched={1} setPlayerTouched={() => {}} index={index} playerIndex={-2} showCircle={false} askedWrong={false}/> <PersonStatus img={Person} state={Person} key={index} name={player.pseudo} playerTouched={1} setPlayerTouched={() => {}} index={index} playerIndex={-2} showCircle={false} askedWrong={false}/>
{!indicenull && (<h6 className='indiceDisplay'>{indices[players.findIndex((p) => p.id == player?.id)].ToString("fr")}</h6>)} {!indicenull && (<h6 className='indiceDisplay'>{indices[players.findIndex((p) => p.id == player?.id)].ToString("fr")}</h6>)}
</div> </div>
)} )
</div>
))} ))}
</div> </div>
</div> </div>

@ -97,6 +97,28 @@ const InGame = ({locale, changeLocale}) => {
const [showLast, setShowLast] = useState(false) const [showLast, setShowLast] = useState(false)
const [askedWrong, setAskedWrong] = useState(false) const [askedWrong, setAskedWrong] = useState(false)
const [importToPdf, setImportToPdf] = useState(false) const [importToPdf, setImportToPdf] = useState(false)
const [importToJSON, setImportToJSON] = useState(false)
const [putCorrectBackground, setPutCorrectBackground] = useState<() => void>(() => {});
const [putGreyBackgroud, setPutGreyBackground] = useState<() => void>(() => {});
const [putImposssibleGrey, setPutImposssibleGrey] = useState<() => void>(() => {});
const setPutCorrectBackgroundData = (func: () => void) => {
setPutCorrectBackground(func)
}
const setPutGreyBackgroundData = (func: () => void) => {
setPutGreyBackground(func)
}
const setPutImposssibleGreyData = (func: () => void) => {
setPutImposssibleGrey(func)
}
const setImportToJSONData = (imp: boolean) => {
setImportToJSON(imp)
}
const setImportToPdfData = (imp: boolean) => { const setImportToPdfData = (imp: boolean) => {
setImportToPdf(imp) setImportToPdf(imp)
@ -306,7 +328,15 @@ const InGame = ({locale, changeLocale}) => {
askedWrong={askedWrong} askedWrong={askedWrong}
setAskedWrong={setAskedWrongData} setAskedWrong={setAskedWrongData}
importToPdf={importToPdf} importToPdf={importToPdf}
setImportToPdf={setImportToPdfData}/> setImportToPdf={setImportToPdfData}
importToJSON={importToJSON}
setImportToJSON={setImportToJSONData}
setPutCorrectBackground={setPutCorrectBackgroundData}
setPutGreyBackground={setPutGreyBackgroundData}
setPutImposssibleGrey={setPutImposssibleGreyData}
putCorrectBackground={putCorrectBackground}
putGreyBackground={putGreyBackgroud}
putImposssibleGrey={putImposssibleGrey}/>
</div> </div>
@ -442,7 +472,7 @@ const InGame = ({locale, changeLocale}) => {
{ !IsSolo && { !IsSolo &&
<div className='playerlistDiv'> <div className='playerlistDiv'>
<PlayerList players={players} setPlayerTouched={handleSetPlayerTouched} playerTouched={playerTouched} playerIndex={playerIndex} askedWrong={askedWrong}/> <PlayerList players={players} setPlayerTouched={handleSetPlayerTouched} playerTouched={playerTouched} playerIndex={playerIndex} askedWrong={askedWrong} greyForEveryone={() => {}}/>
</div> </div>
} }

@ -66,11 +66,17 @@ function Lobby() {
const room = params.get('room'); const room = params.get('room');
function addBot(){ function addBot(){
socket.emit("lobby joined", room, new EasyBot("botId" + Math.floor(Math.random() * 1000), "Bot" + Math.floor(Math.random() * 100), "").toJson()) let json = require("../res/botNames.json")
const tabNames = [...json.names]
let name = tabNames[Math.floor(Math.random() * tabNames.length)]
while (players.find((p) => p.pseudo === name) != undefined){
name = tabNames[Math.floor(Math.random() * tabNames.length)]
}
socket.emit("lobby joined", room, new EasyBot(name, name, "").toJson())
} }
//* nb Node //* nb Node
const [enteredNumber, setEnteredNumber] = useState(20); const [enteredNumber, setEnteredNumber] = useState(25);
//@ts-ignore //@ts-ignore
const handleNumberChange = (event) => { const handleNumberChange = (event) => {
@ -213,6 +219,7 @@ function Lobby() {
function StartGame(){ function StartGame(){
if (players.length > 2){
const [networkPerson, choosenPerson, choosenIndices] = GameCreator.CreateGame(players.length, enteredNumber) const [networkPerson, choosenPerson, choosenIndices] = GameCreator.CreateGame(players.length, enteredNumber)
setPersonData(choosenPerson) setPersonData(choosenPerson)
setPersonNetworkData(networkPerson) setPersonNetworkData(networkPerson)
@ -225,11 +232,12 @@ function Lobby() {
} }
socket.emit('network created', JSON.stringify(networkPerson, null, 2), JSON.stringify(choosenPerson), JSON.stringify(choosenIndices), room, start); socket.emit('network created', JSON.stringify(networkPerson, null, 2), JSON.stringify(choosenPerson), JSON.stringify(choosenIndices), room, start);
} }
}
const copyGameLink = () => { const copyGameLink = () => {
setShow(!show) setShow(!show)
const gameLink = "http://172.20.10.4:3000/lobby?room="+ room; const gameLink = basePath + "/lobby?room="+ room;
navigator.clipboard.writeText(gameLink) navigator.clipboard.writeText(gameLink)
.then(() => { .then(() => {
console.log('Lien copié avec succès !'); console.log('Lien copié avec succès !');
@ -240,7 +248,7 @@ function Lobby() {
}; };
const textAreaRef = useRef<HTMLTextAreaElement>(null); const textAreaRef = useRef<HTMLTextAreaElement>(null);
const linkToCopy = "http://172.20.10.4:3000/lobby?room="+ room const linkToCopy = basePath + "/lobby?room="+ room
const handleCopyClick = () => { const handleCopyClick = () => {
setShow(!show) setShow(!show)
if(textAreaRef.current != null){ if(textAreaRef.current != null){
@ -345,7 +353,7 @@ function Lobby() {
onChange={handleNumberChange} onChange={handleNumberChange}
min={20} min={20}
max={60}/> max={60}/>
<button className='valuebutton' onClick={() => { if (enteredNumber<60) setEnteredNumber(enteredNumber+1)}} <button className='valuebutton' onClick={() => { if (enteredNumber<50) setEnteredNumber(enteredNumber+1)}}
style={{borderColor:theme.colors.secondary}}> + </button> style={{borderColor:theme.colors.secondary}}> + </button>
</div> </div>
</div> </div>

@ -111,6 +111,10 @@ function NewPlay() {
} }
function launchTuto(){
navigate(`${basePath}/tutorial`);
}
useEffect(() => { useEffect(() => {
const handleLobbyCreated = (newRoom: any) => { const handleLobbyCreated = (newRoom: any) => {
@ -221,7 +225,11 @@ function NewPlay() {
</Overlay> </Overlay>
<button onClick={createLobby} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Créer une partie </button> <button onClick={createLobby} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Créer une partie </button>
<button onClick={launchTuto} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Tutoriel </button>
{/* <button onClick= {() => navigate("/join")} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Rejoindre </button> */}
{/* {goBackRoom != -1 && <button onClick={goBack} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}>Retourner à la partie</button>} */}
<button onClick={goBack} className="ButtonNavRejoin" style={{ display:returnVisibility}}>Retourner à la partie</button> <button onClick={goBack} className="ButtonNavRejoin" style={{ display:returnVisibility}}>Retourner à la partie</button>
</div> </div>
{/* Lobbies */} {/* Lobbies */}

@ -0,0 +1,757 @@
import React, { useState, useEffect } from 'react';
import Switch from "react-switch";
import {saveAs} from "file-saver"
/* Style */
import "./InGame.css"
import {useTheme} from '../Style/ThemeContext'
/* Component */
import GraphContainer from '../Components/GraphContainer';
import PlayerList from '../Components/PlayerList';
import TurnBar from '../Components/TurnBar';
/* Icon */
import Param from "../res/icon/param.png";
import Info from "../res/icon/infoGreen.png";
import Check from "../res/icon/checkboxGreen.png";
import MGlass from "../res/icon/magnifying-glass.png";
import Reset from "../res/icon/reset.png";
import Oeye from "../res/icon/eye.png";
import Ceye from "../res/icon/hidden.png";
import ImgBot from "../res/img/bot.png";
import ava from "../res/img/tuto/tuto-ava.png";
import indicetxt from "../res/img/tuto/tuto-indiceTxt.png"
import rep from "../res/img/tuto/tuto-rep.png";
import joueurs from "../res/img/tuto/tuto-joueurs.png";
import graph from "../res/img/tuto/tuto-graph.png";
import step1 from "../res/img/tuto/tuto2-1.png";
import step2 from "../res/img/tuto/tuto2-2.png";
import step3 from "../res/img/tuto/tuto2-3.png";
import step4 from "../res/img/tuto/tuto2-4.png";
import step5 from "../res/img/tuto/tuto2-5.png";
import step6 from "../res/img/tuto/tuto2-6.png";
/* nav */
import { Link, Navigate, useNavigate, useNavigationType } from 'react-router-dom';
/* Boostrap */
import Offcanvas from 'react-bootstrap/Offcanvas';
import Modal from 'react-bootstrap/Modal';
import Card from 'react-bootstrap/Card';
import Button from 'react-bootstrap/Button';
/* Model */
import Stub from '../model/Stub';
import { useGame } from '../Contexts/GameContext';
import { socket } from '../SocketConfig';
import { Network } from 'vis-network';
import {generateLatexCode, generateLatexCodeEnigme} from '../Script/LatexScript';
import Pair from '../model/Pair';
import Indice from '../model/Indices/Indice';
import {basePath} from "../AdressSetup"
import TutorialGraph from '../Components/TutorialGraph';
import { useAuth } from '../Contexts/AuthContext';
import EasyBot from '../model/EasyBot';
import { set } from 'lodash';
let cptNavigation = 0
//@ts-ignore
const Tutorial = ({locale, changeLocale}) => {
const {personNetwork, person, indices, players, setPlayersData, indice, actualPlayerIndex} = useGame()
const {user} = useAuth()
const theme = useTheme();
const navigate = useNavigate()
const [greyForEveryone, setGreyForEveryone] = useState<() => void>(() => {});
const setGreyForEveryoneData = (func: () => void) => {
setGreyForEveryone(func)
}
const navigationType = useNavigationType()
cptNavigation++
if (cptNavigation % 2 == 0){
if (navigationType.toString() == "POP"){
socket.emit("player quit")
navigate(`${basePath}/`)
}
}
//* Historique
const [history, setHistory] = useState<string[]>([]);
const [showLast, setShowLast] = useState(false)
const [askedWrong, setAskedWrong] = useState(false)
const [importToPdf, setImportToPdf] = useState(false)
const [importToJSON, setImportToJSON] = useState(false)
const [tutoStep, setTutoStep] = useState(0)
const setTutoStepData = (step: number) => {
setTutoStep(step)
}
const setImportToJSONData = (imp: boolean) => {
setImportToJSON(imp)
}
const setImportToPdfData = (imp: boolean) => {
setImportToPdf(imp)
}
// Fonction pour ajouter un élément à l'historique
const addToHistory = (message: string) => {
setHistory(prevHistory => [...prevHistory, message]);
};
const setShowLastData = () =>{
setLastVisible(!showLast);
setShowLast(!showLast);
}
const setAskedWrongData = (askedWrong: boolean) => {
setAskedWrong(askedWrong)
}
useEffect(() => {
const historyContainer = document.getElementById('history-container');
if (historyContainer) {
historyContainer.scrollTop = historyContainer.scrollHeight;
}
}, [history]);
useEffect(() => {
if (user == null){
return
}
setPlayersData([user, new EasyBot("Scooby-Doo", "Scooby-Doo", ImgBot), new EasyBot("Batman", "Batman", ImgBot)])
}, [])
const [showChoiceBar, setShowChoiceBar] = useState(false);
const [showTurnBar, setShowTurnBar] = useState(false);
const [turnBarText, setTurnBarText] = useState("");
const [playerTouched, setPlayerTouched] = useState(-2)
const [playerIndex, setPlayerIndex] = useState(-2)
const [network, setNetwork] = useState<Network | null>(null)
const [networkEnigme, setNetworkEnigme] = useState<Map<number, Pair<Indice, boolean>[]> | null>(null)
const setNetworkData = (network: Network) => {
setNetwork(network)
}
const setNetworkEnigmeData = (networkEnigme: Map<number, Pair<Indice, boolean>[]>) => {
setNetworkEnigme(networkEnigme)
}
const handleNodeClick = (shouldShowChoiceBar: boolean) => {
setShowChoiceBar(shouldShowChoiceBar);
};
const handleSetPlayerTouched = (newPlayerTouched: number) => {
setPlayerTouched(newPlayerTouched);
};
const handleShowTurnBar = (shouldShowTurnBar: boolean) => {
setShowTurnBar(shouldShowTurnBar);
};
const handleTurnBarTextChange = (newTurnBarText: string) =>{
setTurnBarText(newTurnBarText)
}
const setPlayerIndexData = (playerIndex: number) => {
setPlayerIndex(playerIndex)
}
const resetGraph = () => {
setisLoading(true);
socket.emit("reset graph", socket.id)
setTimeout(() => {
setisLoading(false);
}, 2000);
}
/* offcanvas */
//? faire une fonction pour close et show en fonction de l'etat du canva ?
//? comment faire pour eviter la recopie de tout le code a chaque canvas boostrap ?
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const [showP, setShowP] = useState(false);
const handleCloseP = () => setShowP(false);
const handleShowP = () => setShowP(true);
const [showS, setShowS] = useState(false);
const handleCloseS = () => setShowS(false);
const handleShowS = () => setShowS(true);
const [cptTour, setcptTour] = useState(0);
const [LastVisible, setLastVisible] = useState(false);
const [isLoading, setisLoading] = useState(false);
//@ts-ignore
const changecptTour = (newcptTour) => {
setcptTour(newcptTour);
};
const handleChange = () => {
if (show){
handleClose()
}
else {
handleShow()
}
};
const handleChangeS = () => {
if (showS){
handleCloseS()
}
else {
handleShowS()
}
};
const eye = LastVisible ? Oeye : Ceye; //icon que l'on affiche pour l'oeil : fermé ou ouvert.
/* Windows open */
//@ts-ignore
const openInNewTab = (url) => { //! avec url ==> dangereux
window.open(url);
};
const [SwitchEnabled, setSwitchEnabled] = useState(false)
const allIndices = Stub.GenerateIndice()
const nbPlayer = players.length;
const navdeduc = 'deduc?actualId=' + actualPlayerIndex + '&nbPlayer=' + nbPlayer;
//* gestion demo */
const [showM, setShowM] = useState(false);
const [showTuto2, setShowTuto2] = useState(false);
const [showTuto21, setShowTuto21] = useState(false);
const [showTuto22, setShowTuto22] = useState(false);
const [showTuto3, setShowTuto3] = useState(false);
const handleCloseM = () => {
setShowM(false);
handleShowHelp()
}
const handleShowM = () => setShowM(true);
const handleCloseHelp = () => {
switch(tutoStep){
case 0:
setShowTuto2(false);
break;
case 1:
setShowTuto21(false);
break;
case 2:
setShowTuto22(false);
break;
case 3:
setShowTuto3(false);
break;
}
}
const handleShowHelp = () => {
switch(tutoStep){
case 0:
setShowTuto2(true);
break;
case 1:
setShowTuto21(true);
break;
case 2:
setShowTuto22(true);
break;
case 3:
setShowTuto3(true);
break;
}
}
const handleCloseTuto2 = () => setShowTuto2(false);
const handleShowTuto2 = () => setShowTuto2(true);
const handleCloseTuto21 = () => setShowTuto21(false);
const handleShowTuto21 = () => setShowTuto21(true);
const handleCloseTuto22 = () => setShowTuto22(false);
const handleShowTuto22 = () => setShowTuto22(true);
const handleCloseTuto3 = () => setShowTuto3(false);
const handleShowTuto3 = () => setShowTuto3(true);
const [step, setStep] = useState(-1);
//@ts-ignore
const displayModalstep = (step: number) => {
//* for step 2
setStep(0); // remise a 0 de step
switch(step) {
case 1:
setShowTuto21(true);
break;
case 2:
setShowTuto22(true);
break;
case 3:
setShowTuto3(true);
break;
}
}
useEffect(() => {
handleShowM();
}, [])
return (
<div id="mainDiv">
{showTurnBar && <TurnBar text={turnBarText}/>}
<div id='graphDiv'>
<TutorialGraph tutoStep={tutoStep}
setTutoStep={setTutoStepData}
handleShowTurnBar={handleShowTurnBar}
handleTurnBarTextChange={handleTurnBarTextChange}
addToHistory={addToHistory}
setPlayerTouched={handleSetPlayerTouched}
playerTouched={playerTouched}
setNetwork={setNetworkData}
showLast={showLast}
setPlayerIndex={setPlayerIndexData}
setGreyForEveryone={setGreyForEveryoneData}
displayModalstep={displayModalstep}/>
</div>
<div className='historique' id="history-container">
{history.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
<div className='menuGame'>
{/* <Button variant="primary" onClick={handleShowM}>
tuto 1
</Button>
<Button variant="primary" onClick={handleShowTuto2} disabled={tuto1disable}>
tuto 2
</Button> */}
<Button variant="primary" onClick={handleShowHelp}>
Aide
</Button>
<Link to={`${basePath}/info`} target='_blank'>
<button className='button'
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary
}}>
<img src={Info} alt="info" height="40"/>
</button>
</Link>
<button className='button' onClick={handleChange}
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary
}}>
<img src={MGlass} alt="indice" height="40"/>
</button>
<button className='button' onClick={setShowLastData}
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary
}}>
<img src={ eye } alt="indice" height="40"/>
</button>
</div>
<div className='playerlistDiv'>
<PlayerList players={players} setPlayerTouched={handleSetPlayerTouched} playerTouched={playerTouched} playerIndex={playerIndex} askedWrong={askedWrong} greyForEveryone={greyForEveryone}/>
</div>
<Offcanvas show={show}
onHide={handleClose}
placement='end'
scroll={true}
backdrop={false}
style={{ height: '20%', width: '25%', top: '60vh' }}>
<Offcanvas.Header closeButton>
<Offcanvas.Title>Indice</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
{indice?.ToString(locale)}
</Offcanvas.Body>
</Offcanvas>
<Modal
show={showM}
onHide={handleCloseM}
backdrop="static"
keyboard={false}
size="lg"
style={{ maxHeight: '100vh'}}>
<Modal.Header>
<Modal.Title>Tutoriel</Modal.Title>
</Modal.Header>
<Modal.Body style={{ display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
{step === -1 && (
<Card style={{ width: '90%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={ava} style={{width:'300px', height:'300px'}}/>
<Card.Body>
<Card.Title> Bienvenue dans SocialGraph</Card.Title>
<Card.Text>
Vous incarnez un détective assoiffé de gloire, confronté à un crime. <br/>
Cependant, d'autres enquêteurs sont également sur le coup, tous cherchant à décrocher le titre de meilleur détective du monde. <br/>
Chacun possède un indice crucial pour identifier le coupable, il va falloir déduire l'indice de vos concurrents si vous souhaitez l'emporter ! <br/>
Interrogez vos concurrents pour obtenir des réponses par oui ou non, mais méfiez-vous, un refus a des conséquences. <br/>
Soyez le premier à déduire les indices des autres et à trouver le coupable pour remporter la reconnaissance tant convoitée. <br />
<i>Si vous avez le moindre doute, cliquer sur le bouton "aide" pour afficher l'étape actuel du tuto</i>
</Card.Text>
</Card.Body>
</Card>
)}
{step === 0 && (
<Card style={{ width: '90%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={ava} style={{width:'300px', height:'300px'}}/>
<Card.Body>
<Card.Title>Les Suspects</Card.Title>
<Card.Text>
Voici comment est représenté un suspect, chaque suspect possède différentes caractéristiques, que ce soit leur nom, âge, sport et leur couleur de cheveux.
<br />
Par exemple, ici, nous avons <b>Ava</b>, qui a <b>40 ans</b>, qui pratique du <b>Basket</b> et du <b>Tennis</b>, qui a les cheveux <b>Roux</b> et qui possède <b>2 amis</b>.
</Card.Text>
</Card.Body>
</Card>
)}
{step === 1 && (
<Card style={{ width: '90%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={indicetxt} style={{width:'300px', height:'auto'}}/>
<Card.Body>
<Card.Title>Les indices</Card.Title>
<Card.Text>
Dans ce jeu, chaque détective possède un indice, qui permet d'identifier une caractéristique du coupable, votre indice est le suivant :
<br />
"<u>Le suspect a entre 20 et 29 ans</u>".
</Card.Text>
</Card.Body>
</Card>
)}
{step === 3 && (
<Card style={{ width: '90%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={joueurs} style={{width:'300px', height:'300px'}}/>
<Card.Body>
<Card.Title>Les Détectives</Card.Title>
<Card.Text>
Il est possible de voir les détectives sur le côté gauche de l'écran, ils sont représentés par des cercles de couleurs différentes. Le contour carré signifie que ce détective est en pleine réflexion.
<br />
Pour interroger un détective à propos d'un suspect, il suffit de le sélectionner, puis de cliquer sur le suspect que vous souhaitez. Il vous répondra donc ce qu'il pense de ce suspect selon son indice.
</Card.Text>
</Card.Body>
</Card>
)}
{step === 4 && (
<Card style={{ width: '90%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={rep} style={{width:'300px', height:'300px'}}/>
<Card.Body>
<Card.Title>Les réponses</Card.Title>
<Card.Text>
Les détéctives vous répondrons que par des ronds ou des carrés de leur couleur.
<br />
<ul>
<li>
Un <u>carré</u> signifie que son indice innocente le suspect.
</li>
<li>
Un <u>rond</u> signifie que son indice peut incréminer le suspect.
</li>
</ul>
Par exemple, ici :<br />
l'indice du détéctive Scooby-Doo (<u>Vert</u>) permet d'innocenter Logan.
<br/>Eleanor peut être suspectée par l'indice du détective Batman (<u>Jaune</u>).
<br/>Evelyn est innocentée par l'indice de <u>3 détéctive différents</u>.
</Card.Text>
</Card.Body>
</Card>
)}
{step === 5 && (
<Card style={{ width: '90%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Header>
<button className='button'
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary
}}>
<img src={Info} alt="info" height="40"/>
</button>
</Card.Header>
<Card.Body>
<Card.Title>Les règles du jeu</Card.Title>
<Card.Text>
Ce bouton vous mène à la page d'<b>information du jeu</b>, avec toutes les règles du jeu, que ce soit les objectifs, les indices, le déroulement, etc.
</Card.Text>
</Card.Body>
</Card>
)}
{step === 2 && (
<Card style={{ width: '90%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Header>
<button className='button'
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary
}}>
<img src={MGlass} alt="info" height="40"/>
</button>
</Card.Header>
<Card.Body>
<Card.Title>L'indice</Card.Title>
<Card.Text>
Ce bouton vous permet d'afficher votre indice personnel, gradez le secret ! Il s'agit de votre meilleur atout pour gagner.
</Card.Text>
</Card.Body>
</Card>
)}
{step === 6 && (
<Card style={{ width: '90%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={graph} style={{width:'auto', height:'300px'}}/>
<Card.Body>
<Card.Title>Place à la pratique !</Card.Title>
<Card.Text>
Bien joué ! Vous avez maintenanttoutes les bases d'un veritable détéctive.
<br/>
Vous allez à présent avoir un exercice pratique pour la résolution d'une enquête, au côté de ces très chère Batman et Scooby-Doo.
<br/>
Cliquer sur "Poursuivre" pour commencer votre première partie.
</Card.Text>
</Card.Body>
</Card>
)}
</Modal.Body>
<Modal.Footer style={{display:'flex'}}>
<div style={{ width:'100%', display:'flex', justifyContent:'start'}}>
<label style={{ color:'gray'}}>Étape {step+1}/7</label>
</div>
{/* <Button variant="secondary" onClick={handleCloseM}>
Fermer
</Button> */}
{ step != 0 && (<Button variant="primary" onClick={() => setStep(step - 1)}>Précédent</Button>)}
{ step === 6 ? (<Button variant="primary" onClick={handleCloseM}>Poursuivre</Button>) :
<Button variant="primary" onClick={() => setStep(step + 1)}>Suivant</Button>}
</Modal.Footer>
</Modal>
<Modal
show={showTuto2}
onHide={handleCloseTuto2}
backdrop="static"
keyboard={false}
size="lg"
style={{ maxHeight: '100vh'}}>
<Modal.Header>
<Modal.Title>Tutoriel</Modal.Title>
</Modal.Header>
<Modal.Body style={{ display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card style={{ width: '100%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={step1} style={{width:'300px', height:'auto'}}/>
<Card.Body>
<Card.Title>Premier pas</Card.Title>
<Card.Text>
Bienvenue dans cette seconde partie, où nous allons apprendre le déroulé d'une veritable enquête.
<br/>
Dans un premier temps, sélectionnez le joueur <b>Scooby-Doo</b> et questionnez le à propos du suspect nommé <b>Violet</b> en cliquant sur cette dernière.
</Card.Text>
</Card.Body>
</Card>
</Modal.Body>
<Modal.Footer style={{display:'flex'}}>
<Button variant="primary" onClick={handleCloseTuto2}>Compris !</Button>
</Modal.Footer>
</Modal>
<Modal
show={showTuto21}
onHide={handleCloseTuto21}
backdrop="static"
keyboard={false}
size="lg"
style={{ maxHeight: '100vh'}}>
<Modal.Header>
<Modal.Title>Tutoriel</Modal.Title>
</Modal.Header>
<Modal.Body style={{ display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
{step === 0 && (
<Card style={{ width: '100%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={step2} style={{width:'300px', height:'auto'}}/>
<Card.Body>
<Card.Title>Votre premier tour</Card.Title>
<Card.Text>
Super, <u>Violet a été identifié par l'indice de Scooby-Doo</u>, c'est une information essentielle ! Cependant, cela ne signifie <i>pas forcément</i> qu'elle est coupable.
<br/>
C'est à présent le tour aux autres joueurs de jouer, regardons ce qu'ils ont fait.
</Card.Text>
</Card.Body>
</Card>
)}
{step === 1 && (
<Card style={{ width: '100%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={step3} style={{width:'200px', height:'auto'}}/>
<Card.Body>
<Card.Title>Premier tour des autres joueurs</Card.Title>
<Card.Text>
Il semblerait que Scooby-Doo ait lui aussi interrogé Batman à propos de Violet, et que ce dernier ait répondu <b>non</b> par un carré.
Cela signifie que Violet n'est pas coupable, et qu'elle est donc innocente !
<br/>
Scooby-Doo a donc fait une erreur, en questionnant quelqu'un pouvant innocenter Violet. En guise de <b>punition</b>, il doit, lui aussi, poser un carré sur un autre joueur, révélant aussi plus d'information sur son indice.
Nous savons donc maintenant que l'indice de Scooby-Doo ne permet pas d'identifier Sebastian.
<br/>
Ensuite, Batman a questionné Scooby-Doo à propos de Charlotte, qui est identifié par l'indice de Scooby-Doo.
</Card.Text>
</Card.Body>
</Card>
)}
{step === 2 && (
<Card style={{ width: '100%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={step4} style={{width:'300px', height:'auto'}}/>
<Card.Body>
<Card.Title>Second tour</Card.Title>
<Card.Text>
Vous remarquez que <u>votre indice identifie lui aussi Charlotte</u>, et si nous demandions à Batman, si ce dernier pense que Charlotte est la coupable ?
<br/>
Cela nous permettrait donc de mettre fin à la partie !
</Card.Text>
</Card.Body>
</Card>
)}
</Modal.Body>
<Modal.Footer style={{display:'flex'}}>
<div style={{ width:'100%', display:'flex', justifyContent:'start'}}>
<label style={{ color:'gray'}}>Étape {step+1}/3</label>
</div>
{ step != 0 && (<Button variant="primary" onClick={() => setStep(step - 1)}>Précédent</Button>)}
{ step === 2 ? (<Button variant="primary" onClick={handleCloseTuto21}>Fermer</Button>) :
<Button variant="primary" onClick={() => setStep(step + 1)}>Suivant</Button>}
</Modal.Footer>
</Modal>
<Modal
show={showTuto22}
onHide={handleCloseTuto22}
backdrop="static"
keyboard={false}
size="lg"
style={{ maxHeight: '100vh'}}>
<Modal.Header>
<Modal.Title>Tutoriel</Modal.Title>
</Modal.Header>
<Modal.Body style={{ display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card style={{ width: '100%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={step5} style={{width:'300px', height:'auto'}}/>
<Card.Body>
<Card.Title>La punition</Card.Title>
<Card.Text>
Mince, il semblerait que l'indice de Batman innocente Charlotte, et que vous avez donc commit une erreur, la <b>punition</b> s'applique !
<br/>
Vous devez donc poser un <u>carré sur un autre joueur</u>, révélant ainsi plus d'information sur votre indice.
<br/>
Mais rien n'est joué ! Posons notre carré sur <b>Liam</b> pour cela, sélectionnez directement le suspect désiré.
</Card.Text>
</Card.Body>
</Card>
</Modal.Body>
<Modal.Footer style={{display:'flex'}}>
<Button variant="primary" onClick={handleCloseTuto22}>Compris !</Button>
</Modal.Footer>
</Modal>
<Modal
show={showTuto3}
onHide={handleCloseTuto3}
backdrop="static"
keyboard={false}
size="lg"
style={{ maxHeight: '100vh'}}>
<Modal.Header>
<Modal.Title>End Game</Modal.Title>
</Modal.Header>
<Modal.Body style={{ display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card style={{ width: '100%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
<Card.Img variant="top" src={step6} style={{width:'250px', height:'auto'}}/>
<Card.Body>
<Card.Title>La fin du jeu</Card.Title>
<Card.Text>
Ce tour est lui aussi riche en informations !
<br/>
Vous avez à présent assez d'information pour deviner les indices des autres :
<ul>
<li>
Scooby-Doo semble avoir : <i>{indices[1]?.ToString(locale)}</i>.
</li>
<li>
Batman semble avoir : <i>{indices[2]?.ToString(locale)}</i>.
</li>
<li>
Et votre indice est : <i>{indices[0]?.ToString(locale)}</i>.
</li>
</ul>
Vous avez à présent toutes les cartes en main pour deviner qui est le coupable, cliquer sur le bouton <b>Ask Everyone</b>, puis séléctionné un suspect pour émettre une <b>accusation</b> pour deviner, bonne chance !
</Card.Text>
</Card.Body>
</Card>
</Modal.Body>
<Modal.Footer style={{display:'flex'}}>
<Button variant="primary" onClick={handleCloseTuto3}>Compris !</Button>
</Modal.Footer>
</Modal>
</div>
);
};
export default Tutorial;

@ -2,6 +2,7 @@ class Edge{
public from: number public from: number
public to: number public to: number
public color: string = "black"
constructor(from: number, to: number){ constructor(from: number, to: number){
this.from = from this.from = from

@ -0,0 +1,22 @@
{
"names": [
"Sherlock Holmes",
"Inspecteur Gadget",
"Scooby-Doo",
"Marple",
"Spocky",
"Phoenix",
"Batman",
"James Bond",
"Sherlockio",
"Verra",
"Hercule Poirot",
"Colombo",
"MacGyver",
"Lupin",
"D. Conan",
"Pr. Layton",
"Dr. Watson"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

@ -0,0 +1 @@
{"persons":[{"id":0,"name":"Charlotte","age":25,"color":2,"sports":[4],"friends":[{"id":4,"name":"Sophia","age":20,"color":4,"sports":[1,3]},{"id":3,"name":"Aurora","age":19,"color":0,"sports":[4,2]},{"id":7,"name":"Violet","age":24,"color":2,"sports":[3]},{"id":5,"name":"Alice","age":68,"color":3,"sports":[0]}]},{"id":1,"name":"Carter","age":20,"color":1,"sports":[1,0],"friends":[{"id":4,"name":"Sophia","age":20,"color":4,"sports":[1,3]},{"id":2,"name":"Ava","age":40,"color":3,"sports":[2,3]}]},{"id":2,"name":"Ava","age":40,"color":3,"sports":[2,3],"friends":[{"id":1,"name":"Carter","age":20,"color":1,"sports":[1,0]},{"id":9,"name":"Liam","age":19,"color":4,"sports":[0]}]},{"id":3,"name":"Aurora","age":19,"color":0,"sports":[4,2],"friends":[{"id":4,"name":"Sophia","age":20,"color":4,"sports":[1,3]},{"id":0,"name":"Charlotte","age":25,"color":2,"sports":[4]},{"id":8,"name":"Sebastian","age":13,"color":0,"sports":[1]},{"id":9,"name":"Liam","age":19,"color":4,"sports":[0]},{"id":6,"name":"Benjamin","age":52,"color":1,"sports":[0,2,4]}]},{"id":4,"name":"Sophia","age":20,"color":4,"sports":[1,3],"friends":[{"id":3,"name":"Aurora","age":19,"color":0,"sports":[4,2]},{"id":0,"name":"Charlotte","age":25,"color":2,"sports":[4]},{"id":1,"name":"Carter","age":20,"color":1,"sports":[1,0]}]},{"id":5,"name":"Alice","age":68,"color":3,"sports":[0],"friends":[{"id":0,"name":"Charlotte","age":25,"color":2,"sports":[4]},{"id":6,"name":"Benjamin","age":52,"color":1,"sports":[0,2,4]}]},{"id":6,"name":"Benjamin","age":52,"color":1,"sports":[0,2,4],"friends":[{"id":3,"name":"Aurora","age":19,"color":0,"sports":[4,2]},{"id":5,"name":"Alice","age":68,"color":3,"sports":[0]}]},{"id":7,"name":"Violet","age":24,"color":2,"sports":[3],"friends":[{"id":0,"name":"Charlotte","age":25,"color":2,"sports":[4]}]},{"id":8,"name":"Sebastian","age":13,"color":0,"sports":[1],"friends":[{"id":3,"name":"Aurora","age":19,"color":0,"sports":[4,2]}]},{"id":9,"name":"Liam","age":19,"color":4,"sports":[0],"friends":[{"id":2,"name":"Ava","age":40,"color":3,"sports":[2,3]},{"id":3,"name":"Aurora","age":19,"color":0,"sports":[4,2]}]}]}

@ -0,0 +1 @@
"[{\"type\":\"AgeIndice\",\"id\":5,\"minimum\":20,\"maximum\":29},{\"type\":\"SportIndice\",\"id\":24,\"sports\":[2,3]},{\"type\":\"ColorEdgesIndice\",\"id\":27,\"neighborsColors\":[0]}]"
Loading…
Cancel
Save