Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

780 lines
26 KiB

//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "baseviewmodel_shared.h"
#include "datacache/imdlcache.h"
#include "cs_shareddefs.h"
#if defined( CLIENT_DLL )
#include "iprediction.h"
#include "prediction.h"
#include "inputsystem/iinputsystem.h"
#include "iclientmode.h"
#ifdef GRENADE_UNDERHAND_FEATURE_ENABLED
#include "weapon_basecsgrenade.h"
#endif
#else
#include "vguiscreen.h"
#endif
#if defined( CLIENT_DLL ) && defined( SIXENSE )
#include "sixense/in_sixense.h"
#include "sixense/sixense_convars_extern.h"
#endif
extern ConVar in_forceuser;
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define VIEWMODEL_ANIMATION_PARITY_BITS 3
#define SCREEN_OVERLAY_MATERIAL "vgui/screens/vgui_overlay"
#if defined( CLIENT_DLL )
ConVar viewmodel_offset_x( "viewmodel_offset_x", "0.0", FCVAR_ARCHIVE ); // the viewmodel offset from default in X
ConVar viewmodel_offset_y( "viewmodel_offset_y", "0.0", FCVAR_ARCHIVE ); // the viewmodel offset from default in Y
ConVar viewmodel_offset_z( "viewmodel_offset_z", "0.0", FCVAR_ARCHIVE ); // the viewmodel offset from default in Z
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseViewModel::CBaseViewModel()
{
#if defined( CLIENT_DLL )
// NOTE: We do this here because the color is never transmitted for the view model.
m_nOldAnimationParity = 0;
m_EntClientFlags |= ENTCLIENTFLAG_ALWAYS_INTERPOLATE;
RenderWithViewModels( true );
m_flStatTrakGlowMultiplier = 0.0f;
m_flStatTrakGlowMultiplierIdeal = 0.0f;
m_szLastSound[0] = '\0';
m_flLastSoundTime = 0.0f;
m_flCamDriverAppliedTime = 0;
m_flCamDriverWeight = 0;
m_vecCamDriverLastPos.Init();
m_angCamDriverLastAng.Init();
#ifdef IRONSIGHT
m_bScopeStencilMaskModeEnabled = false;
#endif
#endif
SetRenderColor( 255, 255, 255 );
SetRenderAlpha( 255 );
// View model of this weapon
m_sVMName = NULL_STRING;
// Prefix of the animations that should be used by the player carrying this weapon
m_sAnimationPrefix = NULL_STRING;
m_nViewModelIndex = 0;
m_nAnimationParity = 0;
m_bShouldIgnoreOffsetAndAccuracy = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseViewModel::~CBaseViewModel()
{
}
void CBaseViewModel::UpdateOnRemove( void )
{
BaseClass::UpdateOnRemove();
DestroyControlPanels();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseViewModel::Precache( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseViewModel::Spawn( void )
{
Precache( );
SetSize( Vector( -8, -4, -2), Vector(8, 4, 2) );
SetSolid( SOLID_NONE );
}
#if defined ( CSTRIKE_DLL ) && !defined ( CLIENT_DLL )
#define VGUI_CONTROL_PANELS
#endif
#if defined ( TF_DLL )
#define VGUI_CONTROL_PANELS
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseViewModel::SetControlPanelsActive( bool bState )
{
#if defined( VGUI_CONTROL_PANELS )
// Activate control panel screens
for ( int i = m_hScreens.Count(); --i >= 0; )
{
if (m_hScreens[i].Get())
{
m_hScreens[i]->SetActive( bState );
}
}
#endif
}
//-----------------------------------------------------------------------------
// This is called by the base object when it's time to spawn the control panels
//-----------------------------------------------------------------------------
void CBaseViewModel::SpawnControlPanels()
{
#if defined( VGUI_CONTROL_PANELS )
char buf[64];
// Destroy existing panels
DestroyControlPanels();
CBaseCombatWeapon *weapon = m_hWeapon.Get();
if ( weapon == NULL )
{
return;
}
MDLCACHE_CRITICAL_SECTION();
// FIXME: Deal with dynamically resizing control panels?
// If we're attached to an entity, spawn control panels on it instead of use
CBaseAnimating *pEntityToSpawnOn = this;
char *pOrgLL = "controlpanel%d_ll";
char *pOrgUR = "controlpanel%d_ur";
char *pAttachmentNameLL = pOrgLL;
char *pAttachmentNameUR = pOrgUR;
/*
if ( IsBuiltOnAttachment() )
{
pEntityToSpawnOn = dynamic_cast<CBaseAnimating*>((CBaseEntity*)m_hBuiltOnEntity.Get());
if ( pEntityToSpawnOn )
{
char sBuildPointLL[64];
char sBuildPointUR[64];
Q_snprintf( sBuildPointLL, sizeof( sBuildPointLL ), "bp%d_controlpanel%%d_ll", m_iBuiltOnPoint );
Q_snprintf( sBuildPointUR, sizeof( sBuildPointUR ), "bp%d_controlpanel%%d_ur", m_iBuiltOnPoint );
pAttachmentNameLL = sBuildPointLL;
pAttachmentNameUR = sBuildPointUR;
}
else
{
pEntityToSpawnOn = this;
}
}
*/
Assert( pEntityToSpawnOn );
// Lookup the attachment point...
int nPanel;
for ( nPanel = 0; true; ++nPanel )
{
Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel );
int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nLLAttachmentIndex <= 0)
{
// Try and use my panels then
pEntityToSpawnOn = this;
Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel );
nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nLLAttachmentIndex <= 0)
return;
}
Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel );
int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nURAttachmentIndex <= 0)
{
// Try and use my panels then
Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel );
nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
if (nURAttachmentIndex <= 0)
return;
}
const char *pScreenName;
weapon->GetControlPanelInfo( nPanel, pScreenName );
if (!pScreenName)
continue;
const char *pScreenClassname;
weapon->GetControlPanelClassName( nPanel, pScreenClassname );
if ( !pScreenClassname )
continue;
// Compute the screen size from the attachment points...
matrix3x4_t panelToWorld;
pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
matrix3x4_t worldToPanel;
MatrixInvert( panelToWorld, worldToPanel );
// Now get the lower right position + transform into panel space
Vector lr, lrlocal;
pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
MatrixGetColumn( panelToWorld, 3, lr );
VectorTransform( lr, worldToPanel, lrlocal );
// Not sure why, but the transform for the vgui panel to the world is improperly scaling.
// We add a fudge value here to compensate.
const float SCALE_FUDGE = 1.6f;
float flWidth = fabs( lrlocal.x ) * SCALE_FUDGE;
float flHeight = fabs( lrlocal.y ) * SCALE_FUDGE;
CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
pScreen->ChangeTeam( GetTeamNumber() );
pScreen->SetActualSize( flWidth, flHeight );
pScreen->SetActive( false );
pScreen->MakeVisibleOnlyToTeammates( false );
pScreen->SetAttachedToViewModel( true );
int nScreen = m_hScreens.AddToTail( );
m_hScreens[nScreen].Set( pScreen );
}
#endif
}
void CBaseViewModel::DestroyControlPanels()
{
#if defined( VGUI_CONTROL_PANELS )
// Kill the control panels
int i;
for ( i = m_hScreens.Count(); --i >= 0; )
{
DestroyVGuiScreen( m_hScreens[i].Get() );
}
m_hScreens.RemoveAll();
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEntity -
//-----------------------------------------------------------------------------
void CBaseViewModel::SetOwner( CBaseEntity *pEntity )
{
m_hOwner = pEntity;
#if !defined( CLIENT_DLL )
// Make sure we're linked into hierarchy
//SetParent( pEntity );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nIndex -
//-----------------------------------------------------------------------------
void CBaseViewModel::SetIndex( int nIndex )
{
m_nViewModelIndex = nIndex;
Assert( m_nViewModelIndex < (1 << VIEWMODEL_INDEX_BITS) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseViewModel::ViewModelIndex( ) const
{
return m_nViewModelIndex;
}
//-----------------------------------------------------------------------------
// Purpose: Pass our visibility on to our child screens
//-----------------------------------------------------------------------------
void CBaseViewModel::AddEffects( int nEffects )
{
if ( nEffects & EF_NODRAW )
{
SetControlPanelsActive( false );
}
BaseClass::AddEffects( nEffects );
}
//-----------------------------------------------------------------------------
// Purpose: Pass our visibility on to our child screens
//-----------------------------------------------------------------------------
void CBaseViewModel::RemoveEffects( int nEffects )
{
if ( nEffects & EF_NODRAW )
{
SetControlPanelsActive( true );
}
BaseClass::RemoveEffects( nEffects );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *modelname -
//-----------------------------------------------------------------------------
void CBaseViewModel::SetWeaponModel( const char *modelname, CBaseCombatWeapon *weapon )
{
m_hWeapon = weapon;
#if defined( CLIENT_DLL )
SetModel( modelname );
#else
string_t str;
if ( modelname != NULL )
{
str = MAKE_STRING( modelname );
}
else
{
str = NULL_STRING;
}
if ( str != m_sVMName )
{
// Msg( "SetWeaponModel %s at %f\n", modelname, gpGlobals->curtime );
m_sVMName = str;
SetModel( STRING( m_sVMName ) );
// Create any vgui control panels associated with the weapon
SpawnControlPanels();
bool showControlPanels = weapon && weapon->ShouldShowControlPanels();
SetControlPanelsActive( showControlPanels );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : CBaseCombatWeapon
//-----------------------------------------------------------------------------
CBaseCombatWeapon *CBaseViewModel::GetOwningWeapon( void )
{
return m_hWeapon.Get();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : sequence -
//-----------------------------------------------------------------------------
void CBaseViewModel::SendViewModelMatchingSequence( int sequence )
{
// since all we do is send a sequence number down to the client,
// set this here so other weapons code knows which sequence is playing.
SetSequence( sequence );
m_nAnimationParity = ( m_nAnimationParity + 1 ) & ( (1<<VIEWMODEL_ANIMATION_PARITY_BITS) - 1 );
#if defined( CLIENT_DLL )
m_nOldAnimationParity = m_nAnimationParity;
// Force frame interpolation to start at exactly frame zero
m_flAnimTime = gpGlobals->curtime;
#else
CBaseCombatWeapon *weapon = m_hWeapon.Get();
bool showControlPanels = weapon && weapon->ShouldShowControlPanels();
SetControlPanelsActive( showControlPanels );
#endif
// Restart animation at frame 0
SetCycle( 0 );
ResetSequenceInfo();
}
#if defined( CLIENT_DLL )
#include "ivieweffects.h"
#endif
#ifdef CLIENT_DLL
void CBaseViewModel::PostBuildTransformations( CStudioHdr *pStudioHdr, BoneVector *pos, BoneQuaternion q[] )
{
int nCamDriverBone = LookupBone( "cam_driver" );
if ( nCamDriverBone != -1 )
{
m_flCamDriverAppliedTime = gpGlobals->curtime;
VectorCopy( pos[nCamDriverBone], m_vecCamDriverLastPos );
QuaternionAngles( q[nCamDriverBone], m_angCamDriverLastAng );
if ( ShouldFlipModel() )
{
m_angCamDriverLastAng[YAW] = -m_angCamDriverLastAng[YAW];
m_vecCamDriverLastPos.y = -m_vecCamDriverLastPos.y;
}
}
}
#endif
void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles )
{
#ifdef GRENADE_UNDERHAND_FEATURE_ENABLED
#ifdef CLIENT_DLL
// apply viewmodel pose param
if ( owner )
{
CBaseCSGrenade* pGrenade = dynamic_cast<CBaseCSGrenade*>( owner->GetActiveWeapon() );
if ( pGrenade )
{
int iPoseParam = LookupPoseParameter( "throwcharge" );
if ( iPoseParam != -1 )
SetPoseParameter( iPoseParam, clamp(pGrenade->ApproachThrownStrength(), 0.0f, 1.0f) );
}
}
#endif
#endif
// UNDONE: Calc this on the server? Disabled for now as it seems unnecessary to have this info on the server
#if defined( CLIENT_DLL )
QAngle vmangoriginal = eyeAngles;
QAngle vmangles = eyeAngles;
Vector vmorigin = eyePosition;
Vector vecRight;
Vector vecUp;
Vector vecForward;
AngleVectors( vmangoriginal, &vecForward, &vecRight, &vecUp );
//Vector vecOffset = Vector( viewmodel_offset_x.GetFloat(), viewmodel_offset_y.GetFloat(), viewmodel_offset_z.GetFloat() );
if ( !m_bShouldIgnoreOffsetAndAccuracy )
{
#ifdef IRONSIGHT
CWeaponCSBase *pIronSightWeapon = (CWeaponCSBase*)owner->GetActiveWeapon();
if ( pIronSightWeapon )
{
CIronSightController* pIronSightController = pIronSightWeapon->GetIronSightController();
if ( pIronSightController && pIronSightController->IsInIronSight() )
{
float flInvIronSightAmount = ( 1.0f - pIronSightController->GetIronSightAmount() );
vecForward *= flInvIronSightAmount;
vecUp *= flInvIronSightAmount;
vecRight *= flInvIronSightAmount;
}
}
#endif
vmorigin += (vecForward * viewmodel_offset_y.GetFloat()) + (vecUp * viewmodel_offset_z.GetFloat()) + (vecRight * viewmodel_offset_x.GetFloat());
}
// TrackIR
if ( IsHeadTrackingEnabled() )
{
vmorigin = owner->EyePosition();
VectorAngles( owner->GetAutoaimVector( AUTOAIM_5DEGREES ), vmangoriginal );
vmangles = vmangoriginal;
}
// TrackIR
CBaseCombatWeapon *pWeapon = m_hWeapon.Get();
//Allow weapon lagging
if ( pWeapon != NULL )
{
if ( !prediction->InPrediction() )
{
// add weapon-specific bob
pWeapon->AddViewmodelBob( this, vmorigin, vmangles );
#if defined ( CSTRIKE_DLL )
CalcViewModelLag( vmorigin, vmangles, vmangoriginal );
#endif
}
}
// Add model-specific bob even if no weapon associated (for head bob for off hand models)
AddViewModelBob( owner, vmorigin, vmangles );
#if !defined ( CSTRIKE_DLL )
// This was causing weapon jitter when rotating in updated CS:S; original Source had this in above InPrediction block 07/14/10
// Add lag
CalcViewModelLag( vmorigin, vmangles, vmangoriginal );
#endif
if ( !prediction->InPrediction() )
{
// Let the viewmodel shake at about 10% of the amplitude of the player's view
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( GetOwner() );
GetViewEffects()->ApplyShake( vmorigin, vmangles, 0.1 );
}
SetLocalOrigin( vmorigin );
SetLocalAngles( vmangles );
#endif //#if defined( CLIENT_DLL )
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles )
{
Vector vOriginalOrigin = origin;
QAngle vOriginalAngles = angles;
// Calculate our drift
Vector forward;
AngleVectors( angles, &forward, NULL, NULL );
if ( gpGlobals->frametime != 0.0f )
{
Vector vDifference;
VectorSubtract( forward, m_vecLastFacing, vDifference );
float flSpeed = 5.0f;
// If we start to lag too far behind, we'll increase the "catch up" speed. Solves the problem with fast cl_yawspeed, m_yaw or joysticks
// rotating quickly. The old code would slam lastfacing with origin causing the viewmodel to pop to a new position
float flDiff = vDifference.Length();
if ( flDiff > 1.5f )
{
float flScale = flDiff / 1.5f;
flSpeed *= flScale;
}
// FIXME: Needs to be predictable?
VectorMA( m_vecLastFacing, flSpeed * gpGlobals->frametime, vDifference, m_vecLastFacing );
// Make sure it doesn't grow out of control!!!
VectorNormalize( m_vecLastFacing );
VectorMA( origin, 5.0f, vDifference * -1.0f, origin );
Assert( m_vecLastFacing.IsValid() );
}
#if !defined( PORTAL ) //floor/wall floor/floor portals cause a sudden and large pitch change, causing a pop unless we write a bunch of interpolation code. Easier to just disable this
Vector right, up;
AngleVectors( original_angles, &forward, &right, &up );
float pitch = original_angles[ PITCH ];
if ( pitch > 180.0f )
pitch -= 360.0f;
else if ( pitch < -180.0f )
pitch += 360.0f;
//FIXME: These are the old settings that caused too many exposed polys on some models
VectorMA( origin, -pitch * 0.035f, forward, origin );
VectorMA( origin, -pitch * 0.03f, right, origin );
VectorMA( origin, -pitch * 0.02f, up, origin);
#endif
}
//-----------------------------------------------------------------------------
// Stub to keep networking consistent for DEM files
//-----------------------------------------------------------------------------
#if defined( CLIENT_DLL )
extern void RecvProxy_EffectFlags( const CRecvProxyData *pData, void *pStruct, void *pOut );
void RecvProxy_ViewmodelSequenceNum( const CRecvProxyData *pData, void *pStruct, void *pOut );
void RecvProxy_Viewmodel( const CRecvProxyData *pData, void *pStruct, void *pOut );
#endif
//-----------------------------------------------------------------------------
// Purpose: Resets anim cycle when the server changes the weapon on us
//-----------------------------------------------------------------------------
#if defined( CLIENT_DLL )
static void RecvProxy_Weapon( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CBaseViewModel *pViewModel = ((CBaseViewModel*)pStruct);
CBaseCombatWeapon *pOldWeapon = pViewModel->GetOwningWeapon();
bool bViewModelWasVisible = pViewModel->IsVisible();
// Chain through to the default recieve proxy ...
RecvProxy_IntToEHandle( pData, pStruct, pOut );
// ... and reset our cycle index if the server is switching weapons on us
CBaseCombatWeapon *pNewWeapon = pViewModel->GetOwningWeapon();
if ( pNewWeapon != pOldWeapon || !bViewModelWasVisible )
{
// Restart animation at frame 0
pViewModel->SetCycle( 0 );
pViewModel->m_flAnimTime = gpGlobals->curtime;
}
}
static void RecvProxy_Owner( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CBaseViewModel *pViewModel = ( ( CBaseViewModel* )pStruct );
//Msg( "BaseViewModel changed from (%d)%x", ( pViewModel->m_hOwner.GetForModify().GetSerialNumber(), pViewModel->m_hOwner.GetForModify().GetEntryIndex() ) );
// Chain through to the default recieve proxy ...
RecvProxy_IntToEHandle( pData, pStruct, pOut );
//Msg( " to (%d)%x\n", ( pViewModel->m_hOwner.GetForModify().GetSerialNumber(), pViewModel->m_hOwner.GetForModify().GetEntryIndex() ) );
pViewModel->UpdateVisibility(); // visibility of a viewmodel is owner-dependant, and other events like SetDormant() may happen out of order with setting owner, especially when doing full frame update after spectator mode, which happens most often (pretty much exclusively) after HLTV replay ends.
}
#endif
IMPLEMENT_NETWORKCLASS_ALIASED( BaseViewModel, DT_BaseViewModel )
LINK_ENTITY_TO_CLASS_ALIASED( viewmodel, BaseViewModel );
BEGIN_NETWORK_TABLE_NOBASE(CBaseViewModel, DT_BaseViewModel)
#if !defined( CLIENT_DLL )
SendPropModelIndex(SENDINFO(m_nModelIndex)),
SendPropEHandle (SENDINFO(m_hWeapon)),
SendPropInt (SENDINFO(m_nBody), ANIMATION_BODY_BITS ), // increased to 32 bits to support number of bits equal to number of bodygroups
SendPropInt (SENDINFO(m_nSkin), 10),
SendPropInt (SENDINFO(m_nSequence), 8, SPROP_UNSIGNED),
SendPropInt (SENDINFO(m_nViewModelIndex), VIEWMODEL_INDEX_BITS, SPROP_UNSIGNED),
SendPropFloat (SENDINFO(m_flPlaybackRate), 8, SPROP_ROUNDUP, -4.0, 12.0f),
SendPropInt (SENDINFO(m_fEffects), EF_MAX_BITS, SPROP_UNSIGNED),
SendPropInt (SENDINFO(m_nAnimationParity), 3, SPROP_UNSIGNED ),
SendPropEHandle (SENDINFO(m_hOwner)),
SendPropInt( SENDINFO( m_nNewSequenceParity ), EF_PARITY_BITS, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_nResetEventsParity ), EF_PARITY_BITS, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_nMuzzleFlashParity ), EF_MUZZLEFLASH_BITS, SPROP_UNSIGNED ),
SendPropBool( SENDINFO( m_bShouldIgnoreOffsetAndAccuracy ) ),
#else
RecvPropInt (RECVINFO(m_nModelIndex), 0, RecvProxy_Viewmodel ),
RecvPropEHandle (RECVINFO(m_hWeapon), RecvProxy_Weapon ),
RecvPropInt (RECVINFO(m_nSkin)),
RecvPropInt (RECVINFO(m_nBody)),
RecvPropInt (RECVINFO(m_nSequence), 0, RecvProxy_ViewmodelSequenceNum ),
RecvPropInt (RECVINFO(m_nViewModelIndex)),
RecvPropFloat (RECVINFO(m_flPlaybackRate)),
RecvPropInt (RECVINFO(m_fEffects), 0, RecvProxy_EffectFlags ),
RecvPropInt (RECVINFO(m_nAnimationParity)),
RecvPropEHandle (RECVINFO(m_hOwner), RecvProxy_Owner ),
RecvPropInt( RECVINFO( m_nNewSequenceParity )),
RecvPropInt( RECVINFO( m_nResetEventsParity )),
RecvPropInt( RECVINFO( m_nMuzzleFlashParity )),
RecvPropBool( RECVINFO( m_bShouldIgnoreOffsetAndAccuracy ) ),
#endif
END_NETWORK_TABLE()
#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CBaseViewModel )
// Networked
DEFINE_PRED_FIELD( m_nModelIndex, FIELD_SHORT, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
DEFINE_PRED_FIELD( m_nSkin, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_nBody, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_nSequence, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_nViewModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD_TOL( m_flPlaybackRate, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.125f ),
DEFINE_PRED_FIELD( m_fEffects, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_OVERRIDE ),
DEFINE_PRED_FIELD( m_nAnimationParity, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_hWeapon, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_flAnimTime, FIELD_FLOAT, 0 ),
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
DEFINE_FIELD( m_flTimeWeaponIdle, FIELD_FLOAT ),
DEFINE_FIELD( m_Activity, FIELD_INTEGER ),
DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_PRIVATE | FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
END_PREDICTION_DATA()
// This needed to be done as a proxy for the surrounding box auto update when animations change.
// This doesn't have to be done for view models as they don't affect the bounding box and it was
// causing some timing problems with our world to view model under the covers swap.
// [msmith] Added back in for CS:GO because without this the m_nSequence number gets reset during prediction causing
// view model animations to freeze up. This issue is probably caused by the fact that prediction doesn't fix up
// m_nSequence, but this fixes it and makes it consistent with CS:S ... which also has the same prediction issues.
void RecvProxy_ViewmodelSequenceNum( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CBaseViewModel *model = (CBaseViewModel *)pStruct;
if (pData->m_Value.m_Int != model->GetSequence())
{
MDLCACHE_CRITICAL_SECTION();
model->SetSequence(pData->m_Value.m_Int);
model->m_flAnimTime = gpGlobals->curtime;
model->SetCycle(0);
}
}
void RecvProxy_Viewmodel( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
// We assign the model index via the SetModelByIndex function so that the model pointer gets updated as soon as we change the model index.
// This is necessary since this new model may be accessed with frame.
// An example is the SetSequence code in RecvProxy_ViewmodelSequenceNum that checks to make sure the sequence number is in range of those available in
// model.
CBaseViewModel *model = (CBaseViewModel *)pStruct;
if ( model )
{
MDLCACHE_CRITICAL_SECTION();
model->SetModelByIndex( pData->m_Value.m_Int );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseViewModel::LookupAttachment( const char *pAttachmentName )
{
if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() )
return m_hWeapon.Get()->LookupAttachment( pAttachmentName );
return BaseClass::LookupAttachment( pAttachmentName );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseViewModel::GetAttachment( int number, matrix3x4_t &matrix )
{
if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() )
return m_hWeapon.Get()->GetAttachment( number, matrix );
return BaseClass::GetAttachment( number, matrix );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseViewModel::GetAttachment( int number, Vector &origin )
{
if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() )
return m_hWeapon.Get()->GetAttachment( number, origin );
return BaseClass::GetAttachment( number, origin );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseViewModel::GetAttachment( int number, Vector &origin, QAngle &angles )
{
if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() )
return m_hWeapon.Get()->GetAttachment( number, origin, angles );
return BaseClass::GetAttachment( number, origin, angles );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseViewModel::GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel )
{
if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() )
return m_hWeapon.Get()->GetAttachmentVelocity( number, originVel, angleVel );
return BaseClass::GetAttachmentVelocity( number, originVel, angleVel );
}
#endif