Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1115 lines
26 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Player for HL1.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tfc_player.h"
#include "tfc_gamerules.h"
#include "KeyValues.h"
#include "viewport_panel_names.h"
#include "client.h"
#include "team.h"
#include "weapon_tfcbase.h"
#include "tfc_client.h"
#include "tfc_mapitems.h"
#include "tfc_timer.h"
#include "tfc_engineer.h"
#include "tfc_team.h"
#define TFC_PLAYER_MODEL "models/player/pyro.mdl"
// -------------------------------------------------------------------------------- //
// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
// -------------------------------------------------------------------------------- //
class CTEPlayerAnimEvent : public CBaseTempEntity
{
public:
DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
DECLARE_SERVERCLASS();
CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
{
}
CNetworkHandle( CBasePlayer, m_hPlayer );
CNetworkVar( int, m_iEvent );
CNetworkVar( int, m_nData );
};
IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
SendPropEHandle( SENDINFO( m_hPlayer ) ),
SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED )
SendPropInt( SENDINFO( m_nData ), 32 )
END_SEND_TABLE()
static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
{
CPVSFilter filter( pPlayer->EyePosition() );
// The player himself doesn't need to be sent his animation events
// unless cs_showanimstate wants to show them.
if ( !ToolsEnabled() && ( cl_showanimstate.GetInt() == pPlayer->entindex() ) )
{
filter.RemoveRecipient( pPlayer );
}
g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
g_TEPlayerAnimEvent.m_iEvent = event;
g_TEPlayerAnimEvent.m_nData = nData;
g_TEPlayerAnimEvent.Create( filter, 0 );
}
// -------------------------------------------------------------------------------- //
// Tables.
// -------------------------------------------------------------------------------- //
LINK_ENTITY_TO_CLASS( player, CTFCPlayer );
PRECACHE_REGISTER(player);
IMPLEMENT_SERVERCLASS_ST( CTFCPlayer, DT_TFCPlayer )
SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
// cs_playeranimstate and clientside animation takes care of these on the client
SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11 ),
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11 ),
SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_TFCPlayerShared ) )
END_SEND_TABLE()
// -------------------------------------------------------------------------------- //
void cc_CreatePredictionError_f()
{
CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) );
}
ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
CTFCPlayer::CTFCPlayer()
{
m_PlayerAnimState = CreatePlayerAnimState( this );
item_list = 0;
UseClientSideAnimation();
m_angEyeAngles.Init();
m_pCurStateInfo = NULL;
m_lifeState = LIFE_DEAD; // Start "dead".
SetViewOffset( TFC_PLAYER_VIEW_OFFSET );
SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
}
void CTFCPlayer::TFCPlayerThink()
{
if ( m_pCurStateInfo && m_pCurStateInfo->pfnThink )
(this->*m_pCurStateInfo->pfnThink)();
SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
}
CTFCPlayer::~CTFCPlayer()
{
m_PlayerAnimState->Release();
}
CTFCPlayer *CTFCPlayer::CreatePlayer( const char *className, edict_t *ed )
{
CTFCPlayer::s_PlayerEdict = ed;
return (CTFCPlayer*)CreateEntityByName( className );
}
void CTFCPlayer::PostThink()
{
BaseClass::PostThink();
QAngle angles = GetLocalAngles();
angles[PITCH] = 0;
SetLocalAngles( angles );
// Store the eye angles pitch so the client can compute its animation state correctly.
m_angEyeAngles = EyeAngles();
m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
}
void CTFCPlayer::Precache()
{
for ( int i=0; i < PC_LASTCLASS; i++ )
PrecacheModel( GetTFCClassInfo( i )->m_pModelName );
PrecacheScriptSound( "Player.Spawn" );
BaseClass::Precache();
}
void CTFCPlayer::InitialSpawn( void )
{
BaseClass::InitialSpawn();
State_Enter( STATE_WELCOME );
}
void CTFCPlayer::Spawn()
{
SetModel( GetTFCClassInfo( m_Shared.GetPlayerClass() )->m_pModelName );
SetMoveType( MOVETYPE_WALK );
m_iLegDamage = 0;
BaseClass::Spawn();
// Kind of lame, but CBasePlayer::Spawn resets a lot of the state that we initially want on.
// So if we're in the welcome state, call its enter function to reset
if ( m_Shared.State_Get() == STATE_WELCOME )
{
State_Enter_WELCOME();
}
// If they were dead, then they're respawning. Put them in the active state.
if ( m_Shared.State_Get() == STATE_DYING )
{
State_Transition( STATE_ACTIVE );
}
// If they're spawning into the world as fresh meat, give them items and stuff.
if ( m_Shared.State_Get() == STATE_ACTIVE )
{
EmitSound( "Player.Spawn" );
GiveDefaultItems();
}
}
void CTFCPlayer::ForceRespawn()
{
//TFCTODO: goldsrc tfc has a big function for this.. doing what I'm doing here may not work.
respawn( this, false );
}
void CTFCPlayer::GiveDefaultItems()
{
switch( m_Shared.GetPlayerClass() )
{
case PC_HWGUY:
{
GiveNamedItem( "weapon_crowbar" );
GiveNamedItem( "weapon_minigun" );
GiveAmmo( 176, TFC_AMMO_SHELLS );
}
break;
case PC_PYRO:
{
GiveNamedItem( "weapon_crowbar" );
}
break;
case PC_ENGINEER:
{
GiveNamedItem( "weapon_spanner" );
GiveNamedItem( "weapon_super_shotgun" );
GiveAmmo( 20, TFC_AMMO_SHELLS );
}
break;
case PC_SCOUT:
{
GiveNamedItem( "weapon_crowbar" );
GiveNamedItem( "weapon_shotgun" );
GiveNamedItem( "weapon_nailgun" );
GiveAmmo( 25, TFC_AMMO_SHELLS );
GiveAmmo( 100, TFC_AMMO_NAILS );
}
break;
case PC_SNIPER:
{
GiveNamedItem( "weapon_crowbar" );
}
break;
case PC_SOLDIER:
{
GiveNamedItem( "weapon_crowbar" );
}
break;
case PC_DEMOMAN:
{
GiveNamedItem( "weapon_crowbar" );
}
break;
case PC_SPY:
{
GiveNamedItem( "weapon_knife" );
}
break;
case PC_MEDIC:
{
GiveNamedItem( "weapon_medikit" );
GiveNamedItem( "weapon_super_nailgun" );
GiveAmmo( 100, TFC_AMMO_NAILS );
}
break;
}
}
void CTFCPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
{
m_PlayerAnimState->DoAnimationEvent( event, nData );
TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
}
void CTFCPlayer::State_Transition( TFCPlayerState newState )
{
State_Leave();
State_Enter( newState );
}
void CTFCPlayer::State_Enter( TFCPlayerState newState )
{
m_Shared.m_iPlayerState = newState;
m_pCurStateInfo = State_LookupInfo( newState );
// Initialize the new state.
if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
(this->*m_pCurStateInfo->pfnEnterState)();
}
void CTFCPlayer::State_Leave()
{
if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
{
(this->*m_pCurStateInfo->pfnLeaveState)();
}
}
CPlayerStateInfo* CTFCPlayer::State_LookupInfo( TFCPlayerState state )
{
// This table MUST match the
static CPlayerStateInfo playerStateInfos[] =
{
{ STATE_ACTIVE, "STATE_ACTIVE", &CTFCPlayer::State_Enter_ACTIVE, NULL, NULL },
{ STATE_WELCOME, "STATE_WELCOME", &CTFCPlayer::State_Enter_WELCOME, NULL, NULL },
{ STATE_PICKINGTEAM, "STATE_PICKINGTEAM", &CTFCPlayer::State_Enter_PICKINGTEAM, NULL, NULL },
{ STATE_PICKINGCLASS, "STATE_PICKINGCLASS", &CTFCPlayer::State_Enter_PICKINGCLASS, NULL, NULL },
{ STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CTFCPlayer::State_Enter_OBSERVER_MODE, NULL, NULL },
{ STATE_DYING, "STATE_DYING", &CTFCPlayer::State_Enter_DYING, NULL, NULL }
};
for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
{
if ( playerStateInfos[i].m_iPlayerState == state )
return &playerStateInfos[i];
}
return NULL;
}
void CTFCPlayer::State_Enter_WELCOME()
{
SetMoveType( MOVETYPE_NONE );
AddEffects( EF_NODRAW );
AddSolidFlags( FSOLID_NOT_SOLID );
PhysObjectSleep();
// Show info panel (if it's not a simple demo map).
KeyValues *data = new KeyValues("data");
data->SetString( "title", "Message of the Day" ); // info panel title
data->SetString( "type", "3" ); // show a file
data->SetString( "msg", "motd.txt" ); // this file
data->SetString( "cmd", "joingame" ); // exec this command if panel closed
ShowViewPortPanel( "info", true, data );
data->deleteThis();
}
void CTFCPlayer::State_Enter_PICKINGTEAM()
{
ShowViewPortPanel( PANEL_TEAM ); // show the team menu
}
void CTFCPlayer::State_Enter_PICKINGCLASS()
{
// go to spec mode, if dying keep deathcam
if ( GetObserverMode() == OBS_MODE_DEATHCAM )
{
StartObserverMode( OBS_MODE_DEATHCAM );
}
else
{
StartObserverMode( OBS_MODE_ROAMING );
}
PhysObjectSleep();
// show the class menu:
ShowViewPortPanel( PANEL_CLASS );
}
void CTFCPlayer::State_Enter_OBSERVER_MODE()
{
StartObserverMode( m_iObserverLastMode );
PhysObjectSleep();
}
void CTFCPlayer::State_Enter_ACTIVE()
{
SetMoveType( MOVETYPE_WALK );
RemoveEffects( EF_NODRAW );
RemoveSolidFlags( FSOLID_NOT_SOLID );
m_Local.m_iHideHUD = 0;
PhysObjectWake();
}
void CTFCPlayer::State_Enter_DYING()
{
SetMoveType( MOVETYPE_NONE );
AddSolidFlags( FSOLID_NOT_SOLID );
}
void CTFCPlayer::PhysObjectSleep()
{
IPhysicsObject *pObj = VPhysicsGetObject();
if ( pObj )
pObj->Sleep();
}
void CTFCPlayer::PhysObjectWake()
{
IPhysicsObject *pObj = VPhysicsGetObject();
if ( pObj )
pObj->Wake();
}
void CTFCPlayer::HandleCommand_JoinTeam( const char *pTeamName )
{
int iTeam = TEAM_RED;
if ( stricmp( pTeamName, "auto" ) == 0 )
{
iTeam = RandomInt( 0, 1 ) ? TEAM_RED : TEAM_BLUE;
}
else if ( stricmp( pTeamName, "spectate" ) == 0 )
{
iTeam = TEAM_SPECTATOR;
}
else
{
for ( int i=0; i < TEAM_MAXCOUNT; i++ )
{
if ( stricmp( pTeamName, teamnames[i] ) == 0 )
{
iTeam = i;
break;
}
}
}
if ( iTeam == TEAM_SPECTATOR )
{
// Prevent this is the cvar is set
if ( !mp_allowspectators.GetInt() && !IsHLTV() )
{
ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" );
return;
}
if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() )
{
CommitSuicide();
// add 1 to frags to balance out the 1 subtracted for killing yourself
IncrementFragCount( 1 );
}
ChangeTeam( TEAM_SPECTATOR );
// do we have fadetoblack on? (need to fade their screen back in)
if ( mp_fadetoblack.GetInt() )
{
color32_s clr = { 0,0,0,0 };
UTIL_ScreenFade( this, clr, 0.001, 0, FFADE_IN );
}
}
else
{
ChangeTeam( iTeam );
State_Transition( STATE_PICKINGCLASS );
}
}
void CTFCPlayer::ChangeTeam( int iTeamNum )
{
if ( !GetGlobalTeam( iTeamNum ) )
{
Warning( "CCSPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum );
return;
}
int iOldTeam = GetTeamNumber();
// if this is our current team, just abort
if ( iTeamNum == iOldTeam )
return;
BaseClass::ChangeTeam( iTeamNum );
if ( iTeamNum == TEAM_UNASSIGNED )
{
State_Transition( STATE_OBSERVER_MODE );
}
else if ( iTeamNum == TEAM_SPECTATOR )
{
State_Transition( STATE_OBSERVER_MODE );
}
else // active player
{
if ( iOldTeam == TEAM_SPECTATOR )
{
// If they're switching from being a spectator to ingame player
GetIntoGame();
}
if ( !IsDead() && iOldTeam != TEAM_UNASSIGNED )
{
// Kill player if switching teams while alive
CommitSuicide();
}
// Put up the class selection menu.
State_Transition( STATE_PICKINGCLASS );
}
}
void CTFCPlayer::HandleCommand_JoinClass( const char *pClassName )
{
int iClass = RandomInt( 0, PC_LAST_NORMAL_CLASS );
if ( stricmp( pClassName, "random" ) != 0 )
{
for ( int i=0; i < PC_LASTCLASS; i++ )
{
if ( stricmp( pClassName, GetTFCClassInfo( i )->m_pClassName ) == 0 )
{
iClass = i;
break;
}
}
if ( i == PC_LAST_NORMAL_CLASS )
{
Warning( "HandleCommand_JoinClass( %s ) - invalid class name.\n", pClassName );
}
}
m_Shared.SetPlayerClass( iClass );
if ( !IsAlive() )
GetIntoGame();
}
void CTFCPlayer::GetIntoGame()
{
State_Transition( STATE_ACTIVE );
Spawn();
}
bool CTFCPlayer::ClientCommand( const CCommand& args )
{
const char *pcmd = args[0];
if ( FStrEq( pcmd, "joingame" ) )
{
// player just closed MOTD dialog
if ( m_Shared.m_iPlayerState == STATE_WELCOME )
{
State_Transition( STATE_PICKINGTEAM );
}
return true;
}
else if ( FStrEq( pcmd, "jointeam" ) )
{
if ( args.ArgC() >= 2 )
{
HandleCommand_JoinTeam( args[1] );
return true;
}
}
else if ( FStrEq( pcmd, "joinclass" ) )
{
if ( args.ArgC() < 2 )
{
Warning( "Player sent bad joinclass syntax\n" );
}
HandleCommand_JoinClass( args[1] );
return true;
}
return BaseClass::ClientCommand( args );
}
bool CTFCPlayer::IsAlly( CBaseEntity *pEnt ) const
{
return pEnt->GetTeamNumber() == GetTeamNumber();
}
void CTFCPlayer::TF_AddFrags( int nFrags )
{
// TFCTODO: implement frags
}
void CTFCPlayer::ResetMenu()
{
current_menu = 0;
}
int CTFCPlayer::GetNumFlames() const
{
// TFCTODO: implement flames
return 0;
}
void CTFCPlayer::SetNumFlames( int nFlames )
{
// TFCTODO: implement frags
Assert( 0 );
}
int CTFCPlayer::TakeHealth( float flHealth, int bitsDamageType )
{
int bResult = false;
// If the bit's set, ignore the monster's max health and add over it
if ( bitsDamageType & DMG_IGNORE_MAXHEALTH )
{
int iDamage = g_pGameRules->Damage_GetTimeBased();
m_bitsDamageType &= ~(bitsDamageType & ~iDamage);
m_iHealth += flHealth;
bResult = true;
}
else
{
bResult = BaseClass::TakeHealth( flHealth, bitsDamageType );
}
// Leg Healing
if (m_iLegDamage > 0)
{
// Allow even at full health
if ( GetHealth() >= (GetMaxHealth() - 5))
m_iLegDamage = 0;
else
m_iLegDamage -= (GetHealth() + flHealth) / 20;
if (m_iLegDamage < 1)
m_iLegDamage = 0;
TeamFortress_SetSpeed();
bResult = true;
}
return bResult;
}
void CTFCPlayer::TeamFortress_SetSpeed()
{
int playerclass = m_Shared.GetPlayerClass();
float maxfbspeed;
// Spectators can move while in Classic Observer mode
if ( IsObserver() )
{
if ( GetObserverMode() == OBS_MODE_ROAMING )
SetMaxSpeed( GetTFCClassInfo( PC_SCOUT )->m_flMaxSpeed );
else
SetMaxSpeed( 0 );
return;
}
// Check for any reason why they can't move at all
if ( (m_Shared.GetStateFlags() & TFSTATE_CANT_MOVE) || (playerclass == PC_UNDEFINED) )
{
SetAbsVelocity( vec3_origin );
SetMaxSpeed( 1 );
return;
}
// First, get their max class speed
maxfbspeed = GetTFCClassInfo( playerclass )->m_flMaxSpeed;
// 2nd, see if any GoalItems are slowing them down
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "item_tfgoal" );
while ( pEnt )
{
CTFGoal *pGoal = dynamic_cast<CTFGoal*>( pEnt );
if ( pGoal )
{
if ( pGoal->GetOwnerEntity() == this )
{
if (pGoal->goal_activation & TFGI_SLOW)
{
maxfbspeed = maxfbspeed / 2;
}
else if (pGoal->speed_reduction)
{
float flPercent = ((float)pGoal->speed_reduction) / 100.0;
maxfbspeed = flPercent * maxfbspeed;
}
}
}
pEnt = gEntList.FindEntityByClassname( pEnt, "item_tfgoal" );
}
// 3rd, See if they're tranquilised
if (m_Shared.GetStateFlags() & TFSTATE_TRANQUILISED)
{
maxfbspeed = maxfbspeed / 2;
}
// 4th, check for leg wounds
if (m_iLegDamage)
{
if (m_iLegDamage > 6)
m_iLegDamage = 6;
// reduce speed by 10% per leg wound
maxfbspeed = (maxfbspeed * ((10 - m_iLegDamage) / 10));
}
// 5th, if they're a sniper, and they're aiming, their speed must be 80 or less
if (m_Shared.GetStateFlags() & TFSTATE_AIMING)
{
if (maxfbspeed > 80)
maxfbspeed = 80;
}
// Set the speed
SetMaxSpeed( maxfbspeed );
}
void CTFCPlayer::Event_Killed( const CTakeDamageInfo &info )
{
DoAnimationEvent( PLAYERANIMEVENT_DIE );
State_Transition( STATE_DYING ); // Transition into the dying state.
// Remove all items..
RemoveAllItems( true );
BaseClass::Event_Killed( info );
// Don't overflow the value for this.
m_iHealth = 0;
}
void CTFCPlayer::ClientHearVox( const char *pSentence )
{
//TFCTODO: implement this.
}
//=========================================================================
// Check all stats to make sure they're good for this class
void CTFCPlayer::TeamFortress_CheckClassStats()
{
// Check armor
if (armortype > armor_allowed)
armortype = armor_allowed;
if (ArmorValue() > GetClassInfo()->m_iMaxArmor)
SetArmorValue( GetClassInfo()->m_iMaxArmor );
if (ArmorValue() < 0)
SetArmorValue( 0 );
if (armortype < 0)
armortype = 0;
// Check ammo
for ( int iAmmoType=0; iAmmoType < TFC_NUM_AMMO_TYPES; iAmmoType++ )
{
if ( GetAmmoCount( iAmmoType ) > GetClassInfo()->m_MaxAmmo[iAmmoType] )
RemoveAmmo( GetAmmoCount( iAmmoType ) - GetClassInfo()->m_MaxAmmo[iAmmoType], iAmmoType );
}
// Check Grenades
Assert( GetAmmoCount( TFC_AMMO_GRENADES1 ) >= 0 );
Assert( GetAmmoCount( TFC_AMMO_GRENADES2 ) >= 0 );
// Limit Nails
if ( no_grenades_1() > g_nMaxGrenades[tp_grenades_1()] )
RemoveAmmo( TFC_AMMO_GRENADES1, no_grenades_1() - g_nMaxGrenades[tp_grenades_1()] );
if ( no_grenades_2() > g_nMaxGrenades[tp_grenades_2()] )
RemoveAmmo( TFC_AMMO_GRENADES2, no_grenades_2() - g_nMaxGrenades[tp_grenades_2()] );
// Check health
if (GetHealth() > GetMaxHealth() && !(m_Shared.GetItemFlags() & IT_SUPERHEALTH))
SetHealth( GetMaxHealth() );
if (GetHealth() < 0)
SetHealth( 0 );
// Update armor picture
m_Shared.RemoveItemFlags( IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3 );
if (armortype >= 0.8)
m_Shared.AddItemFlags( IT_ARMOR3 );
else if (armortype >= 0.6)
m_Shared.AddItemFlags( IT_ARMOR2 );
else if (armortype >= 0.3)
m_Shared.AddItemFlags( IT_ARMOR1 );
}
//======================================================================
// DISGUISE HANDLING
//======================================================================
// Reset spy skin and color or remove invisibility
void CTFCPlayer::Spy_RemoveDisguise()
{
if (m_Shared.GetPlayerClass() == PC_SPY)
{
if ( undercover_team || undercover_skin )
ClientPrint( this, HUD_PRINTCENTER, "#Disguise_Lost" );
// Set their color
undercover_team = 0;
undercover_skin = 0;
immune_to_check = gpGlobals->curtime + 10;
is_undercover = 0;
// undisguise weapon
TeamFortress_SetSkin();
TeamFortress_SpyCalcName();
Spy_ResetExternalWeaponModel();
// get them out of any disguise menus
if ( current_menu == MENU_SPY || current_menu == MENU_SPY_SKIN || current_menu == MENU_SPY_COLOR )
{
ResetMenu();
}
// Remove the Disguise timer
CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_DISGUISE );
if (pTimer)
{
ClientPrint( this, HUD_PRINTCENTER, "#Disguise_stop" );
Timer_Remove( pTimer );
}
}
}
// when the spy loses disguise reset his weapon
void CTFCPlayer::Spy_ResetExternalWeaponModel( void )
{
// we don't show any weapon models if we're feigning
if ( is_feigning )
return;
#ifdef TFCTODO // spy
pev->weaponmodel = MAKE_STRING( m_pszSavedWeaponModel );
strcpy( m_szAnimExtention, m_szSavedAnimExtention );
m_iCurrentAnimationState = 0; // force the current animation sequence to be recalculated
#endif
}
//=========================================================================
// Try and find the player's name who's skin and team closest fit the
// current disguise of the spy
void CTFCPlayer::TeamFortress_SpyCalcName()
{
CBaseEntity *last_target = undercover_target;// don't redisguise self as this person
undercover_target = NULL;
// Find a player on the team the spy is disguised as to pretend to be
if (undercover_team != 0)
{
CTFCPlayer *pPlayer = NULL;
// Loop through players
int i;
for ( i = 1; i <= gpGlobals->maxClients; i++ )
{
pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
if ( pPlayer )
{
if ( pPlayer == last_target )
{
// choose someone else, we're trying to rid ourselves of a disguise as this one
continue;
}
// First, try to find a player with same color and skins
if (pPlayer->GetTeamNumber() == undercover_team && pPlayer->m_Shared.GetPlayerClass() == undercover_skin)
{
undercover_target = pPlayer;
return;
}
}
}
for ( i = 1; i <= gpGlobals->maxClients; i++ )
{
pPlayer = ToTFCPlayer( UTIL_PlayerByIndex( i ) );
if ( pPlayer )
{
if (pPlayer->GetTeamNumber() == undercover_team)
{
undercover_target = pPlayer;
return;
}
}
}
}
}
//=========================================================================
// Set the skin of a player based on his/her class
void CTFCPlayer::TeamFortress_SetSkin()
{
immune_to_check = gpGlobals->curtime + 10;
// Find out whether we should show our actual class or a disguised class
int iClassToUse = m_Shared.GetPlayerClass();
if (iClassToUse == PC_SPY && undercover_skin != 0)
iClassToUse = undercover_skin;
int iTeamToUse = GetTeamNumber();
if (m_Shared.GetPlayerClass() == PC_SPY && undercover_team != 0)
iTeamToUse = undercover_team;
// TFCTODO: handle replacement_model here.
SetModel( GetTFCClassInfo( iClassToUse )->m_pModelName );
// Skins in the models should be setup using the team IDs in tfc_shareddefs.h, subtracting 1
// so they're 0-based.
m_nSkin = iTeamToUse - 1;
if ( FBitSet(GetFlags(), FL_DUCKING) )
UTIL_SetSize(this, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
else
UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX);
}
//=========================================================================
// Displays the state of the items specified by the Goal passed in
void CTFCPlayer::DisplayLocalItemStatus( CTFGoal *pGoal )
{
for (int i = 0; i < 4; i++)
{
if (pGoal->display_item_status[i] != 0)
{
CTFGoalItem *pItem = Finditem(pGoal->display_item_status[i]);
if (pItem)
DisplayItemStatus(pGoal, this, pItem);
else
ClientPrint( this, HUD_PRINTTALK, "#Item_missing" );
}
}
}
//=========================================================================
// Removes all the Engineer's buildings
void CTFCPlayer::Engineer_RemoveBuildings()
{
// If the player's building already, stop
if (is_building == 1)
{
m_Shared.RemoveStateFlags( TFSTATE_CANT_MOVE );
TeamFortress_SetSpeed();
// Remove the timer
CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_BUILD );
if (pTimer)
Timer_Remove(pTimer);
// Remove the building
UTIL_Remove( building );
building = NULL;
is_building = 0;
// Stop Build Sound
StopSound( "Engineer.Building" );
//STOP_SOUND( ENT(pev), CHAN_STATIC, "weapons/building.wav" );
if ( GetActiveWeapon() )
GetActiveWeapon()->Deploy();
}
DestroyBuilding(this, "building_dispenser");
DestroyBuilding(this, "building_sentrygun");
DestroyTeleporter(this, BUILD_TELEPORTER_ENTRY);
DestroyTeleporter(this, BUILD_TELEPORTER_EXIT);
}
//=========================================================================
// Removes all grenades that persist for a period of time from the world
void CTFCPlayer::TeamFortress_RemoveLiveGrenades( void )
{
RemoveOwnedEnt( "tf_weapon_napalmgrenade" );
RemoveOwnedEnt( "tf_weapon_nailgrenade" );
RemoveOwnedEnt( "tf_weapon_gasgrenade" );
RemoveOwnedEnt( "tf_weapon_caltrop" );
}
//=========================================================================
// Removes all rockets the player has fired into the world
// (this prevents a team kill cheat where players would fire rockets
// then change teams to kill their own team)
void CTFCPlayer::TeamFortress_RemoveRockets( void )
{
RemoveOwnedEnt( "tf_rpg_rocket" );
RemoveOwnedEnt( "tf_ic_rocket" );
}
// removes the player's pipebombs with no explosions
void CTFCPlayer::RemovePipebombs( void )
{
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "tf_gl_pipebomb" );
while ( pEnt )
{
CTFCPlayer *pOwner = ToTFCPlayer( pEnt->GetOwnerEntity() );
if ( pOwner == this )
{
pOwner->m_iPipebombCount--;
pEnt->AddFlag( FL_KILLME );
}
pEnt = gEntList.FindEntityByClassname( pEnt, "tf_gl_pipebomb" );
}
}
//=========================================================================
// Stops the setting of the detpack
void CTFCPlayer::TeamFortress_DetpackStop( void )
{
CTimer *pTimer = Timer_FindTimer( this, TF_TIMER_DETPACKSET );
if (!pTimer)
return;
ClientPrint( this, HUD_PRINTNOTIFY, "#Detpack_retrieve" );
// Return the detpack
GiveAmmo( 1, TFC_AMMO_DETPACK );
Timer_Remove(pTimer);
// Release player
m_Shared.RemoveStateFlags( TFSTATE_CANT_MOVE );
is_detpacking = 0;
TeamFortress_SetSpeed();
// Return their weapon
if ( GetActiveWeapon() )
GetActiveWeapon()->Deploy();
}
//=========================================================================
// Removes any detpacks the player may have set
BOOL CTFCPlayer::TeamFortress_RemoveDetpacks( void )
{
// Remove all detpacks owned by the player
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "detpack" );
while ( pEnt )
{
// if the player owns this detpack, remove it
if ( pEnt->GetOwnerEntity() == this )
{
UTIL_Remove( pEnt );
return TRUE;
}
pEnt = gEntList.FindEntityByClassname( pEnt, "detpack" );
}
return FALSE;
}
//=========================================================================
// Remove all of an ent owned by this player
void CTFCPlayer::RemoveOwnedEnt( char *pEntName )
{
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, pEntName );
while ( pEnt )
{
// if the player owns this entity, remove it
if ( pEnt->GetOwnerEntity() == this )
pEnt->AddFlag( FL_KILLME );
pEnt = gEntList.FindEntityByClassname( pEnt, pEntName );
}
}