Team Fortress 2 Source Code as on 22/4/2020
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.

571 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "tf_viewmodel.h"
  8. #include "tf_shareddefs.h"
  9. #include "tf_weapon_minigun.h"
  10. #include "tf_weapon_invis.h"
  11. #ifdef CLIENT_DLL
  12. #include "c_tf_player.h"
  13. // for spy material proxy
  14. #include "tf_proxyentity.h"
  15. #include "materialsystem/imaterial.h"
  16. #include "materialsystem/imaterialvar.h"
  17. #include "prediction.h"
  18. #endif
  19. #include "bone_setup.h" //temp
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. LINK_ENTITY_TO_CLASS( tf_viewmodel, CTFViewModel );
  23. IMPLEMENT_NETWORKCLASS_ALIASED( TFViewModel, DT_TFViewModel )
  24. BEGIN_NETWORK_TABLE( CTFViewModel, DT_TFViewModel )
  25. END_NETWORK_TABLE()
  26. //-----------------------------------------------------------------------------
  27. // Purpose:
  28. //-----------------------------------------------------------------------------
  29. #ifdef CLIENT_DLL
  30. CTFViewModel::CTFViewModel()
  31. : m_LagAnglesHistory("CPredictedViewModel::m_LagAnglesHistory")
  32. , m_bBodygroupsDirty( true )
  33. {
  34. m_vLagAngles.Init();
  35. m_LagAnglesHistory.Setup( &m_vLagAngles, 0 );
  36. m_vLoweredWeaponOffset.Init();
  37. }
  38. #else
  39. CTFViewModel::CTFViewModel()
  40. {
  41. }
  42. #endif
  43. //-----------------------------------------------------------------------------
  44. // Purpose:
  45. //-----------------------------------------------------------------------------
  46. CTFViewModel::~CTFViewModel()
  47. {
  48. }
  49. #ifdef CLIENT_DLL
  50. void DrawEconEntityAttachedModels( CBaseAnimating *pEnt, CEconEntity *pAttachedModelSource, const ClientModelRenderInfo_t *pInfo, int iMatchDisplayFlags );
  51. // TODO: Turning this off by setting interp 0.0 instead of 0.1 for now since we have a timing bug to resolve
  52. ConVar cl_wpn_sway_interp( "cl_wpn_sway_interp", "0.0", FCVAR_CLIENTDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  53. ConVar cl_wpn_sway_scale( "cl_wpn_sway_scale", "5.0", FCVAR_CLIENTDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  54. #endif
  55. //-----------------------------------------------------------------------------
  56. // Purpose: Adds head bob for off hand models
  57. //-----------------------------------------------------------------------------
  58. void CTFViewModel::AddViewModelBob( CBasePlayer *owner, Vector& eyePosition, QAngle& eyeAngles )
  59. {
  60. #ifdef CLIENT_DLL
  61. // if we are an off hand view model (index 1) and we have a model, add head bob.
  62. // (Head bob for main hand model added by the weapon itself.)
  63. if ( ViewModelIndex() == 1 && GetModel() != null )
  64. {
  65. CalcViewModelBobHelper( owner, &m_BobState );
  66. AddViewModelBobHelper( eyePosition, eyeAngles, &m_BobState );
  67. }
  68. #endif
  69. }
  70. void CTFViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles )
  71. {
  72. #ifdef CLIENT_DLL
  73. if ( prediction->InPrediction() )
  74. {
  75. return;
  76. }
  77. if ( cl_wpn_sway_interp.GetFloat() <= 0.0f )
  78. {
  79. return;
  80. }
  81. // Calculate our drift
  82. Vector forward, right, up;
  83. AngleVectors( angles, &forward, &right, &up );
  84. // Add an entry to the history.
  85. m_vLagAngles = angles;
  86. m_LagAnglesHistory.NoteChanged( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat(), false );
  87. // Interpolate back 100ms.
  88. m_LagAnglesHistory.Interpolate( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat() );
  89. // Now take the 100ms angle difference and figure out how far the forward vector moved in local space.
  90. Vector vLaggedForward;
  91. QAngle angleDiff = m_vLagAngles - angles;
  92. AngleVectors( -angleDiff, &vLaggedForward, 0, 0 );
  93. Vector vForwardDiff = Vector(1,0,0) - vLaggedForward;
  94. // Now offset the origin using that.
  95. vForwardDiff *= cl_wpn_sway_scale.GetFloat();
  96. origin += forward*vForwardDiff.x + right*-vForwardDiff.y + up*vForwardDiff.z;
  97. #endif
  98. }
  99. #ifdef CLIENT_DLL
  100. ConVar cl_gunlowerangle( "cl_gunlowerangle", "90", FCVAR_CLIENTDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  101. ConVar cl_gunlowerspeed( "cl_gunlowerspeed", "2", FCVAR_CLIENTDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  102. ConVar tf_use_min_viewmodels( "tf_use_min_viewmodels", "0", FCVAR_ARCHIVE, "Use minimized viewmodels." );
  103. 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'" );
  104. #endif
  105. void CTFViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles )
  106. {
  107. #if defined( CLIENT_DLL )
  108. Vector vecNewOrigin = eyePosition;
  109. QAngle vecNewAngles = eyeAngles;
  110. // Check for lowering the weapon
  111. C_TFPlayer *pPlayer = ToTFPlayer( owner );
  112. Assert( pPlayer );
  113. bool bLowered = pPlayer->IsWeaponLowered();
  114. QAngle vecLoweredAngles(0,0,0);
  115. m_vLoweredWeaponOffset.x = Approach( bLowered ? cl_gunlowerangle.GetFloat() : 0, m_vLoweredWeaponOffset.x, cl_gunlowerspeed.GetFloat() );
  116. vecLoweredAngles.x += m_vLoweredWeaponOffset.x;
  117. vecNewAngles += vecLoweredAngles;
  118. // we want to always enable this internally
  119. bool bShouldUseMinMode = tf_use_min_viewmodels.GetBool();
  120. // are we overriding vm offset?
  121. const char *pszVMOffsetOverride = tf_viewmodels_offset_override.GetString();
  122. bool bOverride = ( pszVMOffsetOverride && *pszVMOffsetOverride );
  123. bShouldUseMinMode |= bOverride;
  124. // alt view model
  125. CTFWeaponBase *pWeapon = assert_cast< CTFWeaponBase* >( GetWeapon() );
  126. if ( bShouldUseMinMode && pWeapon )
  127. {
  128. static float s_inspectInterp = 1.f;
  129. if ( pWeapon->GetInspectStage() != CTFWeaponBase::INSPECT_INVALID )
  130. {
  131. if ( pWeapon->GetInspectStage() == CTFWeaponBase::INSPECT_END )
  132. {
  133. // use the last second of the anim
  134. s_inspectInterp = Clamp( 1.f - ( pWeapon->GetInspectAnimTime() - gpGlobals->curtime ), 0.f, 1.f );
  135. }
  136. else
  137. {
  138. s_inspectInterp = Clamp( s_inspectInterp - gpGlobals->frametime, 0.f, 1.f );
  139. }
  140. }
  141. else
  142. {
  143. s_inspectInterp = Clamp( s_inspectInterp + gpGlobals->frametime, 0.f, 1.f );
  144. }
  145. Vector forward, right, up;
  146. AngleVectors( eyeAngles, &forward, &right, &up );
  147. Vector viewmodelOffset;
  148. if ( bOverride )
  149. {
  150. UTIL_StringToVector( viewmodelOffset.Base(), pszVMOffsetOverride );
  151. }
  152. else
  153. {
  154. viewmodelOffset = pWeapon->GetViewmodelOffset();
  155. }
  156. Vector vOffset = viewmodelOffset.x * forward + viewmodelOffset.y * right + viewmodelOffset.z * up;
  157. vOffset *= Gain( s_inspectInterp, 0.5f );
  158. vecNewOrigin += vOffset;
  159. }
  160. BaseClass::CalcViewModelView( owner, vecNewOrigin, vecNewAngles );
  161. #endif
  162. }
  163. #ifdef CLIENT_DLL
  164. //-----------------------------------------------------------------------------
  165. // Purpose: Don't render the weapon if its supposed to be lowered and we have
  166. // finished the lowering animation
  167. //-----------------------------------------------------------------------------
  168. int CTFViewModel::DrawModel( int flags )
  169. {
  170. // Check for lowering the weapon
  171. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  172. Assert( pPlayer );
  173. if ( m_bBodygroupsDirty )
  174. {
  175. m_nBody = 0;
  176. pPlayer->RecalcBodygroupsIfDirty();
  177. m_bBodygroupsDirty = false;
  178. }
  179. bool bLowered = pPlayer->IsWeaponLowered();
  180. if ( bLowered && fabs( m_vLoweredWeaponOffset.x - cl_gunlowerangle.GetFloat() ) < 0.1 )
  181. {
  182. // fully lowered, stop drawing
  183. return 1;
  184. }
  185. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  186. if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE &&
  187. pLocalPlayer->GetObserverTarget() && pLocalPlayer->GetObserverTarget()->IsPlayer() )
  188. {
  189. pPlayer = ToTFPlayer( pLocalPlayer->GetObserverTarget() );
  190. }
  191. if ( pPlayer != GetOwner() && pPlayer->GetViewModel() != GetMoveParent() )
  192. {
  193. return 0;
  194. }
  195. if ( pPlayer->IsAlive() == false )
  196. {
  197. return 0;
  198. }
  199. return BaseClass::DrawModel( flags );
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose:
  203. //-----------------------------------------------------------------------------
  204. bool CTFViewModel::OnPostInternalDrawModel( ClientModelRenderInfo_t *pInfo )
  205. {
  206. if ( !BaseClass::OnPostInternalDrawModel( pInfo ) )
  207. return false;
  208. CTFWeaponBase *pWeapon = ( CTFWeaponBase * )GetOwningWeapon();
  209. if ( pWeapon && !pWeapon->WantsToOverrideViewmodelAttachments() )
  210. {
  211. // only need to draw the attached models if the weapon doesn't want to override the viewmodel attachments
  212. // (used for Natascha's attachments, the Backburner, and the Kritzkrieg)
  213. DrawEconEntityAttachedModels( this, pWeapon, pInfo, kAttachedModelDisplayFlag_ViewModel );
  214. }
  215. return true;
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose:
  219. //-----------------------------------------------------------------------------
  220. void CTFViewModel::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
  221. {
  222. BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask );
  223. CTFWeaponBase *pWeapon = ( CTFWeaponBase * )GetOwningWeapon();
  224. if ( !pWeapon )
  225. return;
  226. if ( pWeapon->GetWeaponID() == TF_WEAPON_MINIGUN )
  227. {
  228. CTFMinigun *pMinigun = ( CTFMinigun * )pWeapon;
  229. int iBarrelBone = Studio_BoneIndexByName( hdr, "v_minigun_barrel" );
  230. // Assert( iBarrelBone != -1 );
  231. if ( iBarrelBone != -1 )
  232. {
  233. if ( hdr->boneFlags( iBarrelBone ) & boneMask )
  234. {
  235. RadianEuler a;
  236. QuaternionAngles( q[iBarrelBone], a );
  237. a.x = pMinigun->GetBarrelRotation();
  238. AngleQuaternion( a, q[iBarrelBone] );
  239. }
  240. }
  241. }
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose:
  245. //-----------------------------------------------------------------------------
  246. void CTFViewModel::ProcessMuzzleFlashEvent()
  247. {
  248. CTFWeaponBase *pWeapon = ( CTFWeaponBase * )GetOwningWeapon();
  249. if ( !pWeapon || C_BasePlayer::ShouldDrawLocalPlayer() )
  250. return;
  251. pWeapon->ProcessMuzzleFlashEvent();
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Used for spy invisiblity material
  255. //-----------------------------------------------------------------------------
  256. int CTFViewModel::GetSkin()
  257. {
  258. int nSkin = BaseClass::GetSkin();
  259. CTFWeaponBase *pWeapon = ( CTFWeaponBase * )GetOwningWeapon();
  260. if ( !pWeapon )
  261. return nSkin;
  262. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  263. if ( pPlayer )
  264. {
  265. // See if the item wants to override the skin
  266. int iItemSkin = -1;
  267. CEconItemView *pItem = pWeapon->GetAttributeContainer()->GetItem();
  268. if ( pItem->IsValid() )
  269. {
  270. iItemSkin = pItem->GetSkin( pPlayer->GetTeamNumber(), true );
  271. }
  272. if ( iItemSkin != -1 )
  273. {
  274. nSkin = iItemSkin;
  275. }
  276. else if ( pWeapon->GetTFWpnData().m_bHasTeamSkins_Viewmodel )
  277. {
  278. switch( pPlayer->GetTeamNumber() )
  279. {
  280. case TF_TEAM_RED:
  281. nSkin = 0;
  282. break;
  283. case TF_TEAM_BLUE:
  284. nSkin = 1;
  285. break;
  286. }
  287. }
  288. }
  289. return nSkin;
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Purpose:
  293. //-----------------------------------------------------------------------------
  294. const char* CTFViewModel::ModifyEventParticles( const char* token )
  295. {
  296. CTFWeaponBase *pWeapon = (CTFWeaponBase*) GetOwningWeapon();
  297. if ( pWeapon )
  298. {
  299. return pWeapon->ModifyEventParticles( token );
  300. }
  301. return BaseClass::ModifyEventParticles( token );
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose: Used for spy invisiblity material
  305. //-----------------------------------------------------------------------------
  306. class CViewModelInvisProxy : public CBaseInvisMaterialProxy
  307. {
  308. public:
  309. virtual void OnBind( C_BaseEntity *pC_BaseEntity );
  310. };
  311. #define TF_VM_MIN_INVIS 0.22
  312. #define TF_VM_MAX_INVIS 0.5
  313. //-----------------------------------------------------------------------------
  314. // Purpose:
  315. // Input :
  316. //-----------------------------------------------------------------------------
  317. void CViewModelInvisProxy::OnBind( C_BaseEntity *pEnt )
  318. {
  319. if ( !m_pPercentInvisible )
  320. return;
  321. bool bIsViewModel = false;
  322. CTFPlayer *pPlayer = NULL;
  323. C_BaseEntity *pMoveParent = pEnt->GetMoveParent();
  324. //Check if we have a move parent and if its a player
  325. if ( pMoveParent )
  326. {
  327. if ( pMoveParent->IsPlayer() )
  328. {
  329. pPlayer = ToTFPlayer( pMoveParent );
  330. }
  331. }
  332. //If its not a player then check for viewmodel.
  333. if ( pPlayer == NULL )
  334. {
  335. CBaseEntity *pEntParent = pMoveParent;
  336. if ( pEntParent == NULL )
  337. {
  338. pEntParent = pEnt;
  339. }
  340. CTFViewModel *pVM = dynamic_cast<CTFViewModel *>( pEntParent );
  341. if ( pVM )
  342. {
  343. pPlayer = ToTFPlayer( pVM->GetOwner() );
  344. bIsViewModel = true;
  345. }
  346. }
  347. // do we have a player from viewmodel?
  348. if ( !pPlayer )
  349. {
  350. m_pPercentInvisible->SetFloatValue( 0.0f );
  351. return;
  352. }
  353. float flPercentInvisible = pPlayer->GetPercentInvisible();
  354. float flWeaponInvis = flPercentInvisible;
  355. if ( bIsViewModel == true )
  356. {
  357. // remap from 0.22 to 0.5
  358. // but drop to 0.0 if we're not invis at all
  359. flWeaponInvis = ( flPercentInvisible < 0.01 ) ?
  360. 0.0 :
  361. RemapVal( flPercentInvisible, 0.0, 1.0, TF_VM_MIN_INVIS, TF_VM_MAX_INVIS );
  362. // Exaggerated blink effect on bump.
  363. if ( pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) )
  364. {
  365. flWeaponInvis = 0.3f;
  366. }
  367. // Also exaggerate the effect if we're using motion cloak and our well has run dry.
  368. CTFWeaponInvis *pWpn = (CTFWeaponInvis *) pPlayer->Weapon_OwnsThisID( TF_WEAPON_INVIS );
  369. if ( pWpn && pWpn->HasMotionCloak() && (pPlayer->m_Shared.GetSpyCloakMeter() <= 0.f ) )
  370. {
  371. flWeaponInvis = 0.3f;
  372. }
  373. }
  374. m_pPercentInvisible->SetFloatValue( flWeaponInvis );
  375. }
  376. EXPOSE_INTERFACE( CViewModelInvisProxy, IMaterialProxy, "vm_invis" IMATERIAL_PROXY_INTERFACE_VERSION );
  377. //-----------------------------------------------------------------------------
  378. // Purpose: Generic invis proxy that can handle invis for both weapons & viewmodels.
  379. // Makes the vm_invis & weapon_invis proxies obsolete, do not use them.
  380. //-----------------------------------------------------------------------------
  381. class CInvisProxy : public CBaseInvisMaterialProxy
  382. {
  383. public:
  384. virtual void OnBind( C_BaseEntity *pC_BaseEntity ) OVERRIDE;
  385. };
  386. //-----------------------------------------------------------------------------
  387. // Purpose:
  388. //-----------------------------------------------------------------------------
  389. void CInvisProxy::OnBind( C_BaseEntity *pC_BaseEntity )
  390. {
  391. if( !m_pPercentInvisible )
  392. return;
  393. C_BaseEntity *pEnt = pC_BaseEntity;
  394. CTFPlayer *pPlayer = NULL;
  395. // Check if we have a move parent and if it's a player
  396. C_BaseEntity *pMoveParent = pEnt->GetMoveParent();
  397. if ( pMoveParent && pMoveParent->IsPlayer() )
  398. {
  399. pPlayer = ToTFPlayer( pMoveParent );
  400. }
  401. // If it's not a player then check for viewmodel.
  402. if ( !pPlayer )
  403. {
  404. CBaseEntity *pEntParent = pMoveParent ? pMoveParent : pEnt;
  405. CTFViewModel *pVM = dynamic_cast<CTFViewModel *>( pEntParent );
  406. if ( pVM )
  407. {
  408. pPlayer = ToTFPlayer( pVM->GetOwner() );
  409. }
  410. }
  411. if ( !pPlayer )
  412. {
  413. if ( pEnt->IsPlayer() )
  414. {
  415. pPlayer = dynamic_cast<C_TFPlayer*>( pEnt );
  416. }
  417. else
  418. {
  419. IHasOwner *pOwnerInterface = dynamic_cast<IHasOwner*>( pEnt );
  420. if ( pOwnerInterface )
  421. {
  422. pPlayer = ToTFPlayer( pOwnerInterface->GetOwnerViaInterface() );
  423. }
  424. }
  425. }
  426. if ( !pPlayer )
  427. {
  428. m_pPercentInvisible->SetFloatValue( 0.0f );
  429. return;
  430. }
  431. // If we're the local player, use the old "vm_invis" code. Otherwise, use the "weapon_invis".
  432. if ( pPlayer->IsLocalPlayer() )
  433. {
  434. float flPercentInvisible = pPlayer->GetPercentInvisible();
  435. float flWeaponInvis = flPercentInvisible;
  436. // remap from 0.22 to 0.5
  437. // but drop to 0.0 if we're not invis at all
  438. flWeaponInvis = ( flPercentInvisible < 0.01 ) ?
  439. 0.0 :
  440. RemapVal( flPercentInvisible, 0.0, 1.0, TF_VM_MIN_INVIS, TF_VM_MAX_INVIS );
  441. // Exaggerated blink effect on bump.
  442. if ( pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) )
  443. {
  444. flWeaponInvis = 0.3f;
  445. }
  446. // Also exaggerate the effect if we're using motion cloak and our well has run dry.
  447. CTFWeaponInvis *pWpn = (CTFWeaponInvis *) pPlayer->Weapon_OwnsThisID( TF_WEAPON_INVIS );
  448. if ( pWpn && pWpn->HasMotionCloak() && (pPlayer->m_Shared.GetSpyCloakMeter() <= 0.f ) )
  449. {
  450. flWeaponInvis = 0.3f;
  451. }
  452. m_pPercentInvisible->SetFloatValue( flWeaponInvis );
  453. }
  454. else
  455. {
  456. m_pPercentInvisible->SetFloatValue( pPlayer->GetEffectiveInvisibilityLevel() );
  457. }
  458. }
  459. // Generic invis proxy that can handle invis for both weapons & viewmodels.
  460. // Makes the vm_invis & weapon_invis proxies obsolete, do not use them.
  461. EXPOSE_INTERFACE( CInvisProxy, IMaterialProxy, "invis" IMATERIAL_PROXY_INTERFACE_VERSION );
  462. #endif // CLIENT_DLL