diff --git a/data/scripts/mp/teams/_teams.gsc b/data/scripts/mp/teams/_teams.gsc new file mode 100644 index 00000000..bb53019a Binary files /dev/null and b/data/scripts/mp/teams/_teams.gsc differ diff --git a/data/scripts/mp/teams/_teams.gsc_raw b/data/scripts/mp/teams/_teams.gsc_raw new file mode 100644 index 00000000..1260a17b --- /dev/null +++ b/data/scripts/mp/teams/_teams.gsc_raw @@ -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 ); + } +} +