You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2705 lines
78 KiB
2705 lines
78 KiB
//========= 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 ); |
|
}
|
|
|