mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2025-02-01 00:00:42 +03:00
beaae8ac45
* Adds support for Visual Studio 2012 and 2013 * VR Mode: . Switches from headtrack.dll to sourcevr.dll . Improved readability of the UI in VR . Removed the IPD calibration tool. TF2 will now obey the Oculus configuration file. Use the Oculus calibration tool in your SDK or install and run "OpenVR" under Tools in Steam to calibrate your IPD. . Added dropdown to enable VR mode in the Video options. Removed the -vr command line option. . Added the ability to switch in and out of VR mode without quitting the game . By default VR mode will run full screen. To switch back to a borderless window set the vr_force_windowed convar. . Added support for VR mode on Linux * Many assorted bug fixes and other changes from Team Fortress in various shared files
575 lines
14 KiB
C++
575 lines
14 KiB
C++
//========= 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
|