diff options
Diffstat (limited to 'game/shared/tf/tf_viewmodel.cpp')
| -rw-r--r-- | game/shared/tf/tf_viewmodel.cpp | 571 |
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 |