summaryrefslogtreecommitdiff
path: root/game/shared/teamplay_gamerules.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/teamplay_gamerules.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/teamplay_gamerules.cpp')
-rw-r--r--game/shared/teamplay_gamerules.cpp574
1 files changed, 574 insertions, 0 deletions
diff --git a/game/shared/teamplay_gamerules.cpp b/game/shared/teamplay_gamerules.cpp
new file mode 100644
index 0000000..171313b
--- /dev/null
+++ b/game/shared/teamplay_gamerules.cpp
@@ -0,0 +1,574 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "KeyValues.h"
+#include "gamerules.h"
+#include "teamplay_gamerules.h"
+
+#ifdef CLIENT_DLL
+ #include "c_baseplayer.h"
+ #include "c_team.h"
+#else
+ #include "player.h"
+ #include "game.h"
+ #include "gamevars_shared.h"
+ #include "team.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifdef GAME_DLL
+static char team_names[MAX_TEAMS][MAX_TEAMNAME_LENGTH];
+static int team_scores[MAX_TEAMS];
+static int num_teams = 0;
+
+extern bool g_fGameOver;
+
+REGISTER_GAMERULES_CLASS( CTeamplayRules );
+
+CTeamplayRules::CTeamplayRules()
+{
+ m_DisableDeathMessages = false;
+ m_DisableDeathPenalty = false;
+ m_bSwitchTeams = false;
+ m_bScrambleTeams = false;
+
+ memset( team_names, 0, sizeof(team_names) );
+ memset( team_scores, 0, sizeof(team_scores) );
+ num_teams = 0;
+
+ // Copy over the team from the server config
+ m_szTeamList[0] = 0;
+
+ RecountTeams();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTeamplayRules::Precache( void )
+{
+ // Call the Team Manager's precaches
+ for ( int i = 0; i < GetNumberOfTeams(); i++ )
+ {
+ CTeam *pTeam = GetGlobalTeam( i );
+ pTeam->Precache();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTeamplayRules::Think ( void )
+{
+ BaseClass::Think();
+
+ ///// Check game rules /////
+
+ if ( g_fGameOver ) // someone else quit the game already
+ {
+ BaseClass::Think();
+ return;
+ }
+
+ float flTimeLimit = mp_timelimit.GetFloat() * 60;
+
+ if ( flTimeLimit != 0 && gpGlobals->curtime >= flTimeLimit )
+ {
+ ChangeLevel();
+ return;
+ }
+
+ float flFragLimit = fraglimit.GetFloat();
+ if ( flFragLimit )
+ {
+ // check if any team is over the frag limit
+ for ( int i = 0; i < num_teams; i++ )
+ {
+ if ( team_scores[i] >= flFragLimit )
+ {
+ ChangeLevel();
+ return;
+ }
+ }
+ }
+}
+
+//=========================================================
+// ClientCommand
+// the user has typed a command which is unrecognized by everything else;
+// this check to see if the gamerules knows anything about the command
+//=========================================================
+bool CTeamplayRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
+{
+ if( BaseClass::ClientCommand( pEdict, args ) )
+ return true;
+
+ if ( FStrEq( args[0], "menuselect" ) )
+ {
+ if ( args.ArgC() < 2 )
+ return true;
+
+ //int slot = atoi( args[1] );
+
+ // select the item from the current menu
+
+ return true;
+ }
+
+ return false;
+}
+
+const char *CTeamplayRules::SetDefaultPlayerTeam( CBasePlayer *pPlayer )
+{
+ // copy out the team name from the model
+ int clientIndex = pPlayer->entindex();
+ const char *team = (!pPlayer->IsNetClient())?"default":engine->GetClientConVarValue( clientIndex, "cl_team" );
+
+ /* TODO
+
+ pPlayer->SetTeamName( team );
+
+ RecountTeams();
+
+ // update the current player of the team he is joining
+ if ( (pPlayer->TeamName())[0] == '\0' || !IsValidTeam( pPlayer->TeamName() ) || defaultteam.GetFloat() )
+ {
+ const char *pTeamName = NULL;
+
+ if ( defaultteam.GetFloat() )
+ {
+ pTeamName = team_names[0];
+ }
+ else
+ {
+ pTeamName = TeamWithFewestPlayers();
+ }
+ pPlayer->SetTeamName( pTeamName );
+ } */
+
+ return team; //pPlayer->TeamName();
+}
+
+
+//=========================================================
+// InitHUD
+//=========================================================
+void CTeamplayRules::InitHUD( CBasePlayer *pPlayer )
+{
+ SetDefaultPlayerTeam( pPlayer );
+ BaseClass::InitHUD( pPlayer );
+
+ RecountTeams();
+
+ /* TODO this has to be rewritten, maybe add a new USERINFO cvar "team"
+ const char *team = engine->GetClientConVarValue( pPlayer->entindex(), "cl_team" );
+
+ // update the current player of the team he is joining
+ char text[1024];
+ if ( !strcmp( mdls, pPlayer->TeamName() ) )
+ {
+ Q_snprintf( text,sizeof(text), "You are on team \'%s\'\n", pPlayer->TeamName() );
+ }
+ else
+ {
+ Q_snprintf( text,sizeof(text), "You were assigned to team %s\n", pPlayer->TeamName() );
+ }
+
+ ChangePlayerTeam( pPlayer, pPlayer->TeamName(), false, false );
+ if ( Q_strlen( pPlayer->TeamName() ) > 0 )
+ {
+ UTIL_SayText( text, pPlayer );
+ }
+ RecountTeams(); */
+}
+
+
+void CTeamplayRules::ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, bool bKill, bool bGib )
+{
+ int damageFlags = DMG_GENERIC;
+ // int clientIndex = pPlayer->entindex();
+
+ if ( !bGib )
+ {
+ damageFlags |= DMG_NEVERGIB;
+ }
+ else
+ {
+ damageFlags |= DMG_ALWAYSGIB;
+ }
+
+
+ // copy out the team name from the model
+ // pPlayer->SetTeamName( pTeamName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player has just left the game
+//-----------------------------------------------------------------------------
+void CTeamplayRules::ClientDisconnected( edict_t *pClient )
+{
+ // Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" );
+
+ CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
+ if ( pPlayer )
+ {
+ pPlayer->SetConnected( PlayerDisconnecting );
+
+ // Remove the player from his team
+ if ( pPlayer->GetTeam() )
+ {
+ pPlayer->ChangeTeam( 0 );
+ }
+ }
+
+ BaseClass::ClientDisconnected( pClient );
+}
+
+//=========================================================
+// ClientUserInfoChanged
+//=========================================================
+void CTeamplayRules::ClientSettingsChanged( CBasePlayer *pPlayer )
+{
+ /* TODO: handle skin, model & team changes
+
+ char text[1024];
+
+ // skin/color/model changes
+ int iTeam = Q_atoi( engine->GetClientConVarValue( pPlayer->entindex(), "cl_team" ) );
+ int iClass = Q_atoi( engine->GetClientConVarValue( pPlayer->entindex(), "cl_class" ) );
+
+ if ( defaultteam.GetBool() )
+ {
+ // int clientIndex = pPlayer->entindex();
+
+ // engine->SetClientKeyValue( clientIndex, "model", pPlayer->TeamName() );
+ // engine->SetClientKeyValue( clientIndex, "team", pPlayer->TeamName() );
+ UTIL_SayText( "Not allowed to change teams in this game!\n", pPlayer );
+ return;
+ }
+
+ if ( defaultteam.GetFloat() || !IsValidTeam( mdls ) )
+ {
+ // int clientIndex = pPlayer->entindex();
+
+ // engine->SetClientKeyValue( clientIndex, "model", pPlayer->TeamName() );
+ Q_snprintf( text,sizeof(text), "Can't change team to \'%s\'\n", mdls );
+ UTIL_SayText( text, pPlayer );
+ Q_snprintf( text,sizeof(text), "Server limits teams to \'%s\'\n", m_szTeamList );
+ UTIL_SayText( text, pPlayer );
+ return;
+ }
+
+ ChangePlayerTeam( pPlayer, mdls, true, true );
+ // recound stuff
+ RecountTeams(); */
+
+ const char *pszName = engine->GetClientConVarValue( pPlayer->entindex(), "name" );
+
+ const char *pszOldName = pPlayer->GetPlayerName();
+
+ // msg everyone if someone changes their name, and it isn't the first time (changing no name to current name)
+ // Note, not using FStrEq so that this is case sensitive
+ if ( pszOldName[0] != 0 && Q_strcmp( pszOldName, pszName ) )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetString( "oldname", pszOldName );
+ event->SetString( "newname", pszName );
+ gameeventmanager->FireEvent( event );
+ }
+
+ pPlayer->SetPlayerName( pszName );
+ }
+
+ // NVNT see if this user is still or has began using a haptic device
+ const char *pszHH = engine->GetClientConVarValue( pPlayer->entindex(), "hap_HasDevice" );
+ if(pszHH)
+ {
+ int iHH = atoi(pszHH);
+ pPlayer->SetHaptics(iHH!=0);
+ }
+}
+
+//=========================================================
+// Deathnotice.
+//=========================================================
+void CTeamplayRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
+{
+ if ( m_DisableDeathMessages )
+ return;
+
+ CBaseEntity *pKiller = info.GetAttacker();
+ if ( pVictim && pKiller && pKiller->IsPlayer() )
+ {
+ CBasePlayer *pk = (CBasePlayer*)pKiller;
+
+ if ( pk )
+ {
+ if ( (pk != pVictim) && (PlayerRelationship( pVictim, pk ) == GR_TEAMMATE) )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_death" );
+ if ( event )
+ {
+ event->SetInt("killer", pk->GetUserID() );
+ event->SetInt("victim", pVictim->GetUserID() );
+ event->SetInt("priority", 7 ); // HLTV event priority, not transmitted
+
+ gameeventmanager->FireEvent( event );
+ }
+ return;
+ }
+ }
+ }
+
+ BaseClass::DeathNotice( pVictim, info );
+}
+
+//=========================================================
+//=========================================================
+void CTeamplayRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
+{
+ if ( !m_DisableDeathPenalty )
+ {
+ BaseClass::PlayerKilled( pVictim, info );
+ RecountTeams();
+ }
+}
+
+
+//=========================================================
+// IsTeamplay
+//=========================================================
+bool CTeamplayRules::IsTeamplay( void )
+{
+ return true;
+}
+
+bool CTeamplayRules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker, const CTakeDamageInfo &info )
+{
+ if ( pAttacker && PlayerRelationship( pPlayer, pAttacker ) == GR_TEAMMATE && !info.IsForceFriendlyFire() )
+ {
+ // my teammate hit me.
+ if ( (friendlyfire.GetInt() == 0) && (pAttacker != pPlayer) )
+ {
+ // friendly fire is off, and this hit came from someone other than myself, then don't get hurt
+ return false;
+ }
+ }
+
+ return BaseClass::FPlayerCanTakeDamage( pPlayer, pAttacker, info );
+}
+
+//=========================================================
+//=========================================================
+int CTeamplayRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
+{
+ // half life multiplay has a simple concept of Player Relationships.
+ // you are either on another player's team, or you are not.
+ if ( !pPlayer || !pTarget || !pTarget->IsPlayer() )
+ return GR_NOTTEAMMATE;
+
+ if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
+ {
+ return GR_TEAMMATE;
+ }
+
+ return GR_NOTTEAMMATE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pListener -
+// *pSpeaker -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CTeamplayRules::PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker )
+{
+ return ( PlayerRelationship( pListener, pSpeaker ) == GR_TEAMMATE );
+}
+
+//=========================================================
+//=========================================================
+bool CTeamplayRules::ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target )
+{
+ // always autoaim, unless target is a teammate
+ CBaseEntity *pTgt = CBaseEntity::Instance( target );
+ if ( pTgt && pTgt->IsPlayer() )
+ {
+ if ( PlayerRelationship( pPlayer, pTgt ) == GR_TEAMMATE )
+ return false; // don't autoaim at teammates
+ }
+
+ return BaseClass::ShouldAutoAim( pPlayer, target );
+}
+
+//=========================================================
+//=========================================================
+int CTeamplayRules::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
+{
+ if ( !pKilled )
+ return 0;
+
+ if ( !pAttacker )
+ return 1;
+
+ if ( pAttacker != pKilled && PlayerRelationship( pAttacker, pKilled ) == GR_TEAMMATE )
+ return -1;
+
+ return 1;
+}
+
+//=========================================================
+//=========================================================
+const char *CTeamplayRules::GetTeamID( CBaseEntity *pEntity )
+{
+ if ( pEntity == NULL || pEntity->edict() == NULL )
+ return "";
+
+ // return their team name
+ return pEntity->TeamID();
+}
+
+
+int CTeamplayRules::GetTeamIndex( const char *pTeamName )
+{
+ if ( pTeamName && *pTeamName != 0 )
+ {
+ // try to find existing team
+ for ( int tm = 0; tm < num_teams; tm++ )
+ {
+ if ( !stricmp( team_names[tm], pTeamName ) )
+ return tm;
+ }
+ }
+
+ return -1; // No match
+}
+
+
+const char *CTeamplayRules::GetIndexedTeamName( int teamIndex )
+{
+ if ( teamIndex < 0 || teamIndex >= num_teams )
+ return "";
+
+ return team_names[ teamIndex ];
+}
+
+
+bool CTeamplayRules::IsValidTeam( const char *pTeamName )
+{
+ if ( !m_teamLimit ) // Any team is valid if the teamlist isn't set
+ return true;
+
+ return ( GetTeamIndex( pTeamName ) != -1 ) ? true : false;
+}
+
+const char *CTeamplayRules::TeamWithFewestPlayers( void )
+{
+ int i;
+ int minPlayers = MAX_TEAMS;
+ int teamCount[ MAX_TEAMS ];
+ char *pTeamName = NULL;
+
+ memset( teamCount, 0, MAX_TEAMS * sizeof(int) );
+
+ // loop through all clients, count number of players on each team
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBaseEntity *plr = UTIL_PlayerByIndex( i );
+
+ if ( plr )
+ {
+ int team = GetTeamIndex( plr->TeamID() );
+ if ( team >= 0 )
+ teamCount[team] ++;
+ }
+ }
+
+ // Find team with least players
+ for ( i = 0; i < num_teams; i++ )
+ {
+ if ( teamCount[i] < minPlayers )
+ {
+ minPlayers = teamCount[i];
+ pTeamName = team_names[i];
+ }
+ }
+
+ return pTeamName;
+}
+
+
+//=========================================================
+//=========================================================
+void CTeamplayRules::RecountTeams( void )
+{
+ char *pName;
+ char teamlist[TEAMPLAY_TEAMLISTLENGTH];
+
+ // loop through all teams, recounting everything
+ num_teams = 0;
+
+ // Copy all of the teams from the teamlist
+ // make a copy because strtok is destructive
+ Q_strncpy( teamlist, m_szTeamList, sizeof(teamlist) );
+ pName = teamlist;
+ pName = strtok( pName, ";" );
+ while ( pName != NULL && *pName )
+ {
+ if ( GetTeamIndex( pName ) < 0 )
+ {
+ Q_strncpy( team_names[num_teams], pName, sizeof(team_names[num_teams]));
+ num_teams++;
+ }
+ pName = strtok( NULL, ";" );
+ }
+
+ if ( num_teams < 2 )
+ {
+ num_teams = 0;
+ m_teamLimit = false;
+ }
+
+ // Sanity check
+ memset( team_scores, 0, sizeof(team_scores) );
+
+ // loop through all clients
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *plr = UTIL_PlayerByIndex( i );
+
+ if ( plr )
+ {
+ const char *pTeamName = plr->TeamID();
+ // try add to existing team
+ int tm = GetTeamIndex( pTeamName );
+
+ if ( tm < 0 ) // no team match found
+ {
+ if ( !m_teamLimit )
+ {
+ // add to new team
+ tm = num_teams;
+ num_teams++;
+ team_scores[tm] = 0;
+ Q_strncpy( team_names[tm], pTeamName, MAX_TEAMNAME_LENGTH );
+ }
+ }
+
+ if ( tm >= 0 )
+ {
+ team_scores[tm] += plr->FragCount();
+ }
+ }
+ }
+}
+#endif // GAME_DLL