text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  // $NoKeywords: $
  //=============================================================================//
  #include "cbase.h"
  #include "baseviewmodel_shared.h"
  #include "datacache/imdlcache.h"
  
  #if defined( CLIENT_DLL )
  #include "iprediction.h"
  #include "prediction.h"
  #include "client_virtualreality.h"
  #include "headtrack/isourcevirtualreality.h"
  #else
  #include "vguiscreen.h"
  #endif
  
  #if defined( CLIENT_DLL ) && defined( SIXENSE )
  #include "sixense/in_sixense.h"
  #include "sixense/sixense_convars_extern.h"
  #endif
  
  #ifdef SIXENSE
  extern ConVar in_forceuser;
  #include "iclientmode.h"
  #endif
  
  // 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"
  
  //-----------------------------------------------------------------------------
  // 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;
  #endif
  	SetRenderColor( 255, 255, 255, 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;
  }
  
  //-----------------------------------------------------------------------------
  // 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
  
  #ifdef INVASION_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 );
  
  		float flWidth = lrlocal.x;
  		float flHeight = lrlocal.y;
  
  		CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
  		pScreen->ChangeTeam( GetTeamNumber() );
  		pScreen->SetActualSize( flWidth, flHeight );
  		pScreen->SetActive( false );
  		pScreen->MakeVisibleOnlyToTeammates( false );
  	
  #ifdef INVASION_DLL
  		pScreen->SetOverlayMaterial( SCREEN_OVERLAY_MATERIAL );
  #endif
  		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
  #ifdef HOE_DLL
  	// Allow weapons to SetBodygroup() on the viewmodel.
  	// This will clean up the bodygroup when switching weapons.
  	m_nBody = 0;
  #endif // HOE_DLL
  }
  
  //-----------------------------------------------------------------------------
  // 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
  
  void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles )
  {
  	// 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;
  
  	CBaseCombatWeapon *pWeapon = m_hWeapon.Get();
  	//Allow weapon lagging
  	if ( pWeapon != NULL )
  	{
  #if defined( CLIENT_DLL )
  		if ( !prediction->InPrediction() )
  #endif
  		{
  			// 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 defined( CLIENT_DLL )
  	if ( !prediction->InPrediction() )
  	{
  		// Let the viewmodel shake at about 10% of the amplitude of the player's view
  		vieweffects->ApplyShake( vmorigin, vmangles, 0.1 );	
  	}
  #endif
  
  #ifdef HOE_DLL
  	// Allow the weapon to totally override the view.
  	// Added for sniper rifle zoom in/out.
  	if ( pWeapon && pWeapon->CalcViewModelView( this, eyePosition, eyeAngles, vmorigin, vmangles ) )
  	{
  	}
  #endif // HOE_DLL
  #ifdef HOE_IRONSIGHTSxxx
  	if ( pWeapon && pWeapon->IsIronSightsActive() )
  	{
  		Vector offset = pWeapon->GetIronSights_Origin();
  		QAngle ang = eyeAngles + pWeapon->GetIronSights_Angles();
  
  		Vector forward, right, up;
  		AngleVectors(ang, &forward, &right, &up);
  
  		vmorigin = eyePosition;
  		vmorigin += right * offset.x;
  		vmorigin += forward * offset.y;
  		vmorigin += up * offset.z;
  	}
  #endif // HOE_IRONSIGHTS
  
  	if( UseVR() )
  	{
  		g_ClientVirtualReality.OverrideViewModelTransform( vmorigin, vmangles, pWeapon && pWeapon->ShouldUseLargeViewModelVROverride() );
  	}
  
  	SetLocalOrigin( vmorigin );
  	SetLocalAngles( vmangles );
  
  #ifdef SIXENSE
  	if( g_pSixenseInput->IsEnabled() && (owner->GetObserverMode()==OBS_MODE_NONE) && !UseVR() )
  	{
  		const float max_gun_pitch = 20.0f;
  
  		float viewmodel_fov_ratio = g_pClientMode->GetViewModelFOV()/owner->GetFOV();
  		QAngle gun_angles = g_pSixenseInput->GetViewAngleOffset() * -viewmodel_fov_ratio;
  
  		// Clamp pitch a bit to minimize seeing back of viewmodel
  		if( gun_angles[PITCH] < -max_gun_pitch )
  		{ 
  			gun_angles[PITCH] = -max_gun_pitch; 
  		}
  
  #ifdef WIN32 // ShouldFlipViewModel comes up unresolved on osx? Mabye because it's defined inline? fixme
  		if( ShouldFlipViewModel() ) 
  		{
  			gun_angles[YAW] *= -1.0f;
  		}
  #endif
  
  		vmangles = EyeAngles() +  gun_angles;
  
  		SetLocalAngles( vmangles );
  	}
  #endif
  #endif
  
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  float g_fMaxViewModelLag = 1.5f;
  
  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 );
  
  #ifdef HOE_IRONSIGHTS
  	extern ConVar hoe_ironsights_delay;
  	#define IRONSIGHTS_DURATION max(hoe_ironsights_delay.GetFloat(),0.1)
  
  	CBaseCombatWeapon *pWeapon = m_hWeapon.Get();
  	CBaseCombatWeapon::WeaponIronSightState_t state = pWeapon ? pWeapon->GetIronSightsState() : 
  		CBaseCombatWeapon::IRONSIGHT_STATE_NONE;
  #endif // HOE_IRONSIGHTS
  
  	if ( gpGlobals->frametime != 0.0f )
  	{
  		Vector vDifference;
  		VectorSubtract( forward, m_vecLastFacing, vDifference );
  
  		float flSpeed = 5.0f;
  
  #ifdef HOE_IRONSIGHTS
  		float lerpOrgFrac = 1.0f;
  
  		// Decrease lag time when ironsighted
  		if ( pWeapon )
  		{
  			if ( state == CBaseCombatWeapon::IRONSIGHT_STATE_ACTIVE )
  			{
  				flSpeed *= 3.0f;
  				lerpOrgFrac = 0.25f;
  			}
  			else if ( state == CBaseCombatWeapon::IRONSIGHT_STATE_TO )
  			{
  				float flFrac = (gpGlobals->curtime - pWeapon->m_flIronSightsToggleTime) / IRONSIGHTS_DURATION;
  				flFrac = clamp( flFrac, 0.0f, 1.0f );
  				flSpeed = Lerp( flFrac, flSpeed, flSpeed * 3.0f );
  				lerpOrgFrac = Lerp( flFrac, 1.0f, 0.25f );
  			}
  			else if ( state == CBaseCombatWeapon::IRONSIGHT_STATE_FROM )
  			{
  				float flFrac = (gpGlobals->curtime - pWeapon->m_flIronSightsToggleTime) / IRONSIGHTS_DURATION;
  				flFrac = clamp( flFrac, 0.0f, 1.0f );
  				flSpeed = Lerp( flFrac, flSpeed * 3.0f, flSpeed );
  				lerpOrgFrac = Lerp( flFrac, 0.25f, 1.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 > g_fMaxViewModelLag) && (g_fMaxViewModelLag > 0.0f) )
  		{
  			float flScale = flDiff / g_fMaxViewModelLag;
  			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 );
  		Vector vecNewOrigin;
  		VectorMA( origin, 5.0f, vDifference * -1.0f, vecNewOrigin );
  		VectorLerp( origin, vecNewOrigin, lerpOrgFrac, origin );
  		Assert( m_vecLastFacing.IsValid() );
  #else // HOE_IRONSIGHTS
  
  		// 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 > g_fMaxViewModelLag) && (g_fMaxViewModelLag > 0.0f) )
  		{
  			float flScale = flDiff / g_fMaxViewModelLag;
  			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() );
  #endif // HOE_IRONSIGHTS
  	}
  
  #ifdef HOE_IRONSIGHTS
  	// Stop the ironsights getting twisted left/right based on pitch of the player's view.
  	if ( state == CBaseCombatWeapon::IRONSIGHT_STATE_ACTIVE )
  		return;
  #endif // HOE_IRONSIGHTS
  
  	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;
  
  #ifdef HOE_IRONSIGHTS
  	if ( state == CBaseCombatWeapon::IRONSIGHT_STATE_TO )
  	{
  		float flFrac = (gpGlobals->curtime - pWeapon->m_flIronSightsToggleTime) / IRONSIGHTS_DURATION;
  		flFrac = clamp( flFrac, 0.0f, 1.0f );
  		pitch = Lerp( flFrac, pitch, 0.0f );
  	}
  	else if ( state == CBaseCombatWeapon::IRONSIGHT_STATE_FROM )
  	{
  		float flFrac = (gpGlobals->curtime - pWeapon->m_flIronSightsToggleTime) / IRONSIGHTS_DURATION;
  		flFrac = clamp( flFrac, 0.0f, 1.0f );
  		pitch = Lerp( flFrac, 0.0f, pitch );
  	}
  #endif // HOE_IRONSIGHTS
  
  	if ( g_fMaxViewModelLag == 0.0f )
  	{
  		origin = vOriginalOrigin;
  		angles = vOriginalAngles;
  	}
  
  	//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);
  }
  
  //-----------------------------------------------------------------------------
  // 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_SequenceNum( 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();
  
  	// 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 )
  	{
  		// Restart animation at frame 0
  		pViewModel->SetCycle( 0 );
  		pViewModel->m_flAnimTime = gpGlobals->curtime;
  	}
  }
  #endif
  
  
  LINK_ENTITY_TO_CLASS( viewmodel, CBaseViewModel );
  
  IMPLEMENT_NETWORKCLASS_ALIASED( BaseViewModel, DT_BaseViewModel )
  
  BEGIN_NETWORK_TABLE_NOBASE(CBaseViewModel, DT_BaseViewModel)
  #if !defined( CLIENT_DLL )
  	SendPropModelIndex(SENDINFO(m_nModelIndex)),
  	SendPropInt		(SENDINFO(m_nBody), 8),
  	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),		10, SPROP_UNSIGNED),
  	SendPropInt		(SENDINFO(m_nAnimationParity), 3, SPROP_UNSIGNED ),
  	SendPropEHandle (SENDINFO(m_hWeapon)),
  	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 ),
  
  #if !defined( INVASION_DLL ) && !defined( INVASION_CLIENT_DLL )
  	SendPropArray	(SendPropFloat(SENDINFO_ARRAY(m_flPoseParameter),	8, 0, 0.0f, 1.0f), m_flPoseParameter),
  #endif
  #else
  	RecvPropInt		(RECVINFO(m_nModelIndex)),
  	RecvPropInt		(RECVINFO(m_nSkin)),
  	RecvPropInt		(RECVINFO(m_nBody)),
  	RecvPropInt		(RECVINFO(m_nSequence), 0, RecvProxy_SequenceNum ),
  	RecvPropInt		(RECVINFO(m_nViewModelIndex)),
  	RecvPropFloat	(RECVINFO(m_flPlaybackRate)),
  	RecvPropInt		(RECVINFO(m_fEffects), 0, RecvProxy_EffectFlags ),
  	RecvPropInt		(RECVINFO(m_nAnimationParity)),
  	RecvPropEHandle (RECVINFO(m_hWeapon), RecvProxy_Weapon ),
  	RecvPropEHandle (RECVINFO(m_hOwner)),
  
  	RecvPropInt( RECVINFO( m_nNewSequenceParity )),
  	RecvPropInt( RECVINFO( m_nResetEventsParity )),
  	RecvPropInt( RECVINFO( m_nMuzzleFlashParity )),
  
  #if !defined( INVASION_DLL ) && !defined( INVASION_CLIENT_DLL )
  	RecvPropArray(RecvPropFloat(RECVINFO(m_flPoseParameter[0]) ), m_flPoseParameter ),
  #endif
  #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()
  
  void RecvProxy_SequenceNum( 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);
  	}
  }
  
  //-----------------------------------------------------------------------------
  // 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
  