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.
602 lines
14 KiB
602 lines
14 KiB
//========= 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; |
|
}
|
|
|