66bea25aaa
Setting scr_teambalance 1 will now autobalance teams after 60 seconds.
671 lines
14 KiB
Plaintext
671 lines
14 KiB
Plaintext
#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 );
|
|
}
|
|
}
|
|
|