///////////////////////////////// SERVER CONFIG // HOST: https://html5.haxball.com/headless // DOCS: https://github.com/haxball/haxball-issues/wiki/Headless-Host // AUTH: https://www.haxball.com/playerauth var room = HBInit({ roomName: "🇪🇸....3️⃣🆚3️⃣....⚽FUTBOL SALA⚽", maxPlayers: 16, noPlayer: true, // Borrar el jugador "host" de la partida public: false }); room.setScoreLimit(1); room.setTimeLimit(7); room.setTeamsLock(true); var adminsIds = [ "amuZ3Vk-M6Vk8_DuphOjismmfRRo4SQ44RyUuLb9nLc", // borrageiros "_LSQgSnedi0Y2E86tf5-ql6fLYORVHUDsEvilNFXKA4" // xoel ]; ///////////////////////////////// FIN SERVER CONFIG ///////////////////////////////// GLOBAL VARIABLES var nOfPlayers = 0; // Numero de jugadores en la sala var radiusBall = 6.4; // Radio del balón (el clásico es 10) var radiusPlayer = 15; // Radio del jugador (el clásico es 15) var triggerDistance = radiusBall + radiusPlayer + 0.00; // Tolerancia de distancia entre jugador y balón, se puede modificar var lastPlayerToTouchBall = null; // Último jugador en tocar el balón (se guarda el "usarname" no el objeto) var adminsInRoom = []; // Lista de jugadores con permisos de adminitración (lista de objetos) var captains = []; // Lista de capitanes que eligen jugadores (2 por partida y solo los habrá cuando hay 6 o más jugadores) var team1Count = 0; // Numero de jugadores en el equipo 1 var team2Count = 0; // Numero de jugadores en el equipo 2 var captainsChoosing = true; // Boolean que determina el estado de selección (cuando esté activado se le cambia el nombre al jugador para mostrar su ID y se ejecuta la funcion de seleccion) var stadium3v3 = `{ "name": "DISCTOPIA 3v3", "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" } }` room.setCustomStadium(stadium3v3); ///////////////////////////////// FIN GLOBAL VARIABLES ///////////////////////////////// EVENTS room.onPlayerJoin = function(newPlayer) { nOfPlayers += 1; updateAdmins(newPlayer); } room.onPlayerLeave = function( playerLeaving ) { nOfPlayers -= 1; updateAdminsRemove( playerLeaving ); } room.onGameTick = function handleGameTick() { updateLastPlayerToTouchBall(); } room.onTeamGoal = function (team) { golAnimation(); } room.onPlayerChat = function( player, message ) { startTheGame(); // DEV if ( captainsChoosing ) { choosePlayer( player, message ); } } room.onTeamVictory = function( scores ) { printScore(scores); } room.onGameStop = function( byPlayer ) { clearTeams(); startTheGame(); } ///////////////////////////////// FIN EVENTS ///////////////////////////////// FUNCTIONS function choosePlayer( player, message ) { const isCaptain = captains.find(captain => captain.id === player.id) !== undefined; if (isCaptain && Number.isInteger( message )) { // // TO DO // // metar en el equipo del capitan el jugador selecionado por mensaje hasta que los equipos estén completos y empezar el partido } } function updateAdmins(newPlayer) { if (adminsIds.includes(newPlayer.auth)) { room.setPlayerAdmin(newPlayer.id, true); adminsInRoom.push(newPlayer); } } function updateAdminsRemove(playerLeaving) { const index = adminsInRoom.indexOf(playerLeaving); if (index !== -1) { adminsInRoom.splice(index, 1); } } function updateLastPlayerToTouchBall() { function pointDistance(p1, p2) { var d1 = p1.x - p2.x; var d2 = p1.y - p2.y; return Math.sqrt(d1 * d1 + d2 * d2); } var players = room.getPlayerList(); var ballPosition = room.getBallPosition(); for (var player of players) { if ( player.position == null ) continue; var distanceToBall = pointDistance(player.position, ballPosition); if (distanceToBall < triggerDistance) { lastPlayerToTouchBall = player; } } } function golAnimation() { if (lastPlayerToTouchBall) { room.sendAnnouncement("¡GOOOOL DE " + lastPlayerToTouchBall.name + "!!!", null, 0xFF0000, "bold", 0); room.setPlayerDiscProperties(lastPlayerToTouchBall.id, { radius: 30 }); room.setPlayerAvatar(lastPlayerToTouchBall.id, "⚽"); setTimeout(() => { room.setPlayerAvatar(lastPlayerToTouchBall.id, null); room.setPlayerDiscProperties(lastPlayerToTouchBall.id, { radius: radiusPlayer }); }, 1500); } }; // Si el total de jugadores es de 6 o más, 2 capitanes serán asignados de manera aleatoria (los admins tienen preferencia) // estos 2 capitanes tendrán permisos para elegir jugadores por turnos mediante sus ids (estos mostrados delante del nombre sólo en el lobby) function shuffleArray(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } function createTeams() { var players = room.getPlayerList(); // Si el numero de jugadores en la seala es menor que 6: // equipos aleatorios // si es mayor de 6: // ejecutamos sistema de capitanes if (players.length > 6) { // DEV - PRO sería : < // Asignar jugadores a equipos aleatoriamente var middle = 0; var shuffledPlayers = shuffleArray(players); // Si el numero de jugadores es par, coge la mitad de jugadores para cada equipo // Si el numero de jugadores es impar, distribuye de manera aleatoria al 50% el jugador sobrante en cualquiera de los equipos if ( players.length % 2 === 0 ) { middle = players.length / 2; }else { if (Math.random() < 0.5) { middle = Math.ceil(players.length / 2); } else { middle = Math.floor(players.length / 2); } } for (var i = 0; i < shuffledPlayers.length; i++) { var player = shuffledPlayers[i]; if (team1Count < middle) { room.setPlayerTeam(player.id, 1); team1Count++; } else { room.setPlayerTeam(player.id, 2); team2Count++; } } } else{ // Función para obtener un número aleatorio entre min y max (ambos inclusive) function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } // Filtrar administradores de la lista de jugadores const admins = players.filter(player => player.admin); // Si hay al menos un administrador, selecciona uno o dos de ellos como capitanes if (admins.length > 0) { const captainCount = Math.min(2, admins.length); for (let i = 0; i < captainCount; i++) { captains.push(admins[i]); } // Si aún falta un capitán, selecciona un jugador no administrador como capitán if (captainCount === 1) { const nonAdminPlayers = players.filter(player => !player.admin); const randomIndex = getRandomInt(0, nonAdminPlayers.length - 1); captains.push(nonAdminPlayers[randomIndex]); } } else { // Si no hay administradores, selecciona dos jugadores al azar como capitanes const playerCount = Math.min(2, players.length); for (let i = 0; i < playerCount; i++) { const randomIndex = getRandomInt(0, players.length - 1); captains.push(players[randomIndex]); } } room.setPlayerTeam(captains[0].id, 1); // room.setPlayerTeam(captains[1].id, 2); // DEV captainsChoosing = true; } } function printEmptyLine() { room.sendAnnouncement(" ", null, 0xFF0000, "bold", 0); } // Enviar por chat resultado del partido function printScore( scores ) { room.sendAnnouncement(" FINAL DE PARTIDO", null, 0xFF0000, "bold", 0); printEmptyLine(); if (scores.red > scores.blue) { room.sendAnnouncement(" VITORIA DEL EQUIPO ROJO", null, 0xFF0000, "bold", 0); } else if (scores.blue > scores.red) { room.sendAnnouncement(" VITORIA DEL EQUIPO AZUL", null, 0x1700ff, "bold", 0); } else { room.sendAnnouncement(" ¡¡¡EMPATE!!!", null, 0xFF0000, "bold", 0); } printEmptyLine(); } // Eliminar a todos los jugadores de los equipos, mandarlos a espectador y borrar a los espectadores function clearTeams() { captains.splice(0, captains.length); team1Count = 0; team2Count = 0; var players = room.getPlayerList(); for (var player of players) { room.setPlayerTeam(player.id, 0); } } function startTheGame() { if (!nOfPlayers == 0) { setTimeout(() => { room.sendAnnouncement("El partido empieza en:", null, 0xFF0000, "bold", 0); createTeams(); }, 1500); setTimeout(() => { room.sendAnnouncement(" 3", null, 0xFF0000, "bold", 0); }, 4000); setTimeout(() => { room.sendAnnouncement(" 2", null, 0xFF0000, "bold", 0); }, 5000); setTimeout(() => { room.sendAnnouncement(" 1", null, 0xFF0000, "bold", 0); }, 6000); setTimeout(() => { room.sendAnnouncement(" VAMOS!", null, 0xFF0000, "bold", 0); }, 7000); setTimeout(() => { room.startGame(); }, 8000); } } ///////////////////////////////// FIN FUNCTIONS