summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_viewmodel.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_viewmodel.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/tf_viewmodel.cpp')
-rw-r--r--game/shared/tf/tf_viewmodel.cpp571
1 files changed, 571 insertions, 0 deletions
diff --git a/game/shared/tf/tf_viewmodel.cpp b/game/shared/tf/tf_viewmodel.cpp
new file mode 100644
index 0000000..c3d2231
--- /dev/null
+++ b/game/shared/tf/tf_viewmodel.cpp
@@ -0,0 +1,571 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+#include "cbase.h"
+#include "tf_viewmodel.h"
+#include "tf_shareddefs.h"
+#include "tf_weapon_minigun.h"
+#include "tf_weapon_invis.h"
+
+#ifdef CLIENT_DLL
+#include "c_tf_player.h"
+
+// for spy material proxy
+#include "tf_proxyentity.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "prediction.h"
+
+#endif
+
+#include "bone_setup.h" //temp
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+LINK_ENTITY_TO_CLASS( tf_viewmodel, CTFViewModel );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFViewModel, DT_TFViewModel )
+
+BEGIN_NETWORK_TABLE( CTFViewModel, DT_TFViewModel )
+END_NETWORK_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+CTFViewModel::CTFViewModel()
+ : m_LagAnglesHistory("CPredictedViewModel::m_LagAnglesHistory")
+ , m_bBodygroupsDirty( true )
+{
+ m_vLagAngles.Init();
+ m_LagAnglesHistory.Setup( &m_vLagAngles, 0 );
+ m_vLoweredWeaponOffset.Init();
+}
+#else
+CTFViewModel::CTFViewModel()
+{
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFViewModel::~CTFViewModel()
+{
+}
+
+#ifdef CLIENT_DLL
+void DrawEconEntityAttachedModels( CBaseAnimating *pEnt, CEconEntity *pAttachedModelSource, const ClientModelRenderInfo_t *pInfo, int iMatchDisplayFlags );
+
+// TODO: Turning this off by setting interp 0.0 instead of 0.1 for now since we have a timing bug to resolve
+ConVar cl_wpn_sway_interp( "cl_wpn_sway_interp", "0.0", FCVAR_CLIENTDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+ConVar cl_wpn_sway_scale( "cl_wpn_sway_scale", "5.0", FCVAR_CLIENTDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds head bob for off hand models
+//-----------------------------------------------------------------------------
+void CTFViewModel::AddViewModelBob( CBasePlayer *owner, Vector& eyePosition, QAngle& eyeAngles )
+{
+#ifdef CLIENT_DLL
+ // if we are an off hand view model (index 1) and we have a model, add head bob.
+ // (Head bob for main hand model added by the weapon itself.)
+ if ( ViewModelIndex() == 1 && GetModel() != null )
+ {
+ CalcViewModelBobHelper( owner, &m_BobState );
+ AddViewModelBobHelper( eyePosition, eyeAngles, &m_BobState );
+ }
+#endif
+}
+
+void CTFViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles )
+{
+#ifdef CLIENT_DLL
+ if ( prediction->InPrediction() )
+ {
+ return;
+ }
+
+ if ( cl_wpn_sway_interp.GetFloat() <= 0.0f )
+ {
+ return;
+ }
+
+ // Calculate our drift
+ Vector forward, right, up;
+ AngleVectors( angles, &forward, &right, &up );
+
+ // Add an entry to the history.
+ m_vLagAngles = angles;
+ m_LagAnglesHistory.NoteChanged( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat(), false );
+
+ // Interpolate back 100ms.
+ m_LagAnglesHistory.Interpolate( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat() );
+
+ // Now take the 100ms angle difference and figure out how far the forward vector moved in local space.
+ Vector vLaggedForward;
+ QAngle angleDiff = m_vLagAngles - angles;
+ AngleVectors( -angleDiff, &vLaggedForward, 0, 0 );
+ Vector vForwardDiff = Vector(1,0,0) - vLaggedForward;
+
+ // Now offset the origin using that.
+ vForwardDiff *= cl_wpn_sway_scale.GetFloat();
+ origin += forward*vForwardDiff.x + right*-vForwardDiff.y + up*vForwardDiff.z;
+#endif
+}
+
+#ifdef CLIENT_DLL
+ConVar cl_gunlowerangle( "cl_gunlowerangle", "90", FCVAR_CLIENTDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+ConVar cl_gunlowerspeed( "cl_gunlowerspeed", "2", FCVAR_CLIENTDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+
+ConVar tf_use_min_viewmodels( "tf_use_min_viewmodels", "0", FCVAR_ARCHIVE, "Use minimized viewmodels." );
+
+ConVar tf_viewmodels_offset_override( "tf_viewmodels_offset_override", "", FCVAR_CHEAT, "If set, this will override the position of all viewmodels. Usage 'x y z'" );
+#endif
+
+void CTFViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles )
+{
+#if defined( CLIENT_DLL )
+
+ Vector vecNewOrigin = eyePosition;
+ QAngle vecNewAngles = eyeAngles;
+
+ // Check for lowering the weapon
+ C_TFPlayer *pPlayer = ToTFPlayer( owner );
+
+ Assert( pPlayer );
+
+ bool bLowered = pPlayer->IsWeaponLowered();
+
+ QAngle vecLoweredAngles(0,0,0);
+
+ m_vLoweredWeaponOffset.x = Approach( bLowered ? cl_gunlowerangle.GetFloat() : 0, m_vLoweredWeaponOffset.x, cl_gunlowerspeed.GetFloat() );
+ vecLoweredAngles.x += m_vLoweredWeaponOffset.x;
+
+ vecNewAngles += vecLoweredAngles;
+
+ // we want to always enable this internally
+ bool bShouldUseMinMode = tf_use_min_viewmodels.GetBool();
+
+ // are we overriding vm offset?
+ const char *pszVMOffsetOverride = tf_viewmodels_offset_override.GetString();
+ bool bOverride = ( pszVMOffsetOverride && *pszVMOffsetOverride );
+ bShouldUseMinMode |= bOverride;
+
+ // alt view model
+ CTFWeaponBase *pWeapon = assert_cast< CTFWeaponBase* >( GetWeapon() );
+ if ( bShouldUseMinMode && pWeapon )
+ {
+ static float s_inspectInterp = 1.f;
+ if ( pWeapon->GetInspectStage() != CTFWeaponBase::INSPECT_INVALID )
+ {
+ if ( pWeapon->GetInspectStage() == CTFWeaponBase::INSPECT_END )
+ {
+ // use the last second of the anim
+ s_inspectInterp = Clamp( 1.f - ( pWeapon->GetInspectAnimTime() - gpGlobals->curtime ), 0.f, 1.f );
+ }
+ else
+ {
+ s_inspectInterp = Clamp( s_inspectInterp - gpGlobals->frametime, 0.f, 1.f );
+ }
+ }
+ else
+ {
+ s_inspectInterp = Clamp( s_inspectInterp + gpGlobals->frametime, 0.f, 1.f );
+ }
+
+ Vector forward, right, up;
+ AngleVectors( eyeAngles, &forward, &right, &up );
+
+ Vector viewmodelOffset;
+ if ( bOverride )
+ {
+ UTIL_StringToVector( viewmodelOffset.Base(), pszVMOffsetOverride );
+ }
+ else
+ {
+ viewmodelOffset = pWeapon->GetViewmodelOffset();
+ }
+ Vector vOffset = viewmodelOffset.x * forward + viewmodelOffset.y * right + viewmodelOffset.z * up;
+ vOffset *= Gain( s_inspectInterp, 0.5f );
+ vecNewOrigin += vOffset;
+ }
+
+
+
+ BaseClass::CalcViewModelView( owner, vecNewOrigin, vecNewAngles );
+
+#endif
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Don't render the weapon if its supposed to be lowered and we have
+// finished the lowering animation
+//-----------------------------------------------------------------------------
+int CTFViewModel::DrawModel( int flags )
+{
+ // Check for lowering the weapon
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+
+ Assert( pPlayer );
+
+ if ( m_bBodygroupsDirty )
+ {
+ m_nBody = 0;
+ pPlayer->RecalcBodygroupsIfDirty();
+ m_bBodygroupsDirty = false;
+ }
+
+ bool bLowered = pPlayer->IsWeaponLowered();
+
+ if ( bLowered && fabs( m_vLoweredWeaponOffset.x - cl_gunlowerangle.GetFloat() ) < 0.1 )
+ {
+ // fully lowered, stop drawing
+ return 1;
+ }
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE &&
+ pLocalPlayer->GetObserverTarget() && pLocalPlayer->GetObserverTarget()->IsPlayer() )
+ {
+ pPlayer = ToTFPlayer( pLocalPlayer->GetObserverTarget() );
+ }
+
+ if ( pPlayer != GetOwner() && pPlayer->GetViewModel() != GetMoveParent() )
+ {
+ return 0;
+ }
+
+ if ( pPlayer->IsAlive() == false )
+ {
+ return 0;
+ }
+
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFViewModel::OnPostInternalDrawModel( ClientModelRenderInfo_t *pInfo )
+{
+ if ( !BaseClass::OnPostInternalDrawModel( pInfo ) )
+ return false;
+
+ CTFWeaponBase *pWeapon = ( CTFWeaponBase * )GetOwningWeapon();
+
+ if ( pWeapon && !pWeapon->WantsToOverrideViewmodelAttachments() )
+ {
+ // only need to draw the attached models if the weapon doesn't want to override the viewmodel attachments
+ // (used for Natascha's attachments, the Backburner, and the Kritzkrieg)
+ DrawEconEntityAttachedModels( this, pWeapon, pInfo, kAttachedModelDisplayFlag_ViewModel );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFViewModel::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
+{
+ BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask );
+
+ CTFWeaponBase *pWeapon = ( CTFWeaponBase * )GetOwningWeapon();
+
+ if ( !pWeapon )
+ return;
+
+ if ( pWeapon->GetWeaponID() == TF_WEAPON_MINIGUN )
+ {
+ CTFMinigun *pMinigun = ( CTFMinigun * )pWeapon;
+
+ int iBarrelBone = Studio_BoneIndexByName( hdr, "v_minigun_barrel" );
+
+// Assert( iBarrelBone != -1 );
+
+ if ( iBarrelBone != -1 )
+ {
+ if ( hdr->boneFlags( iBarrelBone ) & boneMask )
+ {
+ RadianEuler a;
+ QuaternionAngles( q[iBarrelBone], a );
+
+ a.x = pMinigun->GetBarrelRotation();
+
+ AngleQuaternion( a, q[iBarrelBone] );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFViewModel::ProcessMuzzleFlashEvent()
+{
+ CTFWeaponBase *pWeapon = ( CTFWeaponBase * )GetOwningWeapon();
+
+ if ( !pWeapon || C_BasePlayer::ShouldDrawLocalPlayer() )
+ return;
+
+ pWeapon->ProcessMuzzleFlashEvent();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Used for spy invisiblity material
+//-----------------------------------------------------------------------------
+int CTFViewModel::GetSkin()
+{
+ int nSkin = BaseClass::GetSkin();
+
+ CTFWeaponBase *pWeapon = ( CTFWeaponBase * )GetOwningWeapon();
+
+ if ( !pWeapon )
+ return nSkin;
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // See if the item wants to override the skin
+ int iItemSkin = -1;
+ CEconItemView *pItem = pWeapon->GetAttributeContainer()->GetItem();
+ if ( pItem->IsValid() )
+ {
+ iItemSkin = pItem->GetSkin( pPlayer->GetTeamNumber(), true );
+ }
+
+ if ( iItemSkin != -1 )
+ {
+ nSkin = iItemSkin;
+ }
+ else if ( pWeapon->GetTFWpnData().m_bHasTeamSkins_Viewmodel )
+ {
+ switch( pPlayer->GetTeamNumber() )
+ {
+ case TF_TEAM_RED:
+ nSkin = 0;
+ break;
+ case TF_TEAM_BLUE:
+ nSkin = 1;
+ break;
+ }
+ }
+ }
+
+ return nSkin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char* CTFViewModel::ModifyEventParticles( const char* token )
+{
+ CTFWeaponBase *pWeapon = (CTFWeaponBase*) GetOwningWeapon();
+ if ( pWeapon )
+ {
+ return pWeapon->ModifyEventParticles( token );
+ }
+ return BaseClass::ModifyEventParticles( token );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used for spy invisiblity material
+//-----------------------------------------------------------------------------
+class CViewModelInvisProxy : public CBaseInvisMaterialProxy
+{
+public:
+ virtual void OnBind( C_BaseEntity *pC_BaseEntity );
+};
+
+#define TF_VM_MIN_INVIS 0.22
+#define TF_VM_MAX_INVIS 0.5
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+//-----------------------------------------------------------------------------
+void CViewModelInvisProxy::OnBind( C_BaseEntity *pEnt )
+{
+ if ( !m_pPercentInvisible )
+ return;
+
+ bool bIsViewModel = false;
+
+ CTFPlayer *pPlayer = NULL;
+ C_BaseEntity *pMoveParent = pEnt->GetMoveParent();
+
+ //Check if we have a move parent and if its a player
+ if ( pMoveParent )
+ {
+ if ( pMoveParent->IsPlayer() )
+ {
+ pPlayer = ToTFPlayer( pMoveParent );
+ }
+ }
+
+ //If its not a player then check for viewmodel.
+ if ( pPlayer == NULL )
+ {
+ CBaseEntity *pEntParent = pMoveParent;
+
+ if ( pEntParent == NULL )
+ {
+ pEntParent = pEnt;
+ }
+
+ CTFViewModel *pVM = dynamic_cast<CTFViewModel *>( pEntParent );
+
+ if ( pVM )
+ {
+ pPlayer = ToTFPlayer( pVM->GetOwner() );
+ bIsViewModel = true;
+ }
+ }
+
+ // do we have a player from viewmodel?
+ if ( !pPlayer )
+ {
+ m_pPercentInvisible->SetFloatValue( 0.0f );
+ return;
+ }
+
+ float flPercentInvisible = pPlayer->GetPercentInvisible();
+ float flWeaponInvis = flPercentInvisible;
+
+ if ( bIsViewModel == true )
+ {
+ // remap from 0.22 to 0.5
+ // but drop to 0.0 if we're not invis at all
+ flWeaponInvis = ( flPercentInvisible < 0.01 ) ?
+ 0.0 :
+ RemapVal( flPercentInvisible, 0.0, 1.0, TF_VM_MIN_INVIS, TF_VM_MAX_INVIS );
+
+ // Exaggerated blink effect on bump.
+ if ( pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) )
+ {
+ flWeaponInvis = 0.3f;
+ }
+
+ // Also exaggerate the effect if we're using motion cloak and our well has run dry.
+ CTFWeaponInvis *pWpn = (CTFWeaponInvis *) pPlayer->Weapon_OwnsThisID( TF_WEAPON_INVIS );
+ if ( pWpn && pWpn->HasMotionCloak() && (pPlayer->m_Shared.GetSpyCloakMeter() <= 0.f ) )
+ {
+ flWeaponInvis = 0.3f;
+ }
+ }
+
+ m_pPercentInvisible->SetFloatValue( flWeaponInvis );
+}
+
+EXPOSE_INTERFACE( CViewModelInvisProxy, IMaterialProxy, "vm_invis" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generic invis proxy that can handle invis for both weapons & viewmodels.
+// Makes the vm_invis & weapon_invis proxies obsolete, do not use them.
+//-----------------------------------------------------------------------------
+class CInvisProxy : public CBaseInvisMaterialProxy
+{
+public:
+ virtual void OnBind( C_BaseEntity *pC_BaseEntity ) OVERRIDE;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInvisProxy::OnBind( C_BaseEntity *pC_BaseEntity )
+{
+ if( !m_pPercentInvisible )
+ return;
+
+ C_BaseEntity *pEnt = pC_BaseEntity;
+
+ CTFPlayer *pPlayer = NULL;
+
+ // Check if we have a move parent and if it's a player
+ C_BaseEntity *pMoveParent = pEnt->GetMoveParent();
+ if ( pMoveParent && pMoveParent->IsPlayer() )
+ {
+ pPlayer = ToTFPlayer( pMoveParent );
+ }
+
+ // If it's not a player then check for viewmodel.
+ if ( !pPlayer )
+ {
+ CBaseEntity *pEntParent = pMoveParent ? pMoveParent : pEnt;
+
+ CTFViewModel *pVM = dynamic_cast<CTFViewModel *>( pEntParent );
+ if ( pVM )
+ {
+ pPlayer = ToTFPlayer( pVM->GetOwner() );
+ }
+ }
+
+ if ( !pPlayer )
+ {
+ if ( pEnt->IsPlayer() )
+ {
+ pPlayer = dynamic_cast<C_TFPlayer*>( pEnt );
+ }
+ else
+ {
+ IHasOwner *pOwnerInterface = dynamic_cast<IHasOwner*>( pEnt );
+ if ( pOwnerInterface )
+ {
+ pPlayer = ToTFPlayer( pOwnerInterface->GetOwnerViaInterface() );
+ }
+ }
+ }
+
+ if ( !pPlayer )
+ {
+ m_pPercentInvisible->SetFloatValue( 0.0f );
+ return;
+ }
+
+ // If we're the local player, use the old "vm_invis" code. Otherwise, use the "weapon_invis".
+ if ( pPlayer->IsLocalPlayer() )
+ {
+ float flPercentInvisible = pPlayer->GetPercentInvisible();
+ float flWeaponInvis = flPercentInvisible;
+
+ // remap from 0.22 to 0.5
+ // but drop to 0.0 if we're not invis at all
+ flWeaponInvis = ( flPercentInvisible < 0.01 ) ?
+ 0.0 :
+ RemapVal( flPercentInvisible, 0.0, 1.0, TF_VM_MIN_INVIS, TF_VM_MAX_INVIS );
+
+ // Exaggerated blink effect on bump.
+ if ( pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) )
+ {
+ flWeaponInvis = 0.3f;
+ }
+
+ // Also exaggerate the effect if we're using motion cloak and our well has run dry.
+ CTFWeaponInvis *pWpn = (CTFWeaponInvis *) pPlayer->Weapon_OwnsThisID( TF_WEAPON_INVIS );
+ if ( pWpn && pWpn->HasMotionCloak() && (pPlayer->m_Shared.GetSpyCloakMeter() <= 0.f ) )
+ {
+ flWeaponInvis = 0.3f;
+ }
+
+ m_pPercentInvisible->SetFloatValue( flWeaponInvis );
+ }
+ else
+ {
+ m_pPercentInvisible->SetFloatValue( pPlayer->GetEffectiveInvisibilityLevel() );
+ }
+}
+
+// Generic invis proxy that can handle invis for both weapons & viewmodels.
+// Makes the vm_invis & weapon_invis proxies obsolete, do not use them.
+EXPOSE_INTERFACE( CInvisProxy, IMaterialProxy, "invis" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+
+#endif // CLIENT_DLL