source-engine/game/client/hl1/c_hl1mp_player.cpp
2022-04-16 12:05:19 +03:00

603 lines
14 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "c_hl1mp_player.h"
#include "c_basetempentity.h"
#include "iinput.h"
// Don't alias here
#if defined( CHL1MP_Player )
#undef CHL1MP_Player
#endif
//////////////////////////////////////////////////////////////////////
class C_TEPlayerAnimEvent : public C_BaseTempEntity
{
public:
DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity );
DECLARE_CLIENTCLASS();
virtual void PostDataUpdate( DataUpdateType_t updateType )
{
// Create the effect.
C_HL1MP_Player *pPlayer = dynamic_cast< C_HL1MP_Player* >( m_hPlayer.Get() );
if ( pPlayer && !pPlayer->IsDormant() )
{
pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData );
}
}
public:
CNetworkHandle( CBasePlayer, m_hPlayer );
CNetworkVar( int, m_iEvent );
CNetworkVar( int, m_nData );
};
IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent );
// ------------------------------------------------------------------------------------------ //
// Data tables and prediction tables.
// ------------------------------------------------------------------------------------------ //
BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent )
RecvPropEHandle( RECVINFO( m_hPlayer ) ),
RecvPropInt( RECVINFO( m_iEvent ) ),
RecvPropInt( RECVINFO( m_nData ) )
END_RECV_TABLE()
IMPLEMENT_CLIENTCLASS_DT( C_HL1MP_Player, DT_HL1MP_Player, CHL1MP_Player )
RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
RecvPropEHandle( RECVINFO( m_hRagdoll ) ),
RecvPropInt( RECVINFO( m_iSpawnInterpCounter ) ),
RecvPropInt( RECVINFO( m_iRealSequence ) ),
// RecvPropDataTable( RECVINFO_DT( m_Shared ), 0, &REFERENCE_RECV_TABLE( DT_TFCPlayerShared ) )
END_RECV_TABLE()
BEGIN_PREDICTION_DATA( C_HL1MP_Player )
END_PREDICTION_DATA()
/////////////////////////////////////////////////////////////////////
static ConVar cl_playermodel( "cl_playermodel", "none", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_SERVER_CAN_EXECUTE, "Default Player Model");
C_HL1MP_Player::C_HL1MP_Player( void ) :
m_iv_angEyeAngles( "C_HL1MP_Player::m_iv_angEyeAngles" )
{
m_PlayerAnimState = CreatePlayerAnimState( this );
m_angEyeAngles.Init();
m_fLastPredFreeze = -1;
// cant interpolate ... buggy? it keeps resetting the angle to 0,0,0
// AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
}
C_HL1MP_Player::~C_HL1MP_Player()
{
m_PlayerAnimState->Release();
}
const QAngle& C_HL1MP_Player::GetRenderAngles()
{
if ( IsRagdoll() )
{
return vec3_angle;
}
else
{
return m_PlayerAnimState->GetRenderAngles();
}
}
void C_HL1MP_Player::UpdateClientSideAnimation()
{
// Update the animation data. It does the local check here so this works when using
// a third-person camera (and we don't have valid player angles).
if ( this == C_BasePlayer::GetLocalPlayer() )
m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] );
else
m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
BaseClass::UpdateClientSideAnimation();
}
void C_HL1MP_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
{
m_PlayerAnimState->DoAnimationEvent( event, nData );
}
void C_HL1MP_Player::ProcessMuzzleFlashEvent()
{
#if 0
// Reenable when the weapons have muzzle flash attachments in the right spot.
if ( this != C_BasePlayer::GetLocalPlayer() )
{
Vector vAttachment;
QAngle dummyAngles;
C_WeaponTFCBase *pWeapon = m_Shared.GetActiveTFCWeapon();
if ( pWeapon )
{
int iAttachment = pWeapon->LookupAttachment( "muzzle_flash" );
if ( iAttachment > 0 )
{
float flScale = 1;
pWeapon->GetAttachment( iAttachment, vAttachment, dummyAngles );
// The way the models are setup, the up vector points along the barrel.
Vector vForward, vRight, vUp;
AngleVectors( dummyAngles, &vForward, &vRight, &vUp );
VectorAngles( vUp, dummyAngles );
FX_MuzzleEffect( vAttachment, dummyAngles, flScale, 0, NULL, true );
}
}
}
Vector vAttachment;
QAngle dummyAngles;
bool bFoundAttachment = GetAttachment( 1, vAttachment, dummyAngles );
// If we have an attachment, then stick a light on it.
if ( bFoundAttachment )
{
dlight_t *el = effects->CL_AllocDlight( LIGHT_INDEX_MUZZLEFLASH + index );
el->origin = vAttachment;
el->radius = 24;
el->decay = el->radius / 0.05f;
el->die = gpGlobals->curtime + 0.05f;
el->color.r = 255;
el->color.g = 192;
el->color.b = 64;
el->color.exponent = 5;
}
#endif
}
void C_HL1MP_Player::Spawn( void )
{
BaseClass::Spawn();
// SetModel( "models/player/mp/barney/barney.mdl" );
m_iSpawnInterpCounterCache = 0;
UpdateVisibility();
}
void C_HL1MP_Player::AddEntity( void )
{
BaseClass::AddEntity();
//m_PlayerAnimState.Update();
// SetLocalAnglesDim( X_INDEX, 0 );
}
void C_HL1MP_Player::OnDataChanged( DataUpdateType_t type )
{
BaseClass::OnDataChanged( type );
if ( type == DATA_UPDATE_CREATED )
{
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
UpdateVisibility();
}
C_BaseAnimating *C_HL1MP_Player::BecomeRagdollOnClient()
{
// Handled elsewhere
return NULL;
}
void C_HL1MP_Player::PreThink( void )
{
BaseClass::PreThink();
return;
QAngle vTempAngles = GetLocalAngles();
if ( GetLocalPlayer() == this )
{
vTempAngles[PITCH] = EyeAngles()[PITCH];
}
else
{
vTempAngles[PITCH] = m_angEyeAngles[PITCH];
}
if ( vTempAngles[YAW] < 0.0f )
{
vTempAngles[YAW] += 360.0f;
}
SetLocalAngles( vTempAngles );
BaseClass::PreThink();
#if 0
HandleSpeedChanges();
if ( m_HL2Local.m_flSuitPower <= 0.0f )
{
if( IsSprinting() )
{
StopSprinting();
}
}
#endif
}
void C_HL1MP_Player::PostDataUpdate( DataUpdateType_t updateType )
{
// C_BaseEntity assumes we're networking the entity's angles, so pretend that it
// networked the same value we already have.
SetNetworkAngles( GetLocalAngles() );
BaseClass::PostDataUpdate( updateType );
}
void C_HL1MP_Player::ClientThink( void )
{
BaseClass::ClientThink();
// m_PlayerAnimState.Update();
}
//HL1MPRAGDOLL
IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_HL1MPRagdoll, DT_HL1MPRagdoll, CHL1MPRagdoll )
RecvPropVector( RECVINFO(m_vecRagdollOrigin) ),
RecvPropEHandle( RECVINFO( m_hPlayer ) ),
RecvPropInt( RECVINFO( m_nModelIndex ) ),
RecvPropInt( RECVINFO(m_nForceBone) ),
RecvPropVector( RECVINFO(m_vecForce) ),
RecvPropVector( RECVINFO( m_vecRagdollVelocity ) )
END_RECV_TABLE()
C_HL1MPRagdoll::C_HL1MPRagdoll()
{
}
C_HL1MPRagdoll::~C_HL1MPRagdoll()
{
PhysCleanupFrictionSounds( this );
if ( m_hPlayer )
{
m_hPlayer->CreateModelInstance();
}
}
void C_HL1MPRagdoll::Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity )
{
if ( !pSourceEntity )
return;
VarMapping_t *pSrc = pSourceEntity->GetVarMapping();
VarMapping_t *pDest = GetVarMapping();
// Find all the VarMapEntry_t's that represent the same variable.
for ( int i = 0; i < pDest->m_Entries.Count(); i++ )
{
VarMapEntry_t *pDestEntry = &pDest->m_Entries[i];
const char *pszName = pDestEntry->watcher->GetDebugName();
for ( int j=0; j < pSrc->m_Entries.Count(); j++ )
{
VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j];
if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(), pszName ) )
{
pDestEntry->watcher->Copy( pSrcEntry->watcher );
break;
}
}
}
}
void C_HL1MPRagdoll::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
{
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
if( !pPhysicsObject )
return;
Vector dir = pTrace->endpos - pTrace->startpos;
if ( iDamageType == DMG_BLAST )
{
dir *= 4000; // adjust impact strenght
// apply force at object mass center
pPhysicsObject->ApplyForceCenter( dir );
}
else
{
Vector hitpos;
VectorMA( pTrace->startpos, pTrace->fraction, dir, hitpos );
VectorNormalize( dir );
dir *= 4000; // adjust impact strenght
// apply force where we hit it
pPhysicsObject->ApplyForceOffset( dir, hitpos );
}
m_pRagdoll->ResetRagdollSleepAfterTime();
}
void C_HL1MP_Player::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov )
{
if ( m_lifeState != LIFE_ALIVE )
{
Vector origin = EyePosition();
IRagdoll *pRagdoll = GetRepresentativeRagdoll();
if ( pRagdoll )
{
origin = pRagdoll->GetRagdollOrigin();
origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z; // look over ragdoll, not through
}
BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov );
eyeOrigin = origin;
Vector vForward;
AngleVectors( eyeAngles, &vForward );
VectorNormalize( vForward );
VectorMA( origin, -CHASE_CAM_DISTANCE_MAX, vForward, eyeOrigin );
Vector WALL_MIN( -WALL_OFFSET, -WALL_OFFSET, -WALL_OFFSET );
Vector WALL_MAX( WALL_OFFSET, WALL_OFFSET, WALL_OFFSET );
trace_t trace; // clip against world
C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
UTIL_TraceHull( origin, eyeOrigin, WALL_MIN, WALL_MAX, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trace );
C_BaseEntity::PopEnableAbsRecomputations();
if (trace.fraction < 1.0)
{
eyeOrigin = trace.endpos;
}
return;
}
BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov );
}
IRagdoll* C_HL1MP_Player::GetRepresentativeRagdoll() const
{
if ( m_hRagdoll.Get() )
{
C_HL1MPRagdoll *pRagdoll = (C_HL1MPRagdoll*)m_hRagdoll.Get();
return pRagdoll->GetIRagdoll();
}
else
{
return NULL;
}
}
void C_HL1MPRagdoll::CreateHL1MPRagdoll( void )
{
// First, initialize all our data. If we have the player's entity on our client,
// then we can make ourselves start out exactly where the player is.
C_HL1MP_Player *pPlayer = dynamic_cast< C_HL1MP_Player* >( m_hPlayer.Get() );
if ( pPlayer && !pPlayer->IsDormant() )
{
// move my current model instance to the ragdoll's so decals are preserved.
pPlayer->SnatchModelInstance( this );
VarMapping_t *varMap = GetVarMapping();
// Copy all the interpolated vars from the player entity.
// The entity uses the interpolated history to get bone velocity.
bool bRemotePlayer = (pPlayer != C_BasePlayer::GetLocalPlayer());
if ( bRemotePlayer )
{
Interp_Copy( pPlayer );
SetAbsAngles( pPlayer->GetRenderAngles() );
GetRotationInterpolator().Reset();
m_flAnimTime = pPlayer->m_flAnimTime;
SetSequence( pPlayer->GetSequence() );
m_flPlaybackRate = pPlayer->GetPlaybackRate();
}
else
{
// This is the local player, so set them in a default
// pose and slam their velocity, angles and origin
SetAbsOrigin( m_vecRagdollOrigin );
SetAbsAngles( pPlayer->GetRenderAngles() );
SetAbsVelocity( m_vecRagdollVelocity );
int iSeq = pPlayer->GetSequence();
if ( iSeq == -1 )
{
Assert( false ); // missing walk_lower?
iSeq = 0;
}
SetSequence( iSeq ); // walk_lower, basic pose
SetCycle( 0.0 );
Interp_Reset( varMap );
}
}
else
{
// overwrite network origin so later interpolation will
// use this position
SetNetworkOrigin( m_vecRagdollOrigin );
SetAbsOrigin( m_vecRagdollOrigin );
SetAbsVelocity( m_vecRagdollVelocity );
Interp_Reset( GetVarMapping() );
}
SetModelIndex( m_nModelIndex );
// Make us a ragdoll..
m_nRenderFX = kRenderFxRagdoll;
matrix3x4_t boneDelta0[MAXSTUDIOBONES];
matrix3x4_t boneDelta1[MAXSTUDIOBONES];
matrix3x4_t currentBones[MAXSTUDIOBONES];
const float boneDt = 0.05f;
if ( pPlayer && !pPlayer->IsDormant() )
{
pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
}
else
{
GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
}
InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
}
void C_HL1MPRagdoll::OnDataChanged( DataUpdateType_t type )
{
BaseClass::OnDataChanged( type );
if ( type == DATA_UPDATE_CREATED )
{
CreateHL1MPRagdoll();
}
}
IRagdoll* C_HL1MPRagdoll::GetIRagdoll() const
{
return m_pRagdoll;
}
void C_HL1MPRagdoll::UpdateOnRemove( void )
{
VPhysicsSetObject( NULL );
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose: clear out any face/eye values stored in the material system
//-----------------------------------------------------------------------------
void C_HL1MPRagdoll::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
{
BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights );
static float destweight[128];
static bool bIsInited = false;
CStudioHdr *hdr = GetModelPtr();
if ( !hdr )
return;
int nFlexDescCount = hdr->numflexdesc();
if ( nFlexDescCount )
{
Assert( !pFlexDelayedWeights );
memset( pFlexWeights, 0, nFlexWeightCount * sizeof(float) );
}
if ( m_iEyeAttachment > 0 )
{
matrix3x4_t attToWorld;
if (GetAttachment( m_iEyeAttachment, attToWorld ))
{
Vector local, tmp;
local.Init( 1000.0f, 0.0f, 0.0f );
VectorTransform( local, attToWorld, tmp );
modelrender->SetViewTarget( GetModelPtr(), GetBody(), tmp );
}
}
}
bool C_HL1MP_Player::ShouldDraw( void )
{
if ( !IsAlive() )
return false;
if ( IsLocalPlayer() && IsRagdoll() )
return true;
if ( IsRagdoll() )
return false;
return BaseClass::ShouldDraw();
}
bool C_HL1MP_Player::ShouldPredict( void )
{
// Do this before calling into baseclass so prediction data block gets allocated
if ( IsLocalPlayer() )
{
#if 0
// Disable prediction when player hops onto a moving train or elevator :-/
if ( GetGroundEntity() && GetGroundEntity()->GetMoveType() == MOVETYPE_PUSH )
{
Vector vel = GetGroundEntity()->GetLocalVelocity();
if ( vel.Length() > 0.002f )
{
m_fLastPredFreeze = gpGlobals->curtime;
}
}
// disable prediction for 3 seconds after touching a moving entity
if ( ( gpGlobals->curtime - m_fLastPredFreeze ) < 3 )
{
if ( GetPredictable() )
{
QuickShutdownPredictable();
}
return false;
}
if ( !GetPredictable() && IsIntermediateDataAllocated() )
{
QuickInitPredictable();
}
#endif
return true;
}
return false;
}