summaryrefslogtreecommitdiff
path: root/game/server/tfc/tfc_player.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/server/tfc/tfc_player.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/tfc/tfc_player.cpp')
-rw-r--r--game/server/tfc/tfc_player.cpp1115
1 files changed, 1115 insertions, 0 deletions
diff --git a/game/server/tfc/tfc_player.cpp b/game/server/tfc/tfc_player.cpp
new file mode 100644
index 0000000..d006efc
--- /dev/null
+++ b/game/server/tfc/tfc_player.cpp
@@ -0,0 +1,1115 @@
+//========= 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 );
+ }
+}
+
+