// CHANGES BY BORRAGEIROS // // -Momento en el que se cambia al mapa grande, changeToBigMapOn variable // -Momento en el que se cambia al mapa pequeño, changeToClassicMapOn variable // // -Traducciones // USO: // translate('example', { exampleVariable: "texto de ejemplo" }), // translate('exampleWithOutVariable'), function translate(key, args) { try { var str = key.split('.').reduce((obj, i) => obj[i], translations); if (!args) return str; return str.replace(/\${(.*?)}/g, (match, key) => args[key.trim()]); } catch (error) { console.error("Translation error:", error); return ""; } } var translations = { example: `Esto es un ejemplo de uso. Ejemplo de variable: \${exampleVariable}`, exampleWithOutVariable: `Esto es un ejemplo de uso sin variable.`, /////////////// wellcome: `👋 ¡Bienvenido \${name}! \n¡Escribe "t" antes de tu mensaje para usar el chat de equipo y "@@" seguido del nombre de un jugador para enviarle un mensaje privado!`, commands: { help: `Este comando muestra todos los comandos disponibles. También puede mostrar la descripción de un comando en particular. Ejemplo: '!help bb' mostrará la descripción del comando '!bb'.`, claim: "reclamar", afk: `Este comando te pone en modo AFK. Tiene restricciones: un mínimo de 1 minuto de tiempo AFK, un máximo de 5 minutos y un tiempo de enfriamiento de 10 minutos.`, afks: "Este comando muestra todos los jugadores que están AFK.", bb: "Este comando te hace salir instantáneamente (uso recomendado).", me: "Este comando muestra tus estadísticas globales en la sala.", rename: "Este comando te permite cambiar tu nombre para el marcador.", games: "Este comando muestra a los 5 mejores jugadores con más partidas en la sala.", wins: "Este comando muestra a los 5 mejores jugadores con más victorias en la sala.", goals: "Este comando muestra a los 5 mejores jugadores con más goles en la sala.", assists: "Este comando muestra a los 5 mejores jugadores con más asistencias en la sala.", cs: "Este comando muestra a los 5 mejores jugadores con más CS en la sala.", playtime: "Este comando muestra a los 5 mejores jugadores con más tiempo jugado en la sala.", training: "Este comando carga el estadio de entrenamiento clásico.", classic: "Este comando carga el estadio clásico.", big: "Este comando carga el estadio grande.", rr: "Este comando reinicia el juego.", rrs: "Este comando intercambia los equipos y reinicia el juego.", swap: "Este comando intercambia los equipos cuando el juego está detenido.", kickred: "Este comando expulsa a todos los jugadores del equipo rojo, incluido el jugador que introdujo el comando. Puedes proporcionar como argumento la razón de la expulsión.", kickblue: "Este comando expulsa a todos los jugadores del equipo azul, incluido el jugador que introdujo el comando. Puedes proporcionar como argumento la razón de la expulsión.", kickspec: "Este comando expulsa a todos los jugadores del equipo de espectadores, incluido el jugador que introdujo el comando. Puedes proporcionar como argumento la razón de la expulsión.", mute: `Este comando permite silenciar a un jugador. No podrá hablar durante una cierta duración y puede ser desmutado en cualquier momento por los administradores. Toma 2 argumentos: Argumento 1: # donde es el id del jugador objetivo. Esto no funcionará si el jugador es un administrador. Argumento 2 (opcional): donde es la duración del mute en minutos. Si no se proporciona ningún valor, el mute dura la duración predeterminada, 5 minutos. Ejemplo: '!mute #3 20' silenciará al jugador con id 3 durante 20 minutos.`, unmute: `Este comando permite desmutear a alguien. Toma 1 argumento: Argumento 1: # donde es el id del jugador silenciado. O Argumento 1: donde es el número asociado con el mute dado por el comando 'muteList'. Ejemplo: '!unmute #300' desmuteará al jugador con id 300, '!unmute 8' desmuteará al jugador n°8 según el comando 'muteList'.`, mutes: "Este comando muestra la lista de jugadores silenciados.", clearbans: `Este comando desbanea a todos. También puede desbanear a un jugador en particular, añadiendo su ID como argumento.`, bans: "Este comando muestra a todos los jugadores que fueron baneados y sus IDs.", admins: "Este comando muestra a todos los jugadores que son administradores permanentes.", setadmin: `Este comando permite convertir a alguien en administrador. Podrá conectarse como administrador y puede ser eliminado en cualquier momento por los maestros. Toma 1 argumento: Argumento 1: # donde es el id del jugador objetivo. Ejemplo: '!setadmin #3' dará administrador al jugador con id 3.`, removeadmin: `Este comando permite quitar a alguien como administrador. Toma 1 argumento: Argumento 1: # donde es el id del jugador objetivo. O Argumento 1: donde es el número asociado con el administrador dado por el comando 'admins'. Ejemplo: '!removeadmin #300' quitará el administrador al jugador con id 300, '!removeadmin 2' quitará el administrador n°2 según el comando 'admins'.`, password: `Este comando permite agregar una contraseña a la sala. Toma 1 argumento: Argumento 1: donde es la contraseña que deseas para la sala. Para eliminar la contraseña de la sala, simplemente introduce '!password'.`, }, unmute: `Estás desmuteado.`, invalidPlayer: `Jugador inválido, asegurate de que el nombre es correcto.`, pmYourself: `No puedes enviarte un mensaje privado a ti mismo`, with: `con`, commandsInfo: `\nPara informarte sobre un comando, escribe '!help '`, command: `comando`, invalidCommand: `El comando que deseas comprobar no existe, Para ver los comandos existentes, escribe \'!help\'`, changeUsername: `Te has cambiado el nombre \${name}`, haventPlayed: `Todavía no has jugado en esta sala!`, minAfk: `Hay un mínimo de \${min} minutos de AFK. No abuses del comando!`, noAfk: `🌅 \${player} ya no está AFK!`, everyAfk: `Solo puedes ir AFK cada \${min} minutos. No abuses del comando!`, nowAfk: `😴 \${player} está AFK!`, cantAfk: `No puedes ir AFK si estás en un equipo!`, nobodyInList: `📢 No hay nadie en la lista \${list}`, listAfk: `😴 Lista AFK: `, listBan: `📢 Lista baneados: `, listAdmin: `📢 Lista admins: `, nowMaster: `\${player} es ahora un room master!`, alreadyMaster: `Ya eres un room master!`, teamsSwapped: `✔️ Equipos swapeados`, stopBefore: `Por favor, para el juego antes.`, teamKick: `Equipo kickeado por \${player}`, stadiumNotFound: `No se encontró el estadio`, haveBeenMuted: `\${player} ha sido silenciado por \${mins} minutos`, haveBeenUnmuted: `\${player} ha sido desmutado!`, isntMuted: `Este jugador no está muteado!`, youCantMuteAdmin: `No puedes silenciar a un administrador`, muteNotFound: `No se encontró el jugador con el ID proporcionado. Escribe '!help mute para más información'`, invalidFormat: `Formato inválido. Escribe '!help mute para más información'`, wrongArgsNumber: `Número de argumentos incorrecto. Escribe '!help mute' para más información`, emptyMuteList: `🔇 No hay nadie en la lista de muteados.`, muteList: `🔇 Lista de muteados: `, bansCleared: `✔️ Baneos limpiados`, unbannedFromRoom: `✔️ \${player} ha sido desbaneado de la sala!`, clearBanIdNotFound: `El ID que escribiste no parece tener un ban asociado. Escribe '!help clearbans' para más información`, // AQUI FUE LA ÚLTIMA TRADUCCIÓN, HAY QUE RETOMARLAS masterConected: `El master \${player} se ha conectado a la sala!`, adminConected: `El administrador \${player} se ha conectado a la sala!`, firstGoalWins: `⚽ EL QUE META GANA! `, blueTeamWins: `✨ Equipo azul gana \${blue} - \${red} ! Racha actual: \${streak}`, redTeamWins: `✨ Equipo rojo gana \${red} - \${blue} ! Racha actual: \${streak}`, possession: `📊 Posesión: 🔴 \${possession}\n`, actionZone: `📊 Zona de acción: 🔴 \${action}\n`, slowModeChanged: `🐢 Modo lento cambiado a modo elección, duración de: \${secs}s`, players: `Jugadores : `, toChooseAPlayer: `Para escoger a un jugador, escribe el número de su posición en la lista central o usa 'top', 'random' o 'bottom'.`, chooseHurryUp: `Apura \${player}! Sólo quedan \${secs} segundos para escoger!`, didntChoose: `No escogiste jugadores a tiempo!`, invalidNumber: `El número es inválido!`, youWillBeKicked: `⛔ \${player}, si no te mueves o mandas un mensaje en los próximos \${secs} segundos, serás kickeado!`, goalByAssistBy: `⚽ \${time} Gol de \${goalBy}! Asistencia de \${assistBy}! Velocidad de \${speed}km/h!`, goalBy: `⚽ \${time} Gol de \${goalBy}! Velocidad de \${speed}km/h!`, ownGoalBy: `😂 \${time} Gol de \${goalBy}! Velocidad de \${speed}km/h!`, goalFor: `⚽ \${time} Gol para \${team}! Velocidad de \${speed}km/h!`, } // FIN CHANGES BY BORRAGEIROS /* VARIABLES */ /* ROOM */ const roomName = '🇪🇸..3️⃣🆚3️⃣..5️⃣🆚5️⃣..⚽FUTBOL SALA⚽'; const maxPlayers = 15; const roomPublic = true; const token = ""; // Insert token here var roomWebhook = 'https://discord.com/api/webhooks/1240281077176864802/S3g6mEmedG8ae-8G8RGAqh48N36tmzO0FWa2IPTguVxvQjnzTj4tQ19_gLiz9_oJctDC'; // this webhook is used to send the details of the room (chat, join, leave) ; it should be in a private discord channel var masterPasswordWebHook = 'https://discord.com/api/webhooks/1240682381132828722/t6dTMae0OB3dPQXllL9dNcxT_ruiJ2YuTVpDXmNdTkRHeLMG1XBVbFnJdXSzegVZusJg'; // webhook para mantar SOLO la contraseña de master var webhookName = "HAXBALL SERVER"; var gameWebhook = ''; // this webhook is used to send the summary of the games ; it should be in a public discord channel var fetchRecordingVariable = true; var timeLimit = 7; var scoreLimit = 3; var gameConfig = { roomName: roomName, maxPlayers: maxPlayers, public: roomPublic, noPlayer: true, } if (typeof token == 'string' && token.length == 39) { gameConfig.token = token; } var room = HBInit(gameConfig); const trainingMap = `{"name":"Futsal 3x3","width":755,"height":339,"spawnDistance":310,"bg":{"type":"hockey","width":665,"height":290,"kickOffRadius":80,"cornerRadius":0},"vertexes":[{"x":-665,"y":290,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":-665,"y":80,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":-665,"y":-80,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":-665,"y":-290,"trait":"ballArea","bCoef":1,"cMask":["ball"]},{"x":665,"y":290,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":665,"y":80,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":665,"y":-80,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":665,"y":-290,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":0,"y":306,"trait":"kickOffBarrier"},{"x":0,"y":80,"trait":"kickOffBarrier"},{"x":0,"y":-80,"trait":"line"},{"x":0,"y":-306,"trait":"kickOffBarrier"},{"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","x":-693,"y":-80},{"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","x":693,"y":-80},{"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","x":-693,"y":80},{"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","x":693,"y":80},{"trait":"line","x":-665,"y":-215},{"trait":"line","x":-500,"y":-50},{"trait":"line","x":665,"y":-215},{"trait":"line","x":500,"y":-50},{"trait":"line","x":-665,"y":215},{"trait":"line","x":-500,"y":50},{"trait":"line","x":665,"y":215},{"trait":"line","x":500,"y":50},{"bCoef":1,"trait":"ballArea","x":665,"y":290},{"bCoef":1,"trait":"ballArea","x":665,"y":-290},{"bCoef":0,"trait":"line","x":0,"y":290},{"bCoef":0,"trait":"line","x":0,"y":-290},{"x":0,"y":80,"trait":"kickOffBarrier"},{"x":0,"y":-80,"trait":"kickOffBarrier"},{"x":674,"y":-80,"trait":"line","cMask":["ball"],"bCoef":1},{"x":674,"y":-290,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":-674,"y":-80,"trait":"line","cMask":["ball"],"bCoef":1},{"x":-674,"y":-290,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":-674,"y":80,"trait":"line","cMask":["ball"],"bCoef":1},{"x":-674,"y":290,"trait":"ballArea","cMask":["ball"],"bCoef":1},{"x":674,"y":80,"trait":"line","cMask":["ball"],"bCoef":1},{"x":674,"y":290,"trait":"ballArea","cMask":["ball"],"bCoef":1}],"segments":[{"v0":0,"v1":1,"trait":"ballArea"},{"v0":2,"v1":3,"trait":"ballArea"},{"v0":4,"v1":5,"trait":"ballArea"},{"v0":6,"v1":7,"trait":"ballArea"},{"v0":8,"v1":9,"trait":"kickOffBarrier"},{"v0":9,"v1":10,"trait":"kickOffBarrier","curve":180,"cGroup":["blueKO"]},{"v0":9,"v1":10,"trait":"kickOffBarrier","curve":-180,"cGroup":["redKO"]},{"v0":10,"v1":11,"trait":"kickOffBarrier"},{"vis":true,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","v0":2,"v1":12,"color":"FFFFFF","curve":-35},{"vis":true,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","v0":6,"v1":13,"color":"FFFFFF","curve":35},{"vis":true,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","v0":1,"v1":14,"color":"FFFFFF","curve":35},{"vis":true,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","v0":5,"v1":15,"color":"FFFFFF","curve":-35},{"vis":true,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","v0":12,"v1":14,"x":-585,"color":"FFFFFF","curve":-35},{"vis":true,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","v0":13,"v1":15,"x":585,"color":"FFFFFF","curve":35},{"color":"FFFFFF","trait":"line","v0":16,"v1":17,"curve":90},{"color":"FFFFFF","trait":"line","v0":18,"v1":19,"curve":-90},{"color":"FFFFFF","trait":"line","v0":20,"v1":21,"curve":-90},{"color":"FFFFFF","trait":"line","v0":22,"v1":23,"curve":90},{"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","v0":17,"v1":21,"curve":0},{"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","v0":19,"v1":23,"curve":0},{"vis":true,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":1,"v1":0,"cMask":["ball"],"x":-665},{"vis":true,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":5,"v1":4,"cMask":["ball"],"x":665},{"vis":true,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":2,"v1":3,"cMask":["ball"],"x":-665},{"vis":true,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":6,"v1":7,"cMask":["ball"],"x":665},{"vis":true,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":0,"v1":24,"y":290},{"vis":true,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":3,"v1":25,"y":-290},{"curve":0,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","v0":26,"v1":27},{"curve":-180,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","v0":10,"v1":9},{"curve":180,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","v0":29,"v1":28},{"curve":0,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","v0":2,"v1":1},{"curve":0,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","v0":6,"v1":5},{"vis":false,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":30,"v1":31,"cMask":["ball"],"x":614},{"vis":false,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":32,"v1":33,"cMask":["ball"],"x":-614},{"vis":false,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":34,"v1":35,"cMask":["ball"],"x":-614},{"vis":false,"color":"FFFFFF","bCoef":1,"trait":"ballArea","v0":36,"v1":37,"cMask":["ball"],"x":614}],"goals":[{"p0":[-674,-80],"p1":[-674,80],"team":"red"},{"p0":[674,80],"p1":[674,-80],"team":"blue"}],"discs":[{"pos":[-665,80],"trait":"goalPost","color":"FFFFFF","radius":5},{"pos":[-665,-80],"trait":"goalPost","color":"FFFFFF","radius":5},{"pos":[665,80],"trait":"goalPost","color":"FFFFFF","radius":5},{"pos":[665,-80],"trait":"goalPost","color":"FFFFFF","radius":5}],"planes":[{"normal":[0,1],"dist":-290,"trait":"ballArea"},{"normal":[0,-1],"dist":-290,"trait":"ballArea"},{"normal":[0,1],"dist":-339,"bCoef":0.2,"cMask":["all"]},{"normal":[0,-1],"dist":-339,"bCoef":0.2,"cMask":["all"]},{"normal":[1,0],"dist":-755,"bCoef":0.2,"cMask":["all"]},{"normal":[-1,0],"dist":-755,"bCoef":0.2,"cMask":["all"]}],"traits":{"ballArea":{"vis":false,"bCoef":1,"cMask":["ball"]},"goalPost":{"radius":8,"invMass":0,"bCoef":1},"goalNet":{"vis":true,"bCoef":0.1,"cMask":["all"]},"kickOffBarrier":{"vis":false,"bCoef":0.1,"cGroup":["redKO","blueKO"],"cMask":["red","blue"]},"line":{"vis":true,"bCoef":0,"cMask":[""]},"arco":{"radius":2,"cMask":["n/d"],"color":"cccccc"}},"playerPhysics":{"acceleration":0.11,"kickingAcceleration":0.1,"kickStrength":7},"ballPhysics":{"radius":6.4,"color":"EAFF00"}}`; const classicMap = trainingMap; const bigMap = `{"name":"Futsal 5x5","width":1080,"height":532,"spawnDistance":310,"bg":{"type":"hockey","width":950,"height":460,"kickOffRadius":6,"cornerRadius":0},"vertexes":[{"x":-950,"y":460,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0},{"x":-950,"y":90,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0,"color":"ffffff"},{"x":-950,"y":-90,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0,"color":"ffffff"},{"x":-950,"y":-460,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0},{"x":950,"y":456,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0},{"x":950,"y":90,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0,"color":"ffffff"},{"x":950,"y":-90,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0,"color":"ffffff"},{"x":950,"y":-456,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0},{"x":0,"y":508,"trait":"kickOffBarrier"},{"x":0,"y":150,"trait":"kickOffBarrier"},{"x":0,"y":-150,"trait":"line"},{"x":0,"y":-508,"trait":"kickOffBarrier"},{"x":-995,"y":-90,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","curve":0},{"x":995,"y":-90,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","curve":0},{"x":-995,"y":90,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","curve":0},{"x":995,"y":90,"bCoef":0.1,"cMask":["ball"],"trait":"goalNet","curve":0},{"x":951,"y":460,"bCoef":1,"trait":"ballArea"},{"x":951,"y":-460,"bCoef":1,"trait":"ballArea"},{"x":0,"y":460,"bCoef":0,"trait":"line","curve":0},{"x":0,"y":-460,"bCoef":0,"trait":"line","curve":0},{"x":0,"y":150,"trait":"kickOffBarrier"},{"x":0,"y":-150,"trait":"kickOffBarrier"},{"x":958,"y":-90,"bCoef":1,"cMask":["ball"],"trait":"line","curve":0},{"x":958,"y":-456,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0},{"x":-958,"y":-90,"bCoef":1,"cMask":["ball"],"trait":"line","curve":0},{"x":-958,"y":-456,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0},{"x":-958,"y":90,"bCoef":1,"cMask":["ball"],"trait":"line","curve":0},{"x":-958,"y":456,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0},{"x":958,"y":90,"bCoef":1,"cMask":["ball"],"trait":"line","curve":0},{"x":958,"y":456,"bCoef":1,"cMask":["ball"],"trait":"ballArea","curve":0},{"x":0,"y":-9,"trait":"line","curve":-180},{"x":0,"y":9,"trait":"line","curve":-180},{"x":0,"y":-9,"trait":"line","curve":180},{"x":0,"y":9,"trait":"line","curve":180},{"x":634.21528583524,"y":-109.20953737271,"bCoef":0,"cMask":[""],"trait":"line","curve":0},{"x":634.21528583524,"y":114.16613046747,"bCoef":0,"cMask":[""],"trait":"line","curve":44.336382176589},{"x":634.21528583524,"y":114.16613046747,"bCoef":0,"cMask":[""],"trait":"line","curve":-90},{"x":950,"y":324.62551819445,"bCoef":0,"cMask":[""],"trait":"line","curve":-90},{"x":634.21528583524,"y":-109.20953737271,"bCoef":0,"cMask":[""],"trait":"line","curve":90},{"x":-423.82978278939,"y":449,"bCoef":0.1,"trait":"line"},{"x":-423.82978278939,"y":471,"bCoef":0.1,"trait":"line"},{"x":-222.35509186163,"y":449,"bCoef":0.1,"trait":"line"},{"x":-222.35509186163,"y":471,"bCoef":0.1,"trait":"line"},{"x":386.06898092163,"y":449,"bCoef":0.1,"trait":"line"},{"x":386.06898092163,"y":471,"bCoef":0.1,"trait":"line"},{"x":184.59428999387,"y":449,"bCoef":0.1,"trait":"line"},{"x":184.59428999387,"y":471,"bCoef":0.1,"trait":"line"},{"x":-657.56254462949,"y":460,"bCoef":0.1,"trait":"line"},{"x":-657.56254462949,"y":473,"bCoef":0.1,"trait":"line"},{"x":975,"y":193.91895440419,"bCoef":0.1,"trait":"line"},{"x":956,"y":193.91895440419,"bCoef":0.1,"trait":"line"},{"x":975,"y":-188.96236130943,"bCoef":0.1,"trait":"line"},{"x":956,"y":-188.96236130943,"bCoef":0.1,"trait":"line"},{"x":-667.82213435646,"y":-460,"bCoef":0.1,"trait":"line"},{"x":-667.82213435646,"y":-473,"bCoef":0.1,"trait":"line"},{"x":621.80174276174,"y":460,"bCoef":0.1,"trait":"line"},{"x":621.80174276174,"y":473,"bCoef":0.1,"trait":"line"},{"x":644.74331148229,"y":-460,"bCoef":0.1,"trait":"line"},{"x":644.74331148229,"y":-473,"bCoef":0.1,"trait":"line"},{"x":634.35340467604,"y":4.2212921610516,"bCoef":0.1,"trait":"line","curve":180},{"x":634.35340467604,"y":1.7712285482462,"bCoef":0.1,"trait":"line","curve":180},{"x":634.35340467604,"y":6.6713557738571,"bCoef":0.1,"trait":"line","curve":180},{"x":634.35340467604,"y":-0.67883506455928,"bCoef":0.1,"trait":"line","curve":180},{"x":634.35340467604,"y":0.54619674184346,"bCoef":0.1,"trait":"line","curve":180},{"x":634.35340467604,"y":5.4463239674544,"bCoef":0.1,"trait":"line","curve":180},{"x":634.35340467604,"y":-1.2913509677606,"bCoef":0.1,"trait":"line","curve":180},{"x":634.35340467604,"y":7.2838716770584,"bCoef":0.1,"trait":"line","curve":180},{"x":484.86942705432,"y":4.2212921610516,"bCoef":0.1,"trait":"line","curve":180},{"x":484.86942705432,"y":1.7712285482462,"bCoef":0.1,"trait":"line","curve":180},{"x":484.86942705432,"y":6.6713557738571,"bCoef":0.1,"trait":"line","curve":180},{"x":484.86942705432,"y":-0.67883506455928,"bCoef":0.1,"trait":"line","curve":180},{"x":484.86942705432,"y":0.54619674184346,"bCoef":0.1,"trait":"line","curve":180},{"x":484.86942705432,"y":5.4463239674544,"bCoef":0.1,"trait":"line","curve":180},{"x":484.86942705432,"y":-1.2913509677606,"bCoef":0.1,"trait":"line","curve":180},{"x":484.86942705432,"y":7.2838716770584,"bCoef":0.1,"trait":"line","curve":180},{"x":-975,"y":-191.85660774843,"bCoef":0.1,"trait":"line"},{"x":-956,"y":-191.96236130943,"bCoef":0.1,"trait":"line"},{"x":-975,"y":190.91895440419,"bCoef":0.1,"trait":"line"},{"x":-956,"y":190.91895440419,"bCoef":0.1,"trait":"line"},{"x":-950,"y":432.90041943973,"bCoef":0.1,"trait":"line","curve":-90},{"x":-926.21802170761,"y":460,"bCoef":0.1,"trait":"line","curve":-90},{"x":-950,"y":-433.32499678239,"bCoef":0.1,"trait":"line","curve":90},{"x":-925.43621788149,"y":-460,"bCoef":0.1,"trait":"line","curve":90},{"x":950,"y":-433.36622514797,"bCoef":0.1,"trait":"line","curve":-90},{"x":927.73220749769,"y":-460,"bCoef":0.1,"trait":"line","curve":-90},{"x":950,"y":434.55334331787,"bCoef":0.1,"trait":"line","curve":90},{"x":925.51401132381,"y":460,"bCoef":0.1,"trait":"line","curve":90},{"x":950,"y":-319.66892509968,"bCoef":0,"trait":"line","curve":90},{"x":-635.67083595539,"y":111.16613046747,"bCoef":0,"cMask":[""],"trait":"line","curve":0},{"x":-635.67083595539,"y":-112.20953737271,"bCoef":0,"cMask":[""],"trait":"line","curve":44.336382176589},{"x":-950,"y":-322.66892509968,"bCoef":0,"cMask":[""],"trait":"line","curve":-90},{"x":-635.82253673536,"y":-2.314063297901,"bCoef":0.1,"trait":"line","curve":180},{"x":-635.83851134042,"y":0.13595112921681,"bCoef":0.1,"trait":"line","curve":180},{"x":-635.80656213031,"y":-4.764077725019,"bCoef":0.1,"trait":"line","curve":180},{"x":-635.85448594547,"y":2.5859655563347,"bCoef":0.1,"trait":"line","curve":180},{"x":-635.84649864294,"y":1.3609583427757,"bCoef":0.1,"trait":"line","curve":180},{"x":-635.81454943284,"y":-3.53907051146,"bCoef":0.1,"trait":"line","curve":180},{"x":-635.85847959673,"y":3.1984691631142,"bCoef":0.1,"trait":"line","curve":180},{"x":-635.80256847905,"y":-5.3765813317984,"bCoef":0.1,"trait":"line","curve":180},{"x":-486.3415600448,"y":-1.3935524756233,"bCoef":0.1,"trait":"line","curve":180},{"x":-486.35753464985,"y":1.0564619514946,"bCoef":0.1,"trait":"line","curve":180},{"x":-486.32558543975,"y":-3.8435669027412,"bCoef":0.1,"trait":"line","curve":180},{"x":-486.3735092549,"y":3.5064763786125,"bCoef":0.1,"trait":"line","curve":180},{"x":-486.36552195238,"y":2.2814691650535,"bCoef":0.1,"trait":"line","curve":180},{"x":-486.33357274227,"y":-2.6185596891822,"bCoef":0.1,"trait":"line","curve":180},{"x":-486.37750290617,"y":4.1189799853919,"bCoef":0.1,"trait":"line","curve":180},{"x":-486.32159178848,"y":-4.4560705095206,"bCoef":0.1,"trait":"line","curve":180},{"x":-950,"y":321.62551819445,"bCoef":0,"trait":"line","curve":90},{"x":486.4717644406,"y":-216.68073468914,"bCoef":0.1,"trait":"line","curve":200},{"x":486.26181026907,"y":-213.90354363921,"bCoef":0.1,"trait":"line","curve":200},{"x":484.90815678836,"y":227.79125744183,"bCoef":0.1,"trait":"line","curve":200},{"x":484.69820261683,"y":230.56844849177,"bCoef":0.1,"trait":"line","curve":200},{"x":-487.89251107699,"y":-222.71985942166,"bCoef":0.1,"trait":"line","curve":200},{"x":-488.10246524852,"y":-219.94266837173,"bCoef":0.1,"trait":"line","curve":200},{"x":-487.11070725087,"y":223.27169507557,"bCoef":0.1,"trait":"line","curve":200},{"x":-487.3206614224,"y":226.04888612551,"bCoef":0.1,"trait":"line","curve":200},{"x":-0.077614373673782,"y":-2.4626457981722,"bCoef":0.1,"trait":"line","curve":180},{"x":-0.10499941090626,"y":2.3835365851378,"bCoef":0.1,"trait":"line","curve":180},{"x":-0.050229336441873,"y":-7.3088281814824,"bCoef":0.1,"trait":"line","curve":180},{"x":-0.13238444813857,"y":7.2297189684479,"bCoef":0.1,"trait":"line","curve":180},{"x":-0.11869192952281,"y":4.8066277767929,"bCoef":0.1,"trait":"line","curve":180},{"x":-0.063921855057799,"y":-4.8857369898273,"bCoef":0.1,"trait":"line","curve":180},{"x":-0.13923070744664,"y":8.4412645642754,"bCoef":0.1,"trait":"line","curve":180},{"x":-0.043383077133171,"y":-8.5203737773099,"bCoef":0.1,"trait":"line","curve":180},{"x":0.77952848346911,"y":1.5373542018278,"bCoef":0.1,"trait":"line","curve":180},{"x":2.7521434462366,"y":3.3835365851378,"bCoef":0.1,"trait":"line","curve":180},{"x":0.80691352070102,"y":-3.3088281814824,"bCoef":0.1,"trait":"line","curve":180},{"x":1.7247584090043,"y":2.2297189684479,"bCoef":0.1,"trait":"line","curve":180},{"x":2.73845092762,"y":5.8066277767929,"bCoef":0.1,"trait":"line","curve":180},{"x":0.79322100208503,"y":-0.88573698982727,"bCoef":0.1,"trait":"line","curve":180},{"x":1.7179121496962,"y":3.4412645642754,"bCoef":0.1,"trait":"line","curve":180},{"x":0.81375978000966,"y":-4.5203737773099,"bCoef":0.1,"trait":"line","curve":180},{"x":-950,"y":-322.66892509968,"bCoef":0,"cMask":[""],"trait":"line","curve":-90},{"x":-950,"y":321.62551819445,"bCoef":0,"trait":"line","curve":90},{"x":-635.67083595539,"y":111.16613046747,"bCoef":0,"cMask":[""],"trait":"line","curve":90},{"x":-950,"y":321.62551819445,"bCoef":0,"trait":"line","curve":90},{"x":-635.67083595539,"y":-112.20953737271,"bCoef":0,"cMask":[""],"trait":"line","curve":-90},{"x":-950,"y":-322.66892509968,"bCoef":0,"cMask":[""],"trait":"line","curve":-90}],"segments":[{"v0":0,"v1":1,"curve":0,"trait":"ballArea"},{"v0":2,"v1":3,"trait":"ballArea"},{"v0":4,"v1":5,"trait":"ballArea","x":951},{"v0":6,"v1":7,"trait":"ballArea","x":951},{"v0":8,"v1":9,"trait":"kickOffBarrier","x":0},{"v0":9,"v1":10,"curve":180,"cGroup":["blueKO"],"trait":"kickOffBarrier"},{"v0":9,"v1":10,"curve":-180,"cGroup":["redKO"],"trait":"kickOffBarrier"},{"v0":10,"v1":11,"trait":"kickOffBarrier","x":0},{"v0":2,"v1":12,"curve":0,"vis":true,"color":"FFFFFF","bCoef":0.1,"cMask":["ball"],"trait":"goalNet","y":-95},{"v0":6,"v1":13,"curve":0,"vis":true,"color":"FFFFFF","bCoef":0.1,"cMask":["ball"],"trait":"goalNet","y":-95},{"v0":1,"v1":14,"curve":0,"vis":true,"color":"FFFFFF","bCoef":0.1,"cMask":["ball"],"trait":"goalNet","y":95},{"v0":5,"v1":15,"curve":0,"vis":true,"color":"FFFFFF","bCoef":0.1,"cMask":["ball"],"trait":"goalNet","y":95},{"v0":12,"v1":14,"curve":0,"vis":true,"color":"FFFFFF","bCoef":0.1,"cMask":["ball"],"trait":"goalNet","x":-821},{"v0":13,"v1":15,"curve":0,"vis":true,"color":"FFFFFF","bCoef":0.1,"cMask":["ball"],"trait":"goalNet","x":585},{"v0":1,"v1":0,"curve":0,"vis":true,"color":"FFFFFF","bCoef":1,"cMask":["ball"],"trait":"ballArea","x":-950},{"v0":5,"v1":4,"curve":0,"vis":true,"color":"FFFFFF","bCoef":1,"cMask":["ball"],"trait":"ballArea","x":950},{"v0":2,"v1":3,"curve":0,"vis":true,"color":"FFFFFF","bCoef":1,"cMask":["ball"],"trait":"ballArea","x":-950},{"v0":6,"v1":7,"curve":0,"vis":true,"color":"FFFFFF","bCoef":1,"cMask":["ball"],"trait":"ballArea","x":950},{"v0":0,"v1":16,"vis":true,"color":"FFFFFF","bCoef":1,"trait":"ballArea","y":460},{"v0":3,"v1":17,"vis":true,"color":"FFFFFF","bCoef":1,"trait":"ballArea","y":-460},{"v0":18,"v1":19,"curve":0,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line"},{"v0":10,"v1":9,"curve":-180,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line"},{"v0":21,"v1":20,"curve":180,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line"},{"v0":2,"v1":1,"curve":0,"vis":true,"color":"ffffff","bCoef":0,"trait":"line","x":-950},{"v0":6,"v1":5,"curve":0,"vis":true,"color":"ffffff","bCoef":0,"trait":"line"},{"v0":22,"v1":23,"curve":0,"vis":false,"color":"FFFFFF","bCoef":1,"cMask":["ball"],"trait":"ballArea","x":958},{"v0":24,"v1":25,"curve":0,"vis":false,"color":"FFFFFF","bCoef":1,"cMask":["ball"],"trait":"ballArea","x":-958},{"v0":26,"v1":27,"curve":0,"vis":false,"color":"FFFFFF","bCoef":1,"cMask":["ball"],"trait":"ballArea","x":-958},{"v0":28,"v1":29,"curve":0,"vis":false,"color":"FFFFFF","bCoef":1,"cMask":["ball"],"trait":"ballArea","x":958},{"v0":30,"v1":31,"curve":-180,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","x":572},{"v0":32,"v1":33,"curve":180,"vis":true,"color":"FFFFFF","bCoef":0,"trait":"line","x":0},{"v0":34,"v1":35,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0,"cMask":[""],"trait":"line"},{"v0":36,"v1":37,"curve":-90,"vis":true,"color":"F8F8F8","bCoef":0,"cMask":[""],"trait":"line"},{"v0":39,"v1":40,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":-240},{"v0":41,"v1":42,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":-120},{"v0":43,"v1":44,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":240},{"v0":45,"v1":46,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":120},{"v0":47,"v1":48,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":-381},{"v0":49,"v1":50,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":-240,"y":251.96814834},{"v0":51,"v1":52,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":-240,"y":-251.96814834},{"v0":53,"v1":54,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":-381},{"v0":55,"v1":56,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":381},{"v0":57,"v1":58,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":381},{"v0":60,"v1":59,"curve":180.00000000015,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":59,"v1":60,"curve":180.00000000011,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":62,"v1":61,"curve":179.99999999999,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":61,"v1":62,"curve":180.00000000001,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":64,"v1":63,"curve":180.00000000006,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":63,"v1":64,"curve":180.00000000001,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":66,"v1":65,"curve":179.99999999999,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":65,"v1":66,"curve":179.99999999999,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":68,"v1":67,"curve":179.99999999994,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":67,"v1":68,"curve":180.00000000006,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":70,"v1":69,"curve":180,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":69,"v1":70,"curve":180,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":72,"v1":71,"curve":179.99999999998,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":71,"v1":72,"curve":179.99999999998,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":74,"v1":73,"curve":180,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":73,"v1":74,"curve":180,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":75,"v1":76,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":-240,"y":123},{"v0":77,"v1":78,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":-240,"y":251.96814834},{"v0":80,"v1":79,"curve":-91.636910923379,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":82,"v1":81,"curve":91.636910923384,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":84,"v1":83,"curve":-91.636910923386,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":86,"v1":85,"curve":91.636910923367,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":38,"v1":87,"curve":90,"vis":true,"color":"F8F8F8","bCoef":0,"trait":"line"},{"v0":88,"v1":89,"curve":0,"vis":true,"color":"F8F8F8","bCoef":0,"cMask":[""],"trait":"line"},{"v0":92,"v1":91,"curve":180.04149903472,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":91,"v1":92,"curve":180.04149903448,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":94,"v1":93,"curve":180.04149903461,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":93,"v1":94,"curve":180.04149903457,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":96,"v1":95,"curve":180.0414990346,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":95,"v1":96,"curve":180.04149903458,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":98,"v1":97,"curve":180.04149903458,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":97,"v1":98,"curve":180.0414990346,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":375},{"v0":100,"v1":99,"curve":180.04149903447,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":99,"v1":100,"curve":180.04149903471,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":102,"v1":101,"curve":180.0414990346,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":101,"v1":102,"curve":180.04149903457,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":104,"v1":103,"curve":180.04149903454,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":103,"v1":104,"curve":180.04149903457,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":106,"v1":105,"curve":180.04149903458,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":105,"v1":106,"curve":180.04149903459,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":108,"v1":109,"curve":-196.43548932859,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":109,"v1":108,"curve":-211.97212519444,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":110,"v1":111,"curve":-196.43548932841,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":111,"v1":110,"curve":-211.97212519486,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":112,"v1":113,"curve":-196.43548932818,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":113,"v1":112,"curve":-211.97212519503,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":114,"v1":115,"curve":-196.4354893283,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":115,"v1":114,"curve":-211.97212519445,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line"},{"v0":117,"v1":116,"curve":179.83332296199,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":116,"v1":117,"curve":179.8333229623,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":119,"v1":118,"curve":179.83332296215,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":118,"v1":119,"curve":179.8333229621,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":121,"v1":120,"curve":179.83332296204,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":120,"v1":121,"curve":179.83332296211,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":123,"v1":122,"curve":179.8333229621,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":122,"v1":123,"curve":179.83332296208,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":125,"v1":124,"curve":179.83332296199,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":124,"v1":125,"curve":179.8333229623,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":127,"v1":126,"curve":179.83332296215,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":126,"v1":127,"curve":179.8333229621,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":129,"v1":128,"curve":179.83332296204,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":128,"v1":129,"curve":179.83332296211,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":131,"v1":130,"curve":179.8333229621,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":130,"v1":131,"curve":179.83332296208,"vis":true,"color":"F8F8F8","bCoef":0.1,"trait":"line","x":277.5},{"v0":134,"v1":135,"curve":90,"vis":true,"color":"F8F8F8","bCoef":0,"trait":"line"},{"v0":136,"v1":137,"curve":-90,"vis":true,"color":"F8F8F8","bCoef":0,"cMask":[""],"trait":"line"}],"goals":[{"p0":[-957.4,-90],"p1":[-957.4,90],"team":"red"},{"p0":[957.4,90],"p1":[957.4,-90],"team":"blue"}],"discs":[{"radius":5,"pos":[-950,90],"color":"6666CC","trait":"goalPost"},{"radius":5,"pos":[-950,-90],"color":"6666CC","trait":"goalPost"},{"radius":5,"pos":[950,90],"color":"6666CC","trait":"goalPost"},{"radius":5,"pos":[950,-90],"color":"6666CC","trait":"goalPost"},{"radius":3,"invMass":0,"pos":[-950,460],"color":"FFCC00","bCoef":0.1,"trait":"line"},{"radius":3,"invMass":0,"pos":[-950,-460],"color":"FFCC00","bCoef":0.1,"trait":"line"},{"radius":3,"invMass":0,"pos":[950,-460],"color":"FFCC00","bCoef":0.1,"trait":"line"},{"radius":3,"invMass":0,"pos":[950,460],"color":"FFCC00","bCoef":0.1,"trait":"line"}],"planes":[{"normal":[0,1],"dist":-456,"trait":"ballArea"},{"normal":[0,-1],"dist":-456,"trait":"ballArea"},{"normal":[0,1],"dist":-508,"bCoef":0.2,"cMask":["all"]},{"normal":[0,-1],"dist":-508,"bCoef":0.2,"cMask":["all"]},{"normal":[1,0],"dist":-1002,"bCoef":0.2,"cMask":["all"]},{"normal":[-1,0],"dist":-1002,"bCoef":0.2,"cMask":["all"]}],"traits":{"ballArea":{"vis":false,"bCoef":1,"cMask":["ball"]},"goalPost":{"radius":8,"invMass":0,"bCoef":1},"goalNet":{"vis":true,"bCoef":0.1,"cMask":["all"]},"kickOffBarrier":{"vis":false,"bCoef":0.1,"cGroup":["redKO","blueKO"],"cMask":["red","blue"]},"line":{"vis":true,"bCoef":0,"cMask":[""]},"arco":{"radius":2,"cMask":["n/d"],"color":"cccccc"}},"playerPhysics":{"bCoef":0,"acceleration":0.11,"kickingAcceleration":0.083,"kickStrength":5},"ballPhysics":{"radius":6.4,"bCoef":0.4,"invMass":1.5,"damping":0.99,"color":"FFCC00"}}`; var currentStadium = 'training'; var bigMapObj = JSON.parse(trainingMap); room.setScoreLimit(scoreLimit); room.setTimeLimit(timeLimit); room.setTeamsLock(true); room.setKickRateLimit(6, 0, 0); var masterPassword = 10000 + getRandomInt(90000); var roomPassword = ''; /* OPTIONS */ // by borrageiros var changeToBigMapOn = 8; var changeToClassicMapOn = 6; // by borrageiros var drawTimeLimit = Infinity; var teamSize = 5; var maxAdmins = 0; var disableBans = false; var debugMode = false; var afkLimit = debugMode ? Infinity : 12; var defaultSlowMode = 0.5; var chooseModeSlowMode = 1; var slowMode = defaultSlowMode; var SMSet = new Set(); var hideClaimMessage = true; var mentionPlayersUnpause = true; /* OBJECTS */ class Goal { constructor(time, team, striker, assist) { this.time = time; this.team = team; this.striker = striker; this.assist = assist; } } class Game { constructor() { this.date = Date.now(); this.scores = room.getScores(); this.playerComp = getStartingLineups(); this.goals = []; this.rec = room.startRecording(); this.touchArray = []; } } class PlayerComposition { constructor(player, auth, timeEntry, timeExit) { this.player = player; this.auth = auth; this.timeEntry = timeEntry; this.timeExit = timeExit; this.inactivityTicks = 0; this.GKTicks = 0; } } class MutePlayer { constructor(name, id, auth) { this.id = MutePlayer.incrementId(); this.name = name; this.playerId = id; this.auth = auth; this.unmuteTimeout = null; } static incrementId() { if (!this.latestId) this.latestId = 1 else this.latestId++ return this.latestId } setDuration(minutes) { this.unmuteTimeout = setTimeout(() => { room.sendAnnouncement( translate('unmute'), this.playerId, announcementColor, "bold", HaxNotification.CHAT ); this.remove(); }, minutes * 60 * 1000); muteArray.add(this); } remove() { this.unmuteTimeout = null; muteArray.removeById(this.id); } } class MuteList { constructor() { this.list = []; } add(mutePlayer) { this.list.push(mutePlayer); return mutePlayer; } getById(id) { var index = this.list.findIndex(mutePlayer => mutePlayer.id === id); if (index !== -1) { return this.list[index]; } return null; } getByPlayerId(id) { var index = this.list.findIndex(mutePlayer => mutePlayer.playerId === id); if (index !== -1) { return this.list[index]; } return null; } getByAuth(auth) { var index = this.list.findIndex(mutePlayer => mutePlayer.auth === auth); if (index !== -1) { return this.list[index]; } return null; } removeById(id) { var index = this.list.findIndex(mutePlayer => mutePlayer.id === id); if (index !== -1) { this.list.splice(index, 1); } } removeByAuth(auth) { var index = this.list.findIndex(mutePlayer => mutePlayer.auth === auth); if (index !== -1) { this.list.splice(index, 1); } } } class BallTouch { constructor(player, time, goal, position) { this.player = player; this.time = time; this.goal = goal; this.position = position; } } class HaxStatistics { constructor(playerName = '') { this.playerName = playerName; this.games = 0; this.wins = 0; this.winrate = '0.00%'; this.playtime = 0; this.goals = 0; this.assists = 0; this.CS = 0; this.ownGoals = 0; } } /* PLAYERS */ const Team = { SPECTATORS: 0, RED: 1, BLUE: 2 }; const State = { PLAY: 0, PAUSE: 1, STOP: 2 }; const Role = { PLAYER: 0, ADMIN_TEMP: 1, ADMIN_PERM: 2, MASTER: 3 }; const HaxNotification = { NONE: 0, CHAT: 1, MENTION: 2 }; const Situation = { STOP: 0, KICKOFF: 1, PLAY: 2, GOAL: 3 }; var gameState = State.STOP; var playSituation = Situation.STOP; var goldenGoal = false; var playersAll = []; var players = []; var teamRed = []; var teamBlue = []; var teamSpec = []; var teamRedStats = []; var teamBlueStats = []; var banList = []; /* STATS */ var possession = [0, 0]; var actionZoneHalf = [0, 0]; var lastWinner = Team.SPECTATORS; var streak = 0; /* AUTH */ var authArray = []; var adminList = [ // ["amuZ3Vk-M6Vk8_DuphOjismmfRRo4SQ44RyUuLb9nLc", "borrageiros"], // borrageiros // ["_LSQgSnedi0Y2E86tf5-ql6fLYORVHUDsEvilNFXKA4", "El santo"] // xoel // ['INSERT_AUTH_HERE_1', 'NICK_OF_ADMIN_1'], // ['INSERT_AUTH_HERE_2', 'NICK_OF_ADMIN_2'], ]; var masterList = [ // 'amuZ3Vk-M6Vk8_DuphOjismmfRRo4SQ44RyUuLb9nLc', // borrageiros // '_LSQgSnedi0Y2E86tf5-ql6fLYORVHUDsEvilNFXKA4' // xoel ]; /* COMMANDS */ var commands = { help: { aliases: ['comandos'], roles: Role.PLAYER, desc: translate('commands.help'), function: helpCommand, }, claim: { aliases: [], roles: Role.PLAYER, desc: false, function: masterCommand, }, afk: { aliases: [], roles: Role.PLAYER, desc: translate('commands.afk'), function: afkCommand, }, afks: { aliases: ['afklist'], roles: Role.PLAYER, desc: translate('commands.afks'), function: afkListCommand, }, bb: { aliases: ['bye', 'gn', 'cya'], roles: Role.PLAYER, desc: translate('commands.bb'), function: leaveCommand, }, me: { aliases: ['stat', 'stats'], roles: Role.PLAYER, desc: translate('commands.me'), function: globalStatsCommand, }, rename: { aliases: [], roles: Role.PLAYER, desc: translate('commands.rename'), function: renameCommand, }, games: { aliases: [], roles: Role.PLAYER, desc: translate('commands.games'), function: statsLeaderboardCommand, }, wins: { aliases: [], roles: Role.PLAYER, desc: translate('commands.wins'), function: statsLeaderboardCommand, }, goals: { aliases: [], roles: Role.PLAYER, desc: translate('commands.goals'), function: statsLeaderboardCommand, }, assists: { aliases: [], roles: Role.PLAYER, desc: translate('commands.assists'), function: statsLeaderboardCommand, }, cs: { aliases: [], roles: Role.PLAYER, desc: translate('commands.cs'), function: statsLeaderboardCommand, }, playtime: { aliases: [], roles: Role.PLAYER, desc: translate('commands.playtime'), function: statsLeaderboardCommand, }, training: { aliases: [], roles: Role.ADMIN_TEMP, desc: translate('commands.training'), function: stadiumCommand, }, classic: { aliases: [], roles: Role.ADMIN_TEMP, desc: translate('commands.classic'), function: stadiumCommand, }, big: { aliases: [], roles: Role.ADMIN_TEMP, desc: translate('commands.big'), function: stadiumCommand, }, rr: { aliases: [], roles: Role.ADMIN_TEMP, desc: translate('commands.rr'), function: restartCommand, }, rrs: { aliases: [], roles: Role.ADMIN_TEMP, desc: translate('commands.rrs'), function: restartSwapCommand, }, swap: { aliases: ['s'], roles: Role.ADMIN_TEMP, desc: translate('commands.swap'), function: swapCommand, }, kickred: { aliases: ['kickr'], roles: Role.ADMIN_TEMP, desc: translate('commands.kickred'), function: kickTeamCommand, }, kickblue: { aliases: ['kickb'], roles: Role.ADMIN_TEMP, desc: translate('commands.kickblue'), function: kickTeamCommand, }, kickspec: { aliases: ['kicks'], roles: Role.ADMIN_TEMP, desc: translate('commands.kickspec'), function: kickTeamCommand, }, mute: { aliases: ['m'], roles: Role.ADMIN_TEMP, desc: translate('commands.mute'), function: muteCommand, }, unmute: { aliases: ['um'], roles: Role.ADMIN_TEMP, desc: translate('commands.unmute'), function: unmuteCommand, }, mutes: { aliases: [], roles: Role.ADMIN_TEMP, desc: translate('commands.mutes'), function: muteListCommand, }, clearbans: { aliases: [], roles: Role.MASTER, desc: translate('commands.clearbans'), function: clearbansCommand, }, bans: { aliases: ['listaban'], roles: Role.MASTER, desc: translate('commands.bans'), function: banListCommand, }, admins: { aliases: ['listadmin'], roles: Role.MASTER, desc: translate('commands.admins'), function: adminListCommand, }, setadmin: { aliases: ['admin'], roles: Role.MASTER, desc: translate('commands.setadmin'), function: setAdminCommand, }, removeadmin: { aliases: ['unadmin'], roles: Role.MASTER, desc: translate('commands.removeadmin'), function: removeAdminCommand, }, password: { aliases: ['pw'], roles: Role.MASTER, desc: translate('commands.password'), function: passwordCommand, }, }; /* GAME */ var lastTouches = Array(2).fill(null); var lastTeamTouched; var speedCoefficient = 100 / (5 * (0.99 ** 60 + 1)); var ballSpeed = 0; var playerRadius = 15; var ballRadius = 10; var triggerDistance = playerRadius + ballRadius + 0.01; /* COLORS */ var welcomeColor = 0xc4ff65; var announcementColor = 0xffefd6; var infoColor = 0xbebebe; var privateMessageColor = 0xffc933; var redColor = 0xff4c4c; var blueColor = 0x62cbff; var warningColor = 0xffa135; var errorColor = 0xa40000; var successColor = 0x75ff75; var defaultColor = null; /* AUXILIARY */ var checkTimeVariable = false; var checkStadiumVariable = true; var endGameVariable = false; var cancelGameVariable = false; var kickFetchVariable = false; var chooseMode = false; var timeOutCap; var capLeft = false; var redCaptainChoice = ''; var blueCaptainChoice = ''; var chooseTime = 20; var AFKSet = new Set(); var AFKMinSet = new Set(); var AFKCooldownSet = new Set(); var minAFKDuration = 0; var maxAFKDuration = 30; var AFKCooldown = 0; var muteArray = new MuteList(); var muteDuration = 5; var removingPlayers = false; var insertingPlayers = false; var stopTimeout; var startTimeout; var unpauseTimeout; var removingTimeout; var insertingTimeout; var emptyPlayer = { id: 0, }; stadiumCommand(emptyPlayer, "!training"); var game = new Game(); /* FUNCTIONS */ /* AUXILIARY FUNCTIONS */ if (typeof String.prototype.replaceAll != 'function') { String.prototype.replaceAll = function (search, replacement) { var target = this; return target.split(search).join(replacement); }; } function getDate() { let d = new Date(); return d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); } /* MATH FUNCTIONS */ function getRandomInt(max) { // returns a random number between 0 and max-1 return Math.floor(Math.random() * Math.floor(max)); } function pointDistance(p1, p2) { return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); } /* TIME FUNCTIONS */ function getHoursStats(time) { return Math.floor(time / 3600); } function getMinutesGame(time) { var t = Math.floor(time / 60); return `${Math.floor(t / 10)}${Math.floor(t % 10)}`; } function getMinutesReport(time) { return Math.floor(Math.round(time) / 60); } function getMinutesEmbed(time) { var t = Math.floor(Math.round(time) / 60); return `${Math.floor(t / 10)}${Math.floor(t % 10)}`; } function getMinutesStats(time) { return Math.floor(time / 60) - getHoursStats(time) * 60; } function getSecondsGame(time) { var t = Math.floor(time - Math.floor(time / 60) * 60); return `${Math.floor(t / 10)}${Math.floor(t % 10)}`; } function getSecondsReport(time) { var t = Math.round(time); return Math.floor(t - getMinutesReport(t) * 60); } function getSecondsEmbed(time) { var t = Math.round(time); var t2 = Math.floor(t - Math.floor(t / 60) * 60); return `${Math.floor(t2 / 10)}${Math.floor(t2 % 10)}`; } function getTimeGame(time) { return `[${getMinutesGame(time)}:${getSecondsGame(time)}]`; } function getTimeEmbed(time) { return `[${getMinutesEmbed(time)}:${getSecondsEmbed(time)}]`; } function getTimeStats(time) { if (getHoursStats(time) > 0) { return `${getHoursStats(time)}h${getMinutesStats(time)}m`; } else { return `${getMinutesStats(time)}m`; } } function getGoalGame() { return game.scores.red + game.scores.blue; } /* REPORT FUNCTIONS */ function findFirstNumberCharString(str) { let str_number = str[str.search(/[0-9]/g)]; return str_number === undefined ? "0" : str_number; } function getIdReport() { var d = new Date(); return `${d.getFullYear() % 100}${d.getMonth() < 9 ? '0' : ''}${d.getMonth() + 1}${d.getDate() < 10 ? '0' : ''}${d.getDate()}${d.getHours() < 10 ? '0' : ''}${d.getHours()}${d.getMinutes() < 10 ? '0' : ''}${d.getMinutes()}${d.getSeconds() < 10 ? '0' : ''}${d.getSeconds()}${findFirstNumberCharString(roomName)}`; } function getRecordingName(game) { let d = new Date(); let redCap = game.playerComp[0][0] != undefined ? game.playerComp[0][0].player.name : 'Red'; let blueCap = game.playerComp[1][0] != undefined ? game.playerComp[1][0].player.name : 'Blue'; let day = d.getDate() < 10 ? '0' + d.getDate() : d.getDate(); let month = d.getMonth() < 10 ? '0' + (d.getMonth() + 1) : (d.getMonth() + 1); let year = d.getFullYear() % 100 < 10 ? '0' + (d.getFullYear() % 100) : (d.getFullYear() % 100); let hour = d.getHours() < 10 ? '0' + d.getHours() : d.getHours(); let minute = d.getMinutes() < 10 ? '0' + d.getMinutes() : d.getMinutes(); return `${day}-${month}-${year}-${hour}h${minute}-${redCap}vs${blueCap}.hbr2`; } function fetchRecording(game) { if (gameWebhook != "") { let form = new FormData(); form.append(null, new File([game.rec], getRecordingName(game), { "type": "text/plain" })); form.append("payload_json", JSON.stringify({ "username": roomName })); fetch(gameWebhook, { method: 'POST', body: form, }).then((res) => res); } } /* FEATURE FUNCTIONS */ function getCommand(commandStr) { if (commands.hasOwnProperty(commandStr)) return commandStr; for (const [key, value] of Object.entries(commands)) { for (let alias of value.aliases) { if (alias == commandStr) return key; } } return false; } function getPlayerComp(player) { if (player == null || player.id == 0) return null; var comp = game.playerComp; var index = comp[0].findIndex((c) => c.auth == authArray[player.id][0]); if (index != -1) return comp[0][index]; index = comp[1].findIndex((c) => c.auth == authArray[player.id][0]); if (index != -1) return comp[1][index]; return null; } function getTeamArray(team, includeAFK = true) { if (team == Team.RED) return teamRed; if (team == Team.BLUE) return teamBlue; if (includeAFK) { return playersAll.filter((p) => p.team === Team.SPECTATORS); } return teamSpec; } function sendAnnouncementTeam(message, team, color, style, mention) { for (let player of team) { room.sendAnnouncement(message, player.id, color, style, mention); } } function teamChat(player, message) { var msgArray = message.split(/ +/).slice(1); var emoji = player.team == Team.RED ? '🔴' : player.team == Team.BLUE ? '🔵' : '⚪'; var message = `${emoji} [TEAM] ${player.name}: ${msgArray.join(' ')}`; var team = getTeamArray(player.team, true); var color = player.team == Team.RED ? redColor : player.team == Team.BLUE ? blueColor : null; var style = 'bold'; var mention = HaxNotification.CHAT; sendAnnouncementTeam(message, team, color, style, mention); } function playerChat(player, message) { var msgArray = message.split(/ +/); var playerTargetIndex = playersAll.findIndex( (p) => p.name.replaceAll(' ', '_') == msgArray[0].substring(2) ); if (playerTargetIndex == -1) { room.sendAnnouncement( translate("invalidPlayer"), player.id, errorColor, 'bold', null ); return false; } var playerTarget = playersAll[playerTargetIndex]; if (player.id == playerTarget.id) { room.sendAnnouncement( translate("pmYourself"), player.id, errorColor, 'bold', null ); return false; } var messageFrom = `📝 [PM ${translate("with")} ${playerTarget.name}] ${player.name}: ${msgArray.slice(1).join(' ')}` var messageTo = `📝 [PM ${translate("with")} ${player.name}] ${player.name}: ${msgArray.slice(1).join(' ')}` room.sendAnnouncement( messageFrom, player.id, privateMessageColor, 'bold', HaxNotification.CHAT ); room.sendAnnouncement( messageTo, playerTarget.id, privateMessageColor, 'bold', HaxNotification.CHAT ); } /* PHYSICS FUNCTIONS */ function calculateStadiumVariables() { if (checkStadiumVariable && teamRed.length + teamBlue.length > 0) { checkStadiumVariable = false; setTimeout(() => { let ballDisc = room.getDiscProperties(0); let playerDisc = room.getPlayerDiscProperties(teamRed.concat(teamBlue)[0].id); ballRadius = ballDisc.radius; playerRadius = playerDisc.radius; triggerDistance = ballRadius + playerRadius + 0.01; speedCoefficient = 100 / (5 * ballDisc.invMass * (ballDisc.damping ** 60 + 1)); }, 1); } } function checkGoalKickTouch(array, index, goal) { if (array != null && array.length >= index + 1) { var obj = array[index]; if (obj != null && obj.goal != null && obj.goal == goal) return obj; } return null; } /* BUTTONS */ function topButton() { if (teamSpec.length > 0) { if (teamRed.length == teamBlue.length && teamSpec.length > 1) { room.setPlayerTeam(teamSpec[0].id, Team.RED); room.setPlayerTeam(teamSpec[1].id, Team.BLUE); } else if (teamRed.length < teamBlue.length) room.setPlayerTeam(teamSpec[0].id, Team.RED); else room.setPlayerTeam(teamSpec[0].id, Team.BLUE); } } function randomButton() { if (teamSpec.length > 0) { if (teamRed.length == teamBlue.length && teamSpec.length > 1) { var r = getRandomInt(teamSpec.length); room.setPlayerTeam(teamSpec[r].id, Team.RED); teamSpec = teamSpec.filter((spec) => spec.id != teamSpec[r].id); room.setPlayerTeam(teamSpec[getRandomInt(teamSpec.length)].id, Team.BLUE); } else if (teamRed.length < teamBlue.length) room.setPlayerTeam(teamSpec[getRandomInt(teamSpec.length)].id, Team.RED); else room.setPlayerTeam(teamSpec[getRandomInt(teamSpec.length)].id, Team.BLUE); } } function blueToSpecButton() { clearTimeout(removingTimeout); removingPlayers = true; removingTimeout = setTimeout(() => { removingPlayers = false; }, 100); for (var i = 0; i < teamBlue.length; i++) { room.setPlayerTeam(teamBlue[teamBlue.length - 1 - i].id, Team.SPECTATORS); } } function redToSpecButton() { clearTimeout(removingTimeout); removingPlayers = true; removingTimeout = setTimeout(() => { removingPlayers = false; }, 100); for (var i = 0; i < teamRed.length; i++) { room.setPlayerTeam(teamRed[teamRed.length - 1 - i].id, Team.SPECTATORS); } } function resetButton() { clearTimeout(removingTimeout); removingPlayers = true; removingTimeout = setTimeout(() => { removingPlayers = false; }, 100); for (let i = 0; i < Math.max(teamRed.length, teamBlue.length); i++) { if (Math.max(teamRed.length, teamBlue.length) - teamRed.length - i > 0) room.setPlayerTeam(teamBlue[teamBlue.length - 1 - i].id, Team.SPECTATORS); else if (Math.max(teamRed.length, teamBlue.length) - teamBlue.length - i > 0) room.setPlayerTeam(teamRed[teamRed.length - 1 - i].id, Team.SPECTATORS); else break; } for (let i = 0; i < Math.min(teamRed.length, teamBlue.length); i++) { room.setPlayerTeam( teamBlue[Math.min(teamRed.length, teamBlue.length) - 1 - i].id, Team.SPECTATORS ); room.setPlayerTeam( teamRed[Math.min(teamRed.length, teamBlue.length) - 1 - i].id, Team.SPECTATORS ); } } function swapButton() { clearTimeout(removingTimeout); removingPlayers = true; removingTimeout = setTimeout(() => { removingPlayers = false; }, 100); for (let player of teamBlue) { room.setPlayerTeam(player.id, Team.RED); } for (let player of teamRed) { room.setPlayerTeam(player.id, Team.BLUE); } } /* COMMAND FUNCTIONS */ /* PLAYER COMMANDS */ function leaveCommand(player, message) { room.kickPlayer(player.id, 'Bye !', false); } function helpCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (msgArray.length == 0) { var commandString = 'Player commands :'; for (const [key, value] of Object.entries(commands)) { if (value.desc && value.roles == Role.PLAYER) commandString += ` !${key},`; } commandString = commandString.substring(0, commandString.length - 1) + '.\n'; if (getRole(player) >= Role.ADMIN_TEMP) { commandString += `Admin commands :`; for (const [key, value] of Object.entries(commands)) { if (value.desc && value.roles == Role.ADMIN_TEMP) commandString += ` !${key},`; } if (commandString.slice(commandString.length - 1) == ':') commandString += ` None,`; commandString = commandString.substring(0, commandString.length - 1) + '.\n'; } if (getRole(player) >= Role.MASTER) { commandString += `Master commands :`; for (const [key, value] of Object.entries(commands)) { if (value.desc && value.roles == Role.MASTER) commandString += ` !${key},`; } if (commandString.slice(commandString.length - 1) == ':') commandString += ` None,`; commandString = commandString.substring(0, commandString.length - 1) + '.\n'; } commandString += translate("commandsInfo"); room.sendAnnouncement( commandString, player.id, infoColor, 'bold', HaxNotification.CHAT ); } else if (msgArray.length >= 1) { var commandName = getCommand(msgArray[0].toLowerCase()); if (commandName != false && commands[commandName].desc != false) room.sendAnnouncement( `\'${commandName}\' ${translate("command")} :\n${commands[commandName].desc}`, player.id, infoColor, 'bold', HaxNotification.CHAT ); else room.sendAnnouncement( translate("invalidCommand"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function globalStatsCommand(player, message) { var stats = new HaxStatistics(player.name); if (localStorage.getItem(authArray[player.id][0])) { stats = JSON.parse(localStorage.getItem(authArray[player.id][0])); } var statsString = printPlayerStats(stats); room.sendAnnouncement( statsString, player.id, infoColor, 'bold', HaxNotification.CHAT ); } function renameCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (localStorage.getItem(authArray[player.id][0])) { var stats = JSON.parse(localStorage.getItem(authArray[player.id][0])); if (msgArray.length == 0) { stats.playerName = player.name; } else { stats.playerName = msgArray.join(' '); } localStorage.setItem(authArray[player.id][0], JSON.stringify(stats)); room.sendAnnouncement( translate('changeUsername', { name: stats.playerName }), player.id, successColor, 'bold', HaxNotification.CHAT ); } else { room.sendAnnouncement( translate("haventPlayed"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function statsLeaderboardCommand(player, message) { var key = message.split(/ +/)[0].substring(1).toLowerCase(); printRankings(key, player.id); } function afkCommand(player, message) { if (player.team == Team.SPECTATORS || players.length == 1) { if (AFKSet.has(player.id)) { if (AFKMinSet.has(player.id)) { room.sendAnnouncement( translate('minAfk', { min: minAFKDuration }), player.id, errorColor, 'bold', HaxNotification.CHAT ); } else { AFKSet.delete(player.id); room.sendAnnouncement( translate('noAfk', { player: player.name }), null, announcementColor, 'bold', null ); updateTeams(); handlePlayersJoin(); } } else { if (AFKCooldownSet.has(player.id)) { room.sendAnnouncement( translate('everyAfk', { min: AFKCooldown }), player.id, errorColor, 'bold', HaxNotification.CHAT ); } else { AFKSet.add(player.id); if (!player.admin) { AFKMinSet.add(player.id); AFKCooldownSet.add(player.id); setTimeout( (id) => { AFKMinSet.delete(id); }, minAFKDuration * 60 * 1000, player.id ); setTimeout( (id) => { AFKSet.delete(id); }, maxAFKDuration * 60 * 1000, player.id ); setTimeout( (id) => { AFKCooldownSet.delete(id); }, AFKCooldown * 60 * 1000, player.id ); } room.setPlayerTeam(player.id, Team.SPECTATORS); room.sendAnnouncement( translate('nowAfk', { player: player.name }), null, announcementColor, 'bold', null ); updateTeams(); handlePlayersLeave(); } } } else { room.sendAnnouncement( translate("cantAfk"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function afkListCommand(player, message) { if (AFKSet.size == 0) { room.sendAnnouncement( translate("nobodyInList", { list: 'AFK' }), player.id, announcementColor, 'bold', null ); return; } var cstm = translate("listAfk"); AFKSet.forEach((_, value) => { var p = room.getPlayer(value); if (p != null) cstm += p.name + `, `; }); cstm = cstm.substring(0, cstm.length - 2) + '.'; room.sendAnnouncement(cstm, player.id, announcementColor, 'bold', null); } function masterCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (parseInt(msgArray[0]) == masterPassword) { if (!masterList.includes(authArray[player.id][0])) { room.setPlayerAdmin(player.id, true); adminList = adminList.filter((a) => a[0] != authArray[player.id][0]); masterList.push(authArray[player.id][0]); room.sendAnnouncement( translate("nowMaster", { player: player.name}), null, announcementColor, 'bold', HaxNotification.CHAT ); } else { room.sendAnnouncement( translate("alreadyMaster"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } } /* ADMIN COMMANDS */ function restartCommand(player, message) { instantRestart(); } function restartSwapCommand(player, message) { room.stopGame(); swapButton(); startTimeout = setTimeout(() => { room.startGame(); }, 10); } function swapCommand(player, message) { if (playSituation == Situation.STOP) { swapButton(); room.sendAnnouncement( translate("teamsSwapped"), null, announcementColor, 'bold', null ); } else { room.sendAnnouncement( translate("stopBefore"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function kickTeamCommand(player, message) { var msgArray = message.split(/ +/); var reasonString = translate("teamKick", { player: player.name }); if (msgArray.length > 1) { reasonString = msgArray.slice(1).join(' '); } if (['!kickred', '!kickr'].includes(msgArray[0].toLowerCase())) { for (let i = 0; i < teamRed.length; i++) { setTimeout(() => { room.kickPlayer(teamRed[0].id, reasonString, false); }, i * 20) } } else if (['!kickblue', '!kickb'].includes(msgArray[0].toLowerCase())) { for (let i = 0; i < teamBlue.length; i++) { setTimeout(() => { room.kickPlayer(teamBlue[0].id, reasonString, false); }, i * 20) } } else if (['!kickspec', '!kicks'].includes(msgArray[0].toLowerCase())) { for (let i = 0; i < teamSpec.length; i++) { setTimeout(() => { room.kickPlayer(teamSpec[0].id, reasonString, false); }, i * 20) } } } function stadiumCommand(player, message) { var msgArray = message.split(/ +/); if (gameState == State.STOP) { if (['!classic'].includes(msgArray[0].toLowerCase())) { if (JSON.parse(classicMap).name == 'Classic') { room.setDefaultStadium('Classic'); } else { room.setCustomStadium(classicMap); } currentStadium = 'classic'; } else if (['!big'].includes(msgArray[0].toLowerCase())) { if (JSON.parse(bigMap).name == 'Big') { room.setDefaultStadium('Big'); } else { room.setCustomStadium(bigMap); } currentStadium = 'big'; } else if (['!training'].includes(msgArray[0].toLowerCase())) { room.setCustomStadium(trainingMap); currentStadium = 'training'; } else { room.sendAnnouncement( translate("stadiumNotFound"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( translate("stopBefore"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function muteCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (msgArray.length > 0) { if (msgArray[0].length > 0 && msgArray[0][0] == '#') { msgArray[0] = msgArray[0].substring(1, msgArray[0].length); if (room.getPlayer(parseInt(msgArray[0])) != null) { var playerMute = room.getPlayer(parseInt(msgArray[0])); var minutesMute = muteDuration; if (msgArray.length > 1 && parseInt(msgArray[1]) > 0) { minutesMute = parseInt(msgArray[1]); } if (!playerMute.admin) { var muteObj = new MutePlayer(playerMute.name, playerMute.id, authArray[playerMute.id][0]); muteObj.setDuration(minutesMute); room.sendAnnouncement( translate("haveBeenMuted", { player: playerMute.name, mins: minutesMute }), null, announcementColor, 'bold', null ); } else { room.sendAnnouncement( translate("youCantMuteAdmin"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( translate("muteNotFound"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( translate("invalidFormat"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( translate("wrongArgsNumber", { command: '!help mute' }), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function unmuteCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (msgArray.length > 0) { if (msgArray[0].length > 0 && msgArray[0][0] == '#') { msgArray[0] = msgArray[0].substring(1, msgArray[0].length); if (room.getPlayer(parseInt(msgArray[0])) != null) { var playerUnmute = room.getPlayer(parseInt(msgArray[0])); if (muteArray.getByPlayerId(playerUnmute.id) != null) { var muteObj = muteArray.getByPlayerId(playerUnmute.id); muteObj.remove() room.sendAnnouncement( translate("haveBeenUnmuted", { player: playerUnmute.name }), null, announcementColor, 'bold', HaxNotification.CHAT ); } else { room.sendAnnouncement( translate("isntMuted"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( translate("muteNotFound"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else if (msgArray[0].length > 0 && parseInt(msgArray[0]) > 0 && muteArray.getById(parseInt(msgArray[0])) != null) { var playerUnmute = muteArray.getById(parseInt(msgArray[0])); playerUnmute.remove(); room.sendAnnouncement( translate("haveBeenUnmuted", { player: playerUnmute.name }), null, announcementColor, 'bold', HaxNotification.CHAT ); } else { room.sendAnnouncement( translate("invalidFormat"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( translate("wrongArgsNumber", { command: '!help mute' }), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function muteListCommand(player, message) { if (muteArray.list.length == 0) { room.sendAnnouncement( translate("emptyMuteList"), player.id, announcementColor, 'bold', null ); return false; } var cstm = translate("muteList"); for (let mute of muteArray.list) { cstm += mute.name + `[${mute.id}], `; } cstm = cstm.substring(0, cstm.length - 2) + '.'; room.sendAnnouncement( cstm, player.id, announcementColor, 'bold', null ); } /* MASTER COMMANDS */ function clearbansCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (msgArray.length == 0) { room.clearBans(); room.sendAnnouncement( translate("bansCleared"), null, announcementColor, 'bold', null ); banList = []; } else if (msgArray.length == 1) { if (parseInt(msgArray[0]) > 0) { var ID = parseInt(msgArray[0]); room.clearBan(ID); if (banList.length != banList.filter((p) => p[1] != ID).length) { room.sendAnnouncement( translate("unbannedFromRoom", { player: banList.filter((p) => p[1] == ID)[0][0] }), null, announcementColor, 'bold', null ); } else { room.sendAnnouncement( translate("clearBanIdNotFound"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } banList = banList.filter((p) => p[1] != ID); } else { room.sendAnnouncement( translate("clearBanIdNotFound"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( translate("wrongArgsNumber", { command: '!help clearbans' }), player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function banListCommand(player, message) { if (banList.length == 0) { room.sendAnnouncement( translate("nobodyInList", { list: 'de baneados' }), player.id, announcementColor, 'bold', null ); return false; } var cstm = translate("listBan"); for (let ban of banList) { cstm += ban[0] + `[${ban[1]}], `; } cstm = cstm.substring(0, cstm.length - 2) + '.'; room.sendAnnouncement( cstm, player.id, announcementColor, 'bold', null ); } function adminListCommand(player, message) { if (adminList.length == 0) { room.sendAnnouncement( translate("nobodyInList", { list: 'de admins' }), player.id, announcementColor, 'bold', null ); return false; } var cstm = translate("listAdmin"); for (let i = 0; i < adminList.length; i++) { cstm += adminList[i][1] + `[${i}], `; } cstm = cstm.substring(0, cstm.length - 2) + '.'; room.sendAnnouncement( cstm, player.id, announcementColor, 'bold', null ); } function setAdminCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (msgArray.length > 0) { if (msgArray[0].length > 0 && msgArray[0][0] == '#') { msgArray[0] = msgArray[0].substring(1, msgArray[0].length); if (room.getPlayer(parseInt(msgArray[0])) != null) { var playerAdmin = room.getPlayer(parseInt(msgArray[0])); if (!adminList.map((a) => a[0]).includes(authArray[playerAdmin.id][0])) { if (!masterList.includes(authArray[playerAdmin.id][0])) { room.setPlayerAdmin(playerAdmin.id, true); adminList.push([authArray[playerAdmin.id][0], playerAdmin.name]); room.sendAnnouncement( `${playerAdmin.name} is now a room admin !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else { room.sendAnnouncement( `This player is a master already !`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( `This player is a permanent admin already !`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( `There is no player with such ID in the room. Enter "!help setadmin" for more information.`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( `Incorrect format for your argument. Enter "!help setadmin" for more information.`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( `Wrong number of arguments. Enter "!help setadmin" for more information.`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function removeAdminCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (msgArray.length > 0) { if (msgArray[0].length > 0 && msgArray[0][0] == '#') { msgArray[0] = msgArray[0].substring(1, msgArray[0].length); if (room.getPlayer(parseInt(msgArray[0])) != null) { var playerAdmin = room.getPlayer(parseInt(msgArray[0])); if (adminList.map((a) => a[0]).includes(authArray[playerAdmin.id][0])) { room.setPlayerAdmin(playerAdmin.id, false); adminList = adminList.filter((a) => a[0] != authArray[playerAdmin.id][0]); room.sendAnnouncement( `${playerAdmin.name} is not a room admin anymore !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else { room.sendAnnouncement( `This player isn't a permanent admin !`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( `There is no player with such ID in the room. Enter "!help removeadmin" for more information.`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else if (msgArray[0].length > 0 && parseInt(msgArray[0]) < adminList.length) { var index = parseInt(msgArray[0]); var playerAdmin = adminList[index]; if (playersAll.findIndex((p) => authArray[p.id][0] == playerAdmin[0]) != -1) { // check if there is the removed admin in the room var indexRem = playersAll.findIndex((p) => authArray[p.id][0] == playerAdmin[0]); room.setPlayerAdmin(playersAll[indexRem].id, false); } adminList.splice(index); room.sendAnnouncement( `${playerAdmin[1]} is not a room admin anymore !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else { room.sendAnnouncement( `Incorrect format for your argument. Enter "!help removeadmin" for more information.`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } else { room.sendAnnouncement( `Wrong number of arguments. Enter "!help removeadmin" for more information.`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } function passwordCommand(player, message) { var msgArray = message.split(/ +/).slice(1); if (msgArray.length > 0) { if (msgArray.length == 1 && msgArray[0] == '') { roomPassword = ''; room.setPassword(null); room.sendAnnouncement( `The room password has been removed.`, player.id, announcementColor, 'bold', HaxNotification.CHAT ); } roomPassword = msgArray.join(' '); room.setPassword(roomPassword); room.sendAnnouncement( `The room password has been set to ${roomPassword}`, player.id, announcementColor, 'bold', HaxNotification.CHAT ); } else { if (roomPassword != '') { roomPassword = ''; room.setPassword(null); room.sendAnnouncement( `The room password has been removed.`, player.id, announcementColor, 'bold', HaxNotification.CHAT ); } else { room.sendAnnouncement( `The room currently does not have a password. Enter "!help password" for more information.`, player.id, errorColor, 'bold', HaxNotification.CHAT ); } } } /* GAME FUNCTIONS */ function checkTime() { const scores = room.getScores(); if (game != undefined) game.scores = scores; if (Math.abs(scores.time - scores.timeLimit) <= 0.01 && scores.timeLimit != 0 && playSituation == Situation.PLAY) { if (scores.red != scores.blue) { if (!checkTimeVariable) { checkTimeVariable = true; setTimeout(() => { checkTimeVariable = false; }, 3000); scores.red > scores.blue ? endGame(Team.RED) : endGame(Team.BLUE); stopTimeout = setTimeout(() => { room.stopGame(); }, 2000); } return; } if (drawTimeLimit != 0) { goldenGoal = true; room.sendAnnouncement( translate("firstGoalWins"), null, announcementColor, 'bold', HaxNotification.CHAT ); } } if (Math.abs(scores.time - drawTimeLimit * 60 - scores.timeLimit) <= 0.01 && scores.timeLimit != 0) { if (!checkTimeVariable) { checkTimeVariable = true; setTimeout(() => { checkTimeVariable = false; }, 10); endGame(Team.SPECTATORS); room.stopGame(); goldenGoal = false; } } } function instantRestart() { room.stopGame(); startTimeout = setTimeout(() => { room.startGame(); }, 10); } function resumeGame() { startTimeout = setTimeout(() => { room.startGame(); }, 1000); setTimeout(() => { room.pauseGame(false); }, 500); } function endGame(winner) { if (players.length >= 2 * teamSize - 1) activateChooseMode(); const scores = room.getScores(); game.scores = scores; lastWinner = winner; endGameVariable = true; if (winner == Team.RED) { streak++; room.sendAnnouncement( translate("redTeamWins", { blue: scores.blue, red: scores.red, streak: streak }), null, redColor, 'bold', HaxNotification.CHAT ); } else if (winner == Team.BLUE) { streak = 1; room.sendAnnouncement( translate("blueTeamWins", { blue: scores.blue, red: scores.red, streak: streak }), null, blueColor, 'bold', HaxNotification.CHAT ); } else { streak = 0; room.sendAnnouncement( '💤 Draw limit reached !', null, announcementColor, 'bold', HaxNotification.CHAT ); } let possessionRedPct = (possession[0] / (possession[0] + possession[1])) * 100; let possessionBluePct = 100 - possessionRedPct; let possessionString = `🔴 ${possessionRedPct.toFixed(0)}% - ${possessionBluePct.toFixed(0)}% 🔵`; let actionRedPct = (actionZoneHalf[0] / (actionZoneHalf[0] + actionZoneHalf[1])) * 100; let actionBluePct = 100 - actionRedPct; let actionString = `🔴 ${actionRedPct.toFixed(0)}% - ${actionBluePct.toFixed(0)}% 🔵`; let CSString = getCSString(scores); room.sendAnnouncement( translate("possession", { possession: possessionString }) + translate("actionZone", { action: actionString }) + `${CSString}`, null, announcementColor, 'bold', HaxNotification.NONE ); updateStats(); } /* CHOOSING FUNCTIONS */ function activateChooseMode() { chooseMode = true; slowMode = chooseModeSlowMode; room.sendAnnouncement( translate("slowModeChanged", { secs: chooseModeSlowMode }), null, announcementColor, 'bold', HaxNotification.CHAT ); } function deactivateChooseMode() { chooseMode = false; clearTimeout(timeOutCap); if (slowMode != defaultSlowMode) { slowMode = defaultSlowMode; room.sendAnnouncement( translate("slowModeChanged", { secs: defaultSlowMode }), null, announcementColor, 'bold', HaxNotification.CHAT ); } redCaptainChoice = ''; blueCaptainChoice = ''; } function getSpecList(player) { if (player == null) return null; var cstm = translate("players"); for (let i = 0; i < teamSpec.length; i++) { cstm += teamSpec[i].name + `[${i + 1}], `; } cstm = cstm.substring(0, cstm.length - 2) + '.'; room.sendAnnouncement( cstm, player.id, infoColor, 'bold', HaxNotification.CHAT ); } function choosePlayer() { clearTimeout(timeOutCap); let captain; if (teamRed.length <= teamBlue.length && teamRed.length != 0) { captain = teamRed[0]; } else if (teamBlue.length < teamRed.length && teamBlue.length != 0) { captain = teamBlue[0]; } if (captain != null) { room.sendAnnouncement( translate("toChooseAPlayer"), captain.id, infoColor, 'bold', HaxNotification.MENTION ); timeOutCap = setTimeout( (player) => { room.sendAnnouncement( translate("chooseHurryUp", { player: player.name, secs: Number.parseInt(String(chooseTime / 2)) }), player.id, warningColor, 'bold', HaxNotification.MENTION ); timeOutCap = setTimeout( (player) => { room.kickPlayer( player.id, translate("didntChoose"), false ); }, chooseTime * 500, captain ); }, chooseTime * 1000, captain ); } if (teamRed.length != 0 && teamBlue.length != 0) { getSpecList(teamRed.length <= teamBlue.length ? teamRed[0] : teamBlue[0]); } } function chooseModeFunction(player, message) { var msgArray = message.split(/ +/); if (player.id == teamRed[0].id || player.id == teamBlue[0].id) { if (teamRed.length <= teamBlue.length && player.id == teamRed[0].id) { if (['top', 'auto'].includes(msgArray[0].toLowerCase())) { room.setPlayerTeam(teamSpec[0].id, Team.RED); redCaptainChoice = 'top'; clearTimeout(timeOutCap); room.sendAnnouncement( `${player.name} chose Top !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else if (['random', 'rand'].includes(msgArray[0].toLowerCase())) { var r = getRandomInt(teamSpec.length); room.setPlayerTeam(teamSpec[r].id, Team.RED); redCaptainChoice = 'random'; clearTimeout(timeOutCap); room.sendAnnouncement( `${player.name} chose Random !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else if (['bottom', 'bot'].includes(msgArray[0].toLowerCase())) { room.setPlayerTeam(teamSpec[teamSpec.length - 1].id, Team.RED); redCaptainChoice = 'bottom'; clearTimeout(timeOutCap); room.sendAnnouncement( `${player.name} chose Bottom !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else if (!Number.isNaN(Number.parseInt(msgArray[0]))) { if (Number.parseInt(msgArray[0]) > teamSpec.length || Number.parseInt(msgArray[0]) < 1) { room.sendAnnouncement( translate("invalidNumber"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } else { room.setPlayerTeam( teamSpec[Number.parseInt(msgArray[0]) - 1].id, Team.RED ); room.sendAnnouncement( `${player.name} chose ${teamSpec[Number.parseInt(msgArray[0]) - 1].name} !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } } else return false; return true; } if (teamRed.length > teamBlue.length && player.id == teamBlue[0].id) { if (['top', 'auto'].includes(msgArray[0].toLowerCase())) { room.setPlayerTeam(teamSpec[0].id, Team.BLUE); blueCaptainChoice = 'top'; clearTimeout(timeOutCap); room.sendAnnouncement( `${player.name} chose Top !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else if (['random', 'rand'].includes(msgArray[0].toLowerCase())) { room.setPlayerTeam( teamSpec[getRandomInt(teamSpec.length)].id, Team.BLUE ); blueCaptainChoice = 'random'; clearTimeout(timeOutCap); room.sendAnnouncement( `${player.name} chose Random !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else if (['bottom', 'bot'].includes(msgArray[0].toLowerCase())) { room.setPlayerTeam(teamSpec[teamSpec.length - 1].id, Team.BLUE); blueCaptainChoice = 'bottom'; clearTimeout(timeOutCap); room.sendAnnouncement( `${player.name} chose Bottom !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } else if (!Number.isNaN(Number.parseInt(msgArray[0]))) { if (Number.parseInt(msgArray[0]) > teamSpec.length || Number.parseInt(msgArray[0]) < 1) { room.sendAnnouncement( translate("invalidNumber"), player.id, errorColor, 'bold', HaxNotification.CHAT ); } else { room.setPlayerTeam( teamSpec[Number.parseInt(msgArray[0]) - 1].id, Team.BLUE ); room.sendAnnouncement( `${player.name} chose ${teamSpec[Number.parseInt(msgArray[0]) - 1].name} !`, null, announcementColor, 'bold', HaxNotification.CHAT ); } } else return false; return true; } } } function checkCaptainLeave(player) { if ( (teamRed.findIndex((red) => red.id == player.id) == 0 && chooseMode && teamRed.length <= teamBlue.length) || (teamBlue.findIndex((blue) => blue.id == player.id) == 0 && chooseMode && teamBlue.length < teamRed.length) ) { choosePlayer(); capLeft = true; setTimeout(() => { capLeft = false; }, 10); } } function slowModeFunction(player, message) { if (!player.admin) { if (!SMSet.has(player.id)) { SMSet.add(player.id); setTimeout( (number) => { SMSet.delete(number); }, slowMode * 1000, player.id ); } else { return true; } } return false; } /* PLAYER FUNCTIONS */ function updateTeams() { playersAll = room.getPlayerList(); players = playersAll.filter((p) => !AFKSet.has(p.id)); teamRed = players.filter((p) => p.team == Team.RED); teamBlue = players.filter((p) => p.team == Team.BLUE); teamSpec = players.filter((p) => p.team == Team.SPECTATORS); } function updateAdmins(excludedPlayerID = 0) { if (players.length != 0 && players.filter((p) => p.admin).length < maxAdmins) { let playerArray = players.filter((p) => p.id != excludedPlayerID && !p.admin); let arrayID = playerArray.map((player) => player.id); room.setPlayerAdmin(Math.min(...arrayID), true); } } function getRole(player) { return ( !!masterList.find((a) => a == authArray[player.id][0]) * 2 + !!adminList.find((a) => a[0] == authArray[player.id][0]) * 1 + player.admin * 1 ); } function ghostKickHandle(oldP, newP) { var teamArrayId = getTeamArray(oldP.team, true).map((p) => p.id); teamArrayId.splice(teamArrayId.findIndex((id) => id == oldP.id), 1, newP.id); room.kickPlayer(oldP.id, 'Ghost kick', false); room.setPlayerTeam(newP.id, oldP.team); room.setPlayerAdmin(newP.id, oldP.admin); room.reorderPlayers(teamArrayId, true); if (oldP.team != Team.SPECTATORS && playSituation != Situation.STOP) { var discProp = room.getPlayerDiscProperties(oldP.id); room.setPlayerDiscProperties(newP.id, discProp); } } /* ACTIVITY FUNCTIONS */ function handleActivityPlayer(player) { let pComp = getPlayerComp(player); if (pComp != null) { pComp.inactivityTicks++; if (pComp.inactivityTicks == 60 * ((2 / 3) * afkLimit)) { room.sendAnnouncement( translate("youWillBeKicked", { player: player.name, secs: Math.floor(afkLimit / 3) }), player.id, warningColor, 'bold', HaxNotification.MENTION ); return; } if (pComp.inactivityTicks >= 60 * afkLimit) { pComp.inactivityTicks = 0; if (game.scores.time <= afkLimit - 0.5) { setTimeout(() => { !chooseMode ? instantRestart() : room.stopGame(); }, 10); } room.kickPlayer(player.id, 'AFK', false); } } } function handleActivityPlayerTeamChange(changedPlayer) { if (changedPlayer.team == Team.SPECTATORS) { let pComp = getPlayerComp(changedPlayer); if (pComp != null) pComp.inactivityTicks = 0; } } function handleActivityStop() { for (let player of players) { let pComp = getPlayerComp(player); if (pComp != null) pComp.inactivityTicks = 0; } } function handleActivity() { if (gameState === State.PLAY && players.length > 1) { for (let player of teamRed) { handleActivityPlayer(player); } for (let player of teamBlue) { handleActivityPlayer(player); } } } /* LINEUP FUNCTIONS */ function getStartingLineups() { var compositions = [[], []]; for (let player of teamRed) { compositions[0].push( new PlayerComposition(player, authArray[player.id][0], [0], []) ); } for (let player of teamBlue) { compositions[1].push( new PlayerComposition(player, authArray[player.id][0], [0], []) ); } return compositions; } function handleLineupChangeTeamChange(changedPlayer) { if (gameState != State.STOP) { var playerLineup; if (changedPlayer.team == Team.RED) { // player gets in red team var redLineupAuth = game.playerComp[0].map((p) => p.auth); var ind = redLineupAuth.findIndex((auth) => auth == authArray[changedPlayer.id][0]); if (ind != -1) { // Player goes back in playerLineup = game.playerComp[0][ind]; if (playerLineup.timeExit.includes(game.scores.time)) { // gets subbed off then in at the exact same time -> no sub playerLineup.timeExit = playerLineup.timeExit.filter((t) => t != game.scores.time); } else { playerLineup.timeEntry.push(game.scores.time); } } else { playerLineup = new PlayerComposition( changedPlayer, authArray[changedPlayer.id][0], [game.scores.time], [] ); game.playerComp[0].push(playerLineup); } } else if (changedPlayer.team == Team.BLUE) { // player gets in blue team var blueLineupAuth = game.playerComp[1].map((p) => p.auth); var ind = blueLineupAuth.findIndex((auth) => auth == authArray[changedPlayer.id][0]); if (ind != -1) { // Player goes back in playerLineup = game.playerComp[1][ind]; if (playerLineup.timeExit.includes(game.scores.time)) { // gets subbed off then in at the exact same time -> no sub playerLineup.timeExit = playerLineup.timeExit.filter((t) => t != game.scores.time); } else { playerLineup.timeEntry.push(game.scores.time); } } else { playerLineup = new PlayerComposition( changedPlayer, authArray[changedPlayer.id][0], [game.scores.time], [] ); game.playerComp[1].push(playerLineup); } } if (teamRed.some((r) => r.id == changedPlayer.id)) { // player leaves red team var redLineupAuth = game.playerComp[0].map((p) => p.auth); var ind = redLineupAuth.findIndex((auth) => auth == authArray[changedPlayer.id][0]); playerLineup = game.playerComp[0][ind]; if (playerLineup.timeEntry.includes(game.scores.time)) { // gets subbed off then in at the exact same time -> no sub if (game.scores.time == 0) { game.playerComp[0].splice(ind, 1); } else { playerLineup.timeEntry = playerLineup.timeEntry.filter((t) => t != game.scores.time); } } else { playerLineup.timeExit.push(game.scores.time); } } else if (teamBlue.some((r) => r.id == changedPlayer.id)) { // player leaves blue team var blueLineupAuth = game.playerComp[1].map((p) => p.auth); var ind = blueLineupAuth.findIndex((auth) => auth == authArray[changedPlayer.id][0]); playerLineup = game.playerComp[1][ind]; if (playerLineup.timeEntry.includes(game.scores.time)) { // gets subbed off then in at the exact same time -> no sub if (game.scores.time == 0) { game.playerComp[1].splice(ind, 1); } else { playerLineup.timeEntry = playerLineup.timeEntry.filter((t) => t != game.scores.time); } } else { playerLineup.timeExit.push(game.scores.time); } } } } function handleLineupChangeLeave(player) { if (playSituation != Situation.STOP) { if (player.team == Team.RED) { // player gets in red team var redLineupAuth = game.playerComp[0].map((p) => p.auth); var ind = redLineupAuth.findIndex((auth) => auth == authArray[player.id][0]); var playerLineup = game.playerComp[0][ind]; if (playerLineup.timeEntry.includes(game.scores.time)) { // gets subbed off then in at the exact same time -> no sub if (game.scores.time == 0) { game.playerComp[0].splice(ind, 1); } else { playerLineup.timeEntry = playerLineup.timeEntry.filter((t) => t != game.scores.time); } } else { playerLineup.timeExit.push(game.scores.time); } } else if (player.team == Team.BLUE) { // player gets in blue team var blueLineupAuth = game.playerComp[1].map((p) => p.auth); var ind = blueLineupAuth.findIndex((auth) => auth == authArray[player.id][0]); var playerLineup = game.playerComp[1][ind]; if (playerLineup.timeEntry.includes(game.scores.time)) { // gets subbed off then in at the exact same time -> no sub if (game.scores.time == 0) { game.playerComp[1].splice(ind, 1); } else { playerLineup.timeEntry = playerLineup.timeEntry.filter((t) => t != game.scores.time); } } else { playerLineup.timeExit.push(game.scores.time); } } } } /* TEAM BALANCE FUNCTIONS */ function balanceTeams() { if (!chooseMode) { if (players.length == 0) { room.stopGame(); room.setScoreLimit(scoreLimit); room.setTimeLimit(timeLimit); } else if (players.length == 1 && teamRed.length == 0) { instantRestart(); setTimeout(() => { stadiumCommand(emptyPlayer, `!training`); }, 5); room.setPlayerTeam(players[0].id, Team.RED); } else if (Math.abs(teamRed.length - teamBlue.length) == teamSpec.length && teamSpec.length > 0) { const n = Math.abs(teamRed.length - teamBlue.length); if (players.length == 2) { instantRestart(); setTimeout(() => { stadiumCommand(emptyPlayer, `!classic`); }, 5); } if (teamRed.length > teamBlue.length) { for (var i = 0; i < n; i++) { room.setPlayerTeam(teamSpec[i].id, Team.BLUE); } } else { for (var i = 0; i < n; i++) { room.setPlayerTeam(teamSpec[i].id, Team.RED); } } } else if (Math.abs(teamRed.length - teamBlue.length) > teamSpec.length) { const n = Math.abs(teamRed.length - teamBlue.length); if (players.length == 1) { instantRestart(); setTimeout(() => { stadiumCommand(emptyPlayer, `!training`); }, 5); room.setPlayerTeam(players[0].id, Team.RED); return; } else if (teamSize > 2 && players.length == changeToClassicMapOn) { // antes: (teamSize > 2 && players.length == 5) instantRestart(); setTimeout(() => { stadiumCommand(emptyPlayer, `!classic`); }, 5); } if (players.length == teamSize * 2 - 1) { teamRedStats = []; teamBlueStats = []; } if (teamRed.length > teamBlue.length) { for (var i = 0; i < n; i++) { room.setPlayerTeam( teamRed[teamRed.length - 1 - i].id, Team.SPECTATORS ); } } else { for (var i = 0; i < n; i++) { room.setPlayerTeam( teamBlue[teamBlue.length - 1 - i].id, Team.SPECTATORS ); } } } else if (Math.abs(teamRed.length - teamBlue.length) < teamSpec.length && teamRed.length != teamBlue.length) { room.pauseGame(true); activateChooseMode(); choosePlayer(); } else if (teamSpec.length >= 2 && teamRed.length == teamBlue.length && teamRed.length < teamSize) { if (teamRed.length == 2) { instantRestart(); setTimeout(() => { stadiumCommand(emptyPlayer, `!big`); }, 5); } topButton(); } } } function handlePlayersJoin() { if (chooseMode) { if (teamSize > 3 && players.length == changeToBigMapOn) { // ANTES: (teamSize > 2 && players.length == 6) setTimeout(() => { stadiumCommand(emptyPlayer, `!big`); }, 5); } getSpecList(teamRed.length <= teamBlue.length ? teamRed[0] : teamBlue[0]); } balanceTeams(); } function handlePlayersLeave() { if (gameState != State.STOP) { var scores = room.getScores(); if (players.length >= 2 * teamSize && scores.time >= (5 / 6) * game.scores.timeLimit && teamRed.length != teamBlue.length) { var rageQuitCheck = false; if (teamRed.length < teamBlue.length) { if (scores.blue - scores.red == 2) { endGame(Team.BLUE); rageQuitCheck = true; } } else { if (scores.red - scores.blue == 2) { endGame(Team.RED); rageQuitCheck = true; } } if (rageQuitCheck) { room.sendAnnouncement( "Ragequit detected, game ended.", null, infoColor, 'bold', HaxNotification.MENTION ) stopTimeout = setTimeout(() => { room.stopGame(); }, 100); return; } } } if (chooseMode) { if (teamSize > 2 && players.length == 5) { setTimeout(() => { stadiumCommand(emptyPlayer, `!classic`); }, 5); } if (teamRed.length == 0 || teamBlue.length == 0) { room.setPlayerTeam(teamSpec[0].id, teamRed.length == 0 ? Team.RED : Team.BLUE); return; } if (Math.abs(teamRed.length - teamBlue.length) == teamSpec.length) { deactivateChooseMode(); resumeGame(); var b = teamSpec.length; if (teamRed.length > teamBlue.length) { for (var i = 0; i < b; i++) { clearTimeout(insertingTimeout); insertingPlayers = true; setTimeout(() => { room.setPlayerTeam(teamSpec[0].id, Team.BLUE); }, 5 * i); } insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 5 * b); } else { for (var i = 0; i < b; i++) { clearTimeout(insertingTimeout); insertingPlayers = true; setTimeout(() => { room.setPlayerTeam(teamSpec[0].id, Team.RED); }, 5 * i); } insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 5 * b); } return; } if (streak == 0 && gameState == State.STOP) { if (Math.abs(teamRed.length - teamBlue.length) == 2) { var teamIn = teamRed.length > teamBlue.length ? teamRed : teamBlue; room.setPlayerTeam(teamIn[teamIn.length - 1].id, Team.SPECTATORS) } } if (teamRed.length == teamBlue.length && teamSpec.length < 2) { deactivateChooseMode(); resumeGame(); return; } if (capLeft) { choosePlayer(); } else { getSpecList(teamRed.length <= teamBlue.length ? teamRed[0] : teamBlue[0]); } } balanceTeams(); } function handlePlayersTeamChange(byPlayer) { if (chooseMode && !removingPlayers && byPlayer == null) { if (Math.abs(teamRed.length - teamBlue.length) == teamSpec.length) { deactivateChooseMode(); resumeGame(); var b = teamSpec.length; if (teamRed.length > teamBlue.length) { for (var i = 0; i < b; i++) { clearTimeout(insertingTimeout); insertingPlayers = true; setTimeout(() => { room.setPlayerTeam(teamSpec[0].id, Team.BLUE); }, 5 * i); } insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 5 * b); } else { for (var i = 0; i < b; i++) { clearTimeout(insertingTimeout); insertingPlayers = true; setTimeout(() => { room.setPlayerTeam(teamSpec[0].id, Team.RED); }, 5 * i); } insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 5 * b); } return; } else if ( (teamRed.length == teamSize && teamBlue.length == teamSize) || (teamRed.length == teamBlue.length && teamSpec.length < 2) ) { deactivateChooseMode(); resumeGame(); } else if (teamRed.length <= teamBlue.length && redCaptainChoice != '') { if (redCaptainChoice == 'top') { room.setPlayerTeam(teamSpec[0].id, Team.RED); } else if (redCaptainChoice == 'random') { var r = getRandomInt(teamSpec.length); room.setPlayerTeam(teamSpec[r].id, Team.RED); } else { room.setPlayerTeam(teamSpec[teamSpec.length - 1].id, Team.RED); } return; } else if (teamBlue.length < teamRed.length && blueCaptainChoice != '') { if (blueCaptainChoice == 'top') { room.setPlayerTeam(teamSpec[0].id, Team.BLUE); } else if (blueCaptainChoice == 'random') { var r = getRandomInt(teamSpec.length); room.setPlayerTeam(teamSpec[r].id, Team.BLUE); } else { room.setPlayerTeam(teamSpec[teamSpec.length - 1].id, Team.BLUE); } return; } else { choosePlayer(); } } } function handlePlayersStop(byPlayer) { if (byPlayer == null && endGameVariable) { if (chooseMode) { if (players.length == 2 * teamSize) { chooseMode = false; resetButton(); for (var i = 0; i < teamSize; i++) { clearTimeout(insertingTimeout); insertingPlayers = true; setTimeout(() => { randomButton(); }, 200 * i); } insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 200 * teamSize); startTimeout = setTimeout(() => { room.startGame(); }, 2000); } else { if (lastWinner == Team.RED) { blueToSpecButton(); } else if (lastWinner == Team.BLUE) { redToSpecButton(); setTimeout(() => { swapButton(); }, 10); } else { resetButton(); } clearTimeout(insertingTimeout); insertingPlayers = true; setTimeout(() => { topButton(); }, 300); insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 300); } } else { if (players.length == 2) { if (lastWinner == Team.BLUE) { swapButton(); } startTimeout = setTimeout(() => { room.startGame(); }, 2000); } else if (players.length == 3 || players.length >= 2 * teamSize + 1) { if (lastWinner == Team.RED) { blueToSpecButton(); } else { redToSpecButton(); setTimeout(() => { swapButton(); }, 5); } clearTimeout(insertingTimeout); insertingPlayers = true; setTimeout(() => { topButton(); }, 200); insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 300); startTimeout = setTimeout(() => { room.startGame(); }, 2000); } else if (players.length == 4) { resetButton(); clearTimeout(insertingTimeout); insertingPlayers = true; setTimeout(() => { randomButton(); setTimeout(() => { randomButton(); }, 500); }, 500); insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 2000); startTimeout = setTimeout(() => { room.startGame(); }, 2000); } else if (players.length == 5 || players.length >= 2 * teamSize + 1) { if (lastWinner == Team.RED) { blueToSpecButton(); } else { redToSpecButton(); setTimeout(() => { swapButton(); }, 5); } clearTimeout(insertingTimeout); insertingPlayers = true; insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 200); setTimeout(() => { topButton(); }, 200); activateChooseMode(); } else if (players.length == 6) { resetButton(); clearTimeout(insertingTimeout); insertingPlayers = true; insertingTimeout = setTimeout(() => { insertingPlayers = false; }, 1500); setTimeout(() => { randomButton(); setTimeout(() => { randomButton(); setTimeout(() => { randomButton(); }, 500); }, 500); }, 500); startTimeout = setTimeout(() => { room.startGame(); }, 2000); } } } } /* STATS FUNCTIONS */ /* GK FUNCTIONS */ function handleGKTeam(team) { if (team == Team.SPECTATORS) { return null; } let teamArray = team == Team.RED ? teamRed : teamBlue; let playerGK = teamArray.reduce((prev, current) => { if (team == Team.RED) { return (prev?.position.x < current.position.x) ? prev : current } else { return (prev?.position.x > current.position.x) ? prev : current } }, null); let playerCompGK = getPlayerComp(playerGK); return playerCompGK; } function handleGK() { let redGK = handleGKTeam(Team.RED); if (redGK != null) { redGK.GKTicks++; } let blueGK = handleGKTeam(Team.BLUE); if (blueGK != null) { blueGK.GKTicks++; } } function getGK(team) { if (team == Team.SPECTATORS) { return null; } let teamArray = team == Team.RED ? game.playerComp[0] : game.playerComp[1]; let playerGK = teamArray.reduce((prev, current) => { return (prev?.GKTicks > current.GKTicks) ? prev : current }, null); return playerGK; } function getCS(scores) { let playersNameCS = []; let redGK = getGK(Team.RED); let blueGK = getGK(Team.BLUE); if (redGK != null && scores.blue == 0) { playersNameCS.push(redGK.player.name); } if (blueGK != null && scores.red == 0) { playersNameCS.push(blueGK.player.name); } return playersNameCS; } function getCSString(scores) { let playersCS = getCS(scores); if (playersCS.length == 0) { return "🥅 No CS"; } else if (playersCS.length == 1) { return `🥅 ${playersCS[0]} had a CS.`; } else { return `🥅 ${playersCS[0]} and ${playersCS[1]} had a CS.`; } } /* GLOBAL STATS FUNCTIONS */ function getLastTouchOfTheBall() { const ballPosition = room.getBallPosition(); updateTeams(); let playerArray = []; for (let player of players) { if (player.position != null) { var distanceToBall = pointDistance(player.position, ballPosition); if (distanceToBall < triggerDistance) { if (playSituation == Situation.KICKOFF) playSituation = Situation.PLAY; playerArray.push([player, distanceToBall]); } } } if (playerArray.length != 0) { let playerTouch = playerArray.sort((a, b) => a[1] - b[1])[0][0]; if (lastTeamTouched == playerTouch.team || lastTeamTouched == Team.SPECTATORS) { if (lastTouches[0] == null || (lastTouches[0] != null && lastTouches[0].player.id != playerTouch.id)) { game.touchArray.push( new BallTouch( playerTouch, game.scores.time, getGoalGame(), ballPosition ) ); lastTouches[0] = checkGoalKickTouch( game.touchArray, game.touchArray.length - 1, getGoalGame() ); lastTouches[1] = checkGoalKickTouch( game.touchArray, game.touchArray.length - 2, getGoalGame() ); } } lastTeamTouched = playerTouch.team; } } function getBallSpeed() { var ballProp = room.getDiscProperties(0); return Math.sqrt(ballProp.xspeed ** 2 + ballProp.yspeed ** 2) * speedCoefficient; } function getGameStats() { if (playSituation == Situation.PLAY && gameState == State.PLAY) { lastTeamTouched == Team.RED ? possession[0]++ : possession[1]++; var ballPosition = room.getBallPosition(); ballPosition.x < 0 ? actionZoneHalf[0]++ : actionZoneHalf[1]++; handleGK(); } } /* GOAL ATTRIBUTION FUNCTIONS */ function getGoalAttribution(team) { var goalAttribution = Array(2).fill(null); if (lastTouches[0] != null) { if (lastTouches[0].player.team == team) { // Direct goal scored by player if (lastTouches[1] != null && lastTouches[1].player.team == team) { goalAttribution = [lastTouches[0].player, lastTouches[1].player]; } else { goalAttribution = [lastTouches[0].player, null]; } } else { // Own goal goalAttribution = [lastTouches[0].player, null]; } } return goalAttribution; } function getGoalString(team) { var goalString; var scores = game.scores; var goalAttribution = getGoalAttribution(team); if (goalAttribution[0] != null) { if (goalAttribution[0].team == team) { if (goalAttribution[1] != null && goalAttribution[1].team == team) { goalString = translate("goalByAssistBy", {time: getTimeGame(scores.time), goalBy: goalAttribution[0].name, assistBy: goalAttribution[1].name, speed: ballSpeed.toFixed(2) }); game.goals.push( new Goal( scores.time, team, goalAttribution[0], goalAttribution[1] ) ); } else { goalString = translate("goalBy", {time: getTimeGame(scores.time), goalBy: goalAttribution[0].name, speed: ballSpeed.toFixed(2) }); game.goals.push( new Goal(scores.time, team, goalAttribution[0], null) ); } } else { goalString = translate("ownGoalBy", {time: getTimeGame(scores.time), goalBy: goalAttribution[0].name, speed: ballSpeed.toFixed(2) }); game.goals.push( new Goal(scores.time, team, goalAttribution[0], null) ); } } else { goalString = translate("ownGoalBy", {time: getTimeGame(scores.time), team: team == Team.RED ? 'red' : 'blue', speed: ballSpeed.toFixed(2) }); game.goals.push( new Goal(scores.time, team, null, null) ); } return goalString; } /* ROOM STATS FUNCTIONS */ function updatePlayerStats(player, teamStats) { var stats = new HaxStatistics(player.name); var pComp = getPlayerComp(player); if (localStorage.getItem(authArray[player.id][0])) { stats = JSON.parse(localStorage.getItem(authArray[player.id][0])); } stats.games++; if (lastWinner == teamStats) stats.wins++; stats.winrate = ((100 * stats.wins) / (stats.games || 1)).toFixed(1) + `%`; stats.goals += getGoalsPlayer(pComp); stats.assists += getAssistsPlayer(pComp); stats.ownGoals += getOwnGoalsPlayer(pComp); stats.CS += getCSPlayer(pComp); stats.playtime += getGametimePlayer(pComp); localStorage.setItem(authArray[player.id][0], JSON.stringify(stats)); } function updateStats() { if ( players.length >= 2 * teamSize && ( game.scores.time >= (5 / 6) * game.scores.timeLimit || game.scores.red == game.scores.scoreLimit || game.scores.blue == game.scores.scoreLimit ) && teamRedStats.length >= teamSize && teamBlueStats.length >= teamSize ) { for (let player of teamRedStats) { updatePlayerStats(player, Team.RED); } for (let player of teamBlueStats) { updatePlayerStats(player, Team.BLUE); } } } function printRankings(statKey, id = 0) { var leaderboard = []; statKey = statKey == "cs" ? "CS" : statKey; for (var i = 0; i < localStorage.length; i++) { var key = localStorage.key(i); if (key.length == 43) leaderboard.push([ JSON.parse(localStorage.getItem(key)).playerName, JSON.parse(localStorage.getItem(key))[statKey], ]); } if (leaderboard.length < 5) { if (id != 0) { room.sendAnnouncement( 'Not enough games played yet !', id, errorColor, 'bold', HaxNotification.CHAT ); } return; } leaderboard.sort(function (a, b) { return b[1] - a[1]; }); var rankingString = `${statKey.charAt(0).toUpperCase() + statKey.slice(1)}> `; for (let i = 0; i < 5; i++) { let playerName = leaderboard[i][0]; let playerStat = leaderboard[i][1]; if (statKey == 'playtime') playerStat = getTimeStats(playerStat); rankingString += `#${i + 1} ${playerName} : ${playerStat}, `; } rankingString = rankingString.substring(0, rankingString.length - 2); room.sendAnnouncement( rankingString, id, infoColor, 'bold', HaxNotification.CHAT ); } /* GET STATS FUNCTIONS */ function getGamePlayerStats(player) { var stats = new HaxStatistics(player.name); var pComp = getPlayerComp(player); stats.goals += getGoalsPlayer(pComp); stats.assists += getAssistsPlayer(pComp); stats.ownGoals += getOwnGoalsPlayer(pComp); stats.playtime += getGametimePlayer(pComp); stats.CS += getCSPlayer(pComp); return stats; } function getGametimePlayer(pComp) { if (pComp == null) return 0; var timePlayer = 0; for (let j = 0; j < pComp.timeEntry.length; j++) { if (pComp.timeExit.length < j + 1) { timePlayer += game.scores.time - pComp.timeEntry[j]; } else { timePlayer += pComp.timeExit[j] - pComp.timeEntry[j]; } } return Math.floor(timePlayer); } function getGoalsPlayer(pComp) { if (pComp == null) return 0; var goalPlayer = 0; for (let goal of game.goals) { if (goal.striker != null && goal.team === pComp.player.team) { if (authArray[goal.striker.id][0] == pComp.auth) { goalPlayer++; } } } return goalPlayer; } function getOwnGoalsPlayer(pComp) { if (pComp == null) return 0; var goalPlayer = 0; for (let goal of game.goals) { if (goal.striker != null && goal.team !== pComp.player.team) { if (authArray[goal.striker.id][0] == pComp.auth) { goalPlayer++; } } } return goalPlayer; } function getAssistsPlayer(pComp) { if (pComp == null) return 0; var assistPlayer = 0; for (let goal of game.goals) { if (goal.assist != null) { if (authArray[goal.assist.id][0] == pComp.auth) { assistPlayer++; } } } return assistPlayer; } function getGKPlayer(pComp) { if (pComp == null) return 0; let GKRed = getGK(Team.RED); if (pComp.auth == GKRed?.auth) { return Team.RED; } let GKBlue = getGK(Team.BLUE); if (pComp.auth == GKBlue?.auth) { return Team.BLUE; } return Team.SPECTATORS; } function getCSPlayer(pComp) { if (pComp == null || game.scores == null) return 0; if (getGKPlayer(pComp) == Team.RED && game.scores.blue == 0) { return 1; } else if (getGKPlayer(pComp) == Team.BLUE && game.scores.red == 0) { return 1; } return 0; } function actionReportCountTeam(goals, team) { let playerActionSummaryTeam = []; let indexTeam = team == Team.RED ? 0 : 1; let indexOtherTeam = team == Team.RED ? 1 : 0; for (let goal of goals[indexTeam]) { if (goal[0] != null) { if (playerActionSummaryTeam.find(a => a[0].id == goal[0].id)) { let index = playerActionSummaryTeam.findIndex(a => a[0].id == goal[0].id); playerActionSummaryTeam[index][1]++; } else { playerActionSummaryTeam.push([goal[0], 1, 0, 0]); } if (goal[1] != null) { if (playerActionSummaryTeam.find(a => a[0].id == goal[1].id)) { let index = playerActionSummaryTeam.findIndex(a => a[0].id == goal[1].id); playerActionSummaryTeam[index][2]++; } else { playerActionSummaryTeam.push([goal[1], 0, 1, 0]); } } } } if (goals[indexOtherTeam].length == 0) { let playerCS = getGK(team)?.player; if (playerCS != null) { if (playerActionSummaryTeam.find(a => a[0].id == playerCS.id)) { let index = playerActionSummaryTeam.findIndex(a => a[0].id == playerCS.id); playerActionSummaryTeam[index][3]++; } else { playerActionSummaryTeam.push([playerCS, 0, 0, 1]); } } } playerActionSummaryTeam.sort((a, b) => (a[1] + a[2] + a[3]) - (b[1] + b[2] + b[3])); return playerActionSummaryTeam; } /* PRINT FUNCTIONS */ function printPlayerStats(stats) { let statsString = ''; for (let [key, value] of Object.entries(stats)) { if (key == 'playerName') statsString += `${value}: `; else { if (key == 'playtime') value = getTimeStats(value); let reCamelCase = /([A-Z](?=[a-z]+)|[A-Z]+(?![a-z]))/g; let statName = key.replaceAll(reCamelCase, ' $1').trim(); statsString += `${statName.charAt(0).toUpperCase() + statName.slice(1)}: ${value}, `; } } statsString = statsString.substring(0, statsString.length - 2); return statsString; } /* FETCH FUNCTIONS */ function fetchGametimeReport(game) { var fieldGametimeRed = { name: '🔴 **RED TEAM STATS**', value: '⌛ __**Game Time:**__\n\n', inline: true, }; var fieldGametimeBlue = { name: '🔵 **BLUE TEAM STATS**', value: '⌛ __**Game Time:**__\n\n', inline: true, }; var redTeamTimes = game.playerComp[0].map((p) => [p.player, getGametimePlayer(p)]); var blueTeamTimes = game.playerComp[1].map((p) => [p.player, getGametimePlayer(p)]); for (let time of redTeamTimes) { var minutes = getMinutesReport(time[1]); var seconds = getSecondsReport(time[1]); fieldGametimeRed.value += `> **${time[0].name}:** ${minutes > 0 ? `${minutes}m` : ''}` + `${seconds > 0 || minutes == 0 ? `${seconds}s` : ''}\n`; } fieldGametimeRed.value += `\n${blueTeamTimes.length - redTeamTimes.length > 0 ? '\n'.repeat(blueTeamTimes.length - redTeamTimes.length) : '' }`; fieldGametimeRed.value += '====================='; for (let time of blueTeamTimes) { var minutes = getMinutesReport(time[1]); var seconds = getSecondsReport(time[1]); fieldGametimeBlue.value += `> **${time[0].name}:** ${minutes > 0 ? `${minutes}m` : ''}` + `${seconds > 0 || minutes == 0 ? `${seconds}s` : ''}\n`; } fieldGametimeBlue.value += `\n${redTeamTimes.length - blueTeamTimes.length > 0 ? '\n'.repeat(redTeamTimes.length - blueTeamTimes.length) : '' }`; fieldGametimeBlue.value += '====================='; return [fieldGametimeRed, fieldGametimeBlue]; } function fetchActionsSummaryReport(game) { var fieldReportRed = { name: '🔴 **RED TEAM STATS**', value: '📊 __**Player Stats:**__\n\n', inline: true, }; var fieldReportBlue = { name: '🔵 **BLUE TEAM STATS**', value: '📊 __**Player Stats:**__\n\n', inline: true, }; var goals = [[], []]; for (let i = 0; i < game.goals.length; i++) { goals[game.goals[i].team - 1].push([game.goals[i].striker, game.goals[i].assist]); } var redActions = actionReportCountTeam(goals, Team.RED); if (redActions.length > 0) { for (let act of redActions) { fieldReportRed.value += `> **${act[0].team != Team.RED ? '[OG] ' : ''}${act[0].name}:**` + `${act[1] > 0 ? ` ${act[1]}G` : ''}` + `${act[2] > 0 ? ` ${act[2]}A` : ''}` + `${act[3] > 0 ? ` ${act[3]}CS` : ''}\n`; } } var blueActions = actionReportCountTeam(goals, Team.BLUE); if (blueActions.length > 0) { for (let act of blueActions) { fieldReportBlue.value += `> **${act[0].team != Team.BLUE ? '[OG] ' : ''}${act[0].name}:**` + `${act[1] > 0 ? ` ${act[1]}G` : ''}` + `${act[2] > 0 ? ` ${act[2]}A` : ''}` + `${act[3] > 0 ? ` ${act[3]}CS` : ''}\n`; } } fieldReportRed.value += `\n${blueActions.length - redActions.length > 0 ? '\n'.repeat(blueActions.length - redActions.length) : '' }`; fieldReportRed.value += '====================='; fieldReportBlue.value += `\n${redActions.length - blueActions.length > 0 ? '\n'.repeat(redActions.length - blueActions.length) : '' }`; fieldReportBlue.value += '====================='; return [fieldReportRed, fieldReportBlue]; } function fetchSummaryEmbed(game) { var fetchEndgame = [fetchGametimeReport, fetchActionsSummaryReport]; var logChannel = gameWebhook; var fields = [ { name: '🔴 **RED TEAM STATS**', value: '=====================\n\n', inline: true, }, { name: '🔵 **BLUE TEAM STATS**', value: '=====================\n\n', inline: true, }, ]; for (let i = 0; i < fetchEndgame.length; i++) { var fieldsReport = fetchEndgame[i](game); fields[0].value += fieldsReport[0].value + '\n\n'; fields[1].value += fieldsReport[1].value + '\n\n'; } fields[0].value = fields[0].value.substring(0, fields[0].value.length - 2); fields[1].value = fields[1].value.substring(0, fields[1].value.length - 2); var possR = possession[0] / (possession[0] + possession[1]); var possB = 1 - possR; var possRString = (possR * 100).toFixed(0).toString(); var possBString = (possB * 100).toFixed(0).toString(); var zoneR = actionZoneHalf[0] / (actionZoneHalf[0] + actionZoneHalf[1]); var zoneB = 1 - zoneR; var zoneRString = (zoneR * 100).toFixed(0).toString(); var zoneBString = (zoneB * 100).toFixed(0).toString(); var win = (game.scores.red > game.scores.blue) * 1 + (game.scores.blue > game.scores.red) * 2; var objectBodyWebhook = { embeds: [ { title: `📝 MATCH REPORT #${getIdReport()}`, description: `**${getTimeEmbed(game.scores.time)}** ` + (win == 1 ? '**Red Team** ' : 'Red Team ') + game.scores.red + ' - ' + game.scores.blue + (win == 2 ? ' **Blue Team**' : ' Blue Team') + '\n```c\nPossession: ' + possRString + '% - ' + possBString + '%' + '\nAction Zone: ' + zoneRString + '% - ' + zoneBString + '%\n```\n\n', color: 9567999, fields: fields, footer: { text: `Recording: ${getRecordingName(game)}`, }, timestamp: new Date().toISOString(), }, ], username: roomName }; if (logChannel != '') { fetch(logChannel, { method: 'POST', body: JSON.stringify(objectBodyWebhook), headers: { 'Content-Type': 'application/json', }, }).then((res) => res); } } /* EVENTS */ /* PLAYER MOVEMENT */ room.onPlayerJoin = function (player) { authArray[player.id] = [player.auth, player.conn]; if (roomWebhook != '') { fetch(roomWebhook, { method: 'POST', body: JSON.stringify({ content: `[${getDate()}] ➡️ JOIN (${playersAll.length + 1}/${maxPlayers})\n**` + `${player.name}** [${authArray[player.id][0]}] {${authArray[player.id][1]}}`, username: webhookName, }), headers: { 'Content-Type': 'application/json', }, }).then((res) => res); } room.sendAnnouncement( translate('wellcome', { name: player.name }), player.id, welcomeColor, 'bold', HaxNotification.CHAT ); updateTeams(); updateAdmins(); if (masterList.findIndex((auth) => auth == player.auth) != -1) { room.sendAnnouncement( translate("masterConected", { player: player.name }), null, announcementColor, 'bold', HaxNotification.CHAT ); room.setPlayerAdmin(player.id, true); } else if (adminList.map((a) => a[0]).findIndex((auth) => auth == player.auth) != -1) { room.sendAnnouncement( translate("adminConected", { player: player.name }), null, announcementColor, 'bold', HaxNotification.CHAT ); room.setPlayerAdmin(player.id, true); } var sameAuthCheck = playersAll.filter((p) => p.id != player.id && authArray[p.id][0] == player.auth); if (sameAuthCheck.length > 0 && !debugMode) { var oldPlayerArray = playersAll.filter((p) => p.id != player.id && authArray[p.id][0] == player.auth); for (let oldPlayer of oldPlayerArray) { ghostKickHandle(oldPlayer, player); } } handlePlayersJoin(); }; room.onPlayerTeamChange = function (changedPlayer, byPlayer) { handleLineupChangeTeamChange(changedPlayer); if (AFKSet.has(changedPlayer.id) && changedPlayer.team != Team.SPECTATORS) { room.setPlayerTeam(changedPlayer.id, Team.SPECTATORS); room.sendAnnouncement( `${changedPlayer.name} está AFK !`, null, errorColor, 'bold', HaxNotification.CHAT ); return; } updateTeams(); if (gameState != State.STOP) { if (changedPlayer.team != Team.SPECTATORS && game.scores.time <= (3 / 4) * game.scores.timeLimit && Math.abs(game.scores.blue - game.scores.red) < 2) { changedPlayer.team == Team.RED ? teamRedStats.push(changedPlayer) : teamBlueStats.push(changedPlayer); } } handleActivityPlayerTeamChange(changedPlayer); handlePlayersTeamChange(byPlayer); }; room.onPlayerLeave = function (player) { setTimeout(() => { if (!kickFetchVariable) { if (roomWebhook != '') { var stringContent = `[${getDate()}] ⬅️ LEAVE (${playersAll.length}/${maxPlayers})\n**${player.name}**` + `[${authArray[player.id][0]}] {${authArray[player.id][1]}}`; fetch(roomWebhook, { method: 'POST', body: JSON.stringify({ content: stringContent, username: webhookName, }), headers: { 'Content-Type': 'application/json', }, }).then((res) => res); } } else kickFetchVariable = false; }, 10); handleLineupChangeLeave(player); checkCaptainLeave(player); updateTeams(); updateAdmins(); handlePlayersLeave(); }; room.onPlayerKicked = function (kickedPlayer, reason, ban, byPlayer) { kickFetchVariable = true; if (roomWebhook != '') { var stringContent = `[${getDate()}] ⛔ ${ban ? 'BAN' : 'KICK'} (${playersAll.length}/${maxPlayers})\n` + `**${kickedPlayer.name}** [${authArray[kickedPlayer.id][0]}] {${authArray[kickedPlayer.id][1]}} was ${ban ? 'banned' : 'kicked'}` + `${byPlayer != null ? ' by **' + byPlayer.name + '** [' + authArray[byPlayer.id][0] + '] {' + authArray[byPlayer.id][1] + '}' : ''}` fetch(roomWebhook, { method: 'POST', body: JSON.stringify({ content: stringContent, username: webhookName, }), headers: { 'Content-Type': 'application/json', }, }).then((res) => res); } if ((ban && ((byPlayer != null && (byPlayer.id == kickedPlayer.id || getRole(byPlayer) < Role.MASTER)) || getRole(kickedPlayer) == Role.MASTER)) || disableBans ) { room.clearBan(kickedPlayer.id); return; } if (byPlayer != null && getRole(byPlayer) < Role.ADMIN_PERM) { room.sendAnnouncement( 'You are not allowed to kick/ban players !', byPlayer.id, errorColor, 'bold', HaxNotification.CHAT ); room.setPlayerAdmin(byPlayer.id, false); return; } if (ban) banList.push([kickedPlayer.name, kickedPlayer.id]); }; /* PLAYER ACTIVITY */ room.onPlayerChat = function (player, message) { if (gameState !== State.STOP && player.team != Team.SPECTATORS) { let pComp = getPlayerComp(player); if (pComp != null) pComp.inactivityTicks = 0; } let msgArray = message.split(/ +/); if (!hideClaimMessage || msgArray[0] != '!claim') { if (roomWebhook != '') fetch(roomWebhook, { method: 'POST', body: JSON.stringify({ content: `[${getDate()}] 💬 CHAT\n**${player.name}** : ${message.replace('@', '@ ')}`, username: webhookName, }), headers: { 'Content-Type': 'application/json', }, }).then((res) => res); } if (msgArray[0][0] == '!') { let command = getCommand(msgArray[0].slice(1).toLowerCase()); if (command != false && commands[command].roles <= getRole(player)) commands[command].function(player, message); else room.sendAnnouncement( `The command you tried to enter does not exist for you. Please enter '!help' to get the available commands to you.`, player.id, errorColor, 'bold', HaxNotification.CHAT ); return false; } if (msgArray[0].toLowerCase() == 't') { teamChat(player, message); return false; } if (msgArray[0].substring(0, 2) === '@@') { playerChat(player, message); return false; } if (chooseMode && teamRed.length * teamBlue.length != 0) { var choosingMessageCheck = chooseModeFunction(player, message); if (choosingMessageCheck) return false; } if (slowMode > 0) { var filter = slowModeFunction(player, message); if (filter) return false; } if (!player.admin && muteArray.getByAuth(authArray[player.id][0]) != null) { room.sendAnnouncement( `You are muted !`, player.id, errorColor, 'bold', HaxNotification.CHAT ); return false; } }; room.onPlayerActivity = function (player) { if (gameState !== State.STOP) { let pComp = getPlayerComp(player); if (pComp != null) pComp.inactivityTicks = 0; } }; room.onPlayerBallKick = function (player) { if (playSituation != Situation.GOAL) { var ballPosition = room.getBallPosition(); if (game.touchArray.length == 0 || player.id != game.touchArray[game.touchArray.length - 1].player.id) { if (playSituation == Situation.KICKOFF) playSituation = Situation.PLAY; lastTeamTouched = player.team; game.touchArray.push( new BallTouch( player, game.scores.time, getGoalGame(), ballPosition ) ); lastTouches[0] = checkGoalKickTouch( game.touchArray, game.touchArray.length - 1, getGoalGame() ); lastTouches[1] = checkGoalKickTouch( game.touchArray, game.touchArray.length - 2, getGoalGame() ); } } }; /* GAME MANAGEMENT */ room.onGameStart = function (byPlayer) { clearTimeout(startTimeout); if (byPlayer != null) clearTimeout(stopTimeout); game = new Game(); possession = [0, 0]; actionZoneHalf = [0, 0]; gameState = State.PLAY; endGameVariable = false; goldenGoal = false; playSituation = Situation.KICKOFF; lastTouches = Array(2).fill(null); lastTeamTouched = Team.SPECTATORS; teamRedStats = []; teamBlueStats = []; if (teamRed.length == teamSize && teamBlue.length == teamSize) { for (var i = 0; i < teamSize; i++) { teamRedStats.push(teamRed[i]); teamBlueStats.push(teamBlue[i]); } } calculateStadiumVariables(); }; room.onGameStop = function (byPlayer) { clearTimeout(stopTimeout); clearTimeout(unpauseTimeout); if (byPlayer != null) clearTimeout(startTimeout); game.rec = room.stopRecording(); if ( !cancelGameVariable && game.playerComp[0].length + game.playerComp[1].length > 0 && ( (game.scores.timeLimit != 0 && ((game.scores.time >= 0.5 * game.scores.timeLimit && game.scores.time < 0.75 * game.scores.timeLimit && game.scores.red != game.scores.blue) || game.scores.time >= 0.75 * game.scores.timeLimit) ) || endGameVariable ) ) { fetchSummaryEmbed(game); if (fetchRecordingVariable) { setTimeout((gameEnd) => { fetchRecording(gameEnd); }, 500, game); } } cancelGameVariable = false; gameState = State.STOP; playSituation = Situation.STOP; updateTeams(); handlePlayersStop(byPlayer); handleActivityStop(); }; room.onGamePause = function (byPlayer) { if (mentionPlayersUnpause && gameState == State.PAUSE) { if (byPlayer != null) { room.sendAnnouncement( `Game paused by ${byPlayer.name} !`, null, defaultColor, 'bold', HaxNotification.NONE ); } else { room.sendAnnouncement( `Game paused !`, null, defaultColor, 'bold', HaxNotification.NONE ); } } clearTimeout(unpauseTimeout); gameState = State.PAUSE; }; room.onGameUnpause = function (byPlayer) { unpauseTimeout = setTimeout(() => { gameState = State.PLAY; }, 2000); if (mentionPlayersUnpause) { if (byPlayer != null) { room.sendAnnouncement( `Game unpaused by ${byPlayer.name} !`, null, defaultColor, 'bold', HaxNotification.NONE ); } else { room.sendAnnouncement( `Game unpaused !`, null, defaultColor, 'bold', HaxNotification.NONE ); } } if ( (teamRed.length == teamSize && teamBlue.length == teamSize && chooseMode) || (teamRed.length == teamBlue.length && teamSpec.length < 2 && chooseMode) ) { deactivateChooseMode(); } }; room.onTeamGoal = function (team) { const scores = room.getScores(); game.scores = scores; playSituation = Situation.GOAL; ballSpeed = getBallSpeed(); var goalString = getGoalString(team); for (let player of teamRed) { var playerComp = getPlayerComp(player); team == Team.RED ? playerComp.goalsScoredTeam++ : playerComp.goalsConcededTeam++; } for (let player of teamBlue) { var playerComp = getPlayerComp(player); team == Team.BLUE ? playerComp.goalsScoredTeam++ : playerComp.goalsConcededTeam++; } room.sendAnnouncement( goalString, null, team == Team.RED ? redColor : blueColor, null, HaxNotification.CHAT ); if (roomWebhook != '') { fetch(roomWebhook, { method: 'POST', body: JSON.stringify({ content: `[${getDate()}] ${goalString}`, username: webhookName, }), headers: { 'Content-Type': 'application/json', }, }).then((res) => res); } if ((scores.scoreLimit != 0 && (scores.red == scores.scoreLimit || scores.blue == scores.scoreLimit)) || goldenGoal) { endGame(team); goldenGoal = false; stopTimeout = setTimeout(() => { room.stopGame(); }, 1000); } }; room.onPositionsReset = function () { lastTouches = Array(2).fill(null); lastTeamTouched = Team.SPECTATORS; playSituation = Situation.KICKOFF; }; /* MISCELLANEOUS */ room.onRoomLink = function (url) { console.log(`${url}\nmasterPassword : ${masterPassword}`); if (masterPasswordWebHook != '') { fetch(masterPasswordWebHook, { method: 'POST', body: JSON.stringify({ content: `[${getDate()}] 🔗 LINK ${url}\nmasterPassword : ${masterPassword}`, username: webhookName, }), headers: { 'Content-Type': 'application/json', }, }).then((res) => res); } }; room.onPlayerAdminChange = function (changedPlayer, byPlayer) { updateTeams(); if (!changedPlayer.admin && getRole(changedPlayer) >= Role.ADMIN_TEMP) { room.setPlayerAdmin(changedPlayer.id, true); return; } updateAdmins(byPlayer != null && !changedPlayer.admin && changedPlayer.id == byPlayer.id ? changedPlayer.id : 0); }; room.onKickRateLimitSet = function (min, rate, burst, byPlayer) { if (byPlayer != null) { room.sendAnnouncement( `It is not allowed to change the kickrate limit. It must stay at "6-0-0".`, player.id, errorColor, 'bold', HaxNotification.CHAT ); room.setKickRateLimit(6, 0, 0); } }; room.onStadiumChange = function (newStadiumName, byPlayer) { if (byPlayer !== null) { if (getRole(byPlayer) < Role.MASTER && currentStadium != 'other') { room.sendAnnouncement( `You can't change stadium manually ! Please use the stadium commands.`, byPlayer.id, errorColor, 'bold', HaxNotification.CHAT ); stadiumCommand(emptyPlayer, `!${currentStadium}`); } else { room.sendAnnouncement( `Map changed. After you're done with this map, please use the stadium commands.`, byPlayer.id, infoColor, 'bold', HaxNotification.CHAT ); currentStadium = 'other'; } } checkStadiumVariable = true; }; room.onGameTick = function () { checkTime(); getLastTouchOfTheBall(); getGameStats(); handleActivity(); };