#using scripts\codescripts\struct; #using scripts\shared\callbacks_shared; #using scripts\shared\persistence_shared; #using scripts\shared\system_shared; #using scripts\shared\util_shared; #insert scripts\shared\shared.gsh; #using scripts\mp\gametypes\_globallogic_ui; #using scripts\mp\gametypes\_spectating; #using scripts\mp\_util; #precache( "material", "mpflag_spectator" ); #precache( "string", "MP_AUTOBALANCE_NOW" ); #namespace teams; REGISTER_SYSTEM( "teams", &__init__, undefined ) function __init__() { callback::on_start_gametype( &init ); level.getEnemyTeam = &getEnemyTeam; level.use_team_based_logic_for_locking_on = true; } function init() { game["strings"]["autobalance"] = &"MP_AUTOBALANCE_NOW"; if(GetDvarString( "scr_teambalance") == "") SetDvar("scr_teambalance", "0"); level.teambalance = GetDvarint( "scr_teambalance"); level.teambalancetimer = 0; if(GetDvarString( "scr_timeplayedcap") == "") SetDvar("scr_timeplayedcap", "1800"); level.timeplayedcap = int(GetDvarint( "scr_timeplayedcap")); level.freeplayers = []; if( level.teamBased ) { level.alliesplayers = []; level.axisplayers = []; callback::on_connect( &on_player_connect ); callback::on_joined_team( &on_joined_team ); callback::on_joined_spectate( &on_joined_spectators ); level thread update_team_balance(); wait .15; level thread update_player_times(); } else { callback::on_connect( &on_free_player_connect ); wait .15; level thread update_player_times(); } } function on_player_connect() { self thread track_played_time(); } function on_free_player_connect() { self thread track_free_played_time(); } function on_joined_team() { /#println( "joined team: " + self.pers["team"] );#/ self update_time(); } function on_joined_spectators() { self.pers["teamTime"] = undefined; } function track_played_time() { self endon( "disconnect" ); if ( !isdefined( self.pers["totalTimePlayed"] ) ) { self.pers["totalTimePlayed"] = 0; } foreach ( team in level.teams ) { self.timePlayed[team] = 0; } self.timePlayed["free"] = 0; self.timePlayed["other"] = 0; self.timePlayed["alive"] = 0; // dont reset time played in War when going into final fight, this is used for calculating match bonus if ( !isdefined( self.timePlayed["total"] ) || !( (level.gameType == "twar") && (0 < game["roundsplayed"]) && (0 < self.timeplayed["total"]) ) ) self.timePlayed["total"] = 0; while ( level.inPrematchPeriod ) WAIT_SERVER_FRAME; for ( ;; ) { if ( game["state"] == "playing" ) { if ( isdefined( level.teams[self.sessionteam] ) ) { self.timePlayed[self.sessionteam]++; self.timePlayed["total"]++; if ( level.mpCustomMatch ) { self.pers["sbtimeplayed"] = self.timeplayed["total"]; self.sbtimeplayed = self.pers["sbtimeplayed"]; } if ( IsAlive( self ) ) self.timePlayed["alive"]++; } else if ( self.sessionteam == "spectator" ) { self.timePlayed["other"]++; } } wait ( 1.0 ); } } function update_player_times() { const minWait = 10.0; const step = 1.0; varWait = minWait; nextToUpdate = 0; for ( ;; ) { varWait = varWait - step; nextToUpdate++; if ( nextToUpdate >= level.players.size ) { nextToUpdate = 0; if ( varWait > 0 ) { wait ( varWait ); } varWait = minWait; } if ( isdefined( level.players[nextToUpdate] ) ) { level.players[nextToUpdate] update_played_time(); level.players[nextToUpdate] persistence::check_contract_expirations(); } wait ( step ); } } function update_played_time() { pixbeginevent("updatePlayedTime"); if ( level.rankedMatch || level.leagueMatch ) { foreach( team in level.teams ) { if ( self.timePlayed[team] ) { if ( level.teambased ) { self AddPlayerStat( "time_played_"+team, int( min( self.timePlayed[team], level.timeplayedcap ) ) ); } self AddPlayerStatWithGameType( "time_played_total", int( min( self.timePlayed[team], level.timeplayedcap ) ) ); } } if ( self.timePlayed["other"] ) { self AddPlayerStat( "time_played_other", int( min( self.timePlayed["other"], level.timeplayedcap ) ) ); self AddPlayerStatWithGameType( "time_played_total", int( min( self.timePlayed["other"], level.timeplayedcap ) ) ); } if ( self.timePlayed["alive"] ) { timeAlive = int( min( self.timePlayed["alive"], level.timeplayedcap ) ); self persistence::increment_contract_times( timeAlive ); self AddPlayerStat( "time_played_alive", timeAlive ); } } if ( level.onlineGame ) { timeAlive = int( min( self.timePlayed["alive"], level.timeplayedcap ) ); self.pers["time_played_alive"] += timeAlive; } pixendevent(); if ( game["state"] == "postgame" ) return; foreach( team in level.teams ) { self.timePlayed[team] = 0; } self.timePlayed["other"] = 0; self.timePlayed["alive"] = 0; } function update_time() { if ( game["state"] != "playing" ) return; self.pers["teamTime"] = getTime(); } function update_balance_dvar() { for(;;) { teambalance = GetDvarint( "scr_teambalance"); if(level.teambalance != teambalance) level.teambalance = GetDvarint( "scr_teambalance"); timeplayedcap = GetDvarint( "scr_timeplayedcap"); if(level.timeplayedcap != timeplayedcap) level.timeplayedcap = int(GetDvarint( "scr_timeplayedcap")); wait 1; } } function update_team_balance() { level thread update_balance_dvar(); wait .15; if ( level.teamBalance && util::isRoundBased() && level.numlives ) { if ( isDefined( game["BalanceTeamsNextRound"] ) ) iPrintLnbold( &"MP_AUTOBALANCE_NEXT_ROUND" ); level waittill( "game_ended" ); wait 1; if ( isDefined( game["BalanceTeamsNextRound"] ) ) { level balance_teams(); game["BalanceTeamsNextRound"] = undefined; } else if ( !get_team_balance() ) { game["BalanceTeamsNextRound"] = true; } } else { level endon ( "game_ended" ); for ( ;; ) { if ( level.teamBalance ) { if ( !get_team_balance() ) { iPrintLnBold( &"MP_AUTOBALANCE_SECONDS", 15 ); wait 15.0; if ( !get_team_balance() ) level balance_teams(); } wait 59.0; } wait 1.0; } } } function get_team_balance() { level.team["allies"] = 0; level.team["axis"] = 0; players = level.players; for ( i = 0; i < players.size; i++ ) { if ( ( isdefined( players[i].pers["team"] ) ) && ( players[i].pers["team"] == "allies" ) ) level.team["allies"]++; else if ( ( isdefined( players[i].pers["team"] ) ) && ( players[i].pers["team"] == "axis" ) ) level.team["axis"]++; } if ( ( level.team["allies"] > ( level.team["axis"] + level.teamBalance ) ) || ( level.team["axis"] > ( level.team["allies"] + level.teamBalance ) ) ) return false; else return true; } function balance_teams() { iPrintLnBold( game["strings"]["autobalance"] ); //Create/Clear the team arrays AlliedPlayers = []; AxisPlayers = []; // Populate the team arrays players = level.players; for ( i = 0; i < players.size; i++ ) { if ( !isdefined( players[i].pers["teamTime"] ) ) continue; if ( ( isdefined( players[i].pers["team"] ) ) && ( players[i].pers["team"] == "allies" ) ) AlliedPlayers[AlliedPlayers.size] = players[i]; else if ( ( isdefined( players[i].pers["team"] ) ) && ( players[i].pers["team"] == "axis" ) ) AxisPlayers[AxisPlayers.size] = players[i]; } MostRecent = undefined; while ( ( AlliedPlayers.size > ( AxisPlayers.size + 1 ) ) || ( AxisPlayers.size > ( AlliedPlayers.size + 1 ) ) ) { if ( AlliedPlayers.size > ( AxisPlayers.size + 1 ) ) { // Move the player that's been on the team the shortest ammount of time (highest teamTime value) // Ignore players capturing or carrying objects for ( j = 0; j < AlliedPlayers.size; j++ ) { if ( !isdefined( MostRecent ) ) MostRecent = AlliedPlayers[j]; else if ( AlliedPlayers[j].pers["teamTime"] > MostRecent.pers["teamTime"] ) MostRecent = AlliedPlayers[j]; } if ( isdefined( MostRecent ) ) MostRecent change( "axis" ); else { // Move the player that's been on the team the shortest ammount of time for ( j = 0; j < AlliedPlayers.size; j++ ) { if ( !isdefined( MostRecent ) ) MostRecent = AlliedPlayers[j]; else if ( AlliedPlayers[j].pers["teamTime"] > MostRecent.pers["teamTime"] ) MostRecent = AlliedPlayers[j]; } MostRecent change( "axis" ); } } else if ( AxisPlayers.size > ( AlliedPlayers.size + 1 ) ) { // Move the player that's been on the team the shortest ammount of time (highest teamTime value) // Ignore players capturing or carrying objects for ( j = 0; j < AxisPlayers.size; j++ ) { if ( !isdefined( MostRecent ) ) MostRecent = AxisPlayers[j]; else if ( AxisPlayers[j].pers["teamTime"] > MostRecent.pers["teamTime"] ) MostRecent = AxisPlayers[j]; } if ( isdefined( MostRecent ) ) MostRecent change( "allies" ); else { // Move the player that's been on the team the shortest ammount of time for ( j = 0; j < AxisPlayers.size; j++ ) { if ( !isdefined( MostRecent ) ) MostRecent = AxisPlayers[j]; else if ( AxisPlayers[j].pers["teamTime"] > MostRecent.pers["teamTime"] ) MostRecent = AxisPlayers[j]; } MostRecent change( "allies" ); } } MostRecent = undefined; AlliedPlayers = []; AxisPlayers = []; players = level.players; for ( i = 0; i < players.size; i++ ) { if ( ( isdefined( players[i].pers["team"] ) ) && ( players[i].pers["team"] == "allies" ) ) AlliedPlayers[AlliedPlayers.size] = players[i]; else if ( ( isdefined( players[i].pers["team"] ) ) && ( players[i].pers["team"] == "axis" ) ) AxisPlayers[AxisPlayers.size] = players[i]; } } } function change( team ) { if (self.sessionstate != "dead") { // Set a flag on the player to they aren't robbed points for dying - the callback will remove the flag self.switching_teams = true; self.switchedTeamsResetGadgets = true; self.joining_team = team; self.leaving_team = self.pers["team"]; // Suicide the player so they can't hit escape and fail the team balance self suicide(); } self.pers["team"] = team; self.team = team; self.pers["weapon"] = undefined; self.pers["spawnweapon"] = undefined; self.pers["savedmodel"] = undefined; self.pers["teamTime"] = undefined; self.sessionteam = self.pers["team"]; self globallogic_ui::updateObjectiveText(); // update spectator permissions immediately on change of team self spectating::set_permissions(); self SetClientScriptMainMenu( game[ "menu_start_menu" ] ); self openMenu(game[ "menu_start_menu" ]); self notify("end_respawn"); } function count_players() { players = level.players; playerCounts = []; foreach( team in level.teams ) { playerCounts[team] = 0; } foreach( player in level.players ) { if( player == self ) continue; team = player.pers["team"]; if( isdefined(team) && isdefined( level.teams[team] ) ) playerCounts[team]++; } return playerCounts; } function track_free_played_time() { self endon( "disconnect" ); foreach( team in level.teams ) { self.timePlayed[team] = 0; } self.timePlayed["other"] = 0; self.timePlayed["total"] = 0; self.timePlayed["alive"] = 0; for ( ;; ) { if ( game["state"] == "playing" ) { team = self.pers["team"]; if ( isdefined( team ) && isdefined( level.teams[team] ) && self.sessionteam != "spectator" ) { self.timePlayed[team]++; self.timePlayed["total"]++; if ( IsAlive( self ) ) self.timePlayed["alive"]++; } else { self.timePlayed["other"]++; } } wait ( 1.0 ); } } function set_player_model( team, weapon ) { self DetachAll(); self SetMoveSpeedScale( 1 ); self SetSprintDuration( 4 ); self SetSprintCooldown( 0 ); } function get_flag_model( teamRef ) { assert(isdefined(game["flagmodels"])); assert(isdefined(game["flagmodels"][teamRef])); return ( game["flagmodels"][teamRef] ); } function get_flag_carry_model( teamRef ) { assert(isdefined(game["carry_flagmodels"])); assert(isdefined(game["carry_flagmodels"][teamRef])); return ( game["carry_flagmodels"][teamRef] ); } function getTeamIndex( team ) { if( !isdefined( team ) ) { return TEAM_FREE; } if( team == "free" ) { return TEAM_FREE; } if( team == "allies" ) { return TEAM_ALLIES; } if( team == "axis" ) { return TEAM_AXIS; } return TEAM_FREE; } function getEnemyTeam( player_team ) { foreach( team in level.teams ) { if ( team == player_team ) continue; if ( team == "spectator" ) continue; return team; } return util::getOtherTeam( player_team ); } function GetEnemyPlayers() { enemies = []; foreach( player in level.players ) { if( player.team == "spectator" ) { continue; } if( ( level.teamBased && player.team != self.team ) || ( !level.teamBased && player != self ) ) { ARRAY_ADD( enemies, player ); } } return enemies; } function GetFriendlyPlayers() { friendlies = []; foreach( player in level.players ) { if( ( player.team == self.team ) && ( player != self ) ) { ARRAY_ADD( friendlies, player ); } } return friendlies; } function WaitUntilTeamChange( player, callback, arg, end_condition1, end_condition2, end_condition3 ) { if( isdefined( end_condition1 ) ) self endon( end_condition1 ); if( isdefined( end_condition2 ) ) self endon( end_condition2 ); if( isdefined( end_condition3 ) ) self endon( end_condition3 ); event = player util::waittill_any( "joined_team", "disconnect", "joined_spectators" ); if( isdefined( callback ) ) { self [[ callback ]]( arg, event ); } } function WaitUntilTeamChangeSingleTon( player, singletonString, callback, arg, end_condition1, end_condition2, end_condition3 ) { self notify( singletonString ); self endon( singletonString ); if( isdefined( end_condition1 ) ) self endon( end_condition1 ); if( isdefined( end_condition2 ) ) self endon( end_condition2 ); if( isdefined( end_condition3 ) ) self endon( end_condition3 ); event = player util::waittill_any( "joined_team", "disconnect", "joined_spectators" ); if( isdefined( callback ) ) { self thread [[ callback ]]( arg, event ); } } function HideToSameTeam() { if( level.teambased ) { self SetVisibleToAllExceptTeam( self.team ); } else { self SetVisibleToAll(); self SetInvisibleToPlayer( self.owner ); } }