Merge pull request #395 from efinst0rm/main

Add autobalance support.
This commit is contained in:
Maurice Heumann 2023-04-02 09:38:15 +02:00 committed by GitHub
commit bb8d1e3b1e
6 changed files with 866 additions and 33 deletions

View File

@ -1,33 +0,0 @@
#using scripts\codescripts\struct;
#using scripts\shared\callbacks_shared;
#using scripts\shared\system_shared;
#insert scripts\shared\shared.gsh;
#namespace clientids;
REGISTER_SYSTEM( "clientids", &__init__, undefined )
function __init__()
{
callback::on_start_gametype( &init );
callback::on_connect( &on_player_connect );
}
function init()
{
level.clientid = 0;
wait 0.5;
level.allow_teamchange = "1";
}
function on_player_connect()
{
self.clientid = matchRecordNewPlayer( self );
if ( !isdefined( self.clientid ) || self.clientid == -1 )
{
self.clientid = level.clientid;
level.clientid++;
}
}

Binary file not shown.

View File

@ -0,0 +1,196 @@
#using scripts\codescripts\struct;
#using scripts\shared\callbacks_shared;
#using scripts\shared\system_shared;
#insert scripts\shared\shared.gsh;
#namespace serversettings;
REGISTER_SYSTEM( "serversettings", &__init__, undefined )
function __init__()
{
callback::on_start_gametype( &init );
}
function init()
{
level.hostname = GetDvarString( "sv_hostname");
if(level.hostname == "")
level.hostname = "CoDHost";
SetDvar("sv_hostname", level.hostname);
SetDvar("ui_hostname", level.hostname);
level.motd = GetDvarString( "scr_motd" );
if(level.motd == "")
level.motd = "";
SetDvar("scr_motd", level.motd);
SetDvar("ui_motd", level.motd);
level.allowvote = GetDvarString( "g_allowvote");
if(level.allowvote == "")
level.allowvote = "1";
SetDvar("g_allowvote", level.allowvote);
SetDvar("ui_allowvote", level.allowvote);
level.allow_teamchange = "1";
SetDvar("ui_allow_teamchange", level.allow_teamchange);
level.friendlyfire = GetGametypeSetting( "friendlyfiretype" );
SetDvar("ui_friendlyfire", level.friendlyfire);
if(GetDvarString( "scr_mapsize") == "")
SetDvar("scr_mapsize", "64");
else if(GetDvarfloat( "scr_mapsize") >= 64)
SetDvar("scr_mapsize", "64");
else if(GetDvarfloat( "scr_mapsize") >= 32)
SetDvar("scr_mapsize", "32");
else if(GetDvarfloat( "scr_mapsize") >= 16)
SetDvar("scr_mapsize", "16");
else
SetDvar("scr_mapsize", "8");
level.mapsize = GetDvarfloat( "scr_mapsize");
constrain_gametype(GetDvarString( "g_gametype"));
constrain_map_size(level.mapsize);
for(;;)
{
update();
wait 5;
}
}
function update()
{
sv_hostname = GetDvarString( "sv_hostname");
if(level.hostname != sv_hostname)
{
level.hostname = sv_hostname;
SetDvar("ui_hostname", level.hostname);
}
scr_motd = GetDvarString( "scr_motd");
if(level.motd != scr_motd)
{
level.motd = scr_motd;
SetDvar("ui_motd", level.motd);
}
g_allowvote = GetDvarString( "g_allowvote");
if(level.allowvote != g_allowvote)
{
level.allowvote = g_allowvote;
SetDvar("ui_allowvote", level.allowvote);
}
scr_friendlyfire = GetGametypeSetting( "friendlyfiretype" );
if(level.friendlyfire != scr_friendlyfire)
{
level.friendlyfire = scr_friendlyfire;
SetDvar("ui_friendlyfire", level.friendlyfire);
}
}
function constrain_gametype(gametype)
{
entities = getentarray();
for(i = 0; i < entities.size; i++)
{
entity = entities[i];
if(gametype == "dm")
{
if(isdefined(entity.script_gametype_dm) && entity.script_gametype_dm != "1")
{
//iprintln("DELETED(GameType): ", entity.classname);
entity delete();
}
}
else if(gametype == "tdm")
{
if(isdefined(entity.script_gametype_tdm) && entity.script_gametype_tdm != "1")
{
//iprintln("DELETED(GameType): ", entity.classname);
entity delete();
}
}
else if(gametype == "ctf")
{
if(isdefined(entity.script_gametype_ctf) && entity.script_gametype_ctf != "1")
{
//iprintln("DELETED(GameType): ", entity.classname);
entity delete();
}
}
else if(gametype == "hq")
{
if(isdefined(entity.script_gametype_hq) && entity.script_gametype_hq != "1")
{
//iprintln("DELETED(GameType): ", entity.classname);
entity delete();
}
}
else if(gametype == "sd")
{
if(isdefined(entity.script_gametype_sd) && entity.script_gametype_sd != "1")
{
//iprintln("DELETED(GameType): ", entity.classname);
entity delete();
}
}
else if(gametype == "koth")
{
if(isdefined(entity.script_gametype_koth) && entity.script_gametype_koth != "1")
{
//iprintln("DELETED(GameType): ", entity.classname);
entity delete();
}
}
}
}
function constrain_map_size(mapsize)
{
entities = getentarray();
for(i = 0; i < entities.size; i++)
{
entity = entities[i];
if(int(mapsize) == 8)
{
if(isdefined(entity.script_mapsize_08) && entity.script_mapsize_08 != "1")
{
//iprintln("DELETED(MapSize): ", entity.classname);
entity delete();
}
}
else if(int(mapsize) == 16)
{
if(isdefined(entity.script_mapsize_16) && entity.script_mapsize_16 != "1")
{
//iprintln("DELETED(MapSize): ", entity.classname);
entity delete();
}
}
else if(int(mapsize) == 32)
{
if(isdefined(entity.script_mapsize_32) && entity.script_mapsize_32 != "1")
{
//iprintln("DELETED(MapSize): ", entity.classname);
entity delete();
}
}
else if(int(mapsize) == 64)
{
if(isdefined(entity.script_mapsize_64) && entity.script_mapsize_64 != "1")
{
//iprintln("DELETED(MapSize): ", entity.classname);
entity delete();
}
}
}
}

Binary file not shown.

View File

@ -0,0 +1,670 @@
#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 );
}
}