diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf2/c_basetfplayer.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf2/c_basetfplayer.cpp')
| -rw-r--r-- | game/client/tf2/c_basetfplayer.cpp | 2705 |
1 files changed, 2705 insertions, 0 deletions
diff --git a/game/client/tf2/c_basetfplayer.cpp b/game/client/tf2/c_basetfplayer.cpp new file mode 100644 index 0000000..a85c2ed --- /dev/null +++ b/game/client/tf2/c_basetfplayer.cpp @@ -0,0 +1,2705 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "c_basetfplayer.h" +#include "beamdraw.h" +#include <stdarg.h> +#include "ivmodemanager.h" +#include "shake.h" +#include "ivieweffects.h" +#include "c_tfteam.h" +#include "view.h" +#include "UserCmd.h" +#include "ivrenderview.h" +#include "model_types.h" +#include "view_shared.h" +#include "hud_orders.h" +#include "weapon_twohandedcontainer.h" +#include "particles_simple.h" +#include "playerandobjectenumerator.h" +#include "iclientvehicle.h" +#include "input.h" +#include "basetfvehicle.h" +#include "c_vehicle_teleport_station.h" +#include "weapon_combatshield.h" +#include "hud_vehicle_role.h" +#include "hud_technologytreedoc.h" +#include "iclientmode.h" +#include "weapon_selection.h" +#include "clienteffectprecachesystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +// Don't alias here +#if defined( CBaseTFPlayer ) +#undef CBaseTFPlayer +#endif + +void CAM_ToThirdPerson(void); +void CAM_ToFirstPerson(void); +void FX_ReconParticle( const Vector &vecOrigin, bool bBlue ); + +static ConVar tf2_showstate( "tf2_showstate", "0", 0, "Print state name\n" ); +extern ConVar mannedgun_usethirdperson; +ConVar damageboost_modeloffset( "damageboost_modeloffset", "1" ); +ConVar damageboost_modelphasespeed_min( "damageboost_modelphasespeed_min", "99" ); +ConVar damageboost_modelphasespeed_max( "damageboost_modelphasespeed_max", "170" ); + + +CLIENTEFFECT_REGISTER_BEGIN( PrecacheBaseTFPlayer ) +CLIENTEFFECT_MATERIAL( "sprites/physbeam" ) +CLIENTEFFECT_MATERIAL( "player/infiltratorcamo/infiltratorcamo" ) +CLIENTEFFECT_REGISTER_END() + + +class CPersonalShieldEffect +{ +public: + + static CPersonalShieldEffect* Create( C_BaseTFPlayer *pEnt, const Vector &vOffsetFromEnt, const Vector &vIncomingDirection, int iDamage ); + + ~CPersonalShieldEffect(); + + // Returns false if the effect is done and should be deleted. + bool Update( float dt ); + + void Render(); // Draw the effect. + +private: + + CPersonalShieldEffect(); + +private: + + CHandle<C_BaseTFPlayer> m_hEnt; + Vector m_vOffsetFromEnt; + Vector m_vIncomingDirection; + float m_flDamage; + + enum + { + NUM_TRACERS = 10 + }; + + Vector m_vTracerEndPoints[NUM_TRACERS]; // These are relative to the model's origin. + float m_flLifetimeRemaining; + float m_flTotalLifetime; +}; + + +CPersonalShieldEffect* CPersonalShieldEffect::Create( + C_BaseTFPlayer *pEnt, + const Vector &vOffsetFromEnt, + const Vector &vIncomingDirection, + int iDamage ) +{ + CPersonalShieldEffect *pRet = new CPersonalShieldEffect; + if ( pRet ) + { + pRet->m_hEnt = pEnt; + pRet->m_vOffsetFromEnt = vOffsetFromEnt; + pRet->m_flDamage = iDamage; + + Vector vNormal = vIncomingDirection; + VectorNormalize( vNormal ); + + for ( int i=0; i < CPersonalShieldEffect::NUM_TRACERS; i++ ) + { + pRet->m_vTracerEndPoints[i] = pRet->m_vOffsetFromEnt; + + Vector vOffset = RandomVector( -1, 1 ); + + // Make it tangent to a sphere enclosing the player. + float flDot = vNormal.Dot( vOffset ); + vOffset -= vNormal * flDot; + + VectorNormalize( vOffset ); + vOffset *= 10; + + pRet->m_vTracerEndPoints[i] += vOffset; + } + + pRet->m_flLifetimeRemaining = pRet->m_flTotalLifetime = 0.15; + pRet->m_vIncomingDirection = vIncomingDirection; + } + return pRet; +} + +CPersonalShieldEffect::CPersonalShieldEffect() +{ +} + + +CPersonalShieldEffect::~CPersonalShieldEffect() +{ +} + + +bool CPersonalShieldEffect::Update( float dt ) +{ + if ( !m_hEnt.Get() ) + return false; + + m_flLifetimeRemaining -= dt; + if ( m_flLifetimeRemaining >= 0 ) + { + return true; + } + else + { + return false; + } +} + + +void CPersonalShieldEffect::Render() +{ + if ( !m_hEnt.Get() ) + return; + + Vector vCenter = m_hEnt->WorldSpaceCenter( ); + + const Vector vBasePos = vCenter + m_vOffsetFromEnt; + IMaterial *pMat = materials->FindMaterial( "sprites/physbeam", TEXTURE_GROUP_CLIENT_EFFECTS ); + + CBeamSeg seg; + + // Get redder as their health goes down. + float flColor = (float)m_hEnt->GetHealth() / m_hEnt->GetMaxHealth(); + flColor = clamp(flColor, 0, 1); + seg.m_vColor.Init( 0.5 + 0.5*flColor, flColor, flColor ); + + seg.m_flAlpha = 1; + seg.m_flTexCoord = 0; + seg.m_flWidth = 2; + + for ( int i=0; i < CPersonalShieldEffect::NUM_TRACERS; i++ ) + { + const Vector vEndPos = vCenter + m_vTracerEndPoints[i]; + + static int nSegs = 5; + + CBeamSegDraw beamDraw; + beamDraw.Start( nSegs, pMat ); + + for ( int iSeg=0; iSeg < nSegs; iSeg++ ) + { + float t = (float)iSeg / (nSegs-1); + VectorLerp( vBasePos, vEndPos, t, seg.m_vPos ); + + // Add a random offset. + seg.m_vPos += RandomVector( -3, 3 ); + seg.m_flTexCoord += 0.1f; + + beamDraw.NextSeg( &seg ); + } + + beamDraw.End(); + } + +/* + Vector vEggBounds[2]; + m_hEnt->GetBounds( vEggBounds[0], vEggBounds[1] ); + vEggBounds[0] += m_hEnt->GetAbsOrigin(); + vEggBounds[1] += m_hEnt->GetAbsOrigin(); + Vector vEggCenter = (vEggBounds[0] + vEggBounds[1]) * 0.5f; + Vector vEggDims = (vEggBounds[1] - vEggBounds[0]) * 0.5f; + + Vector vUp( 0, 0, 1 ); + Vector vForward = m_vIncomingDirection; + vForward.z = 0; + VectorNormalize( vForward ); + Vector vRight = vUp.Cross( vForward ); + + // Now draw an eggshell around the player showing their health. + seg.m_vColor.Init( 1, flColor, flColor ); + seg.m_flAlpha = 0.3f; + seg.m_flWidth = 1; + + static int nSamples = 30; + CBeamSegDraw beamDraw; + beamDraw.Start( nSamples, pMat ); + + for ( int iSeg=0; iSeg < nSamples; iSeg++ ) + { + float t = (float)iSeg / (nSamples-1); + float angle = M_PI * 2.0f * t; + seg.m_vPos = vEggCenter + vUp*vEggDims.z*sin( angle ) + vRight*vEggDims.x*cos( angle ); + beamDraw.NextSeg( &seg ); + } + + beamDraw.End(); +*/ +} + + +//----------------------------------------------------------------------------- +// Purpose: Helper for animation state machine +// Input : clear - +// destination - +// *pFormat - +// ... - +//----------------------------------------------------------------------------- +void StatusPrintf( bool clear, int destination, char *pFormat, ... ) +{ + if ( destination != 4 && destination != 5 ) + return; + char data[ 2048 ]; + va_list argptr; + + va_start( argptr, pFormat ); + Q_vsnprintf( data, sizeof( data ), pFormat, argptr ); + va_end( argptr ); + + char *out = data; + if ( destination == 5 ) + out += 2; + + if ( destination == 5 ) + { + char slot[3]; + Q_strncpy( slot, data, 3 ); + + slot[2] = 0; + + int s = atoi( slot ); + s &= 31; + + engine->Con_NPrintf( s, "%s", out ); + } + else + { + Msg( "%s", out ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: RecvProxy that converts the Player's object UtlVector to entindexes +//----------------------------------------------------------------------------- +void RecvProxy_PlayerObjectList( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + CTFPlayerLocalData *pLocalData = (CTFPlayerLocalData*)pStruct; + CBaseHandle *pHandle = (CBaseHandle*)(&(pLocalData->m_aObjects[pData->m_iElement])); + RecvProxy_IntToEHandle( pData, pStruct, pHandle ); +} + + +void RecvProxyArrayLength_PlayerObjects( void *pStruct, int objectID, int currentArrayLength ) +{ + CTFPlayerLocalData *pLocalData = (CTFPlayerLocalData*)pStruct; + + if ( pLocalData->m_aObjects.Count() != currentArrayLength ) + { + pLocalData->m_aObjects.SetSize( currentArrayLength ); + } +} + +BEGIN_RECV_TABLE_NOBASE( CTFPlayerLocalData, DT_TFLocal ) + RecvPropInt( RECVINFO(m_nInTacticalView) ), + RecvPropInt( RECVINFO( m_bKnockedDown )), + RecvPropVector( RECVINFO( m_vecKnockDownDir )), + RecvPropInt( RECVINFO( m_bThermalVision )), + RecvPropInt( RECVINFO( m_iIDEntIndex )), + RecvPropArray( RecvPropInt( RECVINFO(m_iResourceAmmo[0])), m_iResourceAmmo), + RecvPropInt( RECVINFO(m_iBankResources) ), + RecvPropArray2( + RecvProxyArrayLength_PlayerObjects, + RecvPropInt( "player_object_array_element", 0, SIZEOF_IGNORE, 0, RecvProxy_PlayerObjectList ), + MAX_OBJECTS_PER_PLAYER, + 0, + "player_object_array" ), + RecvPropInt( RECVINFO( m_bAttachingSapper ) ), + RecvPropFloat( RECVINFO( m_flSapperAttachmentFrac ) ), + RecvPropInt( RECVINFO( m_bForceMapOverview ) ), +END_RECV_TABLE() + +IMPLEMENT_CLIENTCLASS_DT(C_BaseTFPlayer, DT_BaseTFPlayer, CBaseTFPlayer) + RecvPropDataTable(RECVINFO_DT(m_TFLocal),0, &REFERENCE_RECV_TABLE(DT_TFLocal), DataTableRecvProxy_StaticDataTable), + + // Class Data Tables + RecvPropInt( RECVINFO(m_iPlayerClass)), + RecvPropDataTable( RECVINFO_DT( m_PlayerClasses ), 0, &REFERENCE_RECV_TABLE( DT_AllPlayerClasses ), DataTableRecvProxy_StaticDataTable ), + + RecvPropEHandle( RECVINFO( m_hSelectedMCV ) ), + RecvPropInt( RECVINFO(m_iCurrentZoneState ) ), + RecvPropInt( RECVINFO(m_iMaxHealth ) ), + RecvPropInt( RECVINFO(m_TFPlayerFlags) ), + RecvPropInt( RECVINFO( m_bUnderAttack )), + RecvPropInt( RECVINFO( m_bIsBlocking )), + + // Sniper - will get moved to a class data table + RecvPropVector( RECVINFO(m_vecDeployedAngles) ), + RecvPropInt( RECVINFO( m_bDeployed )), + RecvPropInt( RECVINFO( m_bDeploying )), + RecvPropInt( RECVINFO( m_bUnDeploying )), + + // Infiltrator - will get moved to a class data table + RecvPropFloat( RECVINFO( m_flCamouflageAmount )), + RecvPropEHandle(RECVINFO(m_hSpawnPoint)), +END_RECV_TABLE() + +BEGIN_PREDICTION_DATA_NO_BASE( CTFPlayerLocalData ) + + DEFINE_PRED_FIELD( m_nInTacticalView, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bKnockedDown, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_vecKnockDownDir, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bThermalVision, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), +// DEFINE_PRED_FIELD( m_iIDEntIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_iBankResources, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_ARRAY( m_iResourceAmmo, FIELD_INTEGER, RESOURCE_TYPES, FTYPEDESC_INSENDTABLE ), + //DEFINE_PRED_ARRAY( m_aObjects, FIELD_EHANDLE, MAX_OBJECTS_PER_PLAYER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bAttachingSapper, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_flSapperAttachmentFrac, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bForceMapOverview, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + +END_PREDICTION_DATA() + + +// TODO: consolidate all these includes and the DEFINE_PRED... for each class type +// into tf_shareddefs.h. +#include "c_tf_class_commando.h" +#include "c_tf_class_defender.h" +#include "c_tf_class_escort.h" +#include "c_tf_class_infiltrator.h" +#include "c_tf_class_medic.h" +#include "c_tf_class_recon.h" +#include "c_tf_class_sniper.h" +#include "c_tf_class_support.h" +#include "c_tf_class_sapper.h" +#include "c_tf_class_pyro.h" + + +BEGIN_PREDICTION_DATA( C_BaseTFPlayer ) + + DEFINE_PRED_TYPEDESCRIPTION( m_TFLocal, CTFPlayerLocalData ), + + DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_INSENDTABLE | FTYPEDESC_PRIVATE | FTYPEDESC_OVERRIDE ), + DEFINE_PRED_FIELD( m_flPlaybackRate, FIELD_FLOAT, FTYPEDESC_INSENDTABLE | FTYPEDESC_PRIVATE | FTYPEDESC_OVERRIDE ), + + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_COMMANDO], C_PlayerClassCommando ), + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_DEFENDER], C_PlayerClassDefender ), + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_ESCORT], C_PlayerClassEscort ), + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_INFILTRATOR], C_PlayerClassInfiltrator ), + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_MEDIC], C_PlayerClassMedic ), + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_RECON], C_PlayerClassRecon ), + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_SNIPER], C_PlayerClassSniper ), + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_SUPPORT], C_PlayerClassSupport ), + DEFINE_PRED_TYPEDESCRIPTION_PTR( m_PlayerClasses.m_pClasses[TFCLASS_SAPPER], C_PlayerClassSapper ), + + DEFINE_PRED_FIELD( m_iPlayerClass, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_flCamouflageAmount, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_hSpawnPoint, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_iMaxHealth, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bDeployed, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bDeploying, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bUnDeploying, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_vecDeployedAngles, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_iCurrentZoneState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_TFPlayerFlags, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bUnderAttack, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + + DEFINE_PRED_FIELD( m_vecConstraintCenter, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_flConstraintRadius, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_flConstraintWidth, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_flConstraintSpeedFactor, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), + + DEFINE_FIELD( m_vecPosDelta, FIELD_VECTOR ), + DEFINE_ARRAY( m_aMomentum, FIELD_FLOAT, C_BaseTFPlayer::MOMENTUM_MAXSIZE ), + DEFINE_FIELD( m_iMomentumHead, FIELD_INTEGER ), + //DEFINE_FIELD( m_iSelectedTarget, FIELD_INTEGER ), + //DEFINE_FIELD( m_iPersonalTarget, FIELD_INTEGER ), + //DEFINE_FIELD( m_iLastHealth, FIELD_INTEGER ), + //DEFINE_FIELD( m_nOldTacticalView, FIELD_INTEGER ), + //DEFINE_FIELD( m_nOldPlayerClass, FIELD_INTEGER ), + //DEFINE_FIELD( m_bOldThermalVision, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_bOldKnockDownState, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_flStartKnockdown, FIELD_FLOAT ), + //DEFINE_FIELD( m_flEndKnockdown, FIELD_FLOAT ), + //DEFINE_FIELD( m_vecOriginalViewAngles, FIELD_VECTOR ), + //DEFINE_FIELD( m_vecCurrentKnockdownAngles, FIELD_VECTOR ), + //DEFINE_FIELD( m_vecKnockDownGoalAngles, FIELD_VECTOR ), + //DEFINE_FIELD( m_bKnockdownOverrideAngles, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_flKnockdownViewheightAdjust, FIELD_FLOAT ), + //DEFINE_FIELD( m_flLastMoveTime, FIELD_FLOAT ), + //DEFINE_FIELD( m_vecLastOrigin, FIELD_VECTOR ), + //DEFINE_FIELD( m_flLastDamageTime, FIELD_FLOAT ), + //DEFINE_FIELD( m_flLastGainHealthTime, FIELD_FLOAT ), + //DEFINE_FIELD( m_flDampeningAmount, FIELD_FLOAT ), + //DEFINE_FIELD( m_flGoalDampeningAmount, FIELD_FLOAT ), + //DEFINE_FIELD( m_flDampeningStayoutTime, FIELD_FLOAT ), + //DEFINE_FIELD( m_flMovementCamoSuppression, FIELD_FLOAT ), + //DEFINE_FIELD( m_flGoalMovementCamoSuppressionAmount, FIELD_FLOAT ), + //DEFINE_FIELD( m_flMovementCamoSuppressionStayoutTime, FIELD_FLOAT ), + //DEFINE_FIELD( m_flNextAdrenalinEffect, FIELD_FLOAT ), + //DEFINE_FIELD( m_bFadingIn, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_iIDEntIndex, FIELD_INTEGER ), + //DEFINE_ARRAY( m_BoostModelAngles, FIELD_FLOAT, 3 ), + + // DEFINE_PRED_TYPEDESCRIPTION( m_RideVehicle, CRideVehicle ), + // DEFINE_FIELD( m_aTargetReticles, CUtlVector < CTargetReticle * > ), + // DEFINE_FIELD( m_aSpyCameras, CUtlVector < C_SpyCamera * > ), + // DEFINE_FIELD( m_ParticleEffect, CParticleEffectBinding ), + // DEFINE_FIELD( m_ParticleTimer, TimedEvent ), + // DEFINE_FIELD( m_MaterialHandle, PMaterialHandle ), + // DEFINE_FIELD( m_pParticleMgr, CParticleMgr ), + // DEFINE_FIELD( m_pThermalMaterial, IMaterial ), + // DEFINE_FIELD( m_pCamoEffectMaterial, IMaterial ), + // DEFINE_FIELD( m_BoostMaterial, CMaterialReference ), + // DEFINE_FIELD( m_EMPMaterial, CMaterialReference ), + // DEFINE_FIELD( m_PersonalShieldEffects, CUtlLinkedList < CPersonalShieldEffect* , int > ), + // DEFINE_FIELD( m_hSelectedOrder, CHandle < C_Order > ), + // DEFINE_FIELD( m_hPersonalOrder, FIELD_EHANDLE ), + // DEFINE_FIELD( m_hSelectedObject, CHandle < C_BaseObject > ), + +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( player, C_BaseTFPlayer ); + +ConVar cl_TargetInfoFadeDist("cl_TargetInfoFadeDist", "800", 0, "Distance at which TF player targetting info fades out."); + +//----------------------------------------------------------------------------- +// Purpose: Return true if the local player is the specified class +//----------------------------------------------------------------------------- +bool IsLocalPlayerClass( int iClass ) +{ + C_BaseTFPlayer *pPlayer = C_BaseTFPlayer::GetLocalPlayer(); + if ( pPlayer ) + { + return ( pPlayer->PlayerClass() == iClass ); + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Return the local player's class +//----------------------------------------------------------------------------- +int GetLocalPlayerClass( void ) +{ + C_BaseTFPlayer *pPlayer = C_BaseTFPlayer::GetLocalPlayer(); + if ( pPlayer ) + { + return pPlayer->PlayerClass(); + } + return TFCLASS_UNDECIDED; +} + +//----------------------------------------------------------------------------- +// returns true if the local player is in tactical view +//----------------------------------------------------------------------------- +bool IsLocalPlayerInTactical( void ) +{ + C_BaseTFPlayer *pPlayer = C_BaseTFPlayer::GetLocalPlayer(); + if (!pPlayer) + return false; + + return !!pPlayer->m_TFLocal.m_nInTacticalView; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_BaseTFPlayer::C_BaseTFPlayer() : + m_PlayerClasses( this ), m_PlayerAnimState( this ) +{ + Clear(); +} + +void C_BaseTFPlayer::Clear() +{ + m_iPlayerClass = TFCLASS_UNDECIDED; + m_iCurrentZoneState = 0; + m_TFPlayerFlags = 0; + m_hSelectedOrder = NULL; + m_hPersonalOrder = NULL; + m_hSelectedObject = NULL; + m_iSelectedTarget = 0; + m_iPersonalTarget = 0; + m_iIDEntIndex = 0; + m_bStoreRagdollInfo = true; + m_flNextUseCheck = 0; + m_pSapperAttachmentStatus = NULL; + + int i; + for ( i=0; i < ARRAYSIZE( m_BoostModelAngles ); i++ ) + { + m_BoostModelAngles[i] = RandomFloat( 0, 360 ); + } + + + for ( i = 0; i < MOMENTUM_MAXSIZE; i++ ) + { + m_aMomentum[ i ] = 1.0f; + } +} + + +bool C_BaseTFPlayer::IsHidden() const +{ + return (m_TFPlayerFlags & TF_PLAYER_HIDDEN) != 0; +} + + +bool C_BaseTFPlayer::IsDamageBoosted() const +{ + return (m_TFPlayerFlags & TF_PLAYER_DAMAGE_BOOST) != 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_BaseTFPlayer::~C_BaseTFPlayer() +{ + int iSize = m_aTargetReticles.Size(); + for (int i = iSize-1; i >= 0; i-- ) + { + delete m_aTargetReticles[i]; + } + m_aTargetReticles.Purge(); + + if ( m_pThermalMaterial ) + { + m_pThermalMaterial->DecrementReferenceCount(); + } + if ( m_pCamoEffectMaterial ) + { + m_pCamoEffectMaterial->DecrementReferenceCount(); + } + + m_PersonalShieldEffects.PurgeAndDeleteElements(); + + if ( m_pSapperAttachmentStatus ) + { + delete m_pSapperAttachmentStatus; + } +} + +//----------------------------------------------------------------------------- +// Add, remove object from the panel +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::SetDormant( bool bDormant ) +{ + BaseClass::SetDormant( bDormant ); + ENTITY_PANEL_ACTIVATE( "player", !bDormant ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::HasNamedTechnology( const char *name ) +{ + CTechnologyTree *pTree = GetTechnologyTreeDoc().GetTechnologyTree(); + if ( !pTree ) + return false; + + CBaseTechnology *pItem = pTree->GetTechnology(name); + // If the tech doesn't exist, everyone has it by default + if ( !pItem ) + return true; + + return pItem->GetActive(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::ShouldDraw() +{ + if ( IsHidden() ) + return false; + + C_BaseTFPlayer *local = C_BaseTFPlayer::GetLocalPlayer(); + if ( local && local->IsUsingThermalVision() ) + return true; + + // Draw the local player if he's the driver of a vehicle. + // We can safely return true here because vehicles will hide drivers that shouldn't be visible. + if ( mannedgun_usethirdperson.GetInt() && IsVehicleMounted() && IsLocalPlayer() ) + { + IClientVehicle *pVehicle = GetVehicle(); + int nRole = pVehicle->GetPassengerRole( this ); + if ( nRole == VEHICLE_ROLE_DRIVER ) + return !IsEffectActive(EF_NODRAW); + } + + return BaseClass::ShouldDraw(); +} + + +//----------------------------------------------------------------------------- +// Should this object cast shadows? +//----------------------------------------------------------------------------- +ShadowType_t C_BaseTFPlayer::ShadowCastType() +{ + // FIXME: This check can be removed once we've dealt with the interpolation problem + C_BaseTFPlayer *local = C_BaseTFPlayer::GetLocalPlayer(); + if (local == this) + return SHADOWS_NONE; + + if (IsCamouflaged()) + return SHADOWS_NONE; + + return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC; +} + + +//----------------------------------------------------------------------------- +// Purpose: Called once per frame for the local player only. +// Called after rendering ( called in PostRender() ) the 3d objects ( i.e., other players ) +// but before vgui paints 2d overlays so that we can update the positions of all world +// targets before rendering +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::UpdateTargetReticles( void ) +{ + // Update all the target reticles + for ( int i = 0; i < m_aTargetReticles.Size(); i++ ) + { + m_aTargetReticles[i]->Update(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called to update hud elements contained in the player +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::ClientThink( void ) +{ + BaseClass::ClientThink(); + + SetNextClientThink( CLIENT_THINK_ALWAYS ); + + CheckKnockdownState(); + CheckMovementCamoSuppression(); + CheckCamoDampening(); + CheckLastMovement(); + CheckAdrenalin(); + UpdateIDTarget(); + + // update personal shield effects. + int iNext; + for ( int i=m_PersonalShieldEffects.Head(); i != m_PersonalShieldEffects.InvalidIndex(); i = iNext ) + { + iNext = m_PersonalShieldEffects.Next( i ); + CPersonalShieldEffect *pEffect = m_PersonalShieldEffects[i]; + + if ( !pEffect->Update( gpGlobals->frametime ) ) + { + delete pEffect; + m_PersonalShieldEffects.Remove( i ); + } + } + + // Let the classes think as well. + if ( GetPlayerClass() ) + { + GetPlayerClass()->ClassThink(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Store off old movetype ( commander or not ) +// Input : bnewentity - +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::OnPreDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnPreDataChanged( updateType ); + + m_nOldTacticalView = m_TFLocal.m_nInTacticalView; + + m_iLastHealth = GetHealth(); + m_bOldKnockDownState = m_TFLocal.m_bKnockedDown; + + m_bOldThermalVision = m_TFLocal.m_bThermalVision; + + m_nOldPlayerClass = m_iPlayerClass; + + // Chain. + if ( GetPlayerClass() ) + { + GetPlayerClass()->ClassPreDataUpdate(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Switch to/from commander mode if necessary +// Input : bnewentity - +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::OnDataChanged( DataUpdateType_t updateType ) +{ + bool bnewentity = (updateType == DATA_UPDATE_CREATED); + if ( bnewentity ) + { + // Do the minimap panel thing here because we don't + // want predicted players to have traces + CONSTRUCT_MINIMAP_PANEL( "minimap_player", MINIMAP_PLAYERS ); + + SetNextClientThink( CLIENT_THINK_ALWAYS ); + m_BoostMaterial.Init( "player/damageboost/thermal", TEXTURE_GROUP_CLIENT_EFFECTS ); + m_EMPMaterial.Init( "player/empeffect", TEXTURE_GROUP_CLIENT_EFFECTS ); + } + + BaseClass::OnDataChanged( updateType ); + + // Only care about this stuff for the local player + if ( IsLocalPlayer() ) + { + // Check to see if we switched into/out of the commander mode. + if ( m_TFLocal.m_nInTacticalView != m_nOldTacticalView ) + { + // Is this the local player + C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); + if ( player == this ) + { + // Tell mode switcher that server changed our mode + modemanager->SwitchMode( m_TFLocal.m_nInTacticalView ? true : false, false ); + } + } + + // Knockdown + if ( m_bOldKnockDownState != m_TFLocal.m_bKnockedDown ) + { + if ( IsKnockedDown() ) + { + m_flStartKnockdown = gpGlobals->curtime; + m_vecOriginalViewAngles = GetAbsAngles(); + + m_vecKnockDownGoalAngles = m_TFLocal.m_vecKnockDownDir; + } + else + { + m_flEndKnockdown = gpGlobals->curtime; + } + } + + // Thermal Vision + if ( m_bOldThermalVision != m_TFLocal.m_bThermalVision ) + { + if ( m_TFLocal.m_bThermalVision ) + { + ScreenFade_t sf; + memset( &sf, 0, sizeof( sf ) ); + sf.a = 128; + sf.r = 0; + sf.g = 0; + sf.b = 255; + sf.duration = 0; // not used + sf.holdTime = (float)(1<<SCREENFADE_FRACBITS) * 30.0f; + sf.fadeFlags = FFADE_STAYOUT; + vieweffects->Fade( sf ); + } + else + { + vieweffects->ClearPermanentFades(); + } + } + + if ( m_nOldPlayerClass != m_iPlayerClass ) + { + engine->ClientCmd( "cancelselect\n" ); + } + } + + if ( bnewentity ) + { + m_iLastHealth = GetHealth(); + m_flLastDamageTime = -10000; + m_flLastGainHealthTime = -10000; + } + + if (m_iLastHealth != GetHealth()) + { + if (m_iLastHealth > GetHealth()) + { + if (GetHealth() < GetMaxHealth()) + { + m_flLastDamageTime = gpGlobals->curtime; + + C_TFTeam *pTeam = static_cast<C_TFTeam*>(GetTeam()); + if (pTeam && !IsLocalPlayer() && m_bUnderAttack ) + pTeam->NotifyBaseUnderAttack( GetAbsOrigin(), false ); + } + } + else + { + m_flLastGainHealthTime = gpGlobals->curtime; + + // If we were just fully healed, remove all decals + if ( GetHealth() >= GetMaxHealth() ) + { + RemoveAllDecals(); + } + } + } + + // Snap to 100 % since we round down + if ( m_flCamouflageAmount > 99.0f ) + { + m_flCamouflageAmount = 100.0f; + } + + // Chain. + if ( GetPlayerClass() ) + { + GetPlayerClass()->ClassOnDataChanged(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::PreDataUpdate( DataUpdateType_t updateType ) +{ + BaseClass::PreDataUpdate( updateType ); + + m_bOldAttachingSapper = m_TFLocal.m_bAttachingSapper; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::PostDataUpdate( DataUpdateType_t updateType ) +{ + BaseClass::PostDataUpdate( updateType ); + + // Only care about this stuff for the local player + if ( IsLocalPlayer() ) + { + // Sapper attachment + if ( m_bOldAttachingSapper != m_TFLocal.m_bAttachingSapper || m_TFLocal.m_bAttachingSapper ) + { + if ( !m_pSapperAttachmentStatus ) + { + // Create the attachment status + m_pSapperAttachmentStatus = new CHealthBarPanel(); + vgui::Panel *pParent = g_pClientMode->GetViewport(); + m_pSapperAttachmentStatus->SetParent( pParent ); + m_pSapperAttachmentStatus->SetAutoDelete( false ); + m_pSapperAttachmentStatus->SetPos( XRES(320) - XRES(40), YRES(250) ); + m_pSapperAttachmentStatus->SetSize( XRES(80), YRES(8) ); + m_pSapperAttachmentStatus->SetGoodColor( 240, 180, 63, 192 ); + m_pSapperAttachmentStatus->SetBadColor( 0, 0, 0, 192 ); + } + + m_pSapperAttachmentStatus->SetVisible( m_TFLocal.m_bAttachingSapper ); + if ( m_TFLocal.m_bAttachingSapper ) + { + m_pSapperAttachmentStatus->SetHealth( m_TFLocal.m_flSapperAttachmentFrac ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::ReceiveMessage( int classID, bf_read &msg ) +{ + if ( classID != GetClientClass()->m_ClassID ) + { + // message is for subclass + BaseClass::ReceiveMessage( classID, msg ); + return; + } + + int messageType = msg.ReadByte(); + switch( messageType ) + { + case BASEENTITY_MSG_REMOVE_DECALS: + { + RemoveAllDecals(); + } + break; + case PLAYER_MSG_PERSONAL_SHIELD: + { + Vector vOffsetFromEnt; + msg.ReadBitVec3Coord( vOffsetFromEnt ); + + // Show a personal shield effect. + Vector vIncomingDirection; + msg.ReadBitVec3Normal( vIncomingDirection ); + + short iDamage = msg.ReadShort(); + + // Show the effect. + CPersonalShieldEffect *pEffect = CPersonalShieldEffect::Create( this, vOffsetFromEnt, vIncomingDirection, iDamage ); + if ( pEffect ) + m_PersonalShieldEffects.AddToTail( pEffect ); + } + break; + default: + break; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Free this entity +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::Release( void ) +{ + // Remove any reticles on this entity + C_BaseTFPlayer *pPlayer = C_BaseTFPlayer::GetLocalPlayer(); + if ( pPlayer ) + { + pPlayer->Remove_Target( this ); + } + + BaseClass::Release(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::ItemPostFrame( void ) +{ + if ( m_flNextUseCheck < gpGlobals->curtime ) + { + m_flNextUseCheck = gpGlobals->curtime + 0.3; + + CVehicleRoleHudElement *pElement = GET_HUDELEMENT( CVehicleRoleHudElement ); + Assert( pElement ); + pElement->ShowVehicleRole( -1 ); + + // See if there's an entity we can +use + if ( IsAlive() && !GetVehicle() && ( gpGlobals->curtime > m_flNextAttack ) ) + { + CBaseEntity *pUseEntity = FindUseEntity(); + if ( pUseEntity ) + { + // Vehicles need to show we're going to get in them + C_BaseTFVehicle *pVehicle = dynamic_cast<C_BaseTFVehicle*>( pUseEntity ); + if ( pVehicle && InSameTeam(pVehicle) && !pVehicle->IsPlacing() && !pVehicle->IsBuilding() && pVehicle->GetMaxPassengerCount() >= 2 ) + { + pElement->ShowVehicleRole( pVehicle->LocateEntryPoint( this ) ); + } + } + } + } + + // Don't process items while in a vehicle. + if ( IsInAVehicle() ) + { + IClientVehicle *pVehicle = GetVehicle(); + Assert( pVehicle ); + + // NOTE: We *have* to do this before ItemPostFrame because ItemPostFrame + // may dump us out of the vehicle + int nRole = pVehicle->GetPassengerRole( this ); + bool bUsingStandardWeapons = pVehicle->IsPassengerUsingStandardWeapons( nRole ); + + pVehicle->ItemPostFrame( this ); + + // Fall through and check weapons, etc. if we're using them + if (!bUsingStandardWeapons || !IsInAVehicle()) + return; + } + + // If we're attaching a sapper, handle player use only + if ( m_TFLocal.m_bAttachingSapper ) + { + PlayerUse(); + return; + } + + BaseClass::ItemPostFrame(); + +#if 0 + if ( GetPlayerClass() ) + { + GetPlayerClass()->ItemPostFrame(); // Let the player class handle it. + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if I'm in a vehicle that's mounted on another vehicle +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsVehicleMounted() const +{ + CBaseTFVehicle *pVehicle = dynamic_cast< CBaseTFVehicle* >( GetMoveParent() ); + if ( pVehicle ) + return dynamic_cast< CBaseTFVehicle* >( pVehicle->GetMoveParent() ) != NULL; + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::DrawModel( int flags ) +{ + int drawn = 0; + + if ( !m_bReadyToDraw ) + return 0; + + QAngle saveAngles(0,0,0); + Vector saveLocalOrigin(0,0,0); + bool angleschanged = false; + bool originchanged = false; + + // If we're in a vehicle, use the vehicle's angles for the local player + if ( IsInAVehicle() && !IsInAVehicle() ) + { + IClientVehicle *pVehicle = GetVehicle(); + int nRole = pVehicle->GetPassengerRole( this ); + if ( nRole == VEHICLE_ROLE_DRIVER ) + { + C_BaseTFVehicle *pVehicleEntity = (CBaseTFVehicle*)pVehicle->GetVehicleEnt(); + angleschanged = true; + SetLocalAngles( pVehicleEntity->GetPassengerAngles( saveAngles, VEHICLE_ROLE_DRIVER ) ); + + // HACK: Stomp the origin + originchanged = true; + SetLocalOrigin( vec3_origin ); + } + } + else + { + angleschanged = true; + SetLocalAngles( m_PlayerAnimState.GetRenderAngles() ); + } + + drawn = BaseClass::DrawModel(flags); + + if ( angleschanged ) + { + SetLocalAngles( saveAngles ); + } + if ( originchanged ) + { + SetLocalOrigin( saveLocalOrigin ); + } + + // Draw all personal shield effects. + FOR_EACH_LL( m_PersonalShieldEffects, i ) + { + m_PersonalShieldEffects[i]->Render(); + } + + return drawn; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::GetClass( void ) +{ + if ( !GetPlayerClass() ) + return TFCLASS_UNDECIDED; + + return m_iPlayerClass; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::OverrideView( CViewSetup *pSetup ) +{ + if ( CheckKnockdownAngleOverride() ) + { + float adj = GetKnockdownViewheightAdjust(); + + pSetup->origin.z -= adj; + + return; + } + + BaseClass::OverrideView( pSetup ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::ShouldDrawViewModel() +{ + if ( CheckKnockdownAngleOverride() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Hack to search up the hierarchy looking for bones whose ultimate parent is 1 ( i.e., the pelvis ) since +// these are the lower body bones +//----------------------------------------------------------------------------- +bool ParentIsPelvis( mstudiobone_t *bones, int start, int pelvis, int spine ) +{ + int current = start; + + while ( bones[ current ].parent ) + { + // Root? + if ( bones[ current ].parent <= 0 ) + { + return false; + } + + if ( bones[ current ].parent == pelvis ) + return true; + + if ( bones[ current ].parent == spine ) + return false; + + current = bones[ current ].parent; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Hack to find the bone indices of the pelvis and the spine so we can +// more quickly determine if a bones parent is the pelvis so we can merge bones correctly +//----------------------------------------------------------------------------- +static void FindPelvisAndSpine( int numbones, mstudiobone_t *bones, int *pelvis, int *spine ) +{ + *pelvis = *spine = -1; + + mstudiobone_t *bone = bones; + for ( int i = 0; i < numbones; i++, bone++ ) + { + if ( !stricmp( bone->pszName(), "Bip01 Pelvis" ) ) + { + *pelvis = i; + } + else if ( !stricmp( bone->pszName(), "Bip01 Spine" ) ) + { + *spine = i; + } + + if ( *spine >= 0 && *pelvis >= 0 ) + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Another hack, the TFC 1.5 v. 2 models expect controllers at the midpoint +// Input : controllers[MAXSTUDIOBONECTRLS] - +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS]) +{ + // Set controllers to a their zero value. + for(int i=0; i < MAXSTUDIOBONECTRLS; i++) + { + controllers[i] = 0.5; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get a text description for the object target +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::GetTargetDescription( char *pDest, int bufferSize ) +{ + const char *pStr = GetPlayerName(); + if ( pStr ) + { + Q_strncpy( pDest, pStr, bufferSize ); + } + else + { + pDest[0] = 0; + } +} + +//===================================================================================================== +// ORDERS +//===================================================================================================== + + +//----------------------------------------------------------------------------- +// Purpose: Player has received a new personal order +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::SetPersonalOrder( C_Order *pOrder ) +{ + // Do we have an order already? + RemoveOrderTarget(); + + m_hPersonalOrder = pOrder; + m_iPersonalTarget = pOrder->GetTarget(); + + // Add a new target to our list + if ( pOrder->ShouldDrawReticle() ) + { + int iTargetIndex = pOrder->GetTarget(); + if ( iTargetIndex ) + { + C_BaseEntity *pTarget = cl_entitylist->GetBaseEntity( iTargetIndex ); + if ( pTarget ) + { + char desc[512]; + pOrder->GetTargetDescription( desc, sizeof( desc ) ); + Add_Target( pTarget, desc ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Remove the target reticle for the specified order +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::RemoveOrderTarget() +{ + if ( m_iPersonalTarget ) + { + C_BaseEntity *pTarget = cl_entitylist->GetBaseEntity( m_iPersonalTarget ); + if ( pTarget ) + { + Remove_Target( pTarget ); + } + + m_iPersonalTarget = 0; + } +} + +//==================================================================================================== +// RESOURCES +//==================================================================================================== +// Purpose: +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::GetBankResources( void ) +{ + return m_TFLocal.m_iBankResources; +} + + +//==================================================================================================== +// OBJECTS +//==================================================================================================== +// Purpose: Player has selected an Object +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::SetSelectedObject( C_BaseObject *pObject ) +{ + // Do we have an order already? + if ( m_hSelectedObject ) + { + Remove_Target( m_hSelectedObject ); + + // If we selected our existing one, we just wanted to deselect + if ( pObject == m_hSelectedObject ) + { + m_hSelectedObject = NULL; + return; + } + } + + m_hSelectedObject = pObject; + + // Add a new target to our list + if ( pObject ) + { + Add_Target( pObject, pObject->GetTargetDescription() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the currently selected object +//----------------------------------------------------------------------------- +C_BaseObject *C_BaseTFPlayer::GetSelectedObject( void ) +{ + return m_hSelectedObject; +} + +//==================================================================================================== +// TARGET RETICLES +//==================================================================================================== +// Purpose: Add a new entity to the list of targets +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::Add_Target( C_BaseEntity *pTarget, const char *sName ) +{ + CTargetReticle *pTargetReticle = new CTargetReticle(); + pTargetReticle->Init( pTarget, sName ); + + m_aTargetReticles.AddToTail( pTargetReticle ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove an entity from the list of targets +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::Remove_Target( C_BaseEntity *pTarget ) +{ + for (int i = 0; i < m_aTargetReticles.Size(); i++ ) + { + CTargetReticle *pTargetReticle = m_aTargetReticles[i]; + if ( pTargetReticle->GetTarget() == pTarget ) + { + Remove_Target( pTargetReticle ); + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Remove a specific reticle from our list +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::Remove_Target( CTargetReticle *pTargetReticle ) +{ + m_aTargetReticles.FindAndRemove( pTargetReticle ); + delete pTargetReticle; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsUsingThermalVision( void ) const +{ + return m_TFLocal.m_bThermalVision; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::GetIDTarget( void ) const +{ + return m_iIDEntIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsKnockedDown( void ) const +{ + return m_TFLocal.m_bKnockedDown; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : ang - +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::SetKnockdownAngles( const QAngle& ang ) +{ + m_bKnockdownOverrideAngles = true; + QAngle fixedAngles = ang; + NormalizeAngles( fixedAngles ); + m_vecCurrentKnockdownAngles = fixedAngles; + engine->SetViewAngles( fixedAngles ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : outAngles - +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::GetKnockdownAngles( QAngle& outAngles ) +{ + outAngles = m_vecCurrentKnockdownAngles; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::CheckKnockdownState( void ) +{ + m_bKnockdownOverrideAngles = false; + + if ( m_flStartKnockdown == 0.0f && + m_flEndKnockdown == 0.0f && + !IsKnockedDown() ) + { + m_flKnockdownViewheightAdjust = 0.0f; + return; + } + + float frac = 0.0f; + float dt; + if ( IsKnockedDown() ) + { + if ( m_flStartKnockdown != 0.0f ) + { + + dt = gpGlobals->curtime - m_flStartKnockdown; + if ( dt >= 0.0f && dt < KNOCKDOWN_BLEND_IN ) + { + frac = ( dt / KNOCKDOWN_BLEND_IN ); + frac = MAX( 0.0f, frac ); + frac = MIN( 1.0f, frac ); + } + else if ( dt >= KNOCKDOWN_BLEND_IN) + { + m_flStartKnockdown = 0.0f; + frac = 1.0f; + } + } + else + { + frac = 1.0f; + } + } + else + { + if ( m_flEndKnockdown != 0.0f ) + { + frac = 0.0f; + dt = gpGlobals->curtime - m_flEndKnockdown; + if ( dt >= 0 && dt < KNOCKDOWN_BLEND_OUT ) + { + frac = ( dt / KNOCKDOWN_BLEND_OUT ); + frac = 1.0f - frac; + } + else if ( dt >= KNOCKDOWN_BLEND_OUT ) + { + m_flEndKnockdown = 0.0f; + } + else + { + frac = 1.0f; + } + } + } + + if ( frac == 0.0f ) + { + SetKnockdownAngles( m_vecOriginalViewAngles ); + } + else if ( frac == 1.0f ) + { + SetKnockdownAngles( m_vecKnockDownGoalAngles ); + } + else + { + QAngle current; + InterpolateAngles( m_vecOriginalViewAngles, m_vecKnockDownGoalAngles, current, frac ); + SetKnockdownAngles( current ); + } + + Vector eyeZOffset; + VectorSubtract( EyePosition(), GetAbsOrigin(), eyeZOffset ); + float zsize = eyeZOffset.z; + + m_flKnockdownViewheightAdjust = frac * ( zsize - 12 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::CheckKnockdownAngleOverride( void ) const +{ + return m_bKnockdownOverrideAngles; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float C_BaseTFPlayer::GetKnockdownViewheightAdjust( void ) const +{ + return m_flKnockdownViewheightAdjust; +} + + +//----------------------------------------------------------------------------- +// Purpose: Player has changed to a new team +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::TeamChange( int iNewTeam ) +{ + BaseClass::TeamChange( iNewTeam ); + + // Did we change team? or did we just join our first team? + if ( iNewTeam != GetTeamNumber() ) + { + // Tell the tech tree to reload itself + GetTechnologyTreeDoc().ReloadTechTree(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: // Override so infiltrator's disguised as other team will work +// Output : int +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::GetRenderTeamNumber( void ) +{ + return GetTeamNumber(); +} + + +// 50 % per second +#define CAMO_DAMPENING_CHANGERATE 300.0f +#define CAMO_MOVESUPPRESSION_CHANGERATE 100.0f +#define CAMO_DAMPENINGVELOCITY_CUTOFF 50.0f +#define CAMO_DAMPENINGVELOCITY_MAX 400.0f +#define CAMO_DAMPENINGAVEL_CUTOFF 1.0f +#define CAMO_DAMPENINGAVEL_MAX 5.0f +#define CAMO_STAYOUT_TIME 1.0f +#define CAMO_MOVEMENT_PENALTYTIME 1.0f + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsCamouflaged( void ) +{ + return ( m_flCamouflageAmount > 0.0f ) ? true : false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float C_BaseTFPlayer::GetCamouflageAmount( void ) +{ + return m_flCamouflageAmount; +} + +//----------------------------------------------------------------------------- +// Purpose: 0 to 1, where 1 means hardly visible and 0 means pretty noticeable +// Output : float +//----------------------------------------------------------------------------- + +#define DISTANCE_CAMO_EFFECT_AMOUNT 0.1f +#define LOCAL_MOTION_CAMO_EFFECT_AMOUNT 0.3f +#define MOVEMENT_CAMO_EFFECT_AMOUNT 0.2f + +float C_BaseTFPlayer::ComputeCamoEffectAmount( void ) +{ + if ( !IsCamouflaged() ) + return 1.0f; + + // Start with the amount from the server.... + float effect_amount = m_flCamouflageAmount / 100.0f; + + // If this player has moved recently, camo will not be as effective for him + effect_amount -= MOVEMENT_CAMO_EFFECT_AMOUNT * GetMovementCamoSuppression() / 100.0f; + if (effect_amount < 0) + effect_amount = 0; + + // Determine distance to render origin + Vector delta = GetAbsOrigin() - CurrentViewOrigin(); + float distance = delta.Length(); + + // At the max distance, make it n% less likely to see the camoed dude... + // At min distance, we're no less likely to see the dude + if ( distance >= CAMO_INNER_RADIUS ) + effect_amount *= 1 + DISTANCE_CAMO_EFFECT_AMOUNT; + else + { + float frac = distance / CAMO_INNER_RADIUS; + effect_amount *= 1 - frac + (1 + DISTANCE_CAMO_EFFECT_AMOUNT) * frac; + } + + // Local viewer movements make it n% less likely to see the camoed dude... + // No movement means we're no less likely to see the dude + // Dampening is based on the local viewer's movements + float dampening = 0.0f; + C_BaseTFPlayer *local = GetLocalPlayer(); + if ( local ) + { + dampening = local->GetDampeningAmount() / 100.0f; + } + + // Now apply suppression (i.e., less visible) based on camera and viewer movement + effect_amount *= 1 + LOCAL_MOTION_CAMO_EFFECT_AMOUNT * dampening; + + // Clamp to valid range + effect_amount = MAX( 0.0f, effect_amount ); + effect_amount = MIN( 0.99f, effect_amount ); + + return effect_amount; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::ComputeCamoAlpha( void ) +{ + Assert( IsCamouflaged() ); + + // Determine distance to render origin + Vector delta = GetAbsOrigin() - CurrentViewOrigin(); + float distance = delta.Length(); + + int baseline = 0; + + // Too far, just blend out completely + if ( distance >= CAMO_OUTER_RADIUS ) + { + baseline = 0; + } + else if ( distance >= CAMO_INNER_RADIUS ) + { + float frac = ( distance - CAMO_INNER_RADIUS ) / ( CAMO_OUTER_RADIUS - CAMO_INNER_RADIUS ); + frac = 1 - frac; + baseline = (int)( (float)( 255 - CAMO_INNER_ALPHA ) * frac ); + } + else if ( distance >= CAMO_INVIS_RADIUS ) + { + // We'll also render with the special effect + float frac = ( distance - CAMO_INVIS_RADIUS ) / ( CAMO_INNER_RADIUS - CAMO_INVIS_RADIUS ); + baseline = (int)( (float)( CAMO_INNER_ALPHA ) * frac ); + } + else + { + // NOTE: return 1 or else the renderer will skip drawing and we won't be + // able to draw the up close effect + baseline = 1; + } + + // Suppress everything based on server ramp + return baseline + (int)( (float)( 255 - baseline ) * ( m_flCamouflageAmount / 100.0f ) ); + +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::ComputeFxBlend( void ) +{ + if ( !IsCamouflaged() ) + { + BaseClass::ComputeFxBlend(); + return; + } + + m_nRenderFXBlend = ComputeCamoAlpha(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsTransparent( void ) +{ + if ( IsCamouflaged() ) + { + return true; + } + + return BaseClass::IsTransparent(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::ViewModel_IsTransparent( void ) +{ + if ( IsCamouflaged() ) + { + return true; + } + + return BaseClass::ViewModel_IsTransparent(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if the player's viewmodel should match the player's model data +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsOverridingViewmodel( void ) +{ + // Don't override medic weapons since we have effects for them + if ( PlayerClass() == TFCLASS_MEDIC ) + return BaseClass::IsOverridingViewmodel(); + + if ( IsDamageBoosted() || IsCamouflaged() || HasPowerup(POWERUP_EMP) ) + return true; + + return BaseClass::IsOverridingViewmodel(); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw my viewmodel in some special way +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::DrawOverriddenViewmodel( C_BaseViewModel *pViewmodel, int flags ) +{ + int ret = 0; + + if ( IsCamouflaged() && ( ComputeCamoEffectAmount() != 1.0f ) ) + { + if ( GetCamoMaterial() ) + { + modelrender->ForcedMaterialOverride( GetCamoMaterial() ); + ret = pViewmodel->DrawOverriddenViewmodel( flags ); + modelrender->ForcedMaterialOverride( NULL ); + } + } + + if ( IsDamageBoosted() || HasPowerup(POWERUP_EMP) ) + { + + // First draw the model once normally. + pViewmodel->DrawOverriddenViewmodel( flags ); + + // NOTE: for the demo we do a different "BOOST" effect. What do we want to do here? + if ( !inv_demo.GetInt() ) + { + if ( IsDamageBoosted() && + pViewmodel->ViewModelIndex() != 0 ) + { + return ret; + } + + // Now overlay some shimmering ones. + render->SetBlend( 0.3 ); + + if ( IsDamageBoosted() ) + { + // Radeon 9700 having a problem with this guy: + modelrender->ForcedMaterialOverride( m_BoostMaterial ); + } + else + { + modelrender->ForcedMaterialOverride( m_EMPMaterial ); + } + + Vector vStart = pViewmodel->GetAbsOrigin(); + + float flOffset = damageboost_modeloffset.GetFloat(); + for ( int i=0; i < 3; i++ ) + { + // Place the model at a slight offset. + vStart[i] += flOffset * sin( m_BoostModelAngles[i] ); + pViewmodel->SetLocalOrigin( vStart ); + m_BoostModelAngles[i] += RandomFloat( damageboost_modelphasespeed_min.GetFloat(), damageboost_modelphasespeed_max.GetFloat() ) * gpGlobals->frametime; + + // Invalidate the bones because they've been setup with our original position and cached, + // and we want to render it in a new spot with different bone transforms. + pViewmodel->InvalidateBoneCache(); + ret = pViewmodel->DrawOverriddenViewmodel( flags ); + } + + // Reset the position and bone info. + pViewmodel->SetLocalOrigin( vStart ); + pViewmodel->InvalidateBoneCache(); + + modelrender->ForcedMaterialOverride( NULL ); + render->SetBlend( 1 ); + } + } + + return ret; +} + +//----------------------------------------------------------------------------- +// Purpose: Players under adrenalin animate faster +//----------------------------------------------------------------------------- +float C_BaseTFPlayer::GetDefaultAnimSpeed( void ) +{ + if ( HasPowerup( POWERUP_RUSH ) ) + return ADRENALIN_ANIM_SPEED; + + // Weapons may modify animation times + if ( GetActiveWeapon() ) + return GetActiveWeapon()->GetDefaultAnimSpeed(); + + return 1.0; +} + +//----------------------------------------------------------------------------- +// Purpose: This is used to penalize the local viewer for moving around or rotating +// the camera, it causes camo'd guys who are close to fade out their "white" effect +// for a bit of time +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::CheckCameraMovement( void ) +{ + float dt = gpGlobals->frametime; + + float vel = GetAbsVelocity().Length(); + float avel = fabs( GetLocalAngles().y - GetPrevLocalAngles().y ); + if ( dt > 0.0f ) + { + avel *= 1.0f / dt; + } + + float frac1 = 0.0f; + if ( vel > CAMO_DAMPENINGVELOCITY_CUTOFF ) + { + frac1 = ( vel - CAMO_DAMPENINGVELOCITY_CUTOFF ) / ( CAMO_DAMPENINGVELOCITY_MAX - CAMO_DAMPENINGVELOCITY_CUTOFF ); + frac1 = MIN( 0.0f, frac1 ); + frac1 = MAX( 1.0f, frac1 ); + + frac1 *= 50.0f; + + m_flDampeningStayoutTime = gpGlobals->curtime + CAMO_STAYOUT_TIME; + + } + + float frac2 = 0.0f; + if ( avel > CAMO_DAMPENINGAVEL_CUTOFF ) + { + frac2 = ( avel - CAMO_DAMPENINGAVEL_CUTOFF ) / ( CAMO_DAMPENINGAVEL_MAX - CAMO_DAMPENINGAVEL_CUTOFF ); + frac2 = MIN( 0.0f, frac2 ); + frac2 = MAX( 1.0f, frac2 ); + + frac2 *= 50.0f; + + m_flDampeningStayoutTime = gpGlobals->curtime + CAMO_STAYOUT_TIME; + + } + + // Pick the greater + float amount = MAX( frac1, frac2 ); + + SetCamoDampening( amount ); +} + +//----------------------------------------------------------------------------- +// Purpose: Decay suppression camo amount +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::CheckCamoDampening( void ) +{ + CheckCameraMovement(); + + if ( m_flGoalDampeningAmount == m_flDampeningAmount ) + return; + + // Camouflage + float dt = gpGlobals->frametime; + float changeamount = ( dt * CAMO_DAMPENING_CHANGERATE ); + + // Move but don't overshoot + if ( m_flGoalDampeningAmount > m_flDampeningAmount ) + { + m_flDampeningAmount += changeamount; + m_flDampeningAmount = MIN( m_flDampeningAmount, m_flGoalDampeningAmount ); + } + else + { + if ( gpGlobals->curtime < m_flDampeningStayoutTime ) + return; + + m_flDampeningAmount -= changeamount; + m_flDampeningAmount = MAX( m_flDampeningAmount, m_flGoalDampeningAmount ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Force amount, decay happens in CheckCamoDampening +// Input : amount - +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::SetCamoDampening( float amount ) +{ + m_flGoalDampeningAmount = amount; +} + +//----------------------------------------------------------------------------- +// Purpose: For camera movement by local player +// Output : float +//----------------------------------------------------------------------------- +float C_BaseTFPlayer::GetDampeningAmount( void ) +{ + return m_flDampeningAmount; +} + +//----------------------------------------------------------------------------- +// Purpose: This is used so that if you are watching a camo'd guy who is moving +// he becomes more visible for a bit of time, before fading back out. +// Input : amount - +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::SetMovementCamoSuppression( float amount ) +{ + m_flGoalMovementCamoSuppressionAmount = amount; +} + +//----------------------------------------------------------------------------- +// Purpose: This is used so that if you are watching a camo'd guy who is moving +// he becomes more visible for a bit of time, before fading back out. +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::CheckMovementCamoSuppression( void ) +{ + // FIXME: Don't bother with suppression during deployment... + if ( m_bDeployed ) + { + m_flMovementCamoSuppression = 0; + return; + } + + float flSuppression = 0; + + // Rotation blends them in a bit too + float avel = fabs( GetLocalAngles().y - GetPrevLocalAngles().y ) + fabs( GetLocalAngles().x - GetPrevLocalAngles().x ); + if ( avel > CAMO_DAMPENINGAVEL_CUTOFF ) + { + float frac2 = ( avel - CAMO_DAMPENINGAVEL_CUTOFF ) / ( CAMO_DAMPENINGAVEL_MAX - CAMO_DAMPENINGAVEL_CUTOFF ); + frac2 = MIN( 0.0f, frac2 ); + frac2 = MAX( 1.0f, frac2 ); + flSuppression = 50.0f * frac2; + } + + // Add in velocity blend + float vel = GetAbsVelocity().Length(); + if ( vel > CAMO_DAMPENINGVELOCITY_CUTOFF ) + { + float frac = ( vel- CAMO_DAMPENINGVELOCITY_CUTOFF ) / ( CAMO_DAMPENINGVELOCITY_MAX - CAMO_DAMPENINGVELOCITY_CUTOFF ); + frac = MIN( 1.0f, frac ); + flSuppression = MIN( 100.f, flSuppression + (100.0f * frac) ); + } + + // Set the camo aount + SetMovementCamoSuppression( flSuppression ); + if ( flSuppression ) + { + m_flMovementCamoSuppressionStayoutTime = gpGlobals->curtime + CAMO_MOVEMENT_PENALTYTIME; + } + + if ( m_flGoalMovementCamoSuppressionAmount == m_flMovementCamoSuppression ) + return; + + // Camouflage + float dt = gpGlobals->frametime; + float changeamount = ( dt * CAMO_MOVESUPPRESSION_CHANGERATE ); + + // Move but don't overshoot + if ( m_flGoalMovementCamoSuppressionAmount > m_flMovementCamoSuppression ) + { + m_flMovementCamoSuppression += changeamount; + m_flMovementCamoSuppression = MIN( m_flMovementCamoSuppression, m_flGoalMovementCamoSuppressionAmount ); + } + else + { + if ( gpGlobals->curtime < m_flMovementCamoSuppressionStayoutTime ) + return; + + m_flMovementCamoSuppression -= changeamount; + m_flMovementCamoSuppression = MAX( m_flMovementCamoSuppression, m_flGoalMovementCamoSuppressionAmount ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the material used for this player's camo +//----------------------------------------------------------------------------- +IMaterial *C_BaseTFPlayer::GetCamoMaterial( void ) +{ + if ( !m_pCamoEffectMaterial ) + { + m_pCamoEffectMaterial = materials->FindMaterial("player/infiltratorcamo/infiltratorcamo", TEXTURE_GROUP_CLIENT_EFFECTS); + if ( m_pCamoEffectMaterial ) + { + m_pCamoEffectMaterial->IncrementReferenceCount(); + } + } + + return m_pCamoEffectMaterial; +} + +//----------------------------------------------------------------------------- +// Purpose: Percentage suppression of camo effect up close based on velocity +// Output : float +//----------------------------------------------------------------------------- +float C_BaseTFPlayer::GetMovementCamoSuppression( void ) +{ + return m_flMovementCamoSuppression; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::CheckLastMovement( void ) +{ + if ( GetAbsOrigin() != m_vecLastOrigin ) + { + m_vecLastOrigin = GetAbsOrigin(); + m_flLastMoveTime = gpGlobals->curtime; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float C_BaseTFPlayer::GetLastMoveTime( void ) +{ + return m_flLastMoveTime; +} + +float C_BaseTFPlayer::GetLastDamageTime( void ) const +{ + return m_flLastDamageTime; +} + +float C_BaseTFPlayer::GetLastGainHealthTime( void ) const +{ + return m_flLastGainHealthTime; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float C_BaseTFPlayer::GetOverlayAlpha( void ) +{ + float alpha = 1.0f; + + C_BaseTFPlayer *local = GetLocalPlayer(); + if ( local && local != this ) + { + if ( local && GetTeamNumber() != local->GetTeamNumber() ) + { + if ( IsCamouflaged() ) + { + float frac = ( GetCamouflageAmount() / 100.0f ); + alpha *= ( 1.0f - frac ); + } + + // Only applies to sniper right now + if ( m_iPlayerClass == TFCLASS_SNIPER ) + { + float dt = gpGlobals->curtime - GetLastMoveTime(); + if ( dt > SNIPER_STATIONARY_FADESTART ) + { + if ( dt > SNIPER_STATIONARY_FADEFINISH ) + { + alpha = 0.0; + } + else + { + float frac = ( dt - SNIPER_STATIONARY_FADESTART ) / ( SNIPER_STATIONARY_FADEFINISH - SNIPER_STATIONARY_FADESTART ); + alpha *= ( 1.0f - frac ); + } + } + } + } + } + + alpha = MIN( 1.0f, alpha ); + alpha = MAX( 0.0f, alpha ); + + return alpha; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsDeployed( void ) +{ + return m_bDeployed; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsDeploying( void ) +{ + return m_bDeploying; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsUnDeploying( void ) +{ + return m_bUnDeploying; +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if the player's allowed to switch weapons in his current state +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsAllowedToSwitchWeapons( void ) +{ + // Can't switch while deployed + if ( IsDeployed() || IsDeploying() || IsUnDeploying() ) + return false; + + if ( IsInAVehicle() && GetVehicle() ) + { + IClientVehicle *pVehicle = GetVehicle(); + int nRole = pVehicle->GetPassengerRole( this ); + if (!pVehicle->IsPassengerUsingStandardWeapons(nRole)) + { + return false; + } + } + + // See if the weapon will allow us to switch + if ( GetActiveWeapon() && GetActiveWeapon()->IsAllowedToSwitch() == false ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the number of objects of the specified type that this player has +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::GetNumObjects( int iObjectType ) +{ + int iCount = 0; + for (int i = 0; i < GetObjectCount(); i++) + { + if ( !GetObject(i) ) + continue; + + if ( GetObject(i)->GetType() == iObjectType ) + { + iCount++; + } + } + + return iCount; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int C_BaseTFPlayer::GetObjectCount( void ) +{ + return m_TFLocal.m_aObjects.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +C_BaseObject *C_BaseTFPlayer::GetObject( int index ) +{ + return m_TFLocal.m_aObjects[index].Get(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::AddEntity( void ) +{ + BaseClass::AddEntity(); + + // Zero out model pitch, blending takes care of all of it. + SetLocalAnglesDim( X_INDEX, 0 ); + + m_PlayerAnimState.Update(); +} + +//----------------------------------------------------------------------------- +// Purpose: See if it's time to start another adrenalin effect +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::CheckAdrenalin( void ) +{ + if ( m_flNextAdrenalinEffect && gpGlobals->curtime > m_flNextAdrenalinEffect ) + { + ScreenFade_t sf; + memset( &sf, 0, sizeof( sf ) ); + sf.a = 128; + sf.r = 0; + sf.g = 128; + sf.b = 0; + // One second + sf.duration = (unsigned short)((float)(1<<SCREENFADE_FRACBITS) * 1.0f ); + if ( m_bFadingIn ) + { + sf.fadeFlags = FFADE_IN; + } + else + { + sf.fadeFlags = FFADE_OUT; + } + vieweffects->Fade( sf ); + + m_bFadingIn = !m_bFadingIn; + m_flNextAdrenalinEffect = gpGlobals->curtime + 1.0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Update this client's target entity +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::UpdateIDTarget( void ) +{ + if ( !IsLocalPlayer() ) + return; + + // If the server's forcing us to a specific ID target, use it instead + if ( m_TFLocal.m_iIDEntIndex ) + { + m_iIDEntIndex = m_TFLocal.m_iIDEntIndex; + return; + } + + // Clear old target and find a new one + m_iIDEntIndex = 0; + + trace_t tr; + Vector vecStart, vecEnd; + VectorMA( MainViewOrigin(), 1500, MainViewForward(), vecEnd ); + VectorMA( MainViewOrigin(), 48, MainViewForward(), vecStart ); + UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr ); + if ( tr.DidHitNonWorldEntity() ) + { + C_BaseEntity *pEntity = tr.m_pEnt; + IClientVehicle *vehicle = GetVehicle(); + C_BaseEntity *pVehicleEntity = vehicle ? vehicle->GetVehicleEnt() : NULL; + + if ( pEntity && (pEntity != this) && (pEntity != pVehicleEntity) ) + { + // Make sure it's not an object + if ( !dynamic_cast<C_BaseObject*>( pEntity ) ) + { + m_iIDEntIndex = pEntity->entindex(); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return the weapon to have open the weapon selection on, based upon our currently active weapon +// If the currently active weapon is a two handed weapon, returns it's primary weapon. +//----------------------------------------------------------------------------- +C_BaseCombatWeapon *C_BaseTFPlayer::GetActiveWeaponForSelection( void ) +{ + C_WeaponTwoHandedContainer *pTwoHandedWeapon = dynamic_cast<C_WeaponTwoHandedContainer*>( GetActiveWeapon() ); + if ( pTwoHandedWeapon ) + return pTwoHandedWeapon->m_hLeftWeapon; + + return BaseClass::GetActiveWeaponForSelection(); +} + +//----------------------------------------------------------------------------- +// HACK: Until we get a recon model, trail recon particles +//----------------------------------------------------------------------------- +void FX_ReconParticle( const Vector &vecOrigin, bool bBlue ) +{ + CSmartPtr<CSimpleEmitter> pSimple = CSimpleEmitter::Create( "reconparticle" ); + pSimple->SetSortOrigin( vecOrigin ); + pSimple->SetNearClip( 32, 64 ); + + SimpleParticle *pParticle; + + Vector offset; + + for ( int i = 0; i < 1; i++ ) + { + pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[0], vecOrigin ); + + if ( pParticle != NULL ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = random->RandomFloat( 1.0f, 1.5f ); + + pParticle->m_vecVelocity = vec3_origin; + + int color = random->RandomInt( 128, 192 ); + + if ( bBlue ) + { + pParticle->m_uchColor[0] = color; + pParticle->m_uchColor[1] = color; + pParticle->m_uchColor[2] = 255; + } + else + { + pParticle->m_uchColor[0] = 255; + pParticle->m_uchColor[1] = color; + pParticle->m_uchColor[2] = color; + } + pParticle->m_uchStartAlpha = random->RandomInt( 192, 255 ); + pParticle->m_uchEndAlpha = 0; + pParticle->m_uchStartSize = random->RandomInt( 12, 16 ); + pParticle->m_uchEndSize = 0; + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = random->RandomFloat( -1.0f, 1.0f ); + } + } +} + +// Prediction stuff +void C_BaseTFPlayer::PreThink( void ) +{ + BaseClass::PreThink(); + + // Chain pre-think to player class. + if ( GetPlayerClass() ) + { + GetPlayerClass()->PreClassThink(); + } +} + +void C_BaseTFPlayer::PostThink( void ) +{ + BaseClass::PostThink(); + + // Chain post-think to player class. + if ( GetPlayerClass() ) + { + GetPlayerClass()->PostClassThink(); + } +} + +C_PlayerClass *C_BaseTFPlayer::GetPlayerClass( void ) +{ + return m_PlayerClasses.GetPlayerClass( PlayerClass() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::SetVehicleRole( int nRole ) +{ + if ( IsInAVehicle() ) + { + C_BaseTFVehicle *pVehicle = ( C_BaseTFVehicle* )GetVehicle(); + if ( pVehicle ) + { + if ( nRole >= pVehicle->GetMaxPassengerCount() ) + return; + } + } + + char szCmd[64]; + Q_snprintf( szCmd, sizeof( szCmd ), "vehicleRole %i\n", nRole ); + engine->ServerCmd( szCmd ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::CanGetInVehicle( void ) +{ + if ( GetPlayerClass() ) + { + return GetPlayerClass()->CanGetInVehicle(); + } + + return true; +} + + +// How fast to avoid collisions with center of other object, in units per second +#define AVOID_SPEED 1000.0f +extern ConVar cl_forwardspeed; +extern ConVar cl_backspeed; +extern ConVar cl_sidespeed; + +static ConVar tf2_solidplayers( "tf2_solidplayers", "1", 0, "Treat players and objects as solid." ); + +//----------------------------------------------------------------------------- +// Client-side obstacle avoidance +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::PerformClientSideObstacleAvoidance( float flFrameTime, CUserCmd *pCmd ) +{ + if ( !tf2_solidplayers.GetBool() ) + { + return; + } + + // Don't avoid if noclipping or in movetype none + switch ( GetMoveType() ) + { + case MOVETYPE_NOCLIP: + case MOVETYPE_NONE: + return; + default: + break; + } + + // Try to steer away from any objects/players we might interpenetrate + Vector size = WorldAlignSize(); + + float radius = 0.5f * sqrt( size.x * size.x + size.y * size.y ); + float curspeed = GetAbsVelocity().Length2D(); + + // int slot = 1; + + //engine->Con_NPrintf( slot++, "speed %f\n", curspeed ); + //engine->Con_NPrintf( slot++, "radius %f\n", radius ); + + // If running, use a larger radius + if ( curspeed > 100.0f ) + { + float factor = ( 1.0f + ( curspeed - 100.0f ) / 100.0f ); + + // engine->Con_NPrintf( slot++, "scaleup (%f) to radius %f\n", factor, radius * factor ); + + radius = radius * factor; + } + + CPlayerAndObjectEnumerator avoid( radius ); + partition->EnumerateElementsInSphere( PARTITION_CLIENT_SOLID_EDICTS, GetAbsOrigin(), radius, false, &avoid ); + + // Okay, decide how to avoid if there's anything close by + int c = avoid.GetObjectCount(); + if ( c <= 0 ) + return; + + Vector currentdir; + Vector rightdir; + AngleVectors( pCmd->viewangles, ¤tdir, &rightdir, NULL ); + + bool istryingtomove = false; + bool ismovingforward = false; + if ( fabs( pCmd->forwardmove ) > 0.0f || + fabs( pCmd->sidemove ) > 0.0f ) + { + istryingtomove = true; + if ( pCmd->forwardmove > 1.0f ) + { + ismovingforward = true; + } + } + + //engine->Con_NPrintf( slot++, "moving %s forward %s\n", istryingtomove ? "true" : "false", ismovingforward ? "true" : "false" ); + + float adjustforwardmove = 0.0f; + float adjustsidemove = 0.0f; + + int i; + for ( i = 0; i < c; i++ ) + { + C_BaseEntity *obj = avoid.GetObject( i ); + if( !obj ) + continue; + + float flHit1, flHit2; + + // Figure out a 2D radius for the object + Vector vecWorldMins, vecWorldMaxs; + obj->CollisionProp()->WorldSpaceAABB( &vecWorldMins, &vecWorldMaxs ); + Vector objSize = vecWorldMaxs - vecWorldMins; + + float objectradius = /*0.5f **/ 1.0 * sqrt( objSize.x * objSize.x + objSize.y * objSize.y ); + + if ( !IntersectInfiniteRayWithSphere( + GetAbsOrigin(), + currentdir, + obj->GetAbsOrigin(), + objectradius, + &flHit1, + &flHit2 ) ) + continue; + + float force = 0.0f; + + float forward = 0.0f, side = 0.0f; + + Vector vecToObject = obj->GetAbsOrigin() - GetAbsOrigin(); + Vector cross = vecToObject.Cross( currentdir ); + + //engine->Con_NPrintf( slot++, "object side %s\n", sign > 0.0f ? "right" : "left" ); + + if ( 0 && istryingtomove ) + { +/* + // Okay, line hits sphere in two points + // Determine how close line is to center of sphere and move sideways to avoid if we are + // actually trying to move forward + Vector deltaHit = vHit2 - vHit1; + float leg1 = ( deltaHit.Length() ) / 2.0f; + + float distfromcenter = sqrt( leg1 * leg1 + objectradius * objectradius ); + + force = distfromcenter / radius; + force = clamp( force, 0.0f, 1.0f ); + force = 1.0f - force; + + if ( force <= 0.5f ) + continue; + + side = force * AVOID_SPEED; + + // Move to right or left of object + side *= sign; +*/ + } + else + { + Vector deltaObject = vecToObject; + float dist = deltaObject.Length2D(); + + force = dist / radius; + force = clamp( force, 0.0f, 1.0f ); + force = 1.0f - force; + + //engine->Con_NPrintf( slot++, "dist %f/radius %f == %f\n", dist, radius, force ); + + if ( force <= 0.3f ) + continue; + + force = sqrt( force ); + + //engine->Con_NPrintf( slot++, "sqrt(force) == %f\n", force ); + + Vector moveDir = -vecToObject; + VectorNormalize( moveDir ); + + float fwd = currentdir.Dot( moveDir ); + float rt = rightdir.Dot( moveDir ); + + //engine->Con_NPrintf( slot++, "fwd %f right %f\n", fwd, rt ); + + float sidescale = 2.0f; + float forwardscale = 1.0f; + + if ( istryingtomove ) + { + // If running, then do a lot more sideways veer since we're not going to do anything to + // forward velocity + sidescale = 4.0f; + forwardscale = 2.0f; + } + + forward = forwardscale * fwd * force * AVOID_SPEED; + side = sidescale * rt * force * AVOID_SPEED; + + //engine->Con_NPrintf( slot++, "forward %f side %f\n", forward, side ); + } + + adjustforwardmove += forward; + adjustsidemove += side; + } + + pCmd->forwardmove += adjustforwardmove; + pCmd->sidemove += adjustsidemove; + + if ( pCmd->forwardmove > 0.0f ) + { + pCmd->forwardmove = clamp( pCmd->forwardmove, -cl_forwardspeed.GetFloat(), cl_forwardspeed.GetFloat() ); + } + else + { + pCmd->forwardmove = clamp( pCmd->forwardmove, -cl_backspeed.GetFloat(), cl_backspeed.GetFloat() ); + } + pCmd->sidemove = clamp( pCmd->sidemove, -cl_sidespeed.GetFloat(), cl_sidespeed.GetFloat() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handling +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd ) +{ + BaseClass::CreateMove( flInputSampleTime, pCmd ); + + // If the frozen flag is set, prevent view movement (server prevents the rest of the movement) + if ( GetFlags() & FL_FROZEN ) + { + return; + } + + if (!IsInVGuiInputMode() && !IsInAVehicle()) + { + PerformClientSideObstacleAvoidance( TICK_INTERVAL, pCmd ); + } +} + + +C_BaseAnimating* C_BaseTFPlayer::GetRenderedWeaponModel() +{ + // Attach to either their weapon model or their view model. + if ( C_BasePlayer::ShouldDrawLocalPlayer() || !IsLocalPlayer() ) + { + // Hook it to their external weapon model. + C_BaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ( !pWeapon ) + return NULL; + + // If this a two-handed container (shield + weapon), return the left weapon. + C_WeaponTwoHandedContainer *pContainer = dynamic_cast< C_WeaponTwoHandedContainer* >( pWeapon ); + if ( pContainer ) + { + return pContainer->GetLeftWeapon(); + } + else + { + return pWeapon; + } + } + else + { + return GetViewModel(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::SetIDEnt( C_BaseEntity *pEntity ) +{ + if ( pEntity ) + m_TFLocal.m_iIDEntIndex = pEntity->entindex(); + else + m_TFLocal.m_iIDEntIndex = 0; +} + + +C_VehicleTeleportStation* C_BaseTFPlayer::GetSelectedMCV() const +{ + return dynamic_cast< C_VehicleTeleportStation* >( m_hSelectedMCV.Get() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if this object can be +used by the player +//----------------------------------------------------------------------------- +bool C_BaseTFPlayer::IsUseableEntity( CBaseEntity *pEntity ) +{ + // I can use vehicles + return dynamic_cast<C_BaseTFVehicle*>( pEntity ); +} + +//----------------------------------------------------------------------------- +// Purpose: Powerup has just started +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::PowerupStart( int iPowerup, bool bInitial ) +{ + Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS ); + + switch( iPowerup ) + { + case POWERUP_RUSH: + { + // Play the rage start + if ( bInitial ) + { + EmitSound( "BaseTFPlayer.Rage" ); + } + + // Start the looping breathing + CPASAttenuationFilter filter( this, "BaseTFPlayer.HeavyBreathing" ); + EmitSound( filter, entindex(), "BaseTFPlayer.HeavyBreathing" ); + + if ( IsLocalPlayer() ) + { + // Start the visual effects + if ( !m_flNextAdrenalinEffect ) + { + m_flNextAdrenalinEffect = gpGlobals->curtime; + m_bFadingIn = false; + } + } + } + break; + + default: + break; + } + + BaseClass::PowerupStart( iPowerup, bInitial ); +} + +//----------------------------------------------------------------------------- +// Purpose: Powerup has just finished +//----------------------------------------------------------------------------- +void C_BaseTFPlayer::PowerupEnd( int iPowerup ) +{ + Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS ); + + switch( iPowerup ) + { + case POWERUP_RUSH: + { + // Stop the looping breathing + StopSound( "BaseTFPlayer.HeavyBreathing" ); + + if ( IsLocalPlayer() ) + { + // Stop the visual effects + if ( m_flNextAdrenalinEffect ) + { + vieweffects->ClearAllFades(); + } + + m_flNextAdrenalinEffect = 0; + } + } + break; + + default: + break; + } + + BaseClass::PowerupEnd( iPowerup ); +} |