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.

6558 lines
211 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Weapons.
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "in_buttons.h"
  8. #include "takedamageinfo.h"
  9. #include "tf_weaponbase.h"
  10. #include "ammodef.h"
  11. #include "tf_gamerules.h"
  12. #include "eventlist.h"
  13. #include "econ_item_system.h"
  14. #include "activitylist.h"
  15. #include "gcsdk/gcmsg.h"
  16. #include "econ_gcmessages.h"
  17. #include "tf_gcmessages.h"
  18. #include "tf_weapon_wrench.h"
  19. #include "passtime_convars.h"
  20. // Server specific.
  21. #if !defined( CLIENT_DLL )
  22. #include "tf_player.h"
  23. #include "tf_weapon_medigun.h"
  24. #include "tf_gamestats.h"
  25. #include "tf_player.h"
  26. #include "tf_gamerules.h"
  27. #include "tf_gamestats.h"
  28. #include "ilagcompensationmanager.h"
  29. #include "collisionutils.h"
  30. #include "tf_team.h"
  31. #include "tf_obj.h"
  32. #include "tf_weapon_grenade_pipebomb.h"
  33. #include "particle_parse.h"
  34. #include "tf_weaponbase_grenadeproj.h"
  35. #include "tf_weapon_compound_bow.h"
  36. #include "tf_projectile_arrow.h"
  37. #include "tf_gamestats.h"
  38. #include "bot/tf_bot_manager.h"
  39. #include "bot/tf_bot.h"
  40. #include "halloween/halloween_base_boss.h"
  41. #include "tf_fx.h"
  42. #include "tf_gamestats.h"
  43. // Client specific.
  44. #else
  45. #include "c_tf_player.h"
  46. #include "tf_viewmodel.h"
  47. #include "hud_crosshair.h"
  48. #include "c_tf_playerresource.h"
  49. #include "clientmode_tf.h"
  50. #include "r_efx.h"
  51. #include "dlight.h"
  52. #include "effect_dispatch_data.h"
  53. #include "c_te_effect_dispatch.h"
  54. #include "toolframework_client.h"
  55. #include "hud_chat.h"
  56. #include "econ_notifications.h"
  57. #include "prediction.h"
  58. // for spy material proxy
  59. #include "tf_proxyentity.h"
  60. #include "materialsystem/imaterial.h"
  61. #include "materialsystem/imaterialvar.h"
  62. extern CTFWeaponInfo *GetTFWeaponInfo( int iWeapon );
  63. #endif
  64. #include "gc_clientsystem.h"
  65. // memdbgon must be the last include file in a .cpp file!!!
  66. #include "tier0/memdbgon.h"
  67. extern ConVar tf_useparticletracers;
  68. ConVar tf_scout_hype_pep_mod( "tf_scout_hype_pep_mod", "1.0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  69. ConVar tf_scout_hype_pep_max( "tf_scout_hype_pep_max", "99.0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  70. ConVar tf_scout_hype_pep_min_damage( "tf_scout_hype_pep_min_damage", "5.0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  71. ConVar tf_weapon_criticals_nopred( "tf_weapon_criticals_nopred", "1.0", FCVAR_REPLICATED | FCVAR_CHEAT );
  72. #ifdef _DEBUG
  73. ConVar tf_weapon_criticals_anticheat( "tf_weapon_criticals_anticheat", "1.0", FCVAR_REPLICATED );
  74. ConVar tf_weapon_criticals_debug( "tf_weapon_criticals_debug", "0.0", FCVAR_REPLICATED );
  75. extern ConVar tf_weapon_criticals_force_random;
  76. #endif // _DEBUG
  77. extern ConVar tf_weapon_criticals_bucket_cap;
  78. extern ConVar tf_weapon_criticals_bucket_bottom;
  79. #ifdef CLIENT_DLL
  80. extern ConVar cl_crosshair_file;
  81. extern ConVar cl_flipviewmodels;
  82. #endif
  83. #ifdef STAGING_ONLY
  84. ConVar tf_weapon_force_allow_inspect( "tf_weapon_force_allow_inspect", "0", FCVAR_REPLICATED | FCVAR_ARCHIVE, "Allow the inspect animation on any weapon" );
  85. #endif
  86. //=============================================================================
  87. //
  88. // Global functions.
  89. //
  90. //-----------------------------------------------------------------------------
  91. // Purpose:
  92. //-----------------------------------------------------------------------------
  93. bool IsAmmoType( int iAmmoType, const char *pAmmoName )
  94. {
  95. return GetAmmoDef()->Index( pAmmoName ) == iAmmoType;
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose:
  99. //-----------------------------------------------------------------------------
  100. void FindHullIntersection( const Vector &vecSrc, trace_t &tr, const Vector &mins, const Vector &maxs, CBaseEntity *pEntity )
  101. {
  102. int i, j, k;
  103. trace_t tmpTrace;
  104. Vector vecEnd;
  105. float distance = 1e6f;
  106. Vector minmaxs[2] = {mins, maxs};
  107. Vector vecHullEnd = tr.endpos;
  108. vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
  109. UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tmpTrace );
  110. if ( tmpTrace.fraction < 1.0 )
  111. {
  112. tr = tmpTrace;
  113. return;
  114. }
  115. for ( i = 0; i < 2; i++ )
  116. {
  117. for ( j = 0; j < 2; j++ )
  118. {
  119. for ( k = 0; k < 2; k++ )
  120. {
  121. vecEnd.x = vecHullEnd.x + minmaxs[i][0];
  122. vecEnd.y = vecHullEnd.y + minmaxs[j][1];
  123. vecEnd.z = vecHullEnd.z + minmaxs[k][2];
  124. UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tmpTrace );
  125. if ( tmpTrace.fraction < 1.0 )
  126. {
  127. float thisDistance = (tmpTrace.endpos - vecSrc).Length();
  128. if ( thisDistance < distance )
  129. {
  130. tr = tmpTrace;
  131. distance = thisDistance;
  132. }
  133. }
  134. }
  135. }
  136. }
  137. }
  138. //=============================================================================
  139. //
  140. // TFWeaponBase tables.
  141. //
  142. IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBase, DT_TFWeaponBase )
  143. #ifdef GAME_DLL
  144. void* SendProxy_SendActiveLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID );
  145. void* SendProxy_SendNonLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID );
  146. #endif
  147. //-----------------------------------------------------------------------------
  148. // Purpose: Only sent to the player holding it.
  149. //-----------------------------------------------------------------------------
  150. BEGIN_NETWORK_TABLE_NOBASE( CTFWeaponBase, DT_LocalTFWeaponData )
  151. #if defined( CLIENT_DLL )
  152. RecvPropTime( RECVINFO( m_flLastCritCheckTime ) ),
  153. RecvPropTime( RECVINFO( m_flReloadPriorNextFire ) ),
  154. RecvPropTime( RECVINFO( m_flLastFireTime ) ),
  155. RecvPropTime( RECVINFO( m_flEffectBarRegenTime ) ),
  156. RecvPropFloat( RECVINFO( m_flObservedCritChance ) ),
  157. #else
  158. SendPropTime( SENDINFO( m_flLastCritCheckTime ) ),
  159. SendPropTime( SENDINFO( m_flReloadPriorNextFire ) ),
  160. SendPropTime( SENDINFO( m_flLastFireTime ) ),
  161. SendPropTime( SENDINFO( m_flEffectBarRegenTime ) ),
  162. SendPropFloat( SENDINFO( m_flObservedCritChance ), 16, SPROP_NOSCALE, 0.0, 100.0 ),
  163. #endif
  164. END_NETWORK_TABLE()
  165. //-----------------------------------------------------------------------------
  166. // Purpose: Variables sent at low precision to non-holding observers.
  167. //-----------------------------------------------------------------------------
  168. BEGIN_NETWORK_TABLE_NOBASE( CTFWeaponBase, DT_TFWeaponDataNonLocal )
  169. END_NETWORK_TABLE()
  170. BEGIN_NETWORK_TABLE( CTFWeaponBase, DT_TFWeaponBase )
  171. // Client specific.
  172. #ifdef CLIENT_DLL
  173. RecvPropBool( RECVINFO( m_bLowered ) ),
  174. RecvPropInt( RECVINFO( m_iReloadMode ) ),
  175. RecvPropBool( RECVINFO( m_bResetParity ) ),
  176. RecvPropBool( RECVINFO( m_bReloadedThroughAnimEvent ) ),
  177. RecvPropBool( RECVINFO( m_bDisguiseWeapon ) ),
  178. RecvPropDataTable("LocalActiveTFWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalTFWeaponData)),
  179. RecvPropDataTable("NonLocalTFWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_TFWeaponDataNonLocal)),
  180. RecvPropFloat( RECVINFO(m_flEnergy) ),
  181. RecvPropEHandle( RECVINFO( m_hExtraWearable ) ),
  182. RecvPropEHandle( RECVINFO( m_hExtraWearableViewModel ) ),
  183. RecvPropBool( RECVINFO( m_bBeingRepurposedForTaunt ) ),
  184. RecvPropInt( RECVINFO( m_nKillComboClass ) ),
  185. RecvPropInt( RECVINFO( m_nKillComboCount ) ),
  186. RecvPropFloat( RECVINFO( m_flInspectAnimTime ) ),
  187. RecvPropInt( RECVINFO( m_nInspectStage ) ),
  188. #else
  189. // Server specific.
  190. SendPropBool( SENDINFO( m_bLowered ) ),
  191. SendPropBool( SENDINFO( m_bResetParity ) ),
  192. SendPropInt( SENDINFO( m_iReloadMode ), 4, SPROP_UNSIGNED ),
  193. SendPropBool( SENDINFO( m_bReloadedThroughAnimEvent ) ),
  194. SendPropBool( SENDINFO( m_bDisguiseWeapon ) ),
  195. SendPropDataTable("LocalActiveTFWeaponData", 0, &REFERENCE_SEND_TABLE(DT_LocalTFWeaponData), SendProxy_SendActiveLocalWeaponDataTable ),
  196. SendPropDataTable("NonLocalTFWeaponData", 0, &REFERENCE_SEND_TABLE(DT_TFWeaponDataNonLocal), SendProxy_SendNonLocalWeaponDataTable ),
  197. SendPropFloat( SENDINFO(m_flEnergy) ),
  198. SendPropEHandle( SENDINFO( m_hExtraWearable ) ),
  199. SendPropEHandle( SENDINFO( m_hExtraWearableViewModel ) ),
  200. SendPropBool( SENDINFO( m_bBeingRepurposedForTaunt ) ),
  201. SendPropInt( SENDINFO( m_nKillComboClass ), 4, SPROP_UNSIGNED ),
  202. SendPropInt( SENDINFO( m_nKillComboCount ), 2, SPROP_UNSIGNED ),
  203. SendPropFloat( SENDINFO( m_flInspectAnimTime ) ),
  204. SendPropInt( SENDINFO( m_nInspectStage ), -1, SPROP_VARINT ),
  205. #endif
  206. END_NETWORK_TABLE()
  207. BEGIN_PREDICTION_DATA( CTFWeaponBase )
  208. #ifdef CLIENT_DLL
  209. DEFINE_PRED_FIELD( m_bLowered, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  210. DEFINE_PRED_FIELD( m_iReloadMode, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  211. DEFINE_PRED_FIELD( m_bReloadedThroughAnimEvent, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  212. DEFINE_PRED_FIELD( m_bDisguiseWeapon, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  213. DEFINE_PRED_FIELD_TOL( m_flLastCritCheckTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
  214. DEFINE_PRED_FIELD_TOL( m_flReloadPriorNextFire, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
  215. DEFINE_PRED_FIELD_TOL( m_flLastFireTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
  216. DEFINE_PRED_FIELD( m_bCurrentAttackIsCrit, FIELD_BOOLEAN, 0 ),
  217. DEFINE_PRED_FIELD( m_iCurrentSeed, FIELD_INTEGER, 0 ),
  218. DEFINE_PRED_FIELD( m_flEnergy, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  219. DEFINE_PRED_FIELD_TOL( m_flEffectBarRegenTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
  220. DEFINE_PRED_FIELD( m_bBeingRepurposedForTaunt, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  221. #endif
  222. END_PREDICTION_DATA()
  223. LINK_ENTITY_TO_CLASS( tf_weapon_base, CTFWeaponBase );
  224. // Server specific.
  225. #if !defined( CLIENT_DLL )
  226. BEGIN_DATADESC( CTFWeaponBase )
  227. DEFINE_THINKFUNC( FallThink ),
  228. END_DATADESC()
  229. // Client specific
  230. #else
  231. ConVar cl_crosshaircolor( "cl_crosshaircolor", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  232. ConVar cl_dynamiccrosshair( "cl_dynamiccrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  233. ConVar cl_scalecrosshair( "cl_scalecrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  234. ConVar cl_crosshairalpha( "cl_crosshairalpha", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  235. int g_iScopeTextureID = 0;
  236. int g_iScopeDustTextureID = 0;
  237. #endif
  238. ConVar tf_weapon_criticals( "tf_weapon_criticals", "1", FCVAR_REPLICATED | FCVAR_NOTIFY, "Whether or not random crits are enabled" );
  239. //=============================================================================
  240. //
  241. // TFWeaponBase shared functions.
  242. //
  243. // -----------------------------------------------------------------------------
  244. // Purpose: Constructor.
  245. // -----------------------------------------------------------------------------
  246. CTFWeaponBase::CTFWeaponBase()
  247. {
  248. SetPredictionEligible( true );
  249. // Nothing collides with these, but they get touch calls.
  250. AddSolidFlags( FSOLID_TRIGGER );
  251. // Weapons can fire underwater.
  252. m_bFiresUnderwater = true;
  253. m_bAltFiresUnderwater = true;
  254. // Initialize the weapon modes.
  255. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  256. m_iReloadMode.Set( TF_RELOAD_START );
  257. m_iAltFireHint = 0;
  258. m_bInAttack = false;
  259. m_bInAttack2 = false;
  260. m_flCritTime = 0;
  261. m_flLastCritCheckTime = 0;
  262. m_flLastRapidFireCritCheckTime = 0;
  263. m_iLastCritCheckFrame = 0;
  264. m_flObservedCritChance = 0.f;
  265. m_flLastFireTime = 0;
  266. m_flEffectBarRegenTime = 0;
  267. m_bCurrentAttackIsCrit = false;
  268. m_bCurrentCritIsRandom = false;
  269. m_bCurrentAttackIsDuringDemoCharge = false;
  270. m_iCurrentSeed = -1;
  271. m_flReloadPriorNextFire = 0;
  272. m_flLastDeployTime = 0;
  273. m_bDisguiseWeapon = false;
  274. m_flEnergy = Energy_GetMaxEnergy();
  275. m_iAmmoToAdd = 0;
  276. #ifdef GAME_DLL
  277. m_iHitsInTime = 1;
  278. m_iFiredInTime = 1;
  279. m_iKillStreak = 0;
  280. m_flClipScale = 1.f;
  281. #endif // GAME_DLL
  282. #ifdef CLIENT_DLL
  283. m_iCachedModelIndex = 0;
  284. m_iEjectBrassAttachpoint = -2;
  285. m_bInitViewmodelOffset = false;
  286. m_vecViewmodelOffset = vec3_origin;
  287. #endif // CLIENT_DLL
  288. m_bBeingRepurposedForTaunt = false;
  289. m_nKillComboClass = 0;
  290. ClearKillComboCount();
  291. m_flLastPrimaryAttackTime = 0.f;
  292. m_eStrangeType = STRANGE_UNKNOWN;
  293. m_eStatTrakModuleType = MODULE_UNKNOWN;
  294. m_flInspectAnimTime = -1.f;
  295. m_nInspectStage = INSPECT_INVALID;
  296. }
  297. CTFWeaponBase::~CTFWeaponBase()
  298. {
  299. #ifdef CLIENT_DLL
  300. RemoveWorldmodelStatTrak();
  301. RemoveViewmodelStatTrak();
  302. #endif
  303. }
  304. // -----------------------------------------------------------------------------
  305. // Purpose:
  306. // -----------------------------------------------------------------------------
  307. void CTFWeaponBase::Spawn()
  308. {
  309. // Called manually, because CBaseCombatWeapon::Spawn doesn't call back.
  310. InitializeAttributes();
  311. m_bBeingRepurposedForTaunt = false;
  312. m_nKillComboClass = 0;
  313. ClearKillComboCount();
  314. // Base class spawn.
  315. BaseClass::Spawn();
  316. // Set this here to allow players to shoot dropped weapons.
  317. SetCollisionGroup( COLLISION_GROUP_WEAPON );
  318. // Get the weapon information.
  319. WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( GetClassname() );
  320. Assert( hWpnInfo != GetInvalidWeaponInfoHandle() );
  321. CTFWeaponInfo *pWeaponInfo = dynamic_cast<CTFWeaponInfo*>( GetFileWeaponInfoFromHandle( hWpnInfo ) );
  322. Assert( pWeaponInfo && "Failed to get CTFWeaponInfo in weapon spawn" );
  323. m_pWeaponInfo = pWeaponInfo;
  324. if ( GetPlayerOwner() )
  325. {
  326. ChangeTeam( GetPlayerOwner()->GetTeamNumber() );
  327. }
  328. #ifdef GAME_DLL
  329. // Move it up a little bit, otherwise it'll be at the guy's feet, and its sound origin
  330. // will be in the ground so its EmitSound calls won't do anything.
  331. Vector vecOrigin = GetAbsOrigin();
  332. SetAbsOrigin( Vector( vecOrigin.x, vecOrigin.y, vecOrigin.z + 5.0f ) );
  333. m_flRegenTime = 0.0f;
  334. m_hLastDrainVictim = NULL;
  335. m_lastDrainVictimTimer.Invalidate();
  336. #endif
  337. m_szTracerName[0] = '\0';
  338. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  339. if ( pItem )
  340. {
  341. CEconItemDefinition* pData = pItem->GetStaticData();
  342. if ( pData && pData->GetSubType() )
  343. {
  344. SetSubType( pData->GetSubType() );
  345. }
  346. }
  347. }
  348. //-----------------------------------------------------------------------------
  349. // Purpose:
  350. //-----------------------------------------------------------------------------
  351. void CTFWeaponBase::Activate( void )
  352. {
  353. BaseClass::Activate();
  354. // Reset our clip, in case we've had it modified
  355. GiveDefaultAmmo();
  356. }
  357. // -----------------------------------------------------------------------------
  358. // Purpose:
  359. // -----------------------------------------------------------------------------
  360. void CTFWeaponBase::FallInit( void )
  361. {
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose:
  365. // Input : -
  366. //-----------------------------------------------------------------------------
  367. void CTFWeaponBase::Precache()
  368. {
  369. BaseClass::Precache();
  370. if ( GetMuzzleFlashModel() )
  371. {
  372. PrecacheModel( GetMuzzleFlashModel() );
  373. }
  374. const CTFWeaponInfo *pTFInfo = &GetTFWpnData();
  375. if ( pTFInfo->m_szExplosionSound && pTFInfo->m_szExplosionSound[0] )
  376. {
  377. CBaseEntity::PrecacheScriptSound( pTFInfo->m_szExplosionSound );
  378. }
  379. if ( pTFInfo->m_szBrassModel[0] )
  380. {
  381. PrecacheModel( pTFInfo->m_szBrassModel );
  382. }
  383. if ( GetMuzzleFlashParticleEffect() )
  384. {
  385. PrecacheParticleSystem( GetMuzzleFlashParticleEffect() );
  386. }
  387. if ( pTFInfo->m_szExplosionEffect && pTFInfo->m_szExplosionEffect[0] )
  388. {
  389. PrecacheParticleSystem( pTFInfo->m_szExplosionEffect );
  390. }
  391. if ( pTFInfo->m_szExplosionPlayerEffect && pTFInfo->m_szExplosionPlayerEffect[0] )
  392. {
  393. PrecacheParticleSystem( pTFInfo->m_szExplosionPlayerEffect );
  394. }
  395. if ( pTFInfo->m_szExplosionWaterEffect && pTFInfo->m_szExplosionWaterEffect[0] )
  396. {
  397. PrecacheParticleSystem( pTFInfo->m_szExplosionWaterEffect );
  398. }
  399. const char *pszTracerEffect = pTFInfo->m_szTracerEffect;
  400. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  401. if ( pItem->IsValid() )
  402. {
  403. const char *pszItemTracerEffect = pItem->GetStaticData()->GetTracerEffect( GetTeamNumber() );
  404. if ( pszItemTracerEffect )
  405. {
  406. pszTracerEffect = pszItemTracerEffect;
  407. }
  408. }
  409. if ( pszTracerEffect && pszTracerEffect[0] )
  410. {
  411. char pTracerEffect[128];
  412. char pTracerEffectCrit[128];
  413. Q_snprintf( pTracerEffect, sizeof(pTracerEffect), "%s_red", pszTracerEffect );
  414. Q_snprintf( pTracerEffectCrit, sizeof(pTracerEffectCrit), "%s_red_crit", pszTracerEffect );
  415. PrecacheParticleSystem( pTracerEffect );
  416. PrecacheParticleSystem( pTracerEffectCrit );
  417. Q_snprintf( pTracerEffect, sizeof(pTracerEffect), "%s_blue", pszTracerEffect );
  418. Q_snprintf( pTracerEffectCrit, sizeof(pTracerEffectCrit), "%s_blue_crit", pszTracerEffect );
  419. PrecacheParticleSystem( pTracerEffect );
  420. PrecacheParticleSystem( pTracerEffectCrit );
  421. }
  422. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  423. {
  424. CBaseEntity::PrecacheScriptSound( "Weapon_Upgrade.DamageBonus1" );
  425. CBaseEntity::PrecacheScriptSound( "Weapon_Upgrade.DamageBonus2" );
  426. CBaseEntity::PrecacheScriptSound( "Weapon_Upgrade.DamageBonus3" );
  427. CBaseEntity::PrecacheScriptSound( "Weapon_Upgrade.DamageBonus4" );
  428. }
  429. PrecacheModel( "models/weapons/c_models/stattrack.mdl" );
  430. }
  431. // -----------------------------------------------------------------------------
  432. // Purpose:
  433. // -----------------------------------------------------------------------------
  434. const CTFWeaponInfo &CTFWeaponBase::GetTFWpnData() const
  435. {
  436. const FileWeaponInfo_t *pWeaponInfo = &GetWpnData();
  437. const CTFWeaponInfo *pTFInfo = dynamic_cast< const CTFWeaponInfo* >( pWeaponInfo );
  438. Assert( pTFInfo );
  439. return *pTFInfo;
  440. }
  441. // -----------------------------------------------------------------------------
  442. // Purpose:
  443. // -----------------------------------------------------------------------------
  444. int CTFWeaponBase::GetWeaponID( void ) const
  445. {
  446. Assert( false );
  447. return TF_WEAPON_NONE;
  448. }
  449. // -----------------------------------------------------------------------------
  450. // Purpose:
  451. // -----------------------------------------------------------------------------
  452. bool CTFWeaponBase::IsWeapon( int iWeapon ) const
  453. {
  454. return GetWeaponID() == iWeapon;
  455. }
  456. // -----------------------------------------------------------------------------
  457. // Purpose:
  458. // -----------------------------------------------------------------------------
  459. int CTFWeaponBase::GetMaxClip1( void ) const
  460. {
  461. if ( IsEnergyWeapon() )
  462. {
  463. return Energy_GetMaxEnergy();
  464. }
  465. // Handle the itemdef mod first...
  466. float flClip = BaseClass::GetMaxClip1();
  467. if ( flClip >= 0 )
  468. {
  469. CALL_ATTRIB_HOOK_INT( flClip, mult_clipsize );
  470. }
  471. // Now handle in-game sources, otherwise we get weird numbers on things like the FAN
  472. if ( flClip >= 0 )
  473. {
  474. #ifdef GAME_DLL
  475. flClip *= m_flClipScale;
  476. #endif
  477. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  478. if ( pPlayer )
  479. {
  480. // Blast weps (low clip counts)
  481. if ( IsBlastImpactWeapon() )
  482. {
  483. // MvM-specific upgrade attribute that handles rocket and grenade launchers
  484. int nProjectiles = 0;
  485. CALL_ATTRIB_HOOK_INT( nProjectiles, mult_clipsize_upgrade_atomic );
  486. // Clipsize increase on kills
  487. int iClipSizeOnKills = 0;
  488. CALL_ATTRIB_HOOK_INT( iClipSizeOnKills, clipsize_increase_on_kill );
  489. if ( iClipSizeOnKills )
  490. {
  491. nProjectiles += Min( pPlayer->m_Shared.GetDecapitations(), iClipSizeOnKills ); // max extra projectiles
  492. }
  493. if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_HASTE )
  494. {
  495. flClip *= 2;
  496. }
  497. if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
  498. {
  499. flClip *= 1.5f;
  500. }
  501. return ( flClip + nProjectiles );
  502. }
  503. else
  504. {
  505. CALL_ATTRIB_HOOK_INT( flClip, mult_clipsize_upgrade );
  506. if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_HASTE )
  507. {
  508. flClip *= 2;
  509. }
  510. }
  511. }
  512. }
  513. return flClip;
  514. }
  515. // -----------------------------------------------------------------------------
  516. // Purpose:
  517. // -----------------------------------------------------------------------------
  518. int CTFWeaponBase::GetDefaultClip1( void ) const
  519. {
  520. return GetMaxClip1();
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose:
  524. //-----------------------------------------------------------------------------
  525. bool CTFWeaponBase::UsesPrimaryAmmo( void )
  526. {
  527. if ( IsEnergyWeapon() )
  528. return false;
  529. else
  530. return CBaseCombatWeapon::UsesPrimaryAmmo();
  531. }
  532. // -----------------------------------------------------------------------------
  533. // Purpose:
  534. // -----------------------------------------------------------------------------
  535. const char *CTFWeaponBase::GetViewModel( int iViewModel ) const
  536. {
  537. if ( GetPlayerOwner() == NULL )
  538. return BaseClass::GetViewModel();
  539. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  540. int iHandModelIndex = 0;
  541. if ( pPlayer )
  542. {
  543. //CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, iHandModelIndex, override_hand_model_index ); // this is a cleaner way of doing it, but...
  544. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, iHandModelIndex, wrench_builds_minisentry ); // ...the gunslinger is the only thing that uses this attribute for now
  545. }
  546. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  547. if ( pPlayer && pItem->IsValid() && pItem->GetStaticData()->ShouldAttachToHands() )
  548. {
  549. // Should always be valid, because players without classes shouldn't be carrying items
  550. const char *pszHandModel = pPlayer->GetPlayerClass()->GetHandModelName( iHandModelIndex );
  551. Assert( pszHandModel );
  552. return pszHandModel;
  553. }
  554. return GetTFWpnData().szViewModel;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Purpose:
  558. //-----------------------------------------------------------------------------
  559. const char *CTFWeaponBase::GetWorldModel( void ) const
  560. {
  561. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  562. if ( pItem->IsValid() )
  563. {
  564. if ( pItem->GetWorldDisplayModel() )
  565. return pItem->GetWorldDisplayModel();
  566. int iClass = 0;
  567. int iTeam = 0;
  568. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  569. if ( pPlayer )
  570. {
  571. iClass = pPlayer->GetPlayerClass()->GetClassIndex();
  572. iTeam = pPlayer->GetTeamNumber();
  573. }
  574. return pItem->GetPlayerDisplayModel( iClass, iTeam );
  575. }
  576. return BaseClass::GetWorldModel();
  577. }
  578. bool CTFWeaponBase::IsInspectActivity( int iActivity )
  579. {
  580. return iActivity == GetInspectActivity( INSPECT_START ) || iActivity == GetInspectActivity( INSPECT_IDLE ) || iActivity == GetInspectActivity( INSPECT_END );
  581. }
  582. bool CTFWeaponBase::SendWeaponAnim( int iActivity )
  583. {
  584. CTFPlayer *pPlayer = GetTFPlayerOwner();
  585. if ( !pPlayer )
  586. return BaseClass::SendWeaponAnim( iActivity );
  587. if ( m_nInspectStage != INSPECT_INVALID )
  588. {
  589. if ( iActivity == GetActivity() )
  590. return true;
  591. // ignore idle anim while inspect anim is still playing
  592. if ( iActivity == ACT_VM_IDLE )
  593. {
  594. return true;
  595. }
  596. // allow other activity to override the inspect
  597. if ( !IsInspectActivity( iActivity ) )
  598. {
  599. m_flInspectAnimTime = -1.f;
  600. m_nInspectStage = INSPECT_INVALID;
  601. return BaseClass::SendWeaponAnim( iActivity );
  602. }
  603. // let the idle loop while the inspect key is pressed
  604. if ( pPlayer->IsInspecting() && m_nInspectStage == INSPECT_IDLE )
  605. return true;
  606. }
  607. return BaseClass::SendWeaponAnim( iActivity );
  608. }
  609. //-----------------------------------------------------------------------------
  610. // Purpose:
  611. //-----------------------------------------------------------------------------
  612. void CTFWeaponBase::Equip( CBaseCombatCharacter *pOwner )
  613. {
  614. SetOwner( pOwner );
  615. SetOwnerEntity( pOwner );
  616. ReapplyProvision();
  617. BaseClass::Equip( pOwner );
  618. // If we attach to our hands, we need to update our viewmodel when we get a new owner.
  619. UpdateHands();
  620. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  621. if ( pItem->IsValid() )
  622. {
  623. m_bFlipViewModel = pItem->GetStaticData()->ShouldFlipViewmodels();
  624. // Also precache the vision filtered display models here.
  625. if ( pItem->GetVisionFilteredDisplayModel() )
  626. {
  627. if ( modelinfo->GetModelIndex( pItem->GetVisionFilteredDisplayModel() ) == -1 )
  628. {
  629. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Vision Filtered Display Model Late Precache", __FUNCTION__ );
  630. CBaseEntity::PrecacheModel( pItem->GetVisionFilteredDisplayModel() );
  631. }
  632. }
  633. #ifdef GAME_DLL
  634. UpdateExtraWearables();
  635. CTFPlayer *pTFPlayer = ToTFPlayer( pOwner );
  636. if ( pTFPlayer )
  637. {
  638. pTFPlayer->ReapplyItemUpgrades(pItem);
  639. }
  640. #endif // GAME_DLL
  641. }
  642. }
  643. //-----------------------------------------------------------------------------
  644. // Purpose:
  645. //-----------------------------------------------------------------------------
  646. void CTFWeaponBase::UpdateHands( void )
  647. {
  648. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  649. if ( pItem->IsValid() && pItem->GetStaticData()->ShouldAttachToHands() )
  650. {
  651. m_iViewModelIndex = CBaseEntity::PrecacheModel( GetViewModel() );
  652. }
  653. }
  654. #ifdef GAME_DLL
  655. //-----------------------------------------------------------------------------
  656. // Purpose:
  657. //-----------------------------------------------------------------------------
  658. void CTFWeaponBase::UpdateExtraWearables()
  659. {
  660. CTFWearable *pOldWearable = m_hExtraWearable.Get();
  661. CTFWearable *pOldWearableVM = m_hExtraWearableViewModel.Get();
  662. if ( pOldWearable || pOldWearableVM )
  663. {
  664. CBaseCombatCharacter *pOwner = GetOwner();
  665. if ( pOwner )
  666. {
  667. if ( !( pOldWearable && pOldWearable->GetTeamNumber() != pOwner->GetTeamNumber() ) &&
  668. !( pOldWearableVM && pOldWearableVM->GetTeamNumber() != pOwner->GetTeamNumber() ) )
  669. {
  670. // No need to destroy and recreate them, because they already match the owner's team
  671. return;
  672. }
  673. }
  674. RemoveExtraWearables();
  675. }
  676. bool bHasViewModel = false;
  677. CEconItemView *pEconItemView = GetAttributeContainer()->GetItem();
  678. if ( pEconItemView->GetExtraWearableViewModel() )
  679. {
  680. CTFWearable* pExtraWearableItem = dynamic_cast<CTFWearable*>( CreateEntityByName( "tf_wearable_vm" ) );
  681. if ( pExtraWearableItem )
  682. {
  683. if ( modelinfo->GetModelIndex( pEconItemView->GetExtraWearableViewModel() ) == -1 ) {
  684. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - View Model Late Precache", __FUNCTION__ );
  685. // Precaching may be needed here, because we allow virtually everything to be loaded on demand now.
  686. pExtraWearableItem->PrecacheModel( pEconItemView->GetExtraWearableViewModel() );
  687. }
  688. pExtraWearableItem->AddSpawnFlags( SF_NORESPAWN );
  689. pExtraWearableItem->SetAlwaysAllow( true );
  690. DispatchSpawn( pExtraWearableItem );
  691. pExtraWearableItem->GiveTo( GetOwner() );
  692. pExtraWearableItem->SetModel( pEconItemView->GetExtraWearableViewModel() );
  693. bHasViewModel = true;
  694. pExtraWearableItem->SetWeaponAssociatedWith( this );
  695. ExtraWearableViewModelEquipped( pExtraWearableItem );
  696. }
  697. }
  698. if ( pEconItemView->GetExtraWearableModel() )
  699. {
  700. CTFWearable* pExtraWearableItem = dynamic_cast<CTFWearable*>( CreateEntityByName( "tf_wearable" ) );
  701. if ( pExtraWearableItem )
  702. {
  703. if ( modelinfo->GetModelIndex( pEconItemView->GetExtraWearableModel() ) == -1 ) {
  704. tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "%s - Model Late Precache", __FUNCTION__);
  705. // Precaching may be needed here, because we allow virtually everything to be loaded on demand now.
  706. pExtraWearableItem->PrecacheModel( pEconItemView->GetExtraWearableModel() );
  707. }
  708. pExtraWearableItem->AddSpawnFlags( SF_NORESPAWN );
  709. pExtraWearableItem->SetAlwaysAllow( true );
  710. DispatchSpawn( pExtraWearableItem );
  711. pExtraWearableItem->GiveTo( GetOwner() );
  712. pExtraWearableItem->SetModel( pEconItemView->GetExtraWearableModel() );
  713. if ( bHasViewModel )
  714. {
  715. // If it has a view model we need to have the weapon control visibility of this wearable
  716. pExtraWearableItem->SetWeaponAssociatedWith( this );
  717. }
  718. ExtraWearableEquipped( pExtraWearableItem );
  719. }
  720. }
  721. }
  722. //-----------------------------------------------------------------------------
  723. // Purpose:
  724. //-----------------------------------------------------------------------------
  725. void CTFWeaponBase::ExtraWearableEquipped( CTFWearable *pExtraWearableItem )
  726. {
  727. Assert( m_hExtraWearable == NULL );
  728. Assert( pExtraWearableItem != NULL );
  729. m_hExtraWearable.Set( pExtraWearableItem );
  730. CBasePlayer *pPlayerOwner = dynamic_cast<CBasePlayer *>( GetOwner() );
  731. if ( pPlayerOwner )
  732. {
  733. pExtraWearableItem->Equip( pPlayerOwner );
  734. }
  735. }
  736. void CTFWeaponBase::ExtraWearableViewModelEquipped( CTFWearable *pExtraWearableItem )
  737. {
  738. Assert( m_hExtraWearableViewModel == NULL );
  739. Assert( pExtraWearableItem != NULL );
  740. m_hExtraWearableViewModel.Set( pExtraWearableItem );
  741. CBasePlayer *pPlayerOwner = dynamic_cast<CBasePlayer *>( GetOwner() );
  742. if ( pPlayerOwner )
  743. {
  744. pExtraWearableItem->Equip( pPlayerOwner );
  745. }
  746. }
  747. #else
  748. void CTFWeaponBase::UpdateExtraWearablesVisibility()
  749. {
  750. if ( m_hExtraWearable.Get() )
  751. {
  752. m_hExtraWearable->ValidateModelIndex();
  753. m_hExtraWearable->UpdateVisibility();
  754. m_hExtraWearable->CreateShadow();
  755. }
  756. if ( m_hExtraWearableViewModel.Get() )
  757. {
  758. m_hExtraWearableViewModel->UpdateVisibility();
  759. }
  760. if ( m_viewmodelStatTrakAddon.Get() )
  761. {
  762. m_viewmodelStatTrakAddon->UpdateVisibility();
  763. }
  764. if ( m_worldmodelStatTrakAddon.Get() )
  765. {
  766. m_worldmodelStatTrakAddon->UpdateVisibility();
  767. m_worldmodelStatTrakAddon->CreateShadow();
  768. }
  769. }
  770. #endif // GAME_DLL
  771. //-----------------------------------------------------------------------------
  772. // Purpose:
  773. //-----------------------------------------------------------------------------
  774. void CTFWeaponBase::RemoveExtraWearables( void )
  775. {
  776. if ( m_hExtraWearable )
  777. {
  778. m_hExtraWearable->RemoveFrom( GetOwnerEntity() );
  779. m_hExtraWearable = NULL;
  780. }
  781. if ( m_hExtraWearableViewModel )
  782. {
  783. m_hExtraWearableViewModel->RemoveFrom( GetOwnerEntity() );
  784. m_hExtraWearableViewModel = NULL;
  785. }
  786. }
  787. //-----------------------------------------------------------------------------
  788. // Purpose:
  789. //-----------------------------------------------------------------------------
  790. void CTFWeaponBase::Drop( const Vector &vecVelocity )
  791. {
  792. #ifndef CLIENT_DLL
  793. if ( m_iAltFireHint )
  794. {
  795. CBasePlayer *pPlayer = GetPlayerOwner();
  796. if ( pPlayer )
  797. {
  798. pPlayer->StopHintTimer( m_iAltFireHint );
  799. }
  800. }
  801. #endif
  802. BaseClass::Drop( vecVelocity );
  803. ReapplyProvision();
  804. RemoveExtraWearables();
  805. #ifndef CLIENT_DLL
  806. // Never allow weapons to lie around on the ground
  807. UTIL_Remove( this );
  808. #endif
  809. }
  810. //-----------------------------------------------------------------------------
  811. // Purpose:
  812. //-----------------------------------------------------------------------------
  813. void CTFWeaponBase::UpdateOnRemove( void )
  814. {
  815. RemoveExtraWearables();
  816. BaseClass::UpdateOnRemove();
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Purpose:
  820. //-----------------------------------------------------------------------------
  821. bool CTFWeaponBase::CanHolster( void ) const
  822. {
  823. // Honorbound weapons are unable to be holstered until they have killed someone
  824. // since the last time they were brought out. We ignore this logic for the first
  825. // block of time after a weapon is taken out to allow quickswitching.
  826. // only check the first block of time logic if the weapon is active weapon
  827. // Can always holster if you have enough life cause we'll take that away
  828. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  829. if ( pPlayer && ( pPlayer->GetActiveWeapon() != this || gpGlobals->curtime >= pPlayer->m_Shared.m_flFirstPrimaryAttack ) )
  830. {
  831. if ( IsHonorBound() && pPlayer->m_Shared.m_iKillCountSinceLastDeploy == 0 && pPlayer->GetHealth() <= 50 )
  832. {
  833. #ifdef CLIENT_DLL
  834. pPlayer->EmitSound( "Player.DenyWeaponSelection" );
  835. #endif
  836. return false;
  837. }
  838. }
  839. return BaseClass::CanHolster();
  840. }
  841. //-----------------------------------------------------------------------------
  842. // Purpose:
  843. //-----------------------------------------------------------------------------
  844. bool CTFWeaponBase::Holster( CBaseCombatWeapon *pSwitchingTo )
  845. {
  846. #ifndef CLIENT_DLL
  847. CTFPlayer *pPlayer = GetTFPlayerOwner();
  848. if ( pPlayer && m_iAltFireHint )
  849. {
  850. pPlayer->StopHintTimer( m_iAltFireHint );
  851. }
  852. // Honorbound hurt yourself
  853. if ( pPlayer && ( pPlayer->GetActiveWeapon() != this || gpGlobals->curtime >= pPlayer->m_Shared.m_flFirstPrimaryAttack ) )
  854. {
  855. if ( IsHonorBound() && pPlayer->m_Shared.m_iKillCountSinceLastDeploy == 0 && pPlayer->GetHealth() > 0 && pPlayer->IsAlive() )
  856. {
  857. pPlayer->TakeDamage( CTakeDamageInfo( pPlayer, pPlayer, vec3_origin, pPlayer->WorldSpaceCenter(), 50.f, GetDamageType() | DMG_PREVENT_PHYSICS_FORCE ) );
  858. }
  859. }
  860. #endif
  861. m_iReloadMode.Set( TF_RELOAD_START );
  862. return BaseClass::Holster( pSwitchingTo );
  863. }
  864. //-----------------------------------------------------------------------------
  865. // Purpose:
  866. //-----------------------------------------------------------------------------
  867. bool CTFWeaponBase::Deploy( void )
  868. {
  869. #ifndef CLIENT_DLL
  870. if ( m_iAltFireHint )
  871. {
  872. CBasePlayer *pPlayer = GetPlayerOwner();
  873. if ( pPlayer )
  874. {
  875. pPlayer->StartHintTimer( m_iAltFireHint );
  876. }
  877. }
  878. #endif
  879. m_iReloadMode.Set( TF_RELOAD_START );
  880. float flOriginalPrimaryAttack = m_flNextPrimaryAttack;
  881. float flOriginalSecondaryAttack = m_flNextSecondaryAttack;
  882. bool bDeploy = BaseClass::Deploy();
  883. if ( bDeploy )
  884. {
  885. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  886. if ( !pPlayer )
  887. return false;
  888. float flWeaponSwitchTime = 0.5f;
  889. // Overrides the anim length for calculating ready time.
  890. float flDeployTimeMultiplier = 1.0f;
  891. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, flDeployTimeMultiplier, mult_deploy_time );
  892. CALL_ATTRIB_HOOK_FLOAT( flDeployTimeMultiplier, mult_single_wep_deploy_time );
  893. // don't apply mult_switch_from_wep_deploy_time attribute if the last weapon hasn't been deployed for more than 0.67 second to match to weapon script switch time
  894. // unless the player latched to a hook target, then allow switching right away
  895. CTFWeaponBase *pLastWeapon = dynamic_cast< CTFWeaponBase* >( pPlayer->GetLastWeapon() );
  896. if ( pPlayer->GetGrapplingHookTarget() != NULL || ( pLastWeapon && gpGlobals->curtime - pLastWeapon->m_flLastDeployTime > flWeaponSwitchTime ) )
  897. {
  898. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLastWeapon, flDeployTimeMultiplier, mult_switch_from_wep_deploy_time );
  899. }
  900. if ( pPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) )
  901. {
  902. CALL_ATTRIB_HOOK_FLOAT( flDeployTimeMultiplier, mult_rocketjump_deploy_time );
  903. }
  904. int iIsSword = 0;
  905. CALL_ATTRIB_HOOK_INT_ON_OTHER( pLastWeapon, iIsSword, is_a_sword );
  906. CALL_ATTRIB_HOOK_INT( iIsSword, is_a_sword );
  907. if ( iIsSword )
  908. {
  909. // swords deploy and holster 75% slower
  910. flDeployTimeMultiplier *= 1.75f;
  911. }
  912. #ifdef STAGING_ONLY
  913. if ( pPlayer->m_Shared.InCond( TF_COND_TRANQ_SPY_BOOST ) )
  914. {
  915. flDeployTimeMultiplier /= 2.0f;
  916. }
  917. #endif // STAGING_ONLY
  918. if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_AGILITY )
  919. {
  920. flDeployTimeMultiplier /= 5.0f;
  921. }
  922. int numHealers = pPlayer->m_Shared.GetNumHealers();
  923. if ( numHealers == 0 )
  924. {
  925. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, flDeployTimeMultiplier, mod_medic_healed_deploy_time );
  926. }
  927. flDeployTimeMultiplier = MAX( flDeployTimeMultiplier, 0.00001f );
  928. float flDeployTime = flWeaponSwitchTime * flDeployTimeMultiplier;
  929. float flPlaybackRate = Clamp( ( 1.f / flDeployTimeMultiplier ) * ( 0.67f / flWeaponSwitchTime ), -4.f, 12.f ); // clamp between the range that's defined in send table
  930. if ( pPlayer->GetViewModel(0) )
  931. {
  932. pPlayer->GetViewModel(0)->SetPlaybackRate( flPlaybackRate );
  933. }
  934. if ( pPlayer->GetViewModel(1) )
  935. {
  936. pPlayer->GetViewModel(1)->SetPlaybackRate( flPlaybackRate );
  937. }
  938. // Don't override primary attacks that are already further out than this. This prevents
  939. // people exploiting weapon switches to allow weapons to fire faster.
  940. m_flNextPrimaryAttack = MAX( flOriginalPrimaryAttack, gpGlobals->curtime + flDeployTime );
  941. m_flNextSecondaryAttack = MAX( flOriginalSecondaryAttack, m_flNextPrimaryAttack.Get() );
  942. pPlayer->SetNextAttack( m_flNextPrimaryAttack );
  943. m_flLastDeployTime = gpGlobals->curtime;
  944. #ifdef GAME_DLL
  945. // Reset our deploy-lifetime kill counter.
  946. pPlayer->m_Shared.m_iKillCountSinceLastDeploy = 0;
  947. pPlayer->m_Shared.m_flFirstPrimaryAttack = m_flNextPrimaryAttack;
  948. #endif // GAME_DLL
  949. }
  950. return bDeploy;
  951. }
  952. //-----------------------------------------------------------------------------
  953. // Purpose:
  954. //-----------------------------------------------------------------------------
  955. bool CTFWeaponBase::ForceWeaponSwitch() const
  956. {
  957. // allow knockout rune to force switch to melee
  958. CTFPlayer *pOwner = GetTFPlayerOwner();
  959. if ( pOwner && pOwner->m_Shared.GetCarryingRuneType() == RUNE_KNOCKOUT )
  960. {
  961. int iClass = pOwner->GetPlayerClass()->GetClassIndex();
  962. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  963. if ( pItem && pItem->GetStaticData()->GetLoadoutSlot( iClass ) == LOADOUT_POSITION_MELEE )
  964. {
  965. return true;
  966. }
  967. }
  968. // should force switch to this item
  969. int iForceWeaponSwitch = 0;
  970. CALL_ATTRIB_HOOK_INT( iForceWeaponSwitch, force_weapon_switch );
  971. return iForceWeaponSwitch != 0;
  972. }
  973. //-----------------------------------------------------------------------------
  974. // Purpose:
  975. //-----------------------------------------------------------------------------
  976. void CTFWeaponBase::Detach( void )
  977. {
  978. BaseClass::Detach();
  979. }
  980. //-----------------------------------------------------------------------------
  981. // Purpose:
  982. //-----------------------------------------------------------------------------
  983. void CTFWeaponBase::OnActiveStateChanged( int iOldState )
  984. {
  985. UpdateHiddenParentBodygroup( m_iState == WEAPON_IS_ACTIVE );
  986. // See if we need to reapply our provider based on our active state.
  987. int iProvideMode = 0;
  988. CALL_ATTRIB_HOOK_INT( iProvideMode, provide_on_active );
  989. if ( 1 == iProvideMode )
  990. {
  991. ReapplyProvision();
  992. }
  993. // Check for a speed mod change.
  994. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  995. if ( pPlayer )
  996. {
  997. pPlayer->TeamFortress_SetSpeed();
  998. }
  999. CEconItemView *pScriptItem = GetAttributeContainer()->GetItem();
  1000. if ( pScriptItem && pScriptItem->GetStaticData()->GetHideBodyGroupsDeployedOnly() )
  1001. {
  1002. #ifdef CLIENT_DLL
  1003. if ( pPlayer )
  1004. {
  1005. pPlayer->SetBodygroupsDirty();
  1006. }
  1007. #else
  1008. int iState = 0;
  1009. if ( WeaponState() == WEAPON_IS_ACTIVE )
  1010. {
  1011. iState = 1;
  1012. }
  1013. if ( pPlayer )
  1014. {
  1015. UpdateBodygroups( pPlayer, iState );
  1016. }
  1017. #endif
  1018. }
  1019. }
  1020. //-----------------------------------------------------------------------------
  1021. // Purpose:
  1022. //-----------------------------------------------------------------------------
  1023. bool CTFWeaponBase::VisibleInWeaponSelection( void )
  1024. {
  1025. if ( BaseClass::VisibleInWeaponSelection() == false )
  1026. {
  1027. return false;
  1028. }
  1029. if ( TFGameRules()->IsInTraining() )
  1030. {
  1031. ConVarRef training_can_select_weapon_primary ( "training_can_select_weapon_primary" );
  1032. ConVarRef training_can_select_weapon_secondary ( "training_can_select_weapon_secondary" );
  1033. ConVarRef training_can_select_weapon_melee ( "training_can_select_weapon_melee" );
  1034. ConVarRef training_can_select_weapon_building ( "training_can_select_weapon_building" );
  1035. ConVarRef training_can_select_weapon_pda ( "training_can_select_weapon_pda" );
  1036. ConVarRef training_can_select_weapon_item1 ( "training_can_select_weapon_item1" );
  1037. ConVarRef training_can_select_weapon_item2 ( "training_can_select_weapon_item2" );
  1038. bool bVisible = true;
  1039. switch ( GetTFWpnData().m_iWeaponType )
  1040. {
  1041. case TF_WPN_TYPE_PRIMARY: bVisible = training_can_select_weapon_primary.GetBool(); break;
  1042. case TF_WPN_TYPE_SECONDARY: bVisible = training_can_select_weapon_secondary.GetBool(); break;
  1043. case TF_WPN_TYPE_MELEE: bVisible = training_can_select_weapon_melee.GetBool(); break;
  1044. case TF_WPN_TYPE_BUILDING: bVisible = training_can_select_weapon_building.GetBool(); break;
  1045. case TF_WPN_TYPE_PDA: bVisible = training_can_select_weapon_pda.GetBool(); break;
  1046. case TF_WPN_TYPE_ITEM1: bVisible = training_can_select_weapon_item1.GetBool(); break;
  1047. case TF_WPN_TYPE_ITEM2: bVisible = training_can_select_weapon_item2.GetBool(); break;
  1048. } // switch
  1049. return bVisible;
  1050. }
  1051. return true;
  1052. }
  1053. //-----------------------------------------------------------------------------
  1054. // Purpose:
  1055. //-----------------------------------------------------------------------------
  1056. void CTFWeaponBase::UpdateHiddenParentBodygroup( bool bHide )
  1057. {
  1058. #ifdef GAME_DLL
  1059. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  1060. if (!pPlayer)
  1061. return;
  1062. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  1063. if ( pItem->IsValid() )
  1064. {
  1065. // Old style hidden bodygroups (weapon only):
  1066. int iHiddenBG = pItem->GetStaticData()->GetHiddenParentBodygroup( GetTeamNumber() );
  1067. if ( iHiddenBG != -1 )
  1068. {
  1069. pPlayer->SetBodygroup( iHiddenBG, bHide );
  1070. }
  1071. }
  1072. #endif
  1073. }
  1074. void CTFWeaponBase::Misfire( void )
  1075. {
  1076. CalcIsAttackCritical();
  1077. }
  1078. void CTFWeaponBase::FireFullClipAtOnce( void )
  1079. {
  1080. AssertMsg( 0, "weapon that has AutoFiresFullClipAllAtOnce should implement this function" );
  1081. }
  1082. //-----------------------------------------------------------------------------
  1083. // Purpose:
  1084. // Output :
  1085. //-----------------------------------------------------------------------------
  1086. void CTFWeaponBase::PrimaryAttack( void )
  1087. {
  1088. // Set the weapon mode.
  1089. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  1090. if ( !CanAttack() )
  1091. return;
  1092. BaseClass::PrimaryAttack();
  1093. if ( m_bReloadsSingly )
  1094. {
  1095. m_iReloadMode.Set( TF_RELOAD_START );
  1096. }
  1097. m_flLastPrimaryAttackTime = gpGlobals->curtime;
  1098. #ifdef STAGING_ONLY
  1099. // Remove Cond if I attack
  1100. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  1101. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_NO_COMBAT_SPEED_BOOST ) )
  1102. {
  1103. pPlayer->m_Shared.RemoveCond( TF_COND_NO_COMBAT_SPEED_BOOST );
  1104. }
  1105. #endif
  1106. }
  1107. //-----------------------------------------------------------------------------
  1108. // Purpose:
  1109. // Output : Returns true on success, false on failure.
  1110. //-----------------------------------------------------------------------------
  1111. void CTFWeaponBase::SecondaryAttack( void )
  1112. {
  1113. // Set the weapon mode.
  1114. m_iWeaponMode = TF_WEAPON_SECONDARY_MODE;
  1115. #ifdef STAGING_ONLY
  1116. // Remove Cond if I attack
  1117. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  1118. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_NO_COMBAT_SPEED_BOOST ) )
  1119. {
  1120. pPlayer->m_Shared.RemoveCond( TF_COND_NO_COMBAT_SPEED_BOOST );
  1121. }
  1122. #endif
  1123. // Don't hook secondary for now.
  1124. return;
  1125. }
  1126. //-----------------------------------------------------------------------------
  1127. // Purpose: Most calls use the prediction seed
  1128. //-----------------------------------------------------------------------------
  1129. void CTFWeaponBase::CalcIsAttackCritical( void)
  1130. {
  1131. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  1132. if ( !pPlayer )
  1133. return;
  1134. if ( gpGlobals->framecount == m_iLastCritCheckFrame )
  1135. return;
  1136. m_iLastCritCheckFrame = gpGlobals->framecount;
  1137. m_bCurrentCritIsRandom = false;
  1138. #if !defined( CLIENT_DLL )
  1139. // in training mode, the all bot team does not get crits
  1140. if ( TFGameRules()->IsInTraining() )
  1141. {
  1142. if ( pPlayer->IsBot() && TheTFBots().IsAllBotTeam( pPlayer->GetTeamNumber() ) )
  1143. {
  1144. // Support critboosted even in no crit mode
  1145. m_bCurrentAttackIsCrit = CalcIsAttackCriticalHelperNoCrits();
  1146. return;
  1147. }
  1148. }
  1149. if ( TFGameRules()->IsPVEModeActive() && TFGameRules()->IsPVEModeControlled( pPlayer ) )
  1150. {
  1151. // no crits for enemies in PvE
  1152. // Support critboosted even in no crit mode
  1153. m_bCurrentAttackIsCrit = CalcIsAttackCriticalHelperNoCrits();
  1154. return;
  1155. }
  1156. #endif
  1157. if ( (TFGameRules()->State_Get() == GR_STATE_TEAM_WIN) && (TFGameRules()->GetWinningTeam() == pPlayer->GetTeamNumber()) )
  1158. {
  1159. m_bCurrentAttackIsCrit = true;
  1160. }
  1161. else if ( !AreRandomCritsEnabled() )
  1162. {
  1163. // Support critboosted even in no crit mode
  1164. m_bCurrentAttackIsCrit = CalcIsAttackCriticalHelperNoCrits();
  1165. }
  1166. else
  1167. {
  1168. // call the weapon-specific helper method
  1169. m_bCurrentAttackIsCrit = CalcIsAttackCriticalHelper();
  1170. }
  1171. }
  1172. //-----------------------------------------------------------------------------
  1173. //
  1174. //-----------------------------------------------------------------------------
  1175. bool CTFWeaponBase::CalcIsAttackCriticalHelperNoCrits()
  1176. {
  1177. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  1178. if ( !pPlayer )
  1179. return false;
  1180. return pPlayer->m_Shared.IsCritBoosted();
  1181. }
  1182. //-----------------------------------------------------------------------------
  1183. // Purpose:
  1184. //-----------------------------------------------------------------------------
  1185. ETFDmgCustom CTFWeaponBase::GetPenetrateType() const
  1186. {
  1187. int iMode = 0;
  1188. CALL_ATTRIB_HOOK_INT( iMode, projectile_penetration );
  1189. #ifdef STAGING_ONLY
  1190. // Prototype hack
  1191. if ( !iMode )
  1192. {
  1193. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  1194. if ( pPlayer )
  1195. {
  1196. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iMode, ability_master_sniper );
  1197. }
  1198. }
  1199. #endif // STAGING_ONLY
  1200. return iMode >= 1
  1201. ? TF_DMG_CUSTOM_PENETRATE_ALL_PLAYERS
  1202. : TF_DMG_CUSTOM_NONE;
  1203. }
  1204. //-----------------------------------------------------------------------------
  1205. // Purpose: Weapon-specific helper method to calculate if attack is crit
  1206. //-----------------------------------------------------------------------------
  1207. bool CTFWeaponBase::CalcIsAttackCriticalHelper()
  1208. {
  1209. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  1210. if ( !pPlayer )
  1211. return false;
  1212. float flCritChance = 0.f;
  1213. float flPlayerCritMult = pPlayer->GetCritMult();
  1214. if ( !CanFireCriticalShot() )
  1215. return false;
  1216. // Crit boosted players fire all crits
  1217. if ( pPlayer->m_Shared.IsCritBoosted() )
  1218. return true;
  1219. // For rapid fire weapons, allow crits while period is active
  1220. bool bRapidFire = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_bUseRapidFireCrits;
  1221. if ( bRapidFire && m_flCritTime > gpGlobals->curtime )
  1222. return true;
  1223. // --- Random crits from this point on ---
  1224. // Monitor and enforce short-term random crit rate - via bucket
  1225. // Figure out how much to add/remove from token bucket
  1226. int nProjectilesPerShot = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_nBulletsPerShot;
  1227. if ( nProjectilesPerShot >= 1 )
  1228. {
  1229. CALL_ATTRIB_HOOK_FLOAT( nProjectilesPerShot, mult_bullets_per_shot );
  1230. }
  1231. else
  1232. {
  1233. nProjectilesPerShot = 1;
  1234. }
  1235. // Damage
  1236. float flDamage = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_nDamage;
  1237. CALL_ATTRIB_HOOK_FLOAT( flDamage, mult_dmg );
  1238. flDamage *= nProjectilesPerShot;
  1239. AddToCritBucket( flDamage );
  1240. bool bCrit = false;
  1241. m_bCurrentCritIsRandom = true;
  1242. int iRandom = 0;
  1243. if ( bRapidFire )
  1244. {
  1245. // only perform one crit check per second for rapid fire weapons
  1246. if ( tf_weapon_criticals_nopred.GetBool() )
  1247. {
  1248. if ( gpGlobals->curtime < m_flLastRapidFireCritCheckTime + 1.f )
  1249. return false;
  1250. m_flLastRapidFireCritCheckTime = gpGlobals->curtime;
  1251. }
  1252. else
  1253. {
  1254. if ( gpGlobals->curtime < m_flLastCritCheckTime + 1.f )
  1255. return false;
  1256. m_flLastCritCheckTime = gpGlobals->curtime;
  1257. }
  1258. // get the total crit chance (ratio of total shots fired we want to be crits)
  1259. float flTotalCritChance = clamp( TF_DAMAGE_CRIT_CHANCE_RAPID * flPlayerCritMult, 0.01f, 0.99f );
  1260. // get the fixed amount of time that we start firing crit shots for
  1261. float flCritDuration = TF_DAMAGE_CRIT_DURATION_RAPID;
  1262. // calculate the amount of time, on average, that we want to NOT fire crit shots for in order to achieve the total crit chance we want
  1263. float flNonCritDuration = ( flCritDuration / flTotalCritChance ) - flCritDuration;
  1264. // calculate the chance per second of non-crit fire that we should transition into critting such that on average we achieve the total crit chance we want
  1265. float flStartCritChance = 1 / flNonCritDuration;
  1266. CALL_ATTRIB_HOOK_FLOAT( flStartCritChance, mult_crit_chance );
  1267. // if base entity seed has changed since last calculation, reseed with new seed
  1268. int iMask = ( entindex() << 8 ) | ( pPlayer->entindex() );
  1269. int iSeed = CBaseEntity::GetPredictionRandomSeed() ^ iMask;
  1270. if ( iSeed != m_iCurrentSeed )
  1271. {
  1272. m_iCurrentSeed = iSeed;
  1273. RandomSeed( m_iCurrentSeed );
  1274. }
  1275. // see if we should start firing crit shots
  1276. iRandom = RandomInt( 0, WEAPON_RANDOM_RANGE-1 );
  1277. if ( iRandom < flStartCritChance * WEAPON_RANDOM_RANGE )
  1278. {
  1279. bCrit = true;
  1280. flCritChance = flStartCritChance;
  1281. }
  1282. }
  1283. else
  1284. {
  1285. // single-shot weapon, just use random pct per shot
  1286. flCritChance = TF_DAMAGE_CRIT_CHANCE * flPlayerCritMult;
  1287. CALL_ATTRIB_HOOK_FLOAT( flCritChance, mult_crit_chance );
  1288. // mess with the crit chance seed so it's not based solely on the prediction seed
  1289. int iMask = ( entindex() << 8 ) | ( pPlayer->entindex() );
  1290. int iSeed = CBaseEntity::GetPredictionRandomSeed() ^ iMask;
  1291. if ( iSeed != m_iCurrentSeed )
  1292. {
  1293. m_iCurrentSeed = iSeed;
  1294. RandomSeed( m_iCurrentSeed );
  1295. }
  1296. iRandom = RandomInt( 0, WEAPON_RANDOM_RANGE - 1 );
  1297. bCrit = ( iRandom < flCritChance * WEAPON_RANDOM_RANGE );
  1298. }
  1299. #ifdef _DEBUG
  1300. if ( tf_weapon_criticals_debug.GetBool() )
  1301. {
  1302. #ifdef GAME_DLL
  1303. DevMsg( "Roll (server): %i out of %f (crit: %d)\n", iRandom, ( flCritChance * WEAPON_RANDOM_RANGE ), bCrit );
  1304. #else
  1305. if ( prediction->IsFirstTimePredicted() )
  1306. {
  1307. DevMsg( "\tRoll (client): %i out of %f (crit: %d)\n", iRandom, ( flCritChance * WEAPON_RANDOM_RANGE ), bCrit );
  1308. }
  1309. #endif // GAME_DLL
  1310. }
  1311. // Force seed to always say yes
  1312. if ( tf_weapon_criticals_force_random.GetInt() )
  1313. {
  1314. bCrit = true;
  1315. }
  1316. #endif // _DEBUG
  1317. // Track each check
  1318. #ifdef GAME_DLL
  1319. m_nCritChecks++;
  1320. #else
  1321. if ( prediction->IsFirstTimePredicted() )
  1322. {
  1323. m_nCritChecks++;
  1324. }
  1325. #endif // GAME_DLL
  1326. // Seed says crit. Run it by the manager.
  1327. if ( bCrit )
  1328. {
  1329. bool bAntiCheat = true;
  1330. #ifdef _DEBUG
  1331. bAntiCheat = tf_weapon_criticals_anticheat.GetBool();
  1332. #endif // _DEBUG
  1333. // Monitor and enforce long-term random crit rate - via stats
  1334. if ( bAntiCheat )
  1335. {
  1336. if ( !CanFireRandomCriticalShot( flCritChance ) )
  1337. return false;
  1338. // Make sure rapid fire weapons can pay the cost of the entire period up-front
  1339. if ( bRapidFire )
  1340. {
  1341. flDamage *= TF_DAMAGE_CRIT_DURATION_RAPID / m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay;
  1342. // Never try to drain more than cap
  1343. int nBucketCap = tf_weapon_criticals_bucket_cap.GetInt();
  1344. if ( flDamage * TF_DAMAGE_CRIT_MULTIPLIER > nBucketCap )
  1345. flDamage = (float)nBucketCap / TF_DAMAGE_CRIT_MULTIPLIER;
  1346. }
  1347. bCrit = IsAllowedToWithdrawFromCritBucket( flDamage );
  1348. }
  1349. if ( bCrit && bRapidFire )
  1350. {
  1351. m_flCritTime = gpGlobals->curtime + TF_DAMAGE_CRIT_DURATION_RAPID;
  1352. }
  1353. }
  1354. return bCrit;
  1355. }
  1356. //-----------------------------------------------------------------------------
  1357. // Purpose: Return true if this weapon has some ammo
  1358. //-----------------------------------------------------------------------------
  1359. bool CTFWeaponBase::HasAmmo( void )
  1360. {
  1361. if ( IsEnergyWeapon() )
  1362. return true;
  1363. else
  1364. return BaseClass::HasAmmo();
  1365. }
  1366. //-----------------------------------------------------------------------------
  1367. // Purpose:
  1368. // Output : Returns true on success, false on failure.
  1369. //-----------------------------------------------------------------------------
  1370. bool CTFWeaponBase::Reload( void )
  1371. {
  1372. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  1373. if ( !pPlayer )
  1374. return false;
  1375. if ( IsEnergyWeapon() && !Energy_FullyCharged() )
  1376. {
  1377. return ReloadSingly();
  1378. }
  1379. // If we're not already reloading, check to see if we have ammo to reload and check to see if we are max ammo.
  1380. if ( m_iReloadMode == TF_RELOAD_START )
  1381. {
  1382. // If I don't have any spare ammo, I can't reload
  1383. if ( GetOwner()->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
  1384. return false;
  1385. if ( !CanOverload() && Clip1() >= GetMaxClip1() )
  1386. return false;
  1387. }
  1388. // Reload one object at a time.
  1389. if ( m_bReloadsSingly )
  1390. return ReloadSingly();
  1391. // Normal reload.
  1392. DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
  1393. return true;
  1394. }
  1395. //-----------------------------------------------------------------------------
  1396. // Purpose:
  1397. //-----------------------------------------------------------------------------
  1398. void CTFWeaponBase::AbortReload( void )
  1399. {
  1400. BaseClass::AbortReload();
  1401. m_iReloadMode.Set( TF_RELOAD_START );
  1402. // Make sure our reloading bodygroup is hidden (shells/grenades/etc)
  1403. int indexR = FindBodygroupByName( "reload" );
  1404. if ( indexR >= 0 )
  1405. {
  1406. SetBodygroup( indexR, 0 );
  1407. }
  1408. }
  1409. //-----------------------------------------------------------------------------
  1410. // Is the weapon reloading right now?
  1411. bool CTFWeaponBase::IsReloading() const
  1412. {
  1413. return m_iReloadMode != TF_RELOAD_START;
  1414. }
  1415. bool CTFWeaponBase::AutoFiresFullClip( void ) const
  1416. {
  1417. int nAutoFiresFullClip = 0;
  1418. CALL_ATTRIB_HOOK_INT( nAutoFiresFullClip, auto_fires_full_clip );
  1419. return ( nAutoFiresFullClip != 0 );
  1420. }
  1421. bool CTFWeaponBase::AutoFiresFullClipAllAtOnce( void ) const
  1422. {
  1423. int nAutoFiresFullClipAllAtOnce = 0;
  1424. CALL_ATTRIB_HOOK_INT( nAutoFiresFullClipAllAtOnce, auto_fires_full_clip_all_at_once );
  1425. return ( nAutoFiresFullClipAllAtOnce != 0 );
  1426. }
  1427. bool CTFWeaponBase::CanOverload( void ) const
  1428. {
  1429. int nCanOverload = 0;
  1430. CALL_ATTRIB_HOOK_INT( nCanOverload, can_overload );
  1431. return ( nCanOverload != 0 );
  1432. }
  1433. float CTFWeaponBase::ApplyFireDelay( float flDelay ) const
  1434. {
  1435. float flDelayMult = 1.0f;
  1436. CALL_ATTRIB_HOOK_FLOAT( flDelayMult, mult_postfiredelay );
  1437. float flComboBoost = 0.0f;
  1438. CALL_ATTRIB_HOOK_FLOAT( flComboBoost, kill_combo_fire_rate_boost );
  1439. flComboBoost *= GetKillComboCount();
  1440. flDelayMult -= flComboBoost;
  1441. // Haste Powerup Rune adds multiplier to fire delay time
  1442. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  1443. if ( pPlayer && pPlayer->m_Shared.GetCarryingRuneType() == RUNE_HASTE )
  1444. {
  1445. flDelayMult *= 0.5f;
  1446. }
  1447. else if ( pPlayer && ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_KING || pPlayer->m_Shared.InCond( TF_COND_KING_BUFFED ) ) )
  1448. {
  1449. flDelayMult *= 0.75f;
  1450. }
  1451. return flDelay * flDelayMult;
  1452. }
  1453. //-----------------------------------------------------------------------------
  1454. // Purpose:
  1455. // Output : Returns true on success, false on failure.
  1456. //-----------------------------------------------------------------------------
  1457. bool CTFWeaponBase::ReloadSingly( void )
  1458. {
  1459. // Don't reload.
  1460. if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  1461. return false;
  1462. // Get the current player.
  1463. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  1464. if ( !pPlayer )
  1465. return false;
  1466. // Anti Reload Cancelling Exploit (Beggers Bazooka)
  1467. // Force attack if we try to reload when we have ammo in the clip
  1468. if ( AutoFiresFullClip() && Clip1() > 0 && m_iReloadMode == TF_RELOAD_START )
  1469. {
  1470. PrimaryAttack();
  1471. m_bFiringWholeClip = true;
  1472. #ifdef CLIENT_DLL
  1473. pPlayer->SetFiredWeapon( true );
  1474. #endif
  1475. return false;
  1476. }
  1477. int nAutoFiresWhenFull = 0;
  1478. CALL_ATTRIB_HOOK_INT( nAutoFiresWhenFull, auto_fires_when_full );
  1479. if ( nAutoFiresWhenFull && ( Clip1() == GetMaxClip1() || pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) )
  1480. {
  1481. PrimaryAttack();
  1482. m_bFiringWholeClip = true;
  1483. #ifdef CLIENT_DLL
  1484. pPlayer->SetFiredWeapon( true );
  1485. #endif
  1486. return false;
  1487. }
  1488. // check to see if we're ready to reload
  1489. switch ( m_iReloadMode )
  1490. {
  1491. case TF_RELOAD_START:
  1492. {
  1493. // Play weapon and player animations.
  1494. if ( SendWeaponAnim( ACT_RELOAD_START ) )
  1495. {
  1496. SetReloadTimer( SequenceDuration() );
  1497. }
  1498. else
  1499. {
  1500. // Update the reload timers with script values.
  1501. UpdateReloadTimers( true );
  1502. }
  1503. // Next reload the shells.
  1504. m_iReloadMode.Set( TF_RELOADING );
  1505. m_iReloadStartClipAmount = Clip1();
  1506. return true;
  1507. }
  1508. case TF_RELOADING:
  1509. {
  1510. // Did we finish the reload start? Now we can reload a rocket.
  1511. if ( m_flTimeWeaponIdle > gpGlobals->curtime )
  1512. return false;
  1513. // Play weapon reload animations and sound.
  1514. if ( Clip1() == m_iReloadStartClipAmount )
  1515. {
  1516. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD );
  1517. }
  1518. else
  1519. {
  1520. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_LOOP );
  1521. }
  1522. m_bReloadedThroughAnimEvent = false;
  1523. if ( SendWeaponAnim( ACT_VM_RELOAD ) )
  1524. {
  1525. if ( GetWeaponID() == TF_WEAPON_GRENADELAUNCHER )
  1526. {
  1527. SetReloadTimer( GetTFWpnData().m_WeaponData[TF_WEAPON_PRIMARY_MODE].m_flTimeReload );
  1528. }
  1529. else
  1530. {
  1531. SetReloadTimer( SequenceDuration() );
  1532. }
  1533. }
  1534. else
  1535. {
  1536. // Update the reload timers.
  1537. UpdateReloadTimers( false );
  1538. }
  1539. // Play reload
  1540. #ifdef CLIENT_DLL
  1541. if ( ShouldPlayClientReloadSound() )
  1542. WeaponSound( RELOAD );
  1543. #else
  1544. WeaponSound( RELOAD );
  1545. #endif
  1546. // Next continue to reload shells?
  1547. m_iReloadMode.Set( TF_RELOADING_CONTINUE );
  1548. return true;
  1549. }
  1550. case TF_RELOADING_CONTINUE:
  1551. {
  1552. // Did we finish the reload start? Now we can finish reloading the rocket.
  1553. if ( m_flTimeWeaponIdle > gpGlobals->curtime )
  1554. return false;
  1555. IncrementAmmo();
  1556. if ( IsEnergyWeapon() )
  1557. {
  1558. if ( Energy_FullyCharged() )
  1559. {
  1560. m_iReloadMode.Set( TF_RELOAD_FINISH );
  1561. }
  1562. else
  1563. {
  1564. m_iReloadMode.Set( TF_RELOADING );
  1565. }
  1566. }
  1567. else
  1568. {
  1569. if ( ( !CanOverload() && ( Clip1() == GetMaxClip1() || pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) ) )
  1570. {
  1571. m_iReloadMode.Set( TF_RELOAD_FINISH );
  1572. }
  1573. else
  1574. {
  1575. m_iReloadMode.Set( TF_RELOADING );
  1576. }
  1577. }
  1578. return true;
  1579. }
  1580. case TF_RELOAD_FINISH:
  1581. default:
  1582. {
  1583. if ( SendWeaponAnim( ACT_RELOAD_FINISH ) )
  1584. {
  1585. // We're done, allow primary attack as soon as we like unless we're an energy weapon.
  1586. // if ( IsEnergyWeapon() )
  1587. // {
  1588. // SetReloadTimer( SequenceDuration() );
  1589. // }
  1590. }
  1591. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_END );
  1592. m_iReloadMode.Set( TF_RELOAD_START );
  1593. return true;
  1594. }
  1595. }
  1596. }
  1597. void CTFWeaponBase::IncrementAmmo( void )
  1598. {
  1599. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1600. // If we have ammo, remove ammo and add it to clip
  1601. if ( !m_bReloadedThroughAnimEvent )
  1602. {
  1603. if ( IsEnergyWeapon() )
  1604. {
  1605. Energy_Recharge();
  1606. }
  1607. else if ( !CheckReloadMisfire() )
  1608. {
  1609. if ( pPlayer && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  1610. {
  1611. m_iClip1 = MIN( ( m_iClip1 + 1 ), GetMaxClip1() );
  1612. pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
  1613. }
  1614. }
  1615. }
  1616. }
  1617. //-----------------------------------------------------------------------------
  1618. // Purpose:
  1619. // Input : *pEvent -
  1620. // *pOperator -
  1621. //-----------------------------------------------------------------------------
  1622. void CTFWeaponBase::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  1623. {
  1624. if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) /*&& (pEvent->type & AE_TYPE_SERVER)*/ )
  1625. {
  1626. if ( pEvent->event == AE_WPN_INCREMENTAMMO )
  1627. {
  1628. IncrementAmmo();
  1629. m_bReloadedThroughAnimEvent = true;
  1630. return;
  1631. }
  1632. }
  1633. }
  1634. //-----------------------------------------------------------------------------
  1635. // Purpose:
  1636. //-----------------------------------------------------------------------------
  1637. const char *CTFWeaponBase::GetInventoryModel( void )
  1638. {
  1639. // Return the world model when displaying this item in the inventory
  1640. const model_t *pWorldModel = modelinfo->GetModel( m_iWorldModelIndex );
  1641. if ( pWorldModel )
  1642. return modelinfo->GetModelName( pWorldModel );
  1643. return NULL;//BaseClass::GetInventoryModel();
  1644. }
  1645. //-----------------------------------------------------------------------------
  1646. // Purpose:
  1647. //-----------------------------------------------------------------------------
  1648. bool CTFWeaponBase::NeedsReloadForAmmo1( int iClipSize1 ) const
  1649. {
  1650. CBaseCombatCharacter *pOwner = GetOwner();
  1651. if ( pOwner )
  1652. {
  1653. // If you don't have clips, then don't try to reload them.
  1654. if ( UsesClipsForAmmo1() )
  1655. {
  1656. // need to reload primary clip?
  1657. int primary = MIN( iClipSize1 - m_iClip1, pOwner->GetAmmoCount( m_iPrimaryAmmoType ) );
  1658. if ( primary != 0 )
  1659. return true;
  1660. }
  1661. }
  1662. return false;
  1663. }
  1664. //-----------------------------------------------------------------------------
  1665. // Purpose:
  1666. //-----------------------------------------------------------------------------
  1667. bool CTFWeaponBase::NeedsReloadForAmmo2( int iClipSize2 ) const
  1668. {
  1669. CBaseCombatCharacter *pOwner = GetOwner();
  1670. if ( pOwner )
  1671. {
  1672. if ( UsesClipsForAmmo2() )
  1673. {
  1674. // need to reload secondary clip?
  1675. int secondary = MIN( iClipSize2 - m_iClip2, pOwner->GetAmmoCount( m_iSecondaryAmmoType ) );
  1676. if ( secondary != 0 )
  1677. return true;
  1678. }
  1679. }
  1680. return false;
  1681. }
  1682. // -----------------------------------------------------------------------------
  1683. // Purpose:
  1684. // -----------------------------------------------------------------------------
  1685. bool CTFWeaponBase::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
  1686. {
  1687. // The the owning local player.
  1688. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1689. if ( !pPlayer )
  1690. return false;
  1691. // Setup and check for reload.
  1692. bool bReloadPrimary = NeedsReloadForAmmo1( iClipSize1 );
  1693. bool bReloadSecondary = NeedsReloadForAmmo2( iClipSize2 );
  1694. // We didn't reload.
  1695. if ( !( bReloadPrimary || bReloadSecondary ) )
  1696. return false;
  1697. // Play reload
  1698. #ifdef CLIENT_DLL
  1699. if ( ShouldPlayClientReloadSound() )
  1700. WeaponSound( RELOAD );
  1701. #else
  1702. WeaponSound( RELOAD );
  1703. #endif
  1704. // Play the player's reload animation
  1705. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD );
  1706. float flReloadTime;
  1707. // First, see if we have a reload animation
  1708. if ( SendWeaponAnim( iActivity ) )
  1709. {
  1710. // We consider the reload finished 0.2 sec before the anim is, so that players don't keep accidentally aborting their reloads
  1711. flReloadTime = SequenceDuration() - 0.2;
  1712. }
  1713. else
  1714. {
  1715. // No reload animation. Use the script time.
  1716. flReloadTime = GetTFWpnData().m_WeaponData[TF_WEAPON_PRIMARY_MODE].m_flTimeReload;
  1717. if ( bReloadSecondary )
  1718. {
  1719. flReloadTime = GetTFWpnData().m_WeaponData[TF_WEAPON_SECONDARY_MODE].m_flTimeReload;
  1720. }
  1721. }
  1722. SetReloadTimer( flReloadTime );
  1723. m_bInReload = true;
  1724. return true;
  1725. }
  1726. //-----------------------------------------------------------------------------
  1727. // Purpose:
  1728. //-----------------------------------------------------------------------------
  1729. void CTFWeaponBase::UpdateReloadTimers( bool bStart )
  1730. {
  1731. // Starting a reload?
  1732. if ( bStart )
  1733. {
  1734. // Get the reload start time.
  1735. SetReloadTimer( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeReloadStart );
  1736. }
  1737. // In reload.
  1738. else
  1739. {
  1740. SetReloadTimer( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeReload );
  1741. }
  1742. }
  1743. //-----------------------------------------------------------------------------
  1744. // Purpose:
  1745. //-----------------------------------------------------------------------------
  1746. void CTFWeaponBase::SetReloadTimer( float flReloadTime )
  1747. {
  1748. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1749. if ( !pPlayer )
  1750. return;
  1751. float flBaseReloadTime = flReloadTime;
  1752. CALL_ATTRIB_HOOK_FLOAT( flReloadTime, mult_reload_time );
  1753. CALL_ATTRIB_HOOK_FLOAT( flReloadTime, mult_reload_time_hidden );
  1754. CALL_ATTRIB_HOOK_FLOAT( flReloadTime, fast_reload );
  1755. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, flReloadTime, hwn_mult_reload_time );
  1756. //int iPanicAttack = 0;
  1757. //CALL_ATTRIB_HOOK_INT( iPanicAttack, panic_attack );
  1758. //if ( iPanicAttack )
  1759. //{
  1760. // if ( pPlayer->GetHealth() < pPlayer->GetMaxHealth() * 0.33f )
  1761. // {
  1762. // flReloadTime *= 0.3f;
  1763. // }
  1764. // else if ( pPlayer->GetHealth() < pPlayer->GetMaxHealth() * 0.66f )
  1765. // {
  1766. // flReloadTime *= 0.6f;
  1767. // }
  1768. //}
  1769. // Haste Powerup Rune adds multiplier to reload time
  1770. if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_HASTE )
  1771. {
  1772. flReloadTime *= 0.5f;
  1773. }
  1774. else if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_KING || pPlayer->m_Shared.InCond( TF_COND_KING_BUFFED ) )
  1775. {
  1776. flReloadTime *= 0.75f;
  1777. }
  1778. int numHealers = pPlayer->m_Shared.GetNumHealers();
  1779. if ( numHealers == 1 )
  1780. {
  1781. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, flReloadTime, mult_reload_time_while_healed );
  1782. }
  1783. flReloadTime = MAX( flReloadTime, 0.00001f );
  1784. if ( pPlayer->GetViewModel(0) )
  1785. {
  1786. pPlayer->GetViewModel(0)->SetPlaybackRate( flBaseReloadTime / flReloadTime );
  1787. }
  1788. if ( pPlayer->GetViewModel(1) )
  1789. {
  1790. pPlayer->GetViewModel(1)->SetPlaybackRate( flBaseReloadTime / flReloadTime );
  1791. }
  1792. m_flReloadPriorNextFire = m_flNextPrimaryAttack;
  1793. float flTime = gpGlobals->curtime + flReloadTime;
  1794. // Set next player attack time (weapon independent).
  1795. pPlayer->m_flNextAttack = flTime;
  1796. // Set next weapon attack times (based on reloading).
  1797. m_flNextPrimaryAttack = Max( flTime, (float)m_flReloadPriorNextFire);
  1798. // Don't push out secondary attack, because our secondary fire
  1799. // systems are all separate from primary fire (sniper zooming, demoman pipebomb detonating, etc)
  1800. //m_flNextSecondaryAttack = flTime;
  1801. // Set next idle time (based on reloading).
  1802. SetWeaponIdleTime( flTime );
  1803. }
  1804. // -----------------------------------------------------------------------------
  1805. // Purpose:
  1806. // -----------------------------------------------------------------------------
  1807. bool CTFWeaponBase::PlayEmptySound()
  1808. {
  1809. CPASAttenuationFilter filter( this );
  1810. filter.UsePredictionRules();
  1811. // TFTODO: Add default empty sound here!
  1812. // EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" );
  1813. return false;
  1814. }
  1815. // -----------------------------------------------------------------------------
  1816. // Purpose:
  1817. // -----------------------------------------------------------------------------
  1818. void CTFWeaponBase::SendReloadEvents()
  1819. {
  1820. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1821. if ( !pPlayer )
  1822. return;
  1823. // Make the player play his reload animation.
  1824. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD );
  1825. }
  1826. //-----------------------------------------------------------------------------
  1827. // Purpose:
  1828. //-----------------------------------------------------------------------------
  1829. void CTFWeaponBase::ItemBusyFrame( void )
  1830. {
  1831. // Call into the base ItemBusyFrame.
  1832. BaseClass::ItemBusyFrame();
  1833. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  1834. if ( !pOwner )
  1835. {
  1836. return;
  1837. }
  1838. if ( ( pOwner->m_nButtons & IN_ATTACK2 ) && /*m_bInReload == false &&*/ m_bInAttack2 == false )
  1839. {
  1840. pOwner->DoClassSpecialSkill();
  1841. m_bInAttack2 = true;
  1842. }
  1843. else if ( !(pOwner->m_nButtons & IN_ATTACK2) && m_bInAttack2 )
  1844. {
  1845. m_bInAttack2 = false;
  1846. }
  1847. // Interrupt a reload on reload singly weapons.
  1848. if ( ( pOwner->m_nButtons & IN_ATTACK ) && Clip1() > 0 )
  1849. {
  1850. bool bAbortReload = false;
  1851. if ( m_bReloadsSingly )
  1852. {
  1853. if ( m_iReloadMode != TF_RELOAD_START )
  1854. {
  1855. m_iReloadMode.Set( TF_RELOAD_START );
  1856. bAbortReload = true;
  1857. }
  1858. }
  1859. else if ( m_bInReload )
  1860. {
  1861. // We don't let them abort before the next fire point, so they can't use reload to fire before they would have fired if they hadn't reloaded
  1862. if ( gpGlobals->curtime >= m_flReloadPriorNextFire )
  1863. {
  1864. bAbortReload = true;
  1865. }
  1866. }
  1867. if ( bAbortReload )
  1868. {
  1869. AbortReload();
  1870. m_bInReload = false;
  1871. pOwner->m_flNextAttack = gpGlobals->curtime;
  1872. m_flNextPrimaryAttack = Max<float>( gpGlobals->curtime, m_flReloadPriorNextFire );
  1873. SetWeaponIdleTime( gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeIdle );
  1874. }
  1875. }
  1876. #ifdef GAME_DLL
  1877. // If we have an active-weapon-only regen, we accumulate regen time while active, so that
  1878. // they can't avoid the regen/degen by weapon switching rapidly.
  1879. ApplyItemRegen();
  1880. #endif
  1881. CheckEffectBarRegen();
  1882. }
  1883. //-----------------------------------------------------------------------------
  1884. // Purpose:
  1885. //-----------------------------------------------------------------------------
  1886. void CTFWeaponBase::ItemPostFrame( void )
  1887. {
  1888. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  1889. if ( !pOwner )
  1890. {
  1891. return;
  1892. }
  1893. bool bNeedsReload = NeedsReloadForAmmo1( GetMaxClip1() ) || ( IsEnergyWeapon() && !Energy_FullyCharged() );
  1894. // If we're not shooting, and we want to autoreload, press our reload key
  1895. if ( !AutoFiresFullClip() && pOwner->ShouldAutoReload() && UsesClipsForAmmo1() && !(pOwner->m_nButtons & (IN_ATTACK|IN_ATTACK2)) && bNeedsReload )
  1896. {
  1897. pOwner->m_nButtons |= IN_RELOAD;
  1898. }
  1899. // debounce InAttack flags
  1900. if ( m_bInAttack && !( pOwner->m_nButtons & IN_ATTACK ) )
  1901. {
  1902. m_bInAttack = false;
  1903. }
  1904. if ( m_bInAttack2 && !( pOwner->m_nButtons & IN_ATTACK2 ) )
  1905. {
  1906. m_bInAttack2 = false;
  1907. }
  1908. #ifdef GAME_DLL
  1909. // If we have an active-weapon-only regen, we accumulate regen time while active, so that
  1910. // they can't avoid the regen/degen by weapon switching rapidly.
  1911. ApplyItemRegen();
  1912. #endif
  1913. CheckEffectBarRegen();
  1914. // If we're lowered, we're not allowed to fire
  1915. if ( m_bLowered )
  1916. return;
  1917. // Call the base item post frame.
  1918. BaseClass::ItemPostFrame();
  1919. // Check for reload singly interrupts.
  1920. if ( m_bReloadsSingly )
  1921. {
  1922. ReloadSinglyPostFrame();
  1923. }
  1924. if ( AutoFiresFullClip() && AutoFiresFullClipAllAtOnce() && m_iClip1 == GetMaxClip1() )
  1925. {
  1926. FireFullClipAtOnce();
  1927. }
  1928. }
  1929. //-----------------------------------------------------------------------------
  1930. // Purpose:
  1931. //-----------------------------------------------------------------------------
  1932. int CTFWeaponBase::GetInspectActivity( TFWeaponInspectStage inspectStage )
  1933. {
  1934. static int s_inspectActivities[][INSPECT_STAGE_COUNT] =
  1935. {
  1936. // LOADOUT_POSITION_PRIMARY
  1937. {
  1938. ACT_PRIMARY_VM_INSPECT_START,
  1939. ACT_PRIMARY_VM_INSPECT_IDLE,
  1940. ACT_PRIMARY_VM_INSPECT_END
  1941. },
  1942. //LOADOUT_POSITION_SECONDARY
  1943. {
  1944. ACT_SECONDARY_VM_INSPECT_START,
  1945. ACT_SECONDARY_VM_INSPECT_IDLE,
  1946. ACT_SECONDARY_VM_INSPECT_END
  1947. },
  1948. //LOADOUT_POSITION_MELEE
  1949. {
  1950. ACT_MELEE_VM_INSPECT_START,
  1951. ACT_MELEE_VM_INSPECT_IDLE,
  1952. ACT_MELEE_VM_INSPECT_END
  1953. },
  1954. };
  1955. loadout_positions_t iLoadoutSlot = LOADOUT_POSITION_INVALID;
  1956. CTFPlayer *pOwner = GetTFPlayerOwner();
  1957. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  1958. if ( pOwner && pItem )
  1959. {
  1960. int iClass = pOwner->GetPlayerClass()->GetClassIndex();
  1961. iLoadoutSlot = (loadout_positions_t)pItem->GetStaticData()->GetLoadoutSlot( iClass );
  1962. }
  1963. if ( iLoadoutSlot < LOADOUT_POSITION_PRIMARY || iLoadoutSlot > LOADOUT_POSITION_MELEE )
  1964. {
  1965. // do primary animation for any loadout that we don't support yet
  1966. iLoadoutSlot = LOADOUT_POSITION_PRIMARY;
  1967. }
  1968. return s_inspectActivities[iLoadoutSlot][inspectStage];
  1969. }
  1970. //-----------------------------------------------------------------------------
  1971. // Purpose:
  1972. //-----------------------------------------------------------------------------
  1973. bool CTFWeaponBase::CanInspect() const
  1974. {
  1975. #ifdef STAGING_ONLY
  1976. if ( tf_weapon_force_allow_inspect.GetBool() )
  1977. return true;
  1978. #endif
  1979. float flInspect = 0.f;
  1980. CALL_ATTRIB_HOOK_FLOAT( flInspect, weapon_allow_inspect );
  1981. return flInspect != 0.f;
  1982. }
  1983. //-----------------------------------------------------------------------------
  1984. // Purpose:
  1985. //-----------------------------------------------------------------------------
  1986. void CTFWeaponBase::HandleInspect()
  1987. {
  1988. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1989. if ( !pPlayer )
  1990. return;
  1991. if ( !CanInspect() )
  1992. return;
  1993. // first time pressing inspecting key
  1994. if ( !m_bInspecting && pPlayer->IsInspecting() )
  1995. {
  1996. m_nInspectStage = INSPECT_INVALID;
  1997. m_flInspectAnimTime = -1.f;
  1998. if ( SendWeaponAnim( GetInspectActivity( INSPECT_START ) ) )
  1999. {
  2000. m_flInspectAnimTime = gpGlobals->curtime + SequenceDuration();
  2001. m_nInspectStage = INSPECT_START;
  2002. }
  2003. }
  2004. else if ( !pPlayer->IsInspecting() && m_nInspectStage == INSPECT_IDLE )
  2005. {
  2006. // transition from idle to end when the inspect button is released
  2007. if ( SendWeaponAnim( GetInspectActivity( INSPECT_END ) ) )
  2008. {
  2009. m_flInspectAnimTime = gpGlobals->curtime + SequenceDuration();
  2010. m_nInspectStage = INSPECT_END;
  2011. }
  2012. }
  2013. else if ( m_nInspectStage != INSPECT_INVALID ) // inspecting
  2014. {
  2015. if ( gpGlobals->curtime > m_flInspectAnimTime )
  2016. {
  2017. if ( m_nInspectStage == INSPECT_START )
  2018. {
  2019. TFWeaponInspectStage inspectStage = pPlayer->IsInspecting() ? INSPECT_IDLE : INSPECT_END;
  2020. // transition from start to idle, or end if the inspect button is released
  2021. if ( SendWeaponAnim( GetInspectActivity( inspectStage ) ) )
  2022. {
  2023. m_flInspectAnimTime = gpGlobals->curtime + SequenceDuration();
  2024. m_nInspectStage = inspectStage;
  2025. }
  2026. }
  2027. else if ( m_nInspectStage == INSPECT_END )
  2028. {
  2029. m_flInspectAnimTime = -1.f;
  2030. m_nInspectStage = INSPECT_INVALID;
  2031. SendWeaponAnim( ACT_VM_IDLE );
  2032. }
  2033. }
  2034. }
  2035. m_bInspecting = pPlayer->IsInspecting();
  2036. }
  2037. //-----------------------------------------------------------------------------
  2038. // Purpose:
  2039. //-----------------------------------------------------------------------------
  2040. void CTFWeaponBase::ItemHolsterFrame( void )
  2041. {
  2042. BaseClass::ItemHolsterFrame();
  2043. CheckEffectBarRegen();
  2044. }
  2045. //-----------------------------------------------------------------------------
  2046. // Purpose:
  2047. //-----------------------------------------------------------------------------
  2048. void CTFWeaponBase::ReloadSinglyPostFrame( void )
  2049. {
  2050. if ( m_flTimeWeaponIdle > gpGlobals->curtime )
  2051. return;
  2052. // if the clip is empty and we have ammo remaining,
  2053. if ( IsEnergyWeapon() )
  2054. {
  2055. Reload();
  2056. }
  2057. else if ( ( !AutoFiresFullClip() && Clip1() == 0 && GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) ||
  2058. // or we are already in the process of reloading but not finished
  2059. ( m_iReloadMode != TF_RELOAD_START ) )
  2060. {
  2061. // reload/continue reloading
  2062. Reload();
  2063. }
  2064. }
  2065. //-----------------------------------------------------------------------------
  2066. // Purpose:
  2067. //-----------------------------------------------------------------------------
  2068. bool CTFWeaponBase::WeaponShouldBeLowered( void )
  2069. {
  2070. // Can't be in the middle of another animation
  2071. if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE &&
  2072. GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE )
  2073. return false;
  2074. if ( m_bLowered )
  2075. return true;
  2076. return false;
  2077. }
  2078. //-----------------------------------------------------------------------------
  2079. // Purpose:
  2080. //-----------------------------------------------------------------------------
  2081. bool CTFWeaponBase::Ready( void )
  2082. {
  2083. // If we don't have the anim, just hide for now
  2084. if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE )
  2085. {
  2086. RemoveEffects( EF_NODRAW );
  2087. }
  2088. m_bLowered = false;
  2089. SendWeaponAnim( ACT_VM_IDLE );
  2090. // Prevent firing until our weapon is back up
  2091. CTFPlayer *pPlayer = GetTFPlayerOwner();
  2092. pPlayer->m_flNextAttack = gpGlobals->curtime + SequenceDuration();
  2093. return true;
  2094. }
  2095. //-----------------------------------------------------------------------------
  2096. // Purpose:
  2097. //-----------------------------------------------------------------------------
  2098. bool CTFWeaponBase::Lower( void )
  2099. {
  2100. AbortReload();
  2101. // If we don't have the anim, just hide for now
  2102. if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE )
  2103. {
  2104. AddEffects( EF_NODRAW );
  2105. }
  2106. m_bLowered = true;
  2107. SendWeaponAnim( ACT_VM_IDLE_LOWERED );
  2108. return true;
  2109. }
  2110. //-----------------------------------------------------------------------------
  2111. // Purpose: Show/hide weapon and corresponding view model if any
  2112. // Input : visible -
  2113. //-----------------------------------------------------------------------------
  2114. void CTFWeaponBase::SetWeaponVisible( bool visible )
  2115. {
  2116. if ( visible )
  2117. {
  2118. RemoveEffects( EF_NODRAW );
  2119. }
  2120. else
  2121. {
  2122. AddEffects( EF_NODRAW );
  2123. }
  2124. #ifdef CLIENT_DLL
  2125. UpdateVisibility();
  2126. // Force an update
  2127. PreDataUpdate( DATA_UPDATE_DATATABLE_CHANGED );
  2128. #endif
  2129. }
  2130. //-----------------------------------------------------------------------------
  2131. // Purpose: Allows the weapon to choose proper weapon idle animation
  2132. //-----------------------------------------------------------------------------
  2133. void CTFWeaponBase::WeaponIdle( void )
  2134. {
  2135. //See if we should idle high or low
  2136. if ( WeaponShouldBeLowered() )
  2137. {
  2138. // Move to lowered position if we're not there yet
  2139. if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED && GetActivity() != ACT_TRANSITION )
  2140. {
  2141. SendWeaponAnim( ACT_VM_IDLE_LOWERED );
  2142. }
  2143. else if ( HasWeaponIdleTimeElapsed() )
  2144. {
  2145. // Keep idling low
  2146. SendWeaponAnim( ACT_VM_IDLE_LOWERED );
  2147. }
  2148. }
  2149. else
  2150. {
  2151. // See if we need to raise immediately
  2152. if ( GetActivity() == ACT_VM_IDLE_LOWERED )
  2153. {
  2154. SendWeaponAnim( ACT_VM_IDLE );
  2155. }
  2156. else if ( HasWeaponIdleTimeElapsed() )
  2157. {
  2158. if ( !( m_bReloadsSingly && m_iReloadMode != TF_RELOAD_START ) )
  2159. {
  2160. SendWeaponAnim( ACT_VM_IDLE );
  2161. m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
  2162. }
  2163. #ifdef GAME_DLL
  2164. m_iHitsInTime = 1;
  2165. m_iFiredInTime = 1;
  2166. #endif // GAME_DLL
  2167. }
  2168. }
  2169. }
  2170. // -----------------------------------------------------------------------------
  2171. // Purpose:
  2172. // -----------------------------------------------------------------------------
  2173. const char *CTFWeaponBase::GetMuzzleFlashModel( void )
  2174. {
  2175. const char *pszModel = GetTFWpnData().m_szMuzzleFlashModel;
  2176. if ( Q_strlen( pszModel ) > 0 )
  2177. {
  2178. return pszModel;
  2179. }
  2180. return NULL;
  2181. }
  2182. // -----------------------------------------------------------------------------
  2183. // Purpose:
  2184. // -----------------------------------------------------------------------------
  2185. const char *CTFWeaponBase::GetMuzzleFlashParticleEffect( void )
  2186. {
  2187. const char *pszPEffect = GetTFWpnData().m_szMuzzleFlashParticleEffect;
  2188. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  2189. if ( pItem->IsValid() )
  2190. {
  2191. const char *pszItemMuzzleEffect = pItem->GetStaticData()->GetMuzzleFlash( GetTeamNumber() );
  2192. if ( pszItemMuzzleEffect )
  2193. {
  2194. pszPEffect = pszItemMuzzleEffect;
  2195. }
  2196. }
  2197. if ( Q_strlen( pszPEffect ) > 0 )
  2198. {
  2199. return pszPEffect;
  2200. }
  2201. return NULL;
  2202. }
  2203. // -----------------------------------------------------------------------------
  2204. // Purpose:
  2205. // -----------------------------------------------------------------------------
  2206. float CTFWeaponBase::GetMuzzleFlashModelLifetime( void )
  2207. {
  2208. return GetTFWpnData().m_flMuzzleFlashModelDuration;
  2209. }
  2210. //-----------------------------------------------------------------------------
  2211. // Purpose:
  2212. //-----------------------------------------------------------------------------
  2213. const char *CTFWeaponBase::GetTracerType( void )
  2214. {
  2215. const char* pszTracerEffect = GetTFWpnData().m_szTracerEffect;
  2216. if ( tf_useparticletracers.GetBool() )
  2217. {
  2218. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  2219. if ( pItem->IsValid() )
  2220. {
  2221. // Look for a replacement effect specified in the item's visual attributes.
  2222. const char *pszItemTracerEffect = pItem->GetStaticData()->GetTracerEffect( GetTeamNumber() );
  2223. if ( pszItemTracerEffect )
  2224. {
  2225. pszTracerEffect = pszItemTracerEffect;
  2226. }
  2227. }
  2228. if ( pszTracerEffect && pszTracerEffect[0] )
  2229. {
  2230. if ( !m_szTracerName[0] )
  2231. {
  2232. Q_snprintf( m_szTracerName, MAX_TRACER_NAME, "%s_%s", pszTracerEffect,
  2233. (GetOwner() && GetOwner()->GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue" );
  2234. }
  2235. return m_szTracerName;
  2236. }
  2237. }
  2238. if ( GetWeaponID() == TF_WEAPON_MINIGUN )
  2239. return "BrightTracer";
  2240. return BaseClass::GetTracerType();
  2241. }
  2242. //=============================================================================
  2243. //
  2244. // TFWeaponBase functions (Server specific).
  2245. //
  2246. #if !defined( CLIENT_DLL )
  2247. // -----------------------------------------------------------------------------
  2248. // Purpose:
  2249. // -----------------------------------------------------------------------------
  2250. void CTFWeaponBase::CheckRespawn()
  2251. {
  2252. // Do not respawn.
  2253. return;
  2254. }
  2255. // -----------------------------------------------------------------------------
  2256. // Purpose:
  2257. // -----------------------------------------------------------------------------
  2258. CBaseEntity *CTFWeaponBase::Respawn()
  2259. {
  2260. // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
  2261. // will decide when to make the weapon visible and touchable.
  2262. CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetAbsAngles(), GetOwner() );
  2263. if ( pNewWeapon )
  2264. {
  2265. pNewWeapon->AddEffects( EF_NODRAW );// invisible for now
  2266. pNewWeapon->SetTouch( NULL );// no touch
  2267. pNewWeapon->SetThink( &CTFWeaponBase::AttemptToMaterialize );
  2268. UTIL_DropToFloor( this, MASK_SOLID );
  2269. // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement,
  2270. // but when it should respawn is based on conditions belonging to the weapon that was taken.
  2271. pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) );
  2272. }
  2273. else
  2274. {
  2275. Msg( "Respawn failed to create %s!\n", GetClassname() );
  2276. }
  2277. return pNewWeapon;
  2278. }
  2279. // -----------------------------------------------------------------------------
  2280. // Purpose: Make a weapon visible and tangible.
  2281. // -----------------------------------------------------------------------------
  2282. void CTFWeaponBase::Materialize()
  2283. {
  2284. if ( IsEffectActive( EF_NODRAW ) )
  2285. {
  2286. RemoveEffects( EF_NODRAW );
  2287. DoMuzzleFlash();
  2288. }
  2289. AddSolidFlags( FSOLID_TRIGGER );
  2290. SetThink ( &CTFWeaponBase::SUB_Remove );
  2291. SetNextThink( gpGlobals->curtime + 1 );
  2292. }
  2293. // -----------------------------------------------------------------------------
  2294. // Purpose: The item is trying to materialize, should it do so now or wait longer?
  2295. // -----------------------------------------------------------------------------
  2296. void CTFWeaponBase::AttemptToMaterialize()
  2297. {
  2298. float flTime = g_pGameRules->FlWeaponTryRespawn( this );
  2299. if ( flTime == 0 )
  2300. {
  2301. Materialize();
  2302. return;
  2303. }
  2304. SetNextThink( gpGlobals->curtime + flTime );
  2305. }
  2306. // -----------------------------------------------------------------------------
  2307. // Purpose:
  2308. // -----------------------------------------------------------------------------
  2309. void CTFWeaponBase::SetDieThink( bool bDie )
  2310. {
  2311. if( bDie )
  2312. {
  2313. SetContextThink( &CTFWeaponBase::Die, gpGlobals->curtime + 30.0f, "DieContext" );
  2314. }
  2315. else
  2316. {
  2317. SetContextThink( NULL, gpGlobals->curtime, "DieContext" );
  2318. }
  2319. }
  2320. // -----------------------------------------------------------------------------
  2321. // Purpose:
  2322. // -----------------------------------------------------------------------------
  2323. void CTFWeaponBase::Die( void )
  2324. {
  2325. UTIL_Remove( this );
  2326. }
  2327. void CTFWeaponBase::WeaponReset( void )
  2328. {
  2329. m_iReloadMode.Set( TF_RELOAD_START );
  2330. m_bResetParity = !m_bResetParity;
  2331. m_flEnergy = Energy_GetMaxEnergy();
  2332. }
  2333. //-----------------------------------------------------------------------------
  2334. // Purpose:
  2335. // ----------------------------------------------------------------------------
  2336. const Vector &CTFWeaponBase::GetBulletSpread( void )
  2337. {
  2338. static Vector cone = VECTOR_CONE_15DEGREES;
  2339. return cone;
  2340. }
  2341. //-----------------------------------------------------------------------------
  2342. // Purpose:
  2343. // ----------------------------------------------------------------------------
  2344. void CTFWeaponBase::OnBulletFire( int iEnemyPlayersHit )
  2345. {
  2346. if ( !iEnemyPlayersHit )
  2347. {
  2348. m_iConsecutiveKills = 0;
  2349. }
  2350. else
  2351. {
  2352. m_iHitsInTime++;
  2353. m_flLastHitTime = gpGlobals->curtime;
  2354. }
  2355. m_iFiredInTime++;
  2356. }
  2357. void CTFWeaponBase::OnPlayerKill( CTFPlayer *pVictim, const CTakeDamageInfo &info )
  2358. {
  2359. m_iConsecutiveKills++;
  2360. if ( pVictim )
  2361. {
  2362. int nClassIndex = pVictim->GetPlayerClass()->GetClassIndex();
  2363. float fKillComboFireRateBoost = 0.0f;
  2364. CALL_ATTRIB_HOOK_FLOAT( fKillComboFireRateBoost, kill_combo_fire_rate_boost );
  2365. if ( fKillComboFireRateBoost != 0.0f )
  2366. {
  2367. AddKillCombo( nClassIndex );
  2368. if ( GetKillComboCount() == 1 )
  2369. {
  2370. // Yell when we switch class combo type
  2371. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  2372. if ( pOwner )
  2373. {
  2374. pOwner->SpeakConceptIfAllowed( MP_CONCEPT_COMBO_KILLED, CFmtStr( "victimclass:%s", g_aPlayerClassNames_NonLocalized[ nClassIndex ] ).Access() );
  2375. }
  2376. }
  2377. }
  2378. }
  2379. }
  2380. #else
  2381. void TE_DynamicLight( IRecipientFilter& filter, float delay,
  2382. const Vector* org, int r, int g, int b, int exponent, float radius, float time, float decay, int nLightIndex = LIGHT_INDEX_TE_DYNAMIC );
  2383. //=============================================================================
  2384. //
  2385. // TFWeaponBase functions (Client specific).
  2386. //
  2387. bool CTFWeaponBase::IsFirstPersonView()
  2388. {
  2389. C_TFPlayer *pPlayerOwner = GetTFPlayerOwner();
  2390. if ( pPlayerOwner == NULL )
  2391. {
  2392. return false;
  2393. }
  2394. return pPlayerOwner->InFirstPersonView();
  2395. }
  2396. bool CTFWeaponBase::UsingViewModel()
  2397. {
  2398. C_TFPlayer *pPlayerOwner = GetTFPlayerOwner();
  2399. bool bIsFirstPersonView = IsFirstPersonView();
  2400. bool bUsingViewModel = bIsFirstPersonView && ( pPlayerOwner != NULL ) && !pPlayerOwner->ShouldDrawThisPlayer();
  2401. return bUsingViewModel;
  2402. }
  2403. C_BaseAnimating *CTFWeaponBase::GetAppropriateWorldOrViewModel()
  2404. {
  2405. C_TFPlayer *pPlayerOwner = GetTFPlayerOwner();
  2406. if ( pPlayerOwner && UsingViewModel() )
  2407. {
  2408. // For w_* models the viewmodel itself is just arms+hands. And attached to them is the actual weapon.
  2409. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  2410. if ( pItem->IsValid() && pItem->GetStaticData()->ShouldAttachToHands() )
  2411. {
  2412. C_BaseAnimating *pVMAttach = GetViewmodelAttachment();
  2413. if ( pVMAttach != NULL )
  2414. {
  2415. return pVMAttach;
  2416. }
  2417. }
  2418. // Nope - it's a standard viewmodel.
  2419. C_BaseAnimating *pViewModel = pPlayerOwner->GetViewModel();
  2420. if ( pViewModel != NULL )
  2421. {
  2422. return pViewModel;
  2423. }
  2424. // No viewmodel, so just return the normal model.
  2425. return this;
  2426. }
  2427. else
  2428. {
  2429. return this;
  2430. }
  2431. }
  2432. void CTFWeaponBase::CreateMuzzleFlashEffects( C_BaseEntity *pAttachEnt, int nIndex )
  2433. {
  2434. Vector vecOrigin;
  2435. QAngle angAngles;
  2436. if ( !pAttachEnt )
  2437. return;
  2438. if ( UsingViewModel() && !g_pClientMode->ShouldDrawViewModel() )
  2439. {
  2440. // Prevent effects when the ViewModel is hidden with r_drawviewmodel=0
  2441. return;
  2442. }
  2443. int iMuzzleFlashAttachment = pAttachEnt->LookupAttachment( "muzzle" );
  2444. const char *pszMuzzleFlashEffect = NULL;
  2445. const char *pszMuzzleFlashModel = GetMuzzleFlashModel();
  2446. const char *pszMuzzleFlashParticleEffect = GetMuzzleFlashParticleEffect();
  2447. // Pick the right muzzleflash (3rd / 1st person)
  2448. // (this uses IsFirstPersonView() rather than UsingViewModel() because even when NOT using the viewmodel, in 1st-person mode we still want the 1st-person muzzleflash effect)
  2449. if ( IsFirstPersonView() )
  2450. {
  2451. pszMuzzleFlashEffect = GetMuzzleFlashEffectName_1st();
  2452. }
  2453. else
  2454. {
  2455. pszMuzzleFlashEffect = GetMuzzleFlashEffectName_3rd();
  2456. }
  2457. // If we have an attachment, then stick a light on it.
  2458. if ( iMuzzleFlashAttachment > 0 && (pszMuzzleFlashEffect || pszMuzzleFlashModel || pszMuzzleFlashParticleEffect ) )
  2459. {
  2460. pAttachEnt->GetAttachment( iMuzzleFlashAttachment, vecOrigin, angAngles );
  2461. // Muzzleflash light
  2462. /*
  2463. CLocalPlayerFilter filter;
  2464. TE_DynamicLight( filter, 0.0f, &vecOrigin, 255, 192, 64, 5, 70.0f, 0.05f, 70.0f / 0.05f, LIGHT_INDEX_MUZZLEFLASH );
  2465. */
  2466. if ( pszMuzzleFlashEffect )
  2467. {
  2468. // Using an muzzle flash dispatch effect
  2469. CEffectData muzzleFlashData;
  2470. muzzleFlashData.m_vOrigin = vecOrigin;
  2471. muzzleFlashData.m_vAngles = angAngles;
  2472. muzzleFlashData.m_hEntity = pAttachEnt->GetRefEHandle();
  2473. muzzleFlashData.m_nAttachmentIndex = iMuzzleFlashAttachment;
  2474. //muzzleFlashData.m_nHitBox = GetDODWpnData().m_iMuzzleFlashType;
  2475. //muzzleFlashData.m_flMagnitude = GetDODWpnData().m_flMuzzleFlashScale;
  2476. muzzleFlashData.m_flMagnitude = 0.2;
  2477. DispatchEffect( pszMuzzleFlashEffect, muzzleFlashData );
  2478. }
  2479. if ( pszMuzzleFlashModel )
  2480. {
  2481. float flEffectLifetime = GetMuzzleFlashModelLifetime();
  2482. // Using a model as a muzzle flash.
  2483. if ( m_hMuzzleFlashModel[nIndex] )
  2484. {
  2485. // Increase the lifetime of the muzzleflash
  2486. m_hMuzzleFlashModel[nIndex]->SetLifetime( flEffectLifetime );
  2487. }
  2488. else
  2489. {
  2490. m_hMuzzleFlashModel[nIndex] = C_MuzzleFlashModel::CreateMuzzleFlashModel( pszMuzzleFlashModel, pAttachEnt, iMuzzleFlashAttachment, flEffectLifetime );
  2491. // FIXME: This is an incredibly brutal hack to get muzzle flashes positioned correctly for recording
  2492. m_hMuzzleFlashModel[nIndex]->SetIs3rdPersonFlash( nIndex == 1 );
  2493. }
  2494. }
  2495. if ( pszMuzzleFlashParticleEffect )
  2496. {
  2497. DispatchMuzzleFlash( pszMuzzleFlashParticleEffect, pAttachEnt );
  2498. }
  2499. }
  2500. }
  2501. void CTFWeaponBase::DispatchMuzzleFlash( const char* effectName, C_BaseEntity* pAttachEnt )
  2502. {
  2503. DispatchParticleEffect( effectName, PATTACH_POINT_FOLLOW, pAttachEnt, "muzzle" );
  2504. }
  2505. //-----------------------------------------------------------------------------
  2506. // Purpose:
  2507. //-----------------------------------------------------------------------------
  2508. bool CTFWeaponBase::ShouldDraw( void )
  2509. {
  2510. C_BaseCombatCharacter *pOwner = GetOwner();
  2511. if ( !pOwner )
  2512. return true;
  2513. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  2514. if ( !pLocalPlayer )
  2515. return true;
  2516. if ( pOwner->IsPlayer() )
  2517. {
  2518. CTFPlayer *pTFOwner = ToTFPlayer( GetOwner() );
  2519. if ( !pTFOwner )
  2520. return true;
  2521. if ( pTFOwner->m_Shared.IsControlStunned() )
  2522. return false;
  2523. // Ghosts dont have weapons
  2524. if ( pTFOwner->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  2525. return false;
  2526. if ( pTFOwner->m_Shared.GetDisguiseWeapon() )
  2527. {
  2528. if ( pTFOwner->m_Shared.InCond( TF_COND_DISGUISED ) )
  2529. {
  2530. int iLocalPlayerTeam = pLocalPlayer->GetTeamNumber();
  2531. if ( pLocalPlayer->m_bIsCoaching && pLocalPlayer->m_hStudent )
  2532. {
  2533. iLocalPlayerTeam = pLocalPlayer->m_hStudent->GetTeamNumber();
  2534. }
  2535. // If we are disguised we may want to draw the disguise weapon.
  2536. if ( iLocalPlayerTeam != pOwner->GetTeamNumber() && (iLocalPlayerTeam != TEAM_SPECTATOR) )
  2537. {
  2538. // We are a disguised enemy, so only draw the disguise weapon.
  2539. if ( pTFOwner->m_Shared.GetDisguiseWeapon() != this )
  2540. {
  2541. return false;
  2542. }
  2543. }
  2544. else
  2545. {
  2546. // We are a disguised friendly. Don't draw the disguise weapon.
  2547. if ( m_bDisguiseWeapon )
  2548. {
  2549. return false;
  2550. }
  2551. }
  2552. }
  2553. else
  2554. {
  2555. // We are not disguised. Never draw the disguise weapon.
  2556. if ( m_bDisguiseWeapon )
  2557. {
  2558. return false;
  2559. }
  2560. }
  2561. }
  2562. }
  2563. return BaseClass::ShouldDraw();
  2564. }
  2565. void CTFWeaponBase::UpdateVisibility( void )
  2566. {
  2567. BaseClass::UpdateVisibility();
  2568. UpdateExtraWearablesVisibility();
  2569. C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  2570. if ( pOwner )
  2571. {
  2572. pOwner->SetBodygroupsDirty();
  2573. }
  2574. }
  2575. //-----------------------------------------------------------------------------
  2576. // Purpose:
  2577. //-----------------------------------------------------------------------------
  2578. int CTFWeaponBase::InternalDrawModel( int flags )
  2579. {
  2580. C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  2581. bool bNotViewModel = ( pOwner->ShouldDrawThisPlayer() );
  2582. bool bUseInvulnMaterial = ( bNotViewModel && pOwner && pOwner->m_Shared.IsInvulnerable() &&
  2583. ( !pOwner->m_Shared.InCond( TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED ) || gpGlobals->curtime < pOwner->GetLastDamageTime() + 2.0f ) );
  2584. if ( bUseInvulnMaterial )
  2585. {
  2586. modelrender->ForcedMaterialOverride( *pOwner->GetInvulnMaterialRef() );
  2587. }
  2588. int ret = BaseClass::InternalDrawModel( flags );
  2589. if ( bUseInvulnMaterial )
  2590. {
  2591. modelrender->ForcedMaterialOverride( NULL );
  2592. }
  2593. return ret;
  2594. }
  2595. void CTFWeaponBase::ProcessMuzzleFlashEvent( void )
  2596. {
  2597. C_BaseAnimating *pAttachEnt = GetAppropriateWorldOrViewModel();
  2598. C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  2599. if ( pOwner == NULL )
  2600. return;
  2601. bool bDrawMuzzleFlashOnViewModel = ( pAttachEnt != this );
  2602. {
  2603. CRecordEffectOwner recordOwner( pOwner, bDrawMuzzleFlashOnViewModel );
  2604. CreateMuzzleFlashEffects( pAttachEnt, 0 );
  2605. }
  2606. // Quasi-evil
  2607. int nModelIndex = GetModelIndex();
  2608. int nWorldModelIndex = GetWorldModelIndex();
  2609. bool bInToolRecordingMode = ToolsEnabled() && clienttools->IsInRecordingMode();
  2610. if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex && pOwner->IsLocalPlayer() )
  2611. {
  2612. CRecordEffectOwner recordOwner( pOwner, false );
  2613. SetModelIndex( nWorldModelIndex );
  2614. CreateMuzzleFlashEffects( this, 1 );
  2615. SetModelIndex( nModelIndex );
  2616. }
  2617. }
  2618. //-----------------------------------------------------------------------------
  2619. // Purpose:
  2620. // ----------------------------------------------------------------------------
  2621. bool CTFWeaponBase::ShouldPredict()
  2622. {
  2623. if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() )
  2624. {
  2625. return true;
  2626. }
  2627. return BaseClass::ShouldPredict();
  2628. }
  2629. //-----------------------------------------------------------------------------
  2630. // Purpose:
  2631. // ----------------------------------------------------------------------------
  2632. void CTFWeaponBase::WeaponReset( void )
  2633. {
  2634. UpdateVisibility();
  2635. }
  2636. //-----------------------------------------------------------------------------
  2637. // Purpose:
  2638. // Input : updateType -
  2639. //-----------------------------------------------------------------------------
  2640. void CTFWeaponBase::PostDataUpdate( DataUpdateType_t updateType )
  2641. {
  2642. // We need to do this before the C_BaseAnimating code starts to drive
  2643. // clientside animation sequences on this model, which will be using bad sequences for the world model.
  2644. int iDesiredModelIndex = 0;
  2645. C_BasePlayer *pOwner = ToBasePlayer(GetOwner());
  2646. if ( !pOwner->ShouldDrawThisPlayer() )
  2647. {
  2648. iDesiredModelIndex = m_iViewModelIndex;
  2649. }
  2650. else
  2651. {
  2652. iDesiredModelIndex = GetWorldModelIndex();
  2653. // Our world models never animate
  2654. SetSequence( 0 );
  2655. }
  2656. if ( GetModelIndex() != iDesiredModelIndex )
  2657. {
  2658. SetModelIndex( iDesiredModelIndex );
  2659. }
  2660. BaseClass::PostDataUpdate( updateType );
  2661. }
  2662. //-----------------------------------------------------------------------------
  2663. // Purpose:
  2664. // ----------------------------------------------------------------------------
  2665. void CTFWeaponBase::OnPreDataChanged( DataUpdateType_t type )
  2666. {
  2667. BaseClass::OnPreDataChanged( type );
  2668. m_bOldResetParity = m_bResetParity;
  2669. }
  2670. //-----------------------------------------------------------------------------
  2671. // Purpose:
  2672. // ----------------------------------------------------------------------------
  2673. void CTFWeaponBase::OnDataChanged( DataUpdateType_t type )
  2674. {
  2675. BaseClass::OnDataChanged( type );
  2676. if ( type == DATA_UPDATE_CREATED )
  2677. {
  2678. ListenForGameEvent( "localplayer_changeteam" );
  2679. }
  2680. if ( GetPredictable() && !ShouldPredict() )
  2681. {
  2682. ShutdownPredictable();
  2683. }
  2684. //If its a world (held or dropped) model then set the correct skin color here.
  2685. if ( m_nModelIndex == GetWorldModelIndex() )
  2686. {
  2687. m_nSkin = GetSkin();
  2688. }
  2689. if ( m_bResetParity != m_bOldResetParity )
  2690. {
  2691. WeaponReset();
  2692. }
  2693. //Here we go...
  2694. //Since we can't get a repro for the invisible weapon thing, I'll fix it right up here:
  2695. CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  2696. // Our owner is alive and not a loser.
  2697. if ( pOwner && pOwner->IsAlive() && !pOwner->m_Shared.IsLoser() && !pOwner->m_Shared.InCond( TF_COND_COMPETITIVE_LOSER ) && ( pOwner->GetActiveWeapon() == this ) )
  2698. {
  2699. // And he is NOT taunting or control stunned.
  2700. if ( !pOwner->m_Shared.InCond ( TF_COND_TAUNTING ) && !pOwner->m_Shared.InCond ( TF_COND_HALLOWEEN_KART ) &&
  2701. (!pOwner->m_Shared.IsControlStunned() || !pOwner->m_Shared.IsLoserStateStunned() || !HideWhileStunned()) )
  2702. {
  2703. if ( IsEffectActive( EF_NODRAW ) )
  2704. {
  2705. RemoveEffects( EF_NODRAW );
  2706. UpdateVisibility();
  2707. }
  2708. }
  2709. }
  2710. UpdateParticleSystems();
  2711. if ( m_iOldTeam != m_iTeamNum )
  2712. {
  2713. // Recompute our tracer name
  2714. m_szTracerName[0] = '\0';
  2715. }
  2716. //if ( m_hExtraWearable.Get() && m_hExtraWearable->IsVisible() != IsVisible() )
  2717. if ( m_hExtraWearable.Get() )
  2718. {
  2719. m_hExtraWearable->UpdateVisibility();
  2720. }
  2721. }
  2722. //-----------------------------------------------------------------------------
  2723. // Purpose:
  2724. //-----------------------------------------------------------------------------
  2725. void CTFWeaponBase::FireGameEvent( IGameEvent *event )
  2726. {
  2727. // If we were the active weapon, we need to update our visibility
  2728. // because we may switch visibility due to Spy disguises.
  2729. const char *pszEventName = event->GetName();
  2730. if ( Q_strcmp( pszEventName, "localplayer_changeteam" ) == 0 )
  2731. {
  2732. UpdateVisibility();
  2733. }
  2734. }
  2735. //-----------------------------------------------------------------------------
  2736. // Purpose:
  2737. // ----------------------------------------------------------------------------
  2738. int CTFWeaponBase::GetWorldModelIndex( void )
  2739. {
  2740. CTFPlayer *pPlayer = GetTFPlayerOwner();
  2741. // Guitar Riff taunt support.
  2742. if ( pPlayer )
  2743. {
  2744. bool bReplaceModel = true;
  2745. const char* pszCustomTauntProp = NULL;
  2746. if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) && ( pPlayer->m_Shared.GetTauntIndex() == TAUNT_MISC_ITEM || pPlayer->m_Shared.GetTauntIndex() == TAUNT_LONG ) )
  2747. {
  2748. int iClass = pPlayer->GetPlayerClass()->GetClassIndex();
  2749. CEconItemView *pMiscItemView = pPlayer->GetTauntEconItemView();
  2750. if ( pMiscItemView && pMiscItemView->GetStaticData()->GetTauntData() )
  2751. {
  2752. // if prop has its own animation, don't replace weapon model
  2753. if ( !pMiscItemView->GetStaticData()->GetTauntData()->GetPropIntroScene( iClass ) )
  2754. {
  2755. pszCustomTauntProp = pMiscItemView->GetStaticData()->GetTauntData()->GetProp( iClass );
  2756. }
  2757. }
  2758. }
  2759. if ( pszCustomTauntProp )
  2760. {
  2761. m_iWorldModelIndex = modelinfo->GetModelIndex( pszCustomTauntProp );
  2762. }
  2763. else
  2764. {
  2765. bReplaceModel = false;
  2766. }
  2767. if ( bReplaceModel )
  2768. {
  2769. return m_iWorldModelIndex;
  2770. }
  2771. }
  2772. if ( m_iCachedModelIndex == 0 )
  2773. {
  2774. // Remember our normal world model index so we can quickly replace it later.
  2775. m_iCachedModelIndex = modelinfo->GetModelIndex( GetWorldModel() );
  2776. }
  2777. // We aren't taunting, so we want to use the cached model index.
  2778. if ( m_iWorldModelIndex != m_iCachedModelIndex )
  2779. {
  2780. m_iWorldModelIndex = m_iCachedModelIndex;
  2781. }
  2782. if ( IsEffectActive( EF_NODRAW ) && ShouldDraw() ) // Early-out if we are visible.
  2783. {
  2784. // Some taunts (guitar, bubble wand, etc.) hide us at the end of the animation.
  2785. // We want to re-enable drawing if we should be drawing now but aren't.
  2786. SetWeaponVisible( true );
  2787. }
  2788. if ( pPlayer )
  2789. {
  2790. // if we're a spy and we're disguised, we also
  2791. // want to disguise our weapon's world model
  2792. CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  2793. if ( !pLocalPlayer )
  2794. return 0;
  2795. int iLocalTeam = pLocalPlayer->GetTeamNumber();
  2796. // We only show disguise weapon to the enemy team when owner is disguised
  2797. bool bUseDisguiseWeapon = ( pPlayer->GetTeamNumber() != iLocalTeam && iLocalTeam > LAST_SHARED_TEAM );
  2798. if ( bUseDisguiseWeapon && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  2799. {
  2800. CTFWeaponBase *pDisguiseWeapon = pPlayer->m_Shared.GetDisguiseWeapon();
  2801. if ( !pDisguiseWeapon )
  2802. return BaseClass::GetWorldModelIndex();
  2803. if ( pDisguiseWeapon == this )
  2804. return BaseClass::GetWorldModelIndex();
  2805. else
  2806. return pDisguiseWeapon->GetWorldModelIndex();
  2807. }
  2808. }
  2809. return BaseClass::GetWorldModelIndex();
  2810. }
  2811. bool CTFWeaponBase::ShouldDrawCrosshair( void )
  2812. {
  2813. const char *crosshairfile = cl_crosshair_file.GetString();
  2814. if ( !crosshairfile || !crosshairfile[0] )
  2815. {
  2816. // Default crosshair.
  2817. return GetTFWpnData().m_WeaponData[TF_WEAPON_PRIMARY_MODE].m_bDrawCrosshair;
  2818. }
  2819. // Custom crosshair.
  2820. return true;
  2821. }
  2822. void CTFWeaponBase::Redraw()
  2823. {
  2824. if ( ShouldDrawCrosshair() && g_pClientMode->ShouldDrawCrosshair() )
  2825. {
  2826. DrawCrosshair();
  2827. }
  2828. }
  2829. #endif
  2830. acttable_t s_acttablePrimary[] =
  2831. {
  2832. { ACT_MP_STAND_IDLE, ACT_MP_STAND_PRIMARY, false },
  2833. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_PRIMARY, false },
  2834. { ACT_MP_DEPLOYED, ACT_MP_DEPLOYED_PRIMARY, false },
  2835. { ACT_MP_CROUCH_DEPLOYED, ACT_MP_CROUCHWALK_DEPLOYED, false },
  2836. { ACT_MP_CROUCH_DEPLOYED_IDLE, ACT_MP_CROUCH_DEPLOYED_IDLE, false },
  2837. { ACT_MP_RUN, ACT_MP_RUN_PRIMARY, false },
  2838. { ACT_MP_WALK, ACT_MP_WALK_PRIMARY, false },
  2839. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_PRIMARY, false },
  2840. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_PRIMARY, false },
  2841. { ACT_MP_JUMP, ACT_MP_JUMP_PRIMARY, false },
  2842. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_PRIMARY, false },
  2843. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_PRIMARY, false },
  2844. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_PRIMARY, false },
  2845. { ACT_MP_SWIM, ACT_MP_SWIM_PRIMARY, false },
  2846. { ACT_MP_SWIM_DEPLOYED, ACT_MP_SWIM_DEPLOYED_PRIMARY, false },
  2847. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_PRIMARY, false },
  2848. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_PRIMARY, false },
  2849. { ACT_MP_ATTACK_STAND_PRIMARYFIRE_DEPLOYED, ACT_MP_ATTACK_STAND_PRIMARY_DEPLOYED, false },
  2850. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_PRIMARY, false },
  2851. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE_DEPLOYED, ACT_MP_ATTACK_CROUCH_PRIMARY_DEPLOYED, false },
  2852. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_PRIMARY, false },
  2853. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_PRIMARY, false },
  2854. { ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_PRIMARY, false },
  2855. { ACT_MP_RELOAD_STAND_LOOP, ACT_MP_RELOAD_STAND_PRIMARY_LOOP, false },
  2856. { ACT_MP_RELOAD_STAND_END, ACT_MP_RELOAD_STAND_PRIMARY_END, false },
  2857. { ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_PRIMARY, false },
  2858. { ACT_MP_RELOAD_CROUCH_LOOP,ACT_MP_RELOAD_CROUCH_PRIMARY_LOOP, false },
  2859. { ACT_MP_RELOAD_CROUCH_END, ACT_MP_RELOAD_CROUCH_PRIMARY_END, false },
  2860. { ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_PRIMARY, false },
  2861. { ACT_MP_RELOAD_SWIM_LOOP, ACT_MP_RELOAD_SWIM_PRIMARY_LOOP, false },
  2862. { ACT_MP_RELOAD_SWIM_END, ACT_MP_RELOAD_SWIM_PRIMARY_END, false },
  2863. { ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_PRIMARY, false },
  2864. { ACT_MP_RELOAD_AIRWALK_LOOP, ACT_MP_RELOAD_AIRWALK_PRIMARY_LOOP, false },
  2865. { ACT_MP_RELOAD_AIRWALK_END,ACT_MP_RELOAD_AIRWALK_PRIMARY_END, false },
  2866. { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_PRIMARY, false },
  2867. { ACT_MP_GRENADE1_DRAW, ACT_MP_PRIMARY_GRENADE1_DRAW, false },
  2868. { ACT_MP_GRENADE1_IDLE, ACT_MP_PRIMARY_GRENADE1_IDLE, false },
  2869. { ACT_MP_GRENADE1_ATTACK, ACT_MP_PRIMARY_GRENADE1_ATTACK, false },
  2870. { ACT_MP_GRENADE2_DRAW, ACT_MP_PRIMARY_GRENADE2_DRAW, false },
  2871. { ACT_MP_GRENADE2_IDLE, ACT_MP_PRIMARY_GRENADE2_IDLE, false },
  2872. { ACT_MP_GRENADE2_ATTACK, ACT_MP_PRIMARY_GRENADE2_ATTACK, false },
  2873. { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2874. { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2875. { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2876. { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2877. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_PRIMARY, false },
  2878. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_PRIMARY, false },
  2879. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_PRIMARY, false },
  2880. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_PRIMARY, false },
  2881. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_PRIMARY, false },
  2882. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_PRIMARY, false },
  2883. };
  2884. acttable_t s_acttableSecondary[] =
  2885. {
  2886. { ACT_MP_STAND_IDLE, ACT_MP_STAND_SECONDARY, false },
  2887. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_SECONDARY, false },
  2888. { ACT_MP_RUN, ACT_MP_RUN_SECONDARY, false },
  2889. { ACT_MP_WALK, ACT_MP_WALK_SECONDARY, false },
  2890. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_SECONDARY, false },
  2891. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_SECONDARY, false },
  2892. { ACT_MP_JUMP, ACT_MP_JUMP_SECONDARY, false },
  2893. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_SECONDARY, false },
  2894. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_SECONDARY, false },
  2895. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_SECONDARY, false },
  2896. { ACT_MP_SWIM, ACT_MP_SWIM_SECONDARY, false },
  2897. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_SECONDARY, false },
  2898. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_SECONDARY, false },
  2899. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_SECONDARY, false },
  2900. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_SECONDARY, false },
  2901. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_SECONDARY, false },
  2902. { ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_SECONDARY, false },
  2903. { ACT_MP_RELOAD_STAND_LOOP, ACT_MP_RELOAD_STAND_SECONDARY_LOOP, false },
  2904. { ACT_MP_RELOAD_STAND_END, ACT_MP_RELOAD_STAND_SECONDARY_END, false },
  2905. { ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_SECONDARY, false },
  2906. { ACT_MP_RELOAD_CROUCH_LOOP,ACT_MP_RELOAD_CROUCH_SECONDARY_LOOP,false },
  2907. { ACT_MP_RELOAD_CROUCH_END, ACT_MP_RELOAD_CROUCH_SECONDARY_END, false },
  2908. { ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_SECONDARY, false },
  2909. { ACT_MP_RELOAD_SWIM_LOOP, ACT_MP_RELOAD_SWIM_SECONDARY_LOOP, false },
  2910. { ACT_MP_RELOAD_SWIM_END, ACT_MP_RELOAD_SWIM_SECONDARY_END, false },
  2911. { ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_SECONDARY, false },
  2912. { ACT_MP_RELOAD_AIRWALK_LOOP, ACT_MP_RELOAD_AIRWALK_SECONDARY_LOOP, false },
  2913. { ACT_MP_RELOAD_AIRWALK_END,ACT_MP_RELOAD_AIRWALK_SECONDARY_END,false },
  2914. { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_SECONDARY, false },
  2915. { ACT_MP_GRENADE1_DRAW, ACT_MP_SECONDARY_GRENADE1_DRAW, false },
  2916. { ACT_MP_GRENADE1_IDLE, ACT_MP_SECONDARY_GRENADE1_IDLE, false },
  2917. { ACT_MP_GRENADE1_ATTACK, ACT_MP_SECONDARY_GRENADE1_ATTACK, false },
  2918. { ACT_MP_GRENADE2_DRAW, ACT_MP_SECONDARY_GRENADE2_DRAW, false },
  2919. { ACT_MP_GRENADE2_IDLE, ACT_MP_SECONDARY_GRENADE2_IDLE, false },
  2920. { ACT_MP_GRENADE2_ATTACK, ACT_MP_SECONDARY_GRENADE2_ATTACK, false },
  2921. { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2922. { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2923. { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2924. { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2925. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_SECONDARY, false },
  2926. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_SECONDARY, false },
  2927. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_SECONDARY, false },
  2928. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_SECONDARY, false },
  2929. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_SECONDARY, false },
  2930. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_SECONDARY, false },
  2931. };
  2932. acttable_t s_acttablePrimary2[] =
  2933. {
  2934. { ACT_MP_STAND_IDLE, ACT_MP_STAND_PRIMARY, false },
  2935. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_PRIMARY, false },
  2936. { ACT_MP_DEPLOYED, ACT_MP_DEPLOYED_PRIMARY, false },
  2937. { ACT_MP_CROUCH_DEPLOYED, ACT_MP_CROUCHWALK_DEPLOYED, false },
  2938. { ACT_MP_CROUCH_DEPLOYED_IDLE, ACT_MP_CROUCH_DEPLOYED_IDLE, false },
  2939. { ACT_MP_RUN, ACT_MP_RUN_PRIMARY, false },
  2940. { ACT_MP_WALK, ACT_MP_WALK_PRIMARY, false },
  2941. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_PRIMARY, false },
  2942. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_PRIMARY, false },
  2943. { ACT_MP_JUMP, ACT_MP_JUMP_PRIMARY, false },
  2944. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_PRIMARY, false },
  2945. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_PRIMARY, false },
  2946. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_PRIMARY, false },
  2947. { ACT_MP_SWIM, ACT_MP_SWIM_PRIMARY, false },
  2948. { ACT_MP_SWIM_DEPLOYED, ACT_MP_SWIM_DEPLOYED_PRIMARY, false },
  2949. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_PRIMARY, false },
  2950. { ACT_MP_ATTACK_STAND_PRIMARY_SUPER, ACT_MP_ATTACK_STAND_PRIMARY_SUPER, false },
  2951. { ACT_MP_ATTACK_CROUCH_PRIMARY_SUPER, ACT_MP_ATTACK_CROUCH_PRIMARY_SUPER, false },
  2952. { ACT_MP_ATTACK_SWIM_PRIMARY_SUPER, ACT_MP_ATTACK_SWIM_PRIMARY_SUPER, false },
  2953. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_PRIMARY_ALT, false },
  2954. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_PRIMARY_ALT, false },
  2955. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_PRIMARY_ALT, false },
  2956. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_PRIMARY, false },
  2957. { ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_PRIMARY_ALT, false },
  2958. { ACT_MP_RELOAD_STAND_LOOP, ACT_MP_RELOAD_STAND_PRIMARY_LOOP_ALT, false },
  2959. { ACT_MP_RELOAD_STAND_END, ACT_MP_RELOAD_STAND_PRIMARY_END_ALT, false },
  2960. { ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_PRIMARY_ALT, false },
  2961. { ACT_MP_RELOAD_CROUCH_LOOP,ACT_MP_RELOAD_CROUCH_PRIMARY_LOOP_ALT, false },
  2962. { ACT_MP_RELOAD_CROUCH_END, ACT_MP_RELOAD_CROUCH_PRIMARY_END_ALT, false },
  2963. { ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_PRIMARY_ALT, false },
  2964. { ACT_MP_RELOAD_SWIM_LOOP, ACT_MP_RELOAD_SWIM_PRIMARY_LOOP, false },
  2965. { ACT_MP_RELOAD_SWIM_END, ACT_MP_RELOAD_SWIM_PRIMARY_END, false },
  2966. { ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_PRIMARY_ALT, false },
  2967. { ACT_MP_RELOAD_AIRWALK_LOOP, ACT_MP_RELOAD_AIRWALK_PRIMARY_LOOP_ALT, false },
  2968. { ACT_MP_RELOAD_AIRWALK_END,ACT_MP_RELOAD_AIRWALK_PRIMARY_END_ALT, false },
  2969. { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2970. { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2971. { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2972. { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  2973. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_PRIMARY, false },
  2974. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_PRIMARY, false },
  2975. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_PRIMARY, false },
  2976. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_PRIMARY, false },
  2977. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_PRIMARY, false },
  2978. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_PRIMARY, false },
  2979. };
  2980. acttable_t s_acttableSecondary2[] =
  2981. {
  2982. { ACT_MP_STAND_IDLE, ACT_MP_STAND_SECONDARY2, false },
  2983. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_SECONDARY2, false },
  2984. { ACT_MP_RUN, ACT_MP_RUN_SECONDARY2, false },
  2985. { ACT_MP_WALK, ACT_MP_WALK_SECONDARY2, false },
  2986. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_SECONDARY2, false },
  2987. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_SECONDARY2, false },
  2988. { ACT_MP_JUMP, ACT_MP_JUMP_SECONDARY2, false },
  2989. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_SECONDARY2, false },
  2990. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_SECONDARY2, false },
  2991. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_SECONDARY2, false },
  2992. { ACT_MP_SWIM, ACT_MP_SWIM_SECONDARY2, false },
  2993. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_SECONDARY2, false },
  2994. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_SECONDARY2, false },
  2995. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_SECONDARY2, false },
  2996. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_SECONDARY2, false },
  2997. { ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_SECONDARY2, false },
  2998. { ACT_MP_RELOAD_STAND_LOOP, ACT_MP_RELOAD_STAND_SECONDARY2_LOOP, false },
  2999. { ACT_MP_RELOAD_STAND_END, ACT_MP_RELOAD_STAND_SECONDARY2_END, false },
  3000. { ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_SECONDARY2, false },
  3001. { ACT_MP_RELOAD_CROUCH_LOOP,ACT_MP_RELOAD_CROUCH_SECONDARY2_LOOP,false },
  3002. { ACT_MP_RELOAD_CROUCH_END, ACT_MP_RELOAD_CROUCH_SECONDARY2_END, false },
  3003. { ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_SECONDARY2, false },
  3004. { ACT_MP_RELOAD_SWIM_LOOP, ACT_MP_RELOAD_SWIM_SECONDARY2_LOOP, false },
  3005. { ACT_MP_RELOAD_SWIM_END, ACT_MP_RELOAD_SWIM_SECONDARY2_END, false },
  3006. { ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_SECONDARY2, false },
  3007. { ACT_MP_RELOAD_AIRWALK_LOOP, ACT_MP_RELOAD_AIRWALK_SECONDARY2_LOOP, false },
  3008. { ACT_MP_RELOAD_AIRWALK_END,ACT_MP_RELOAD_AIRWALK_SECONDARY2_END,false },
  3009. { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  3010. { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  3011. { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  3012. { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  3013. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_SECONDARY, false },
  3014. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_SECONDARY, false },
  3015. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_SECONDARY, false },
  3016. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_SECONDARY, false },
  3017. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_SECONDARY, false },
  3018. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_SECONDARY, false },
  3019. };
  3020. acttable_t s_acttableMelee[] =
  3021. {
  3022. { ACT_MP_STAND_IDLE, ACT_MP_STAND_MELEE, false },
  3023. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_MELEE, false },
  3024. { ACT_MP_RUN, ACT_MP_RUN_MELEE, false },
  3025. { ACT_MP_WALK, ACT_MP_WALK_MELEE, false },
  3026. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_MELEE, false },
  3027. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_MELEE, false },
  3028. { ACT_MP_JUMP, ACT_MP_JUMP_MELEE, false },
  3029. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_MELEE, false },
  3030. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_MELEE, false },
  3031. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_MELEE, false },
  3032. { ACT_MP_SWIM, ACT_MP_SWIM_MELEE, false },
  3033. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_MELEE, false },
  3034. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_MELEE, false },
  3035. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_MELEE, false },
  3036. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_MELEE, false },
  3037. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_MELEE, false },
  3038. { ACT_MP_ATTACK_STAND_SECONDARYFIRE, ACT_MP_ATTACK_STAND_MELEE_SECONDARY, false },
  3039. { ACT_MP_ATTACK_CROUCH_SECONDARYFIRE, ACT_MP_ATTACK_CROUCH_MELEE_SECONDARY,false },
  3040. { ACT_MP_ATTACK_SWIM_SECONDARYFIRE, ACT_MP_ATTACK_SWIM_MELEE, false },
  3041. { ACT_MP_ATTACK_AIRWALK_SECONDARYFIRE, ACT_MP_ATTACK_AIRWALK_MELEE, false },
  3042. { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_MELEE, false },
  3043. { ACT_MP_GRENADE1_DRAW, ACT_MP_MELEE_GRENADE1_DRAW, false },
  3044. { ACT_MP_GRENADE1_IDLE, ACT_MP_MELEE_GRENADE1_IDLE, false },
  3045. { ACT_MP_GRENADE1_ATTACK, ACT_MP_MELEE_GRENADE1_ATTACK, false },
  3046. { ACT_MP_GRENADE2_DRAW, ACT_MP_MELEE_GRENADE2_DRAW, false },
  3047. { ACT_MP_GRENADE2_IDLE, ACT_MP_MELEE_GRENADE2_IDLE, false },
  3048. { ACT_MP_GRENADE2_ATTACK, ACT_MP_MELEE_GRENADE2_ATTACK, false },
  3049. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_MELEE, false },
  3050. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_MELEE, false },
  3051. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_MELEE, false },
  3052. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_MELEE, false },
  3053. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_MELEE, false },
  3054. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_MELEE, false },
  3055. };
  3056. acttable_t s_acttableItem1[] =
  3057. {
  3058. { ACT_MP_STAND_IDLE, ACT_MP_STAND_ITEM1, false },
  3059. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_ITEM1, false },
  3060. { ACT_MP_RUN, ACT_MP_RUN_ITEM1, false },
  3061. { ACT_MP_WALK, ACT_MP_WALK_ITEM1, false },
  3062. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_ITEM1, false },
  3063. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_ITEM1, false },
  3064. { ACT_MP_JUMP, ACT_MP_JUMP_ITEM1, false },
  3065. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_ITEM1, false },
  3066. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_ITEM1, false },
  3067. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_ITEM1, false },
  3068. { ACT_MP_SWIM, ACT_MP_SWIM_ITEM1, false },
  3069. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_ITEM1, false },
  3070. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_ITEM1, false },
  3071. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_ITEM1, false },
  3072. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_ITEM1, false },
  3073. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_ITEM1, false },
  3074. { ACT_MP_ATTACK_STAND_SECONDARYFIRE, ACT_MP_ATTACK_STAND_ITEM1_SECONDARY, false },
  3075. { ACT_MP_ATTACK_CROUCH_SECONDARYFIRE, ACT_MP_ATTACK_CROUCH_ITEM1_SECONDARY,false },
  3076. { ACT_MP_ATTACK_SWIM_SECONDARYFIRE, ACT_MP_ATTACK_SWIM_ITEM1, false },
  3077. { ACT_MP_ATTACK_AIRWALK_SECONDARYFIRE, ACT_MP_ATTACK_AIRWALK_ITEM1, false },
  3078. { ACT_MP_DEPLOYED, ACT_MP_DEPLOYED_ITEM1, false },
  3079. { ACT_MP_DEPLOYED_IDLE, ACT_MP_DEPLOYED_IDLE_ITEM1, false },
  3080. { ACT_MP_CROUCH_DEPLOYED, ACT_MP_CROUCHWALK_DEPLOYED_ITEM1, false },
  3081. { ACT_MP_CROUCH_DEPLOYED_IDLE, ACT_MP_CROUCH_DEPLOYED_IDLE_ITEM1, false },
  3082. //{ ACT_MP_ATTACK_STAND_PRIMARY_DEPLOYED, ACT_MP_ATTACK_STAND_PRIMARY_DEPLOYED_ITEM1, false },
  3083. //{ ACT_MP_ATTACK_CROUCH_PRIMARY_DEPLOYED,ACT_MP_ATTACK_CROUCH_PRIMARY_DEPLOYED_ITEM1, false },
  3084. { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_ITEM1, false },
  3085. { ACT_MP_GRENADE1_DRAW, ACT_MP_ITEM1_GRENADE1_DRAW, false },
  3086. { ACT_MP_GRENADE1_IDLE, ACT_MP_ITEM1_GRENADE1_IDLE, false },
  3087. { ACT_MP_GRENADE1_ATTACK, ACT_MP_ITEM1_GRENADE1_ATTACK, false },
  3088. { ACT_MP_GRENADE2_DRAW, ACT_MP_ITEM1_GRENADE2_DRAW, false },
  3089. { ACT_MP_GRENADE2_IDLE, ACT_MP_ITEM1_GRENADE2_IDLE, false },
  3090. { ACT_MP_GRENADE2_ATTACK, ACT_MP_ITEM1_GRENADE2_ATTACK, false },
  3091. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_ITEM1, false },
  3092. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_ITEM1, false },
  3093. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_ITEM1, false },
  3094. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_ITEM1, false },
  3095. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_ITEM1, false },
  3096. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_ITEM1, false },
  3097. };
  3098. acttable_t s_acttableItem2[] =
  3099. {
  3100. { ACT_MP_STAND_IDLE, ACT_MP_STAND_ITEM2, false },
  3101. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_ITEM2, false },
  3102. { ACT_MP_RUN, ACT_MP_RUN_ITEM2, false },
  3103. { ACT_MP_WALK, ACT_MP_WALK_ITEM2, false },
  3104. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_ITEM2, false },
  3105. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_ITEM2, false },
  3106. { ACT_MP_JUMP, ACT_MP_JUMP_ITEM2, false },
  3107. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_ITEM2, false },
  3108. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_ITEM2, false },
  3109. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_ITEM2, false },
  3110. { ACT_MP_SWIM, ACT_MP_SWIM_ITEM2, false },
  3111. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_ITEM2, false },
  3112. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_ITEM2, false },
  3113. { ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_ITEM2, false },
  3114. { ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_ITEM2, false },
  3115. { ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_ITEM2, false },
  3116. { ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_ITEM2, false },
  3117. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_ITEM2, false },
  3118. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_ITEM2, false },
  3119. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_ITEM2, false },
  3120. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_ITEM2, false },
  3121. { ACT_MP_DEPLOYED, ACT_MP_DEPLOYED_ITEM2, false },
  3122. { ACT_MP_DEPLOYED_IDLE, ACT_MP_DEPLOYED_IDLE_ITEM2, false },
  3123. { ACT_MP_CROUCH_DEPLOYED, ACT_MP_CROUCHWALK_DEPLOYED_ITEM2, false },
  3124. { ACT_MP_CROUCH_DEPLOYED_IDLE, ACT_MP_CROUCH_DEPLOYED_IDLE_ITEM2, false },
  3125. //{ ACT_MP_ATTACK_STAND_PRIMARY_DEPLOYED, ACT_MP_ATTACK_STAND_PRIMARY_DEPLOYED_ITEM2, false },
  3126. //{ ACT_MP_ATTACK_CROUCH_PRIMARY_DEPLOYED,ACT_MP_ATTACK_CROUCH_PRIMARY_DEPLOYED_ITEM2, false },
  3127. { ACT_MP_ATTACK_STAND_SECONDARYFIRE, ACT_MP_ATTACK_STAND_ITEM2_SECONDARY, false },
  3128. { ACT_MP_ATTACK_CROUCH_SECONDARYFIRE, ACT_MP_ATTACK_CROUCH_ITEM2_SECONDARY,false },
  3129. { ACT_MP_ATTACK_SWIM_SECONDARYFIRE, ACT_MP_ATTACK_SWIM_ITEM2, false },
  3130. { ACT_MP_ATTACK_AIRWALK_SECONDARYFIRE, ACT_MP_ATTACK_AIRWALK_ITEM2, false },
  3131. { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_ITEM2, false },
  3132. { ACT_MP_GRENADE1_DRAW, ACT_MP_ITEM2_GRENADE1_DRAW, false },
  3133. { ACT_MP_GRENADE1_IDLE, ACT_MP_ITEM2_GRENADE1_IDLE, false },
  3134. { ACT_MP_GRENADE1_ATTACK, ACT_MP_ITEM2_GRENADE1_ATTACK, false },
  3135. { ACT_MP_GRENADE2_DRAW, ACT_MP_ITEM2_GRENADE2_DRAW, false },
  3136. { ACT_MP_GRENADE2_IDLE, ACT_MP_ITEM2_GRENADE2_IDLE, false },
  3137. { ACT_MP_GRENADE2_ATTACK, ACT_MP_ITEM2_GRENADE2_ATTACK, false },
  3138. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_ITEM2, false },
  3139. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_ITEM2, false },
  3140. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_ITEM2, false },
  3141. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_ITEM2, false },
  3142. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_ITEM2, false },
  3143. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_ITEM2, false },
  3144. };
  3145. acttable_t s_acttableBuilding[] =
  3146. {
  3147. { ACT_MP_STAND_IDLE, ACT_MP_STAND_BUILDING, false },
  3148. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_BUILDING, false },
  3149. { ACT_MP_RUN, ACT_MP_RUN_BUILDING, false },
  3150. { ACT_MP_WALK, ACT_MP_WALK_BUILDING, false },
  3151. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_BUILDING, false },
  3152. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_BUILDING, false },
  3153. { ACT_MP_JUMP, ACT_MP_JUMP_BUILDING, false },
  3154. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_BUILDING, false },
  3155. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_BUILDING, false },
  3156. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_BUILDING, false },
  3157. { ACT_MP_SWIM, ACT_MP_SWIM_BUILDING, false },
  3158. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_BUILDING, false },
  3159. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_BUILDING, false },
  3160. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_BUILDING, false },
  3161. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_BUILDING, false },
  3162. { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING, false },
  3163. { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING, false },
  3164. { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING, false },
  3165. { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING, false },
  3166. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_BUILDING, false },
  3167. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_BUILDING, false },
  3168. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_BUILDING, false },
  3169. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_BUILDING, false },
  3170. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_BUILDING, false },
  3171. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_BUILDING, false },
  3172. };
  3173. acttable_t s_acttablePDA[] =
  3174. {
  3175. { ACT_MP_STAND_IDLE, ACT_MP_STAND_PDA, false },
  3176. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_PDA, false },
  3177. { ACT_MP_RUN, ACT_MP_RUN_PDA, false },
  3178. { ACT_MP_WALK, ACT_MP_WALK_PDA, false },
  3179. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_PDA, false },
  3180. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_PDA, false },
  3181. { ACT_MP_JUMP, ACT_MP_JUMP_PDA, false },
  3182. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_PDA, false },
  3183. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_PDA, false },
  3184. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_PDA, false },
  3185. { ACT_MP_SWIM, ACT_MP_SWIM_PDA, false },
  3186. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_PDA, false },
  3187. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_PDA, false },
  3188. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_PDA, false },
  3189. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_PDA, false },
  3190. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_PDA, false },
  3191. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_PDA, false },
  3192. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_PDA, false },
  3193. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_PDA, false },
  3194. };
  3195. acttable_t s_acttableMeleeAllclass[] =
  3196. {
  3197. { ACT_MP_STAND_IDLE, ACT_MP_STAND_MELEE_ALLCLASS, false },
  3198. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_MELEE_ALLCLASS, false },
  3199. { ACT_MP_RUN, ACT_MP_RUN_MELEE_ALLCLASS, false },
  3200. { ACT_MP_WALK, ACT_MP_WALK_MELEE_ALLCLASS, false },
  3201. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_MELEE_ALLCLASS, false },
  3202. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_MELEE_ALLCLASS, false },
  3203. { ACT_MP_JUMP, ACT_MP_JUMP_MELEE_ALLCLASS, false },
  3204. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_MELEE_ALLCLASS, false },
  3205. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_MELEE_ALLCLASS, false },
  3206. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_MELEE_ALLCLASS, false },
  3207. { ACT_MP_SWIM, ACT_MP_SWIM_MELEE_ALLCLASS, false },
  3208. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_MELEE, false },
  3209. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_MELEE_ALLCLASS, false },
  3210. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_MELEE_ALLCLASS, false },
  3211. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_MELEE_ALLCLASS, false },
  3212. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_MELEE_ALLCLASS, false },
  3213. { ACT_MP_ATTACK_STAND_SECONDARYFIRE, ACT_MP_ATTACK_STAND_MELEE_SECONDARY, false },
  3214. { ACT_MP_ATTACK_CROUCH_SECONDARYFIRE, ACT_MP_ATTACK_CROUCH_MELEE_SECONDARY,false },
  3215. { ACT_MP_ATTACK_SWIM_SECONDARYFIRE, ACT_MP_ATTACK_SWIM_MELEE_ALLCLASS, false },
  3216. { ACT_MP_ATTACK_AIRWALK_SECONDARYFIRE, ACT_MP_ATTACK_AIRWALK_MELEE_ALLCLASS, false },
  3217. { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_MELEE, false },
  3218. { ACT_MP_GRENADE1_DRAW, ACT_MP_MELEE_GRENADE1_DRAW, false },
  3219. { ACT_MP_GRENADE1_IDLE, ACT_MP_MELEE_GRENADE1_IDLE, false },
  3220. { ACT_MP_GRENADE1_ATTACK, ACT_MP_MELEE_GRENADE1_ATTACK, false },
  3221. { ACT_MP_GRENADE2_DRAW, ACT_MP_MELEE_GRENADE2_DRAW, false },
  3222. { ACT_MP_GRENADE2_IDLE, ACT_MP_MELEE_GRENADE2_IDLE, false },
  3223. { ACT_MP_GRENADE2_ATTACK, ACT_MP_MELEE_GRENADE2_ATTACK, false },
  3224. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_MELEE, false },
  3225. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_MELEE, false },
  3226. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_MELEE, false },
  3227. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_MELEE, false },
  3228. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_MELEE, false },
  3229. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_MELEE, false },
  3230. };
  3231. ConVar mp_forceactivityset( "mp_forceactivityset", "-1", FCVAR_CHEAT|FCVAR_REPLICATED|FCVAR_DEVELOPMENTONLY );
  3232. int CTFWeaponBase::GetActivityWeaponRole() const
  3233. {
  3234. int iWeaponRole = GetTFWpnData().m_iWeaponType;
  3235. const CEconItemView *pEconItemView = GetAttributeContainer()->GetItem();
  3236. if ( pEconItemView )
  3237. {
  3238. int iMaybeOverrideAnimSlot = pEconItemView->GetAnimationSlot();
  3239. if ( iMaybeOverrideAnimSlot >= 0 )
  3240. {
  3241. iWeaponRole = iMaybeOverrideAnimSlot;
  3242. }
  3243. }
  3244. if ( mp_forceactivityset.GetInt() >= 0 )
  3245. {
  3246. iWeaponRole = mp_forceactivityset.GetInt();
  3247. }
  3248. return iWeaponRole;
  3249. }
  3250. acttable_t *CTFWeaponBase::ActivityList( int &iActivityCount )
  3251. {
  3252. int iWeaponRole = GetActivityWeaponRole();
  3253. #ifdef CLIENT_DLL
  3254. CTFPlayer *pPlayer = GetTFPlayerOwner();
  3255. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pPlayer->IsEnemyPlayer() )
  3256. {
  3257. CTFWeaponBase *pDisguiseWeapon = pPlayer->m_Shared.GetDisguiseWeapon();
  3258. if ( pDisguiseWeapon && pDisguiseWeapon != this )
  3259. {
  3260. return pDisguiseWeapon->ActivityList( iActivityCount );
  3261. }
  3262. }
  3263. #endif
  3264. acttable_t *pTable;
  3265. switch( iWeaponRole )
  3266. {
  3267. case TF_WPN_TYPE_PRIMARY:
  3268. default:
  3269. iActivityCount = ARRAYSIZE( s_acttablePrimary );
  3270. pTable = s_acttablePrimary;
  3271. break;
  3272. case TF_WPN_TYPE_SECONDARY:
  3273. iActivityCount = ARRAYSIZE( s_acttableSecondary );
  3274. pTable = s_acttableSecondary;
  3275. break;
  3276. case TF_WPN_TYPE_MELEE:
  3277. iActivityCount = ARRAYSIZE( s_acttableMelee );
  3278. pTable = s_acttableMelee;
  3279. break;
  3280. case TF_WPN_TYPE_BUILDING:
  3281. iActivityCount = ARRAYSIZE( s_acttableBuilding );
  3282. pTable = s_acttableBuilding;
  3283. break;
  3284. case TF_WPN_TYPE_PDA:
  3285. iActivityCount = ARRAYSIZE( s_acttablePDA );
  3286. pTable = s_acttablePDA;
  3287. break;
  3288. case TF_WPN_TYPE_ITEM1:
  3289. iActivityCount = ARRAYSIZE( s_acttableItem1 );
  3290. pTable = s_acttableItem1;
  3291. break;
  3292. case TF_WPN_TYPE_ITEM2:
  3293. iActivityCount = ARRAYSIZE( s_acttableItem2 );
  3294. pTable = s_acttableItem2;
  3295. break;
  3296. case TF_WPN_TYPE_MELEE_ALLCLASS:
  3297. iActivityCount = ARRAYSIZE( s_acttableMeleeAllclass );
  3298. pTable = s_acttableMeleeAllclass;
  3299. break;
  3300. case TF_WPN_TYPE_SECONDARY2:
  3301. iActivityCount = ARRAYSIZE( s_acttableSecondary2 );
  3302. pTable = s_acttableSecondary2;
  3303. break;
  3304. case TF_WPN_TYPE_PRIMARY2:
  3305. iActivityCount = ARRAYSIZE( s_acttablePrimary2 );
  3306. pTable = s_acttablePrimary2;
  3307. break;
  3308. }
  3309. return pTable;
  3310. }
  3311. typedef struct
  3312. {
  3313. int baseAct;
  3314. int weaponAct;
  3315. int weaponRole;
  3316. } viewmodelacttable_t;
  3317. // Remaps viewmodel activities to specific ones for the weapon role.
  3318. // Needed this for weapons that bonemerge themselves to the hand models to create their viewmodel.
  3319. // The hand model needs to have all the animations, and be able to choose the right anims to play for the active weapon.
  3320. // We use this acttable to remap the base viewmodel anims to the right one for the weapon.
  3321. viewmodelacttable_t s_viewmodelacttable[] =
  3322. {
  3323. { ACT_VM_DRAW, ACT_PRIMARY_VM_DRAW, TF_WPN_TYPE_PRIMARY },
  3324. { ACT_VM_HOLSTER, ACT_PRIMARY_VM_HOLSTER, TF_WPN_TYPE_PRIMARY },
  3325. { ACT_VM_IDLE, ACT_PRIMARY_VM_IDLE, TF_WPN_TYPE_PRIMARY },
  3326. { ACT_VM_PULLBACK, ACT_PRIMARY_VM_PULLBACK, TF_WPN_TYPE_PRIMARY },
  3327. { ACT_VM_PRIMARYATTACK, ACT_PRIMARY_VM_PRIMARYATTACK, TF_WPN_TYPE_PRIMARY },
  3328. { ACT_VM_SECONDARYATTACK, ACT_PRIMARY_VM_SECONDARYATTACK, TF_WPN_TYPE_PRIMARY },
  3329. { ACT_VM_RELOAD, ACT_PRIMARY_VM_RELOAD, TF_WPN_TYPE_PRIMARY },
  3330. { ACT_RELOAD_START, ACT_PRIMARY_RELOAD_START, TF_WPN_TYPE_PRIMARY },
  3331. { ACT_RELOAD_FINISH, ACT_PRIMARY_RELOAD_FINISH, TF_WPN_TYPE_PRIMARY },
  3332. { ACT_VM_DRYFIRE, ACT_PRIMARY_VM_DRYFIRE, TF_WPN_TYPE_PRIMARY },
  3333. { ACT_VM_IDLE_TO_LOWERED, ACT_PRIMARY_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_PRIMARY },
  3334. { ACT_VM_IDLE_LOWERED, ACT_PRIMARY_VM_IDLE_LOWERED, TF_WPN_TYPE_PRIMARY },
  3335. { ACT_VM_LOWERED_TO_IDLE, ACT_PRIMARY_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_PRIMARY },
  3336. { ACT_MP_ATTACK_STAND_PREFIRE, ACT_PRIMARY_ATTACK_STAND_PREFIRE, TF_WPN_TYPE_PRIMARY },
  3337. { ACT_MP_ATTACK_STAND_POSTFIRE, ACT_PRIMARY_ATTACK_STAND_POSTFIRE, TF_WPN_TYPE_PRIMARY },
  3338. { ACT_MP_ATTACK_STAND_STARTFIRE, ACT_PRIMARY_ATTACK_STAND_STARTFIRE, TF_WPN_TYPE_PRIMARY },
  3339. { ACT_MP_ATTACK_CROUCH_PREFIRE, ACT_PRIMARY_ATTACK_CROUCH_PREFIRE, TF_WPN_TYPE_PRIMARY },
  3340. { ACT_MP_ATTACK_CROUCH_POSTFIRE, ACT_PRIMARY_ATTACK_CROUCH_POSTFIRE, TF_WPN_TYPE_PRIMARY },
  3341. { ACT_MP_ATTACK_SWIM_PREFIRE, ACT_PRIMARY_ATTACK_SWIM_PREFIRE, TF_WPN_TYPE_PRIMARY },
  3342. { ACT_MP_ATTACK_SWIM_POSTFIRE, ACT_PRIMARY_ATTACK_SWIM_POSTFIRE, TF_WPN_TYPE_PRIMARY },
  3343. { ACT_VM_DRAW, ACT_SECONDARY_VM_DRAW, TF_WPN_TYPE_SECONDARY },
  3344. { ACT_VM_HOLSTER, ACT_SECONDARY_VM_HOLSTER, TF_WPN_TYPE_SECONDARY },
  3345. { ACT_VM_IDLE, ACT_SECONDARY_VM_IDLE, TF_WPN_TYPE_SECONDARY },
  3346. { ACT_VM_PULLBACK, ACT_SECONDARY_VM_PULLBACK, TF_WPN_TYPE_SECONDARY },
  3347. { ACT_VM_PRIMARYATTACK, ACT_SECONDARY_VM_PRIMARYATTACK, TF_WPN_TYPE_SECONDARY },
  3348. { ACT_VM_SECONDARYATTACK, ACT_SECONDARY_VM_SECONDARYATTACK, TF_WPN_TYPE_SECONDARY },
  3349. { ACT_VM_RELOAD, ACT_SECONDARY_VM_RELOAD, TF_WPN_TYPE_SECONDARY },
  3350. { ACT_RELOAD_START, ACT_SECONDARY_RELOAD_START, TF_WPN_TYPE_SECONDARY },
  3351. { ACT_RELOAD_FINISH, ACT_SECONDARY_RELOAD_FINISH, TF_WPN_TYPE_SECONDARY },
  3352. { ACT_VM_DRYFIRE, ACT_SECONDARY_VM_DRYFIRE, TF_WPN_TYPE_SECONDARY },
  3353. { ACT_VM_IDLE_TO_LOWERED, ACT_SECONDARY_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_SECONDARY },
  3354. { ACT_VM_IDLE_LOWERED, ACT_SECONDARY_VM_IDLE_LOWERED, TF_WPN_TYPE_SECONDARY },
  3355. { ACT_VM_LOWERED_TO_IDLE, ACT_SECONDARY_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_SECONDARY },
  3356. { ACT_MP_ATTACK_STAND_PREFIRE, ACT_SECONDARY_ATTACK_STAND_PREFIRE, TF_WPN_TYPE_SECONDARY },
  3357. { ACT_MP_ATTACK_STAND_POSTFIRE, ACT_SECONDARY_ATTACK_STAND_POSTFIRE, TF_WPN_TYPE_SECONDARY },
  3358. { ACT_MP_ATTACK_STAND_STARTFIRE, ACT_SECONDARY_ATTACK_STAND_STARTFIRE, TF_WPN_TYPE_SECONDARY },
  3359. { ACT_MP_ATTACK_CROUCH_PREFIRE, ACT_SECONDARY_ATTACK_CROUCH_PREFIRE, TF_WPN_TYPE_SECONDARY },
  3360. { ACT_MP_ATTACK_CROUCH_POSTFIRE, ACT_SECONDARY_ATTACK_CROUCH_POSTFIRE, TF_WPN_TYPE_SECONDARY },
  3361. { ACT_MP_ATTACK_SWIM_PREFIRE, ACT_SECONDARY_ATTACK_SWIM_PREFIRE, TF_WPN_TYPE_SECONDARY },
  3362. { ACT_MP_ATTACK_SWIM_POSTFIRE, ACT_SECONDARY_ATTACK_SWIM_POSTFIRE, TF_WPN_TYPE_SECONDARY },
  3363. { ACT_VM_DRAW, ACT_MELEE_VM_DRAW, TF_WPN_TYPE_MELEE },
  3364. { ACT_VM_HOLSTER, ACT_MELEE_VM_HOLSTER, TF_WPN_TYPE_MELEE },
  3365. { ACT_VM_IDLE, ACT_MELEE_VM_IDLE, TF_WPN_TYPE_MELEE },
  3366. { ACT_VM_PULLBACK, ACT_MELEE_VM_PULLBACK, TF_WPN_TYPE_MELEE },
  3367. { ACT_VM_PRIMARYATTACK, ACT_MELEE_VM_PRIMARYATTACK, TF_WPN_TYPE_MELEE },
  3368. { ACT_VM_SECONDARYATTACK, ACT_MELEE_VM_SECONDARYATTACK, TF_WPN_TYPE_MELEE },
  3369. { ACT_VM_RELOAD, ACT_MELEE_VM_RELOAD, TF_WPN_TYPE_MELEE },
  3370. { ACT_VM_DRYFIRE, ACT_MELEE_VM_DRYFIRE, TF_WPN_TYPE_MELEE },
  3371. { ACT_VM_IDLE_TO_LOWERED, ACT_MELEE_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_MELEE },
  3372. { ACT_VM_IDLE_LOWERED, ACT_MELEE_VM_IDLE_LOWERED, TF_WPN_TYPE_MELEE },
  3373. { ACT_VM_LOWERED_TO_IDLE, ACT_MELEE_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_MELEE },
  3374. { ACT_VM_HITCENTER, ACT_MELEE_VM_HITCENTER, TF_WPN_TYPE_MELEE },
  3375. { ACT_VM_SWINGHARD, ACT_MELEE_VM_SWINGHARD, TF_WPN_TYPE_MELEE },
  3376. { ACT_MP_ATTACK_STAND_PREFIRE, ACT_MELEE_ATTACK_STAND_PREFIRE, TF_WPN_TYPE_MELEE },
  3377. { ACT_MP_ATTACK_STAND_POSTFIRE, ACT_MELEE_ATTACK_STAND_POSTFIRE, TF_WPN_TYPE_MELEE },
  3378. { ACT_MP_ATTACK_STAND_STARTFIRE, ACT_MELEE_ATTACK_STAND_STARTFIRE, TF_WPN_TYPE_MELEE },
  3379. { ACT_MP_ATTACK_CROUCH_PREFIRE, ACT_MELEE_ATTACK_CROUCH_PREFIRE, TF_WPN_TYPE_MELEE },
  3380. { ACT_MP_ATTACK_CROUCH_POSTFIRE, ACT_MELEE_ATTACK_CROUCH_POSTFIRE, TF_WPN_TYPE_MELEE },
  3381. { ACT_MP_ATTACK_SWIM_PREFIRE, ACT_MELEE_ATTACK_SWIM_PREFIRE, TF_WPN_TYPE_MELEE },
  3382. { ACT_MP_ATTACK_SWIM_POSTFIRE, ACT_MELEE_ATTACK_SWIM_POSTFIRE, TF_WPN_TYPE_MELEE },
  3383. // Scout Pack -- Bat Special State Support
  3384. { ACT_VM_DRAW_SPECIAL, ACT_VM_DRAW_SPECIAL, TF_WPN_TYPE_MELEE },
  3385. { ACT_VM_HOLSTER_SPECIAL, ACT_VM_HOLSTER_SPECIAL, TF_WPN_TYPE_MELEE },
  3386. { ACT_VM_IDLE_SPECIAL, ACT_VM_IDLE_SPECIAL, TF_WPN_TYPE_MELEE },
  3387. { ACT_VM_PULLBACK_SPECIAL, ACT_VM_PULLBACK_SPECIAL, TF_WPN_TYPE_MELEE },
  3388. { ACT_VM_PRIMARYATTACK_SPECIAL, ACT_VM_PRIMARYATTACK_SPECIAL, TF_WPN_TYPE_MELEE },
  3389. { ACT_VM_SECONDARYATTACK_SPECIAL, ACT_VM_SECONDARYATTACK_SPECIAL, TF_WPN_TYPE_MELEE },
  3390. { ACT_VM_HITCENTER_SPECIAL, ACT_VM_HITCENTER_SPECIAL, TF_WPN_TYPE_MELEE },
  3391. { ACT_VM_SWINGHARD_SPECIAL, ACT_VM_SWINGHARD_SPECIAL, TF_WPN_TYPE_MELEE },
  3392. { ACT_VM_IDLE_TO_LOWERED_SPECIAL, ACT_VM_IDLE_TO_LOWERED_SPECIAL, TF_WPN_TYPE_MELEE },
  3393. { ACT_VM_IDLE_LOWERED_SPECIAL, ACT_VM_IDLE_LOWERED_SPECIAL, TF_WPN_TYPE_MELEE },
  3394. { ACT_VM_LOWERED_TO_IDLE_SPECIAL, ACT_VM_LOWERED_TO_IDLE_SPECIAL, TF_WPN_TYPE_MELEE },
  3395. // Spy Pack -- New Knife Anims
  3396. { ACT_BACKSTAB_VM_DOWN, ACT_BACKSTAB_VM_DOWN, TF_WPN_TYPE_MELEE },
  3397. { ACT_BACKSTAB_VM_UP, ACT_BACKSTAB_VM_UP, TF_WPN_TYPE_MELEE },
  3398. { ACT_BACKSTAB_VM_IDLE, ACT_BACKSTAB_VM_IDLE, TF_WPN_TYPE_MELEE },
  3399. { ACT_VM_DRAW, ACT_PDA_VM_DRAW, TF_WPN_TYPE_PDA },
  3400. { ACT_VM_HOLSTER, ACT_PDA_VM_HOLSTER, TF_WPN_TYPE_PDA },
  3401. { ACT_VM_IDLE, ACT_PDA_VM_IDLE, TF_WPN_TYPE_PDA },
  3402. { ACT_VM_PULLBACK, ACT_PDA_VM_PULLBACK, TF_WPN_TYPE_PDA },
  3403. { ACT_VM_PRIMARYATTACK, ACT_PDA_VM_PRIMARYATTACK, TF_WPN_TYPE_PDA },
  3404. { ACT_VM_SECONDARYATTACK, ACT_PDA_VM_SECONDARYATTACK, TF_WPN_TYPE_PDA },
  3405. { ACT_VM_RELOAD, ACT_PDA_VM_RELOAD, TF_WPN_TYPE_PDA },
  3406. { ACT_VM_DRYFIRE, ACT_PDA_VM_DRYFIRE, TF_WPN_TYPE_PDA },
  3407. { ACT_VM_IDLE_TO_LOWERED, ACT_PDA_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_PDA },
  3408. { ACT_VM_IDLE_LOWERED, ACT_PDA_VM_IDLE_LOWERED, TF_WPN_TYPE_PDA },
  3409. { ACT_VM_LOWERED_TO_IDLE, ACT_PDA_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_PDA },
  3410. // ITEM1
  3411. { ACT_VM_DRAW, ACT_ITEM1_VM_DRAW, TF_WPN_TYPE_ITEM1 },
  3412. { ACT_VM_HOLSTER, ACT_ITEM1_VM_HOLSTER, TF_WPN_TYPE_ITEM1 },
  3413. { ACT_VM_IDLE, ACT_ITEM1_VM_IDLE, TF_WPN_TYPE_ITEM1 },
  3414. { ACT_VM_PULLBACK, ACT_ITEM1_VM_PULLBACK, TF_WPN_TYPE_ITEM1 },
  3415. { ACT_VM_PRIMARYATTACK, ACT_ITEM1_VM_PRIMARYATTACK, TF_WPN_TYPE_ITEM1 },
  3416. { ACT_VM_SECONDARYATTACK, ACT_ITEM1_VM_SECONDARYATTACK, TF_WPN_TYPE_ITEM1 },
  3417. { ACT_VM_RELOAD, ACT_ITEM1_VM_RELOAD, TF_WPN_TYPE_ITEM1 },
  3418. { ACT_RELOAD_START, ACT_ITEM1_RELOAD_START, TF_WPN_TYPE_ITEM1 },
  3419. { ACT_RELOAD_FINISH, ACT_ITEM1_RELOAD_FINISH, TF_WPN_TYPE_ITEM1 },
  3420. { ACT_VM_DRYFIRE, ACT_ITEM1_VM_DRYFIRE, TF_WPN_TYPE_ITEM1 },
  3421. { ACT_VM_IDLE_TO_LOWERED, ACT_ITEM1_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_ITEM1 },
  3422. { ACT_VM_IDLE_LOWERED, ACT_ITEM1_VM_IDLE_LOWERED, TF_WPN_TYPE_ITEM1 },
  3423. { ACT_VM_LOWERED_TO_IDLE, ACT_ITEM1_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_ITEM1 },
  3424. { ACT_MP_ATTACK_STAND_PREFIRE, ACT_ITEM1_ATTACK_STAND_PREFIRE, TF_WPN_TYPE_ITEM1 },
  3425. { ACT_MP_ATTACK_STAND_POSTFIRE, ACT_ITEM1_ATTACK_STAND_POSTFIRE, TF_WPN_TYPE_ITEM1 },
  3426. { ACT_MP_ATTACK_STAND_STARTFIRE, ACT_ITEM1_ATTACK_STAND_STARTFIRE, TF_WPN_TYPE_ITEM1 },
  3427. { ACT_MP_ATTACK_CROUCH_PREFIRE, ACT_ITEM1_ATTACK_CROUCH_PREFIRE, TF_WPN_TYPE_ITEM1 },
  3428. { ACT_MP_ATTACK_CROUCH_POSTFIRE, ACT_ITEM1_ATTACK_CROUCH_POSTFIRE, TF_WPN_TYPE_ITEM1 },
  3429. { ACT_MP_ATTACK_SWIM_PREFIRE, ACT_ITEM1_ATTACK_SWIM_PREFIRE, TF_WPN_TYPE_ITEM1 },
  3430. { ACT_MP_ATTACK_SWIM_POSTFIRE, ACT_ITEM1_ATTACK_SWIM_POSTFIRE, TF_WPN_TYPE_ITEM1 },
  3431. // ITEM2
  3432. { ACT_VM_DRAW, ACT_ITEM2_VM_DRAW, TF_WPN_TYPE_ITEM2 },
  3433. { ACT_VM_HOLSTER, ACT_ITEM2_VM_HOLSTER, TF_WPN_TYPE_ITEM2 },
  3434. { ACT_VM_IDLE, ACT_ITEM2_VM_IDLE, TF_WPN_TYPE_ITEM2 },
  3435. { ACT_VM_PULLBACK, ACT_ITEM2_VM_PULLBACK, TF_WPN_TYPE_ITEM2 },
  3436. { ACT_VM_PRIMARYATTACK, ACT_ITEM2_VM_PRIMARYATTACK, TF_WPN_TYPE_ITEM2 },
  3437. { ACT_VM_SECONDARYATTACK, ACT_ITEM2_VM_SECONDARYATTACK, TF_WPN_TYPE_ITEM2 },
  3438. { ACT_VM_RELOAD, ACT_ITEM2_VM_RELOAD, TF_WPN_TYPE_ITEM2 },
  3439. { ACT_VM_DRYFIRE, ACT_ITEM2_VM_DRYFIRE, TF_WPN_TYPE_ITEM2 },
  3440. { ACT_VM_IDLE_TO_LOWERED, ACT_ITEM2_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_ITEM2 },
  3441. { ACT_VM_IDLE_LOWERED, ACT_ITEM2_VM_IDLE_LOWERED, TF_WPN_TYPE_ITEM2 },
  3442. { ACT_VM_LOWERED_TO_IDLE, ACT_ITEM2_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_ITEM2 },
  3443. { ACT_MP_ATTACK_STAND_PREFIRE, ACT_ITEM2_ATTACK_STAND_PREFIRE, TF_WPN_TYPE_ITEM2 },
  3444. { ACT_MP_ATTACK_STAND_POSTFIRE, ACT_ITEM2_ATTACK_STAND_POSTFIRE, TF_WPN_TYPE_ITEM2 },
  3445. { ACT_MP_ATTACK_STAND_STARTFIRE, ACT_ITEM2_ATTACK_STAND_STARTFIRE, TF_WPN_TYPE_ITEM2 },
  3446. { ACT_MP_ATTACK_CROUCH_PREFIRE, ACT_ITEM2_ATTACK_CROUCH_PREFIRE, TF_WPN_TYPE_ITEM2 },
  3447. { ACT_MP_ATTACK_CROUCH_POSTFIRE, ACT_ITEM2_ATTACK_CROUCH_POSTFIRE, TF_WPN_TYPE_ITEM2 },
  3448. { ACT_MP_ATTACK_SWIM_PREFIRE, ACT_ITEM2_ATTACK_SWIM_PREFIRE, TF_WPN_TYPE_ITEM2 },
  3449. { ACT_MP_ATTACK_SWIM_POSTFIRE, ACT_ITEM2_ATTACK_SWIM_POSTFIRE, TF_WPN_TYPE_ITEM2 },
  3450. { ACT_VM_DRAW, ACT_MELEE_ALLCLASS_VM_DRAW, TF_WPN_TYPE_MELEE_ALLCLASS },
  3451. { ACT_VM_HOLSTER, ACT_MELEE_ALLCLASS_VM_HOLSTER, TF_WPN_TYPE_MELEE_ALLCLASS },
  3452. { ACT_VM_IDLE, ACT_MELEE_ALLCLASS_VM_IDLE, TF_WPN_TYPE_MELEE_ALLCLASS },
  3453. { ACT_VM_PULLBACK, ACT_MELEE_ALLCLASS_VM_PULLBACK, TF_WPN_TYPE_MELEE_ALLCLASS },
  3454. { ACT_VM_PRIMARYATTACK, ACT_MELEE_ALLCLASS_VM_PRIMARYATTACK, TF_WPN_TYPE_MELEE_ALLCLASS },
  3455. { ACT_VM_SECONDARYATTACK, ACT_MELEE_ALLCLASS_VM_SECONDARYATTACK, TF_WPN_TYPE_MELEE_ALLCLASS },
  3456. { ACT_VM_RELOAD, ACT_MELEE_ALLCLASS_VM_RELOAD, TF_WPN_TYPE_MELEE_ALLCLASS },
  3457. { ACT_VM_DRYFIRE, ACT_MELEE_ALLCLASS_VM_DRYFIRE, TF_WPN_TYPE_MELEE_ALLCLASS },
  3458. { ACT_VM_IDLE_TO_LOWERED, ACT_MELEE_ALLCLASS_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_MELEE_ALLCLASS },
  3459. { ACT_VM_IDLE_LOWERED, ACT_MELEE_ALLCLASS_VM_IDLE_LOWERED, TF_WPN_TYPE_MELEE_ALLCLASS },
  3460. { ACT_VM_LOWERED_TO_IDLE, ACT_MELEE_ALLCLASS_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_MELEE_ALLCLASS },
  3461. { ACT_VM_HITCENTER, ACT_MELEE_ALLCLASS_VM_HITCENTER, TF_WPN_TYPE_MELEE_ALLCLASS },
  3462. { ACT_VM_SWINGHARD, ACT_MELEE_ALLCLASS_VM_SWINGHARD, TF_WPN_TYPE_MELEE_ALLCLASS },
  3463. { ACT_VM_DRAW, ACT_SECONDARY2_VM_DRAW, TF_WPN_TYPE_SECONDARY2 },
  3464. { ACT_VM_HOLSTER, ACT_SECONDARY2_VM_HOLSTER, TF_WPN_TYPE_SECONDARY2 },
  3465. { ACT_VM_IDLE, ACT_SECONDARY2_VM_IDLE, TF_WPN_TYPE_SECONDARY2 },
  3466. { ACT_VM_PULLBACK, ACT_SECONDARY2_VM_PULLBACK, TF_WPN_TYPE_SECONDARY2 },
  3467. { ACT_VM_PRIMARYATTACK, ACT_SECONDARY2_VM_PRIMARYATTACK, TF_WPN_TYPE_SECONDARY2 },
  3468. { ACT_VM_RELOAD, ACT_SECONDARY2_VM_RELOAD, TF_WPN_TYPE_SECONDARY2 },
  3469. { ACT_RELOAD_START, ACT_SECONDARY2_RELOAD_START, TF_WPN_TYPE_SECONDARY2 },
  3470. { ACT_RELOAD_FINISH, ACT_SECONDARY2_RELOAD_FINISH, TF_WPN_TYPE_SECONDARY2 },
  3471. { ACT_VM_DRYFIRE, ACT_SECONDARY2_VM_DRYFIRE, TF_WPN_TYPE_SECONDARY2 },
  3472. { ACT_VM_IDLE_TO_LOWERED, ACT_SECONDARY2_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_SECONDARY2 },
  3473. { ACT_VM_IDLE_LOWERED, ACT_SECONDARY2_VM_IDLE_LOWERED, TF_WPN_TYPE_SECONDARY2 },
  3474. { ACT_VM_LOWERED_TO_IDLE, ACT_SECONDARY2_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_SECONDARY2 },
  3475. { ACT_VM_DRAW, ACT_PRIMARY_VM_DRAW, TF_WPN_TYPE_PRIMARY2 },
  3476. { ACT_VM_HOLSTER, ACT_PRIMARY_VM_HOLSTER, TF_WPN_TYPE_PRIMARY2 },
  3477. { ACT_VM_IDLE, ACT_PRIMARY_VM_IDLE, TF_WPN_TYPE_PRIMARY2 },
  3478. { ACT_VM_PULLBACK, ACT_PRIMARY_VM_PULLBACK, TF_WPN_TYPE_PRIMARY2 },
  3479. { ACT_VM_PRIMARYATTACK, ACT_PRIMARY_VM_PRIMARYATTACK, TF_WPN_TYPE_PRIMARY2 },
  3480. { ACT_VM_RELOAD, ACT_PRIMARY_VM_RELOAD_3, TF_WPN_TYPE_PRIMARY2 },
  3481. { ACT_RELOAD_START, ACT_PRIMARY_RELOAD_START_3, TF_WPN_TYPE_PRIMARY2 },
  3482. { ACT_RELOAD_FINISH, ACT_PRIMARY_RELOAD_FINISH_3, TF_WPN_TYPE_PRIMARY2 },
  3483. { ACT_VM_DRYFIRE, ACT_PRIMARY_VM_DRYFIRE, TF_WPN_TYPE_PRIMARY2 },
  3484. { ACT_VM_IDLE_TO_LOWERED, ACT_PRIMARY_VM_IDLE_TO_LOWERED, TF_WPN_TYPE_PRIMARY2 },
  3485. { ACT_VM_IDLE_LOWERED, ACT_PRIMARY_VM_IDLE_LOWERED, TF_WPN_TYPE_PRIMARY2 },
  3486. { ACT_VM_LOWERED_TO_IDLE, ACT_PRIMARY_VM_LOWERED_TO_IDLE, TF_WPN_TYPE_PRIMARY2 },
  3487. };
  3488. // -----------------------------------------------------------------------------
  3489. // Purpose:
  3490. // -----------------------------------------------------------------------------
  3491. Activity CTFWeaponBase::TranslateViewmodelHandActivityInternal( Activity actBase )
  3492. {
  3493. CEconItemView *pEconItemView = GetAttributeContainer()->GetItem();
  3494. if ( pEconItemView && pEconItemView->IsValid() && GetOwnerEntity() )
  3495. {
  3496. Activity translatedActivity = pEconItemView->GetStaticData()->GetActivityOverride( GetOwnerEntity()->GetTeamNumber(), actBase );
  3497. if ( translatedActivity != actBase )
  3498. return translatedActivity;
  3499. }
  3500. int iWeaponRole = GetViewModelWeaponRole();
  3501. if ( pEconItemView )
  3502. {
  3503. int iMaybeOverrideAnimSlot = pEconItemView->GetAnimationSlot();
  3504. if ( iMaybeOverrideAnimSlot >= 0 )
  3505. {
  3506. iWeaponRole = iMaybeOverrideAnimSlot;
  3507. }
  3508. }
  3509. viewmodelacttable_t *pTable = s_viewmodelacttable;
  3510. for ( int i = 0; i < ARRAYSIZE(s_viewmodelacttable); i++ )
  3511. {
  3512. const viewmodelacttable_t &act = pTable[i];
  3513. if ( actBase == act.baseAct && act.weaponRole == iWeaponRole )
  3514. return (Activity)act.weaponAct;
  3515. }
  3516. return actBase;
  3517. }
  3518. // -----------------------------------------------------------------------------
  3519. // Purpose:
  3520. // -----------------------------------------------------------------------------
  3521. CBasePlayer *CTFWeaponBase::GetPlayerOwner() const
  3522. {
  3523. return dynamic_cast<CBasePlayer*>( GetOwner() );
  3524. }
  3525. // -----------------------------------------------------------------------------
  3526. // Purpose:
  3527. // -----------------------------------------------------------------------------
  3528. CTFPlayer *CTFWeaponBase::GetTFPlayerOwner() const
  3529. {
  3530. return dynamic_cast<CTFPlayer*>( GetOwner() );
  3531. }
  3532. #ifdef CLIENT_DLL
  3533. // -----------------------------------------------------------------------------
  3534. // Purpose:
  3535. // -----------------------------------------------------------------------------
  3536. C_BaseEntity *CTFWeaponBase::GetWeaponForEffect()
  3537. {
  3538. return GetAppropriateWorldOrViewModel();
  3539. }
  3540. //-----------------------------------------------------------------------------
  3541. // Purpose:
  3542. //-----------------------------------------------------------------------------
  3543. ShadowType_t CTFWeaponBase::ShadowCastType( void )
  3544. {
  3545. // Some weapons (fists) don't actually get set to NODRAW when holstered so we
  3546. // need some extra checks
  3547. if ( IsEffectActive( EF_NODRAW | EF_NOSHADOW ) || m_iState != WEAPON_IS_ACTIVE )
  3548. return SHADOWS_NONE;
  3549. return BaseClass::ShadowCastType();
  3550. }
  3551. #endif
  3552. // -----------------------------------------------------------------------------
  3553. // Purpose:
  3554. // -----------------------------------------------------------------------------
  3555. bool CTFWeaponBase::CanAttack()
  3556. {
  3557. CTFPlayer *pPlayer = GetTFPlayerOwner();
  3558. if ( pPlayer )
  3559. return pPlayer->CanAttack( GetCanAttackFlags() );
  3560. return false;
  3561. }
  3562. //-----------------------------------------------------------------------------
  3563. bool CTFWeaponBase::CanFireCriticalShot( bool bIsHeadshot )
  3564. {
  3565. #ifdef GAME_DLL
  3566. CTFPlayer *player = GetTFPlayerOwner();
  3567. if ( TFGameRules()->IsPVEModeControlled( player ) )
  3568. {
  3569. // scenario bots cant crit (unless they always do)
  3570. CTFBot *bot = ToTFBot( player );
  3571. return ( bot && bot->HasAttribute( CTFBot::ALWAYS_CRIT ) );
  3572. }
  3573. #ifdef TF_CREEP_MODE
  3574. if ( TFGameRules()->IsCreepWaveMode() && player )
  3575. {
  3576. CTFBot *bot = ToTFBot( player );
  3577. if ( bot && bot->HasAttribute( CTFBot::IS_NPC ) )
  3578. {
  3579. // creeps can't crit
  3580. return false;
  3581. }
  3582. }
  3583. #endif // TF_CREEP_MODE
  3584. #endif
  3585. return true;
  3586. }
  3587. //-----------------------------------------------------------------------------
  3588. // Purpose:
  3589. //-----------------------------------------------------------------------------
  3590. bool CTFWeaponBase::CanFireRandomCriticalShot( float flCritChance )
  3591. {
  3592. #ifdef GAME_DLL
  3593. // Todo: Create a version of this in tf_weaponbase_melee
  3594. CTFPlayer *pPlayer = GetTFPlayerOwner();
  3595. if ( !pPlayer )
  3596. return false;
  3597. PlayerStats_t *pPlayerStats = CTF_GameStats.FindPlayerStats( pPlayer );
  3598. if ( pPlayerStats )
  3599. {
  3600. // Compare total damage done against total crit damage done. If this
  3601. // ratio is out of range for the expected crit chance, deny the crit.
  3602. int nRandomRangedCritDamage = pPlayerStats->statsCurrentRound.m_iStat[TFSTAT_DAMAGE_RANGED_CRIT_RANDOM];
  3603. int nTotalDamage = pPlayerStats->statsCurrentRound.m_iStat[TFSTAT_DAMAGE_RANGED];
  3604. // Early out
  3605. if ( !nTotalDamage )
  3606. return true;
  3607. float flNormalizedDamage = (float)nRandomRangedCritDamage / TF_DAMAGE_CRIT_MULTIPLIER;
  3608. m_flObservedCritChance.Set( flNormalizedDamage / ( flNormalizedDamage + ( nTotalDamage - nRandomRangedCritDamage ) ) );
  3609. // DevMsg ( "SERVER: CritChance: %f Observed: %f\n", flCritChance, m_flObservedCritChance.Get() );
  3610. }
  3611. #else
  3612. // DevMsg ( "CLIENT: CritChance: %f Observed: %f\n", flCritChance, m_flObservedCritChance.Get() );
  3613. #endif // GAME_DLL
  3614. if ( m_flObservedCritChance.Get() > flCritChance + 0.1f )
  3615. return false;
  3616. return true;
  3617. }
  3618. //-----------------------------------------------------------------------------
  3619. // Purpose:
  3620. //-----------------------------------------------------------------------------
  3621. char const *CTFWeaponBase::GetShootSound( int iIndex ) const
  3622. {
  3623. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  3624. if ( pItem->IsValid() )
  3625. {
  3626. int nTeam = GetTeamNumber();
  3627. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && nTeam == TF_TEAM_PVE_INVADERS )
  3628. {
  3629. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  3630. if ( pPlayer && pPlayer->IsMiniBoss() )
  3631. {
  3632. // Not a real team - just a define used in replacing visuals via itemdefs ("visuals_mvm")
  3633. nTeam = TF_TEAM_PVE_INVADERS_GIANTS;
  3634. }
  3635. }
  3636. const char *pszSound = pItem->GetStaticData()->GetWeaponReplacementSound( nTeam, (WeaponSound_t)iIndex );
  3637. if ( pszSound )
  3638. return pszSound;
  3639. }
  3640. return BaseClass::GetShootSound(iIndex);
  3641. }
  3642. //-----------------------------------------------------------------------------
  3643. // Purpose: Owner is stunned.
  3644. //-----------------------------------------------------------------------------
  3645. void CTFWeaponBase::OnControlStunned( void )
  3646. {
  3647. // Abort reloading.
  3648. AbortReload();
  3649. // Hide the weapon.
  3650. SetWeaponVisible( false );
  3651. }
  3652. #if defined( CLIENT_DLL )
  3653. static ConVar cl_bobcycle( "cl_bobcycle","0.8", FCVAR_CHEAT );
  3654. static ConVar cl_bobup( "cl_bobup","0.5", FCVAR_CHEAT );
  3655. //-----------------------------------------------------------------------------
  3656. // Purpose: Helper function to calculate head bob
  3657. //-----------------------------------------------------------------------------
  3658. float CalcViewModelBobHelper( CBasePlayer *player, BobState_t *pBobState )
  3659. {
  3660. Assert( pBobState );
  3661. if ( !pBobState )
  3662. return 0;
  3663. float cycle;
  3664. // Don't allow zeros, because we divide by them.
  3665. float flBobup = cl_bobup.GetFloat();
  3666. if ( flBobup <= 0 )
  3667. {
  3668. flBobup = 0.01;
  3669. }
  3670. float flBobCycle = cl_bobcycle.GetFloat();
  3671. if ( flBobCycle <= 0 )
  3672. {
  3673. flBobCycle = 0.01;
  3674. }
  3675. //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it
  3676. if ( ( !gpGlobals->frametime ) || ( player == NULL ) )
  3677. {
  3678. //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
  3679. return 0.0f;// just use old value
  3680. }
  3681. //Find the speed of the player
  3682. float speed = player->GetLocalVelocity().Length2D();
  3683. float flmaxSpeedDelta = MAX( 0, (gpGlobals->curtime - pBobState->m_flLastBobTime ) * 320.0f );
  3684. // don't allow too big speed changes
  3685. speed = clamp( speed, pBobState->m_flLastSpeed-flmaxSpeedDelta, pBobState->m_flLastSpeed+flmaxSpeedDelta );
  3686. speed = clamp( speed, -320.f, 320.f );
  3687. pBobState->m_flLastSpeed = speed;
  3688. //FIXME: This maximum speed value must come from the server.
  3689. // MaxSpeed() is not sufficient for dealing with sprinting - jdw
  3690. float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f );
  3691. pBobState->m_flBobTime += ( gpGlobals->curtime - pBobState->m_flLastBobTime ) * bob_offset;
  3692. pBobState->m_flLastBobTime = gpGlobals->curtime;
  3693. //Calculate the vertical bob
  3694. cycle = pBobState->m_flBobTime - (int)(pBobState->m_flBobTime/flBobCycle)*flBobCycle;
  3695. cycle /= flBobCycle;
  3696. if ( cycle < flBobup )
  3697. {
  3698. cycle = M_PI * cycle / flBobup;
  3699. }
  3700. else
  3701. {
  3702. cycle = M_PI + M_PI*(cycle-flBobup)/(1.0 - flBobup);
  3703. }
  3704. pBobState->m_flVerticalBob = speed*0.005f;
  3705. pBobState->m_flVerticalBob = pBobState->m_flVerticalBob*0.3 + pBobState->m_flVerticalBob*0.7*sin(cycle);
  3706. pBobState->m_flVerticalBob = clamp( pBobState->m_flVerticalBob, -7.0f, 4.0f );
  3707. //Calculate the lateral bob
  3708. cycle = pBobState->m_flBobTime - (int)(pBobState->m_flBobTime/flBobCycle*2)*flBobCycle*2;
  3709. cycle /= flBobCycle*2;
  3710. if ( cycle < flBobup )
  3711. {
  3712. cycle = M_PI * cycle / flBobup;
  3713. }
  3714. else
  3715. {
  3716. cycle = M_PI + M_PI*(cycle-flBobup)/(1.0 - flBobup);
  3717. }
  3718. pBobState->m_flLateralBob = speed*0.005f;
  3719. pBobState->m_flLateralBob = pBobState->m_flLateralBob*0.3 + pBobState->m_flLateralBob*0.7*sin(cycle);
  3720. pBobState->m_flLateralBob = clamp( pBobState->m_flLateralBob, -7.0f, 4.0f );
  3721. //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
  3722. return 0.0f;
  3723. }
  3724. //-----------------------------------------------------------------------------
  3725. // Purpose: Helper function to add head bob
  3726. //-----------------------------------------------------------------------------
  3727. void AddViewModelBobHelper( Vector &origin, QAngle &angles, BobState_t *pBobState )
  3728. {
  3729. Assert( pBobState );
  3730. if ( !pBobState )
  3731. return;
  3732. Vector forward, right;
  3733. AngleVectors( angles, &forward, &right, NULL );
  3734. // Apply bob, but scaled down to 40%
  3735. VectorMA( origin, pBobState->m_flVerticalBob * 0.4f, forward, origin );
  3736. // Z bob a bit more
  3737. origin[2] += pBobState->m_flVerticalBob * 0.1f;
  3738. // bob the angles
  3739. angles[ ROLL ] += pBobState->m_flVerticalBob * 0.5f;
  3740. angles[ PITCH ] -= pBobState->m_flVerticalBob * 0.4f;
  3741. angles[ YAW ] -= pBobState->m_flLateralBob * 0.3f;
  3742. VectorMA( origin, pBobState->m_flLateralBob * 0.2f, right, origin );
  3743. }
  3744. //-----------------------------------------------------------------------------
  3745. // Purpose:
  3746. // Output : float
  3747. //-----------------------------------------------------------------------------
  3748. float CTFWeaponBase::CalcViewmodelBob( void )
  3749. {
  3750. CBasePlayer *player = ToBasePlayer( GetOwner() );
  3751. //Assert( player );
  3752. BobState_t *pBobState = GetBobState();
  3753. if ( pBobState )
  3754. return ::CalcViewModelBobHelper( player, pBobState );
  3755. else
  3756. return 0;
  3757. }
  3758. //-----------------------------------------------------------------------------
  3759. // Purpose:
  3760. // Input : &origin -
  3761. // &angles -
  3762. // viewmodelindex -
  3763. //-----------------------------------------------------------------------------
  3764. void CTFWeaponBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
  3765. {
  3766. // call helper functions to do the calculation
  3767. BobState_t *pBobState = GetBobState();
  3768. if ( pBobState )
  3769. {
  3770. CalcViewmodelBob();
  3771. ::AddViewModelBobHelper( origin, angles, pBobState );
  3772. }
  3773. }
  3774. //-----------------------------------------------------------------------------
  3775. // Purpose: Returns the head bob state for this weapon, which is stored
  3776. // in the view model. Note that this this function can return
  3777. // NULL if the player is dead or the view model is otherwise not present.
  3778. //-----------------------------------------------------------------------------
  3779. BobState_t *CTFWeaponBase::GetBobState()
  3780. {
  3781. // get the view model for this weapon
  3782. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  3783. if ( pOwner == NULL )
  3784. return NULL;
  3785. CBaseViewModel *baseViewModel = pOwner->GetViewModel( m_nViewModelIndex );
  3786. if ( baseViewModel == NULL )
  3787. return NULL;
  3788. CTFViewModel *viewModel = dynamic_cast<CTFViewModel *>(baseViewModel);
  3789. Assert( viewModel );
  3790. // get the bob state out of the view model
  3791. return &( viewModel->GetBobState() );
  3792. }
  3793. #endif // defined( CLIENT_DLL )
  3794. //-----------------------------------------------------------------------------
  3795. // Purpose: Used for spy invisiblity material, skin overrides, and team colors
  3796. //-----------------------------------------------------------------------------
  3797. int CTFWeaponBase::GetSkin()
  3798. {
  3799. CTFPlayer *pPlayer = GetTFPlayerOwner();
  3800. if ( !pPlayer )
  3801. return 0;
  3802. int iTeamNumber = pPlayer->GetTeamNumber();
  3803. #if defined( CLIENT_DLL )
  3804. // Run client-only "is the viewer on the same team as the wielder" logic. Assumed to
  3805. // always be false on the server.
  3806. CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  3807. if ( !pLocalPlayer )
  3808. return 0;
  3809. int iLocalTeam = pLocalPlayer->GetTeamNumber();
  3810. // We only show disguise weapon to the enemy team when owner is disguised
  3811. bool bUseDisguiseWeapon = ( iTeamNumber != iLocalTeam && iLocalTeam > LAST_SHARED_TEAM );
  3812. if ( bUseDisguiseWeapon && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  3813. {
  3814. if ( pLocalPlayer != pPlayer )
  3815. {
  3816. iTeamNumber = pPlayer->m_Shared.GetDisguiseTeam();
  3817. }
  3818. }
  3819. #endif // defined( CLIENT_DLL )
  3820. // See if the item wants to override the skin
  3821. int nSkin = GetSkinOverride(); // give custom gameplay code a chance to set whatever
  3822. if ( nSkin == -1 )
  3823. {
  3824. const CEconItemView *pItem = GetAttributeContainer()->GetItem();
  3825. if ( pItem->IsValid() )
  3826. {
  3827. nSkin = pItem->GetSkin( iTeamNumber ); // if we didn't have custom code, fall back to the item definition
  3828. }
  3829. }
  3830. // If it didn't, fall back to the base skins
  3831. if ( nSkin == -1 )
  3832. {
  3833. switch( iTeamNumber )
  3834. {
  3835. case TF_TEAM_RED:
  3836. nSkin = 0;
  3837. break;
  3838. case TF_TEAM_BLUE:
  3839. nSkin = 1;
  3840. break;
  3841. default:
  3842. nSkin = 0;
  3843. break;
  3844. }
  3845. }
  3846. return nSkin;
  3847. }
  3848. #if defined( CLIENT_DLL )
  3849. //-----------------------------------------------------------------------------
  3850. // Purpose:
  3851. //-----------------------------------------------------------------------------
  3852. bool CTFWeaponBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
  3853. {
  3854. if ( event == 6002 && ShouldEjectBrass() )
  3855. {
  3856. if ( UsingViewModel() && !g_pClientMode->ShouldDrawViewModel() )
  3857. {
  3858. // Prevent effects when the ViewModel is hidden with r_drawviewmodel=0
  3859. return true;
  3860. }
  3861. CEffectData data;
  3862. // Look for 'eject_brass' attachment first instead of using options which is a seemingly magic number
  3863. if ( m_iEjectBrassAttachpoint == -2 )
  3864. {
  3865. m_iEjectBrassAttachpoint = pViewModel->LookupAttachment( "eject_brass" );
  3866. }
  3867. if ( m_iEjectBrassAttachpoint > 0 )
  3868. {
  3869. pViewModel->GetAttachment( m_iEjectBrassAttachpoint, data.m_vOrigin, data.m_vAngles );
  3870. }
  3871. else
  3872. {
  3873. pViewModel->GetAttachment( atoi(options), data.m_vOrigin, data.m_vAngles );
  3874. }
  3875. data.m_nDamageType = GetAttributeContainer()->GetItem() ? GetAttributeContainer()->GetItem()->GetItemDefIndex() : 0;
  3876. data.m_nHitBox = GetWeaponID();
  3877. DispatchEffect( "TF_EjectBrass", data );
  3878. return true;
  3879. }
  3880. if ( event == AE_WPN_INCREMENTAMMO )
  3881. {
  3882. IncrementAmmo();
  3883. m_bReloadedThroughAnimEvent = true;
  3884. return true;
  3885. }
  3886. return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
  3887. }
  3888. //-----------------------------------------------------------------------------
  3889. // Purpose: Used for spy invisiblity material
  3890. //-----------------------------------------------------------------------------
  3891. class CWeaponInvisProxy : public CBaseInvisMaterialProxy
  3892. {
  3893. public:
  3894. virtual void OnBind( C_BaseEntity *pBaseEntity ) OVERRIDE;
  3895. };
  3896. extern ConVar tf_teammate_max_invis;
  3897. //-----------------------------------------------------------------------------
  3898. // Purpose:
  3899. // Input :
  3900. //-----------------------------------------------------------------------------
  3901. void CWeaponInvisProxy::OnBind( C_BaseEntity *pBaseEntity )
  3902. {
  3903. if( !m_pPercentInvisible )
  3904. return;
  3905. C_BaseEntity *pMoveParent = pBaseEntity->GetMoveParent();
  3906. if ( !pMoveParent )
  3907. {
  3908. m_pPercentInvisible->SetFloatValue( 0.0f );
  3909. return;
  3910. }
  3911. if ( !pMoveParent->IsPlayer() )
  3912. {
  3913. C_TFPlayer *pOwningPlayer = ToTFPlayer( pMoveParent->GetOwnerEntity() );
  3914. if ( pOwningPlayer )
  3915. {
  3916. // mimic the owner's invisibility
  3917. float flInvis = pOwningPlayer->GetEffectiveInvisibilityLevel();
  3918. m_pPercentInvisible->SetFloatValue( flInvis );
  3919. }
  3920. else
  3921. {
  3922. m_pPercentInvisible->SetFloatValue( 0.0f );
  3923. }
  3924. return;
  3925. }
  3926. CTFPlayer *pPlayer = ToTFPlayer( pMoveParent );
  3927. Assert( pPlayer );
  3928. float flInvis = pPlayer->GetEffectiveInvisibilityLevel();
  3929. m_pPercentInvisible->SetFloatValue( flInvis );
  3930. }
  3931. EXPOSE_INTERFACE( CWeaponInvisProxy, IMaterialProxy, "weapon_invis" IMATERIAL_PROXY_INTERFACE_VERSION );
  3932. #endif // CLIENT_DLL
  3933. #ifdef GAME_DLL
  3934. //-----------------------------------------------------------------------------
  3935. // Purpose:
  3936. //-----------------------------------------------------------------------------
  3937. ConVar tf_dev_marked_for_death_lifetime( "tf_dev_marked_for_death_lifetime", "15.0", FCVAR_DEVELOPMENTONLY );
  3938. ConVar tf_dev_health_on_damage_recover_percentage( "tf_dev_health_on_damage_recover_percentage", "0.35", FCVAR_DEVELOPMENTONLY );
  3939. void CTFWeaponBase::ApplyOnHitAttributes( CBaseEntity *pVictimBaseEntity, CTFPlayer *pAttacker, const CTakeDamageInfo &info )
  3940. {
  3941. if ( !pAttacker )
  3942. return;
  3943. CTFPlayer *pVictim = ToTFPlayer( pVictimBaseEntity );
  3944. // Ammo on hit
  3945. int iModAmmoOnHit = 0;
  3946. CALL_ATTRIB_HOOK_INT( iModAmmoOnHit, add_onhit_addammo );
  3947. if ( iModAmmoOnHit > 0 )
  3948. {
  3949. // this will save the value so we can add it after we're doing firing
  3950. // the projectile and have subtracted the ammo for the current shot
  3951. float flPercentage = (float)iModAmmoOnHit / 100.0f;
  3952. // No ammo for disguised Spies that are NOT stealthed so you can't use this to check for Spies
  3953. if ( pVictim &&
  3954. pVictim->IsPlayerClass( TF_CLASS_SPY ) &&
  3955. pVictim->m_Shared.InCond( TF_COND_DISGUISED ) &&
  3956. !( pVictim->m_Shared.IsStealthed() || pVictim->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) )
  3957. {
  3958. flPercentage = 0.0f;
  3959. }
  3960. m_iAmmoToAdd += (int)( flPercentage * info.GetDamage() );
  3961. }
  3962. int iExtraDamageOnHit = 0;
  3963. CALL_ATTRIB_HOOK_INT( iExtraDamageOnHit, extra_damage_on_hit );
  3964. if ( iExtraDamageOnHit )
  3965. {
  3966. // Adds 'Heads'. Reusing this data field
  3967. int iDecap = pAttacker->m_Shared.GetDecapitations();
  3968. pAttacker->m_Shared.SetDecapitations( Min( 200, iDecap + iExtraDamageOnHit ) );
  3969. }
  3970. // Everything else is only for player enemies or Halloween bosses
  3971. // We don't want buildables or the tank doing things like giving health or increasing ubercharge
  3972. if ( !( pVictim || dynamic_cast< CHalloweenBaseBoss* >( pVictimBaseEntity ) ) )
  3973. {
  3974. return;
  3975. }
  3976. bool bIsSpyRevealed = false;
  3977. if ( pVictim )
  3978. {
  3979. // Reveal cloaked Spy on hit
  3980. if ( pVictim->IsPlayerClass( TF_CLASS_SPY ) && pVictim->m_Shared.IsStealthed() )
  3981. {
  3982. int iRevealCloakedSpyOnHit = 0;
  3983. CALL_ATTRIB_HOOK_INT( iRevealCloakedSpyOnHit, reveal_cloaked_victim_on_hit );
  3984. if ( iRevealCloakedSpyOnHit > 0 )
  3985. {
  3986. pVictim->RemoveInvisibility();
  3987. bIsSpyRevealed = true;
  3988. }
  3989. }
  3990. // Reveal disguised Spy on hit
  3991. if ( pVictim->IsPlayerClass( TF_CLASS_SPY ) && pVictim->m_Shared.InCond( TF_COND_DISGUISED ) )
  3992. {
  3993. int iRevealDisguisedSpyOnHit = 0;
  3994. CALL_ATTRIB_HOOK_INT( iRevealDisguisedSpyOnHit, reveal_disguised_victim_on_hit );
  3995. if ( iRevealDisguisedSpyOnHit > 0 )
  3996. {
  3997. pVictim->RemoveDisguise();
  3998. bIsSpyRevealed = true;
  3999. }
  4000. }
  4001. if ( bIsSpyRevealed )
  4002. {
  4003. color32 colorHit = { 255, 255, 255, 255 };
  4004. UTIL_ScreenFade( pVictim, colorHit, 0.25f, 0.1f, FFADE_IN );
  4005. // pVictim->EmitSound( "Weapon_DRG_Wrench.RevealSpy" );
  4006. }
  4007. // On hit attributes don't work when you shoot disguised spies
  4008. if ( pVictim->m_Shared.InCond( TF_COND_DISGUISED ) )
  4009. return;
  4010. }
  4011. // Or from burn damage
  4012. if ( (info.GetDamageType() & DMG_BURN) )
  4013. return;
  4014. // Heal on hits
  4015. int iModHealthOnHit = 0;
  4016. CALL_ATTRIB_HOOK_INT( iModHealthOnHit, add_onhit_addhealth );
  4017. if ( iModHealthOnHit )
  4018. {
  4019. // Scale Health mod with damage dealt, input being the maximum amount of health possible
  4020. float flScale = Clamp( info.GetDamage() / info.GetBaseDamage(), 0.f, 1.0f );
  4021. iModHealthOnHit = (int)((float)iModHealthOnHit * flScale );
  4022. }
  4023. // Charge meter on hit
  4024. float flChargeRefill = 0.0f;
  4025. CALL_ATTRIB_HOOK_FLOAT( flChargeRefill, charge_meter_on_hit );
  4026. if ( flChargeRefill > 0 )
  4027. {
  4028. pAttacker->m_Shared.SetDemomanChargeMeter( pAttacker->m_Shared.GetDemomanChargeMeter() + flChargeRefill * 100.0f );
  4029. }
  4030. // Speed on hit
  4031. int iSpeedBoostOnHit = 0;
  4032. CALL_ATTRIB_HOOK_INT( iSpeedBoostOnHit, speed_boost_on_hit );
  4033. if ( iSpeedBoostOnHit )
  4034. {
  4035. pAttacker->m_Shared.AddCond( TF_COND_SPEED_BOOST, iSpeedBoostOnHit );
  4036. }
  4037. if ( pVictim )
  4038. {
  4039. if ( pVictim->m_Shared.InCond( TF_COND_MAD_MILK ) )
  4040. {
  4041. int nAmount = info.GetDamage() * 0.6f;
  4042. iModHealthOnHit += nAmount;
  4043. CTFPlayer *pProvider = ToTFPlayer( pVictim->m_Shared.GetConditionProvider( TF_COND_MAD_MILK ) );
  4044. if ( pProvider )
  4045. {
  4046. // Only give points for the portion they're responsible for
  4047. if ( pProvider != pAttacker )
  4048. {
  4049. CTF_GameStats.Event_PlayerHealedOtherAssist( pProvider, nAmount );
  4050. }
  4051. // Show in the medic's UI as primary healing
  4052. IGameEvent *event = gameeventmanager->CreateEvent( "player_healed" );
  4053. if ( event )
  4054. {
  4055. event->SetInt( "priority", 1 ); // HLTV event priority
  4056. event->SetInt( "patient", pAttacker->GetUserID() );
  4057. event->SetInt( "healer", pProvider->GetUserID() );
  4058. event->SetInt( "amount", iModHealthOnHit );
  4059. gameeventmanager->FireEvent( event );
  4060. }
  4061. // Give them a little bit of Uber
  4062. CWeaponMedigun *pMedigun = static_cast<CWeaponMedigun *>( pProvider->Weapon_OwnsThisID( TF_WEAPON_MEDIGUN ) );
  4063. if ( pMedigun )
  4064. {
  4065. int iHealedAmount = Max( Min( (int)pAttacker->GetMaxHealth() - (int)pAttacker->GetHealth(), nAmount ), 0 );
  4066. // On Mediguns, per frame, the amount of uber added is based on
  4067. // Default heal rate is 24per second, we scale based on that and frametime
  4068. pMedigun->AddCharge( (iHealedAmount / 24.0f ) * gpGlobals->frametime );
  4069. }
  4070. }
  4071. }
  4072. }
  4073. if ( pAttacker->m_Shared.InCond( TF_COND_REGENONDAMAGEBUFF ) )
  4074. {
  4075. int nAmount = info.GetDamage() * tf_dev_health_on_damage_recover_percentage.GetFloat();
  4076. iModHealthOnHit += nAmount;
  4077. // Increment provider's healing assist stat
  4078. CTFPlayer *pProvider = ToTFPlayer( pAttacker->m_Shared.GetConditionProvider( TF_COND_REGENONDAMAGEBUFF ) );
  4079. if ( pProvider && pProvider != pAttacker )
  4080. {
  4081. // Only give points for the portion they're responsible for
  4082. CTF_GameStats.Event_PlayerHealedOtherAssist( pProvider, nAmount );
  4083. }
  4084. }
  4085. if ( iModHealthOnHit )
  4086. {
  4087. if ( iModHealthOnHit > 0 )
  4088. {
  4089. int iHealed = pAttacker->TakeHealth( iModHealthOnHit, DMG_GENERIC );
  4090. // Increment attacker's healing stat
  4091. if ( iHealed )
  4092. {
  4093. CTF_GameStats.Event_PlayerHealedOther( pAttacker, iHealed );
  4094. }
  4095. }
  4096. else
  4097. {
  4098. pAttacker->TakeDamage( CTakeDamageInfo( pAttacker, this, (iModHealthOnHit * -1), DMG_GENERIC ) );
  4099. }
  4100. IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" );
  4101. if ( event )
  4102. {
  4103. event->SetInt( "amount", iModHealthOnHit );
  4104. event->SetInt( "entindex", pAttacker->entindex() );
  4105. item_definition_index_t healingItemDef = INVALID_ITEM_DEF_INDEX;
  4106. if ( GetAttributeContainer() && GetAttributeContainer()->GetItem() )
  4107. {
  4108. healingItemDef = GetAttributeContainer()->GetItem()->GetItemDefIndex();
  4109. }
  4110. event->SetInt( "weapon_def_index", healingItemDef );
  4111. gameeventmanager->FireEvent( event );
  4112. }
  4113. }
  4114. // Add ubercharge on hit
  4115. if ( pAttacker->IsPlayerClass( TF_CLASS_MEDIC ) )
  4116. {
  4117. float flUberChargeBonus = 0;
  4118. CALL_ATTRIB_HOOK_FLOAT( flUberChargeBonus, add_onhit_ubercharge );
  4119. if ( flUberChargeBonus )
  4120. {
  4121. CWeaponMedigun *pMedigun = (CWeaponMedigun *)pAttacker->Weapon_OwnsThisID( TF_WEAPON_MEDIGUN );
  4122. if ( pMedigun )
  4123. {
  4124. pMedigun->AddCharge( flUberChargeBonus );
  4125. }
  4126. }
  4127. }
  4128. // Lower rage on hit.
  4129. if ( pAttacker->IsPlayerClass( TF_CLASS_SOLDIER ) || pAttacker->IsPlayerClass( TF_CLASS_PYRO ) )
  4130. {
  4131. int iRageOnHit = 0;
  4132. CALL_ATTRIB_HOOK_INT( iRageOnHit, rage_on_hit );
  4133. pAttacker->m_Shared.ModifyRage( iRageOnHit );
  4134. }
  4135. // rune charge on hit
  4136. if ( pAttacker->m_Shared.CanRuneCharge() )
  4137. {
  4138. const float flMaxRuneCharge = 400.f;
  4139. float flAdd = (float)info.GetDamage() * ( 100.f / flMaxRuneCharge );
  4140. pAttacker->m_Shared.SetRuneCharge( pAttacker->m_Shared.GetRuneCharge() + flAdd );
  4141. }
  4142. // Increase Boost on hit
  4143. int iBoostOnDamage = 0;
  4144. CALL_ATTRIB_HOOK_INT_ON_OTHER( pAttacker, iBoostOnDamage, boost_on_damage );
  4145. if ( iBoostOnDamage != 0 )
  4146. {
  4147. float fHype = MIN( tf_scout_hype_pep_max.GetFloat(), pAttacker->m_Shared.GetScoutHypeMeter() + ( MAX( tf_scout_hype_pep_min_damage.GetFloat(), info.GetDamage() ) / tf_scout_hype_pep_mod.GetFloat() ) );
  4148. pAttacker->m_Shared.SetScoutHypeMeter( fHype );
  4149. pAttacker->TeamFortress_SetSpeed();
  4150. }
  4151. // Procs!
  4152. if( pVictim )
  4153. {
  4154. // Detemine weapon speed
  4155. float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay );
  4156. // Proc chance for AOE Heal
  4157. float flPPM = 0.f;
  4158. CALL_ATTRIB_HOOK_FLOAT( flPPM, aoe_heal_chance );
  4159. float flProcChance = flFireDelay * (flPPM / 60.f);
  4160. if( RandomFloat() < flProcChance )
  4161. {
  4162. pAttacker->m_Shared.AddCond( TF_COND_RADIUSHEAL_ON_DAMAGE, 1.0f );
  4163. }
  4164. // Proc chance for crit boost
  4165. flPPM = 0.f;
  4166. CALL_ATTRIB_HOOK_FLOAT( flPPM, crits_on_damage );
  4167. flProcChance = flFireDelay * (flPPM / 60.f);
  4168. if( RandomFloat() < flProcChance )
  4169. {
  4170. pAttacker->m_Shared.AddCond( TF_COND_CRITBOOSTED_CARD_EFFECT, 3 );
  4171. }
  4172. // Proc chance for stun
  4173. flPPM = 0.f;
  4174. CALL_ATTRIB_HOOK_FLOAT( flPPM, stun_on_damage );
  4175. flProcChance = flFireDelay * (flPPM / 60.f);
  4176. if( RandomFloat() < flProcChance )
  4177. {
  4178. pVictim->m_Shared.StunPlayer( 3.0, 1.f, TF_STUN_MOVEMENT | TF_STUN_CONTROLS, pAttacker );
  4179. }
  4180. // Proc chance for AOE Blast
  4181. flPPM = 0.f;
  4182. CALL_ATTRIB_HOOK_FLOAT ( flPPM, aoe_blast_on_damage );
  4183. flProcChance = flFireDelay * (flPPM / 60.f);
  4184. if ( (RandomFloat() < flProcChance) )
  4185. {
  4186. // Stun the source
  4187. float flStunDuration = 2.f;
  4188. float flStunAmt = 1.f;
  4189. pVictim->m_Shared.StunPlayer( flStunDuration, flStunAmt, TF_STUN_MOVEMENT | TF_STUN_CONTROLS | TF_STUN_NO_EFFECTS, pAttacker );
  4190. pVictim->m_Shared.MakeBleed( ToTFPlayer( pAttacker ), NULL, flStunDuration, 75 );
  4191. // Generate an explosion and look for nearby bots
  4192. float flDmgRange = 100.f;
  4193. const int nMaxEnts = 12;
  4194. CBaseEntity *pObjects[ nMaxEnts ];
  4195. CTFPlayer* pPrevTFPlayer = NULL;
  4196. int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, pVictim->GetAbsOrigin(), flDmgRange, FL_CLIENT );
  4197. for ( int i = 0; i < nCount; i++ )
  4198. {
  4199. if ( !pObjects[i] )
  4200. continue;
  4201. if ( !pObjects[i]->IsAlive() )
  4202. continue;
  4203. if ( pObjects[i]->GetTeamNumber() != pVictim->GetTeamNumber() )
  4204. continue;
  4205. if ( !FVisible( pObjects[i], MASK_OPAQUE ) )
  4206. continue;
  4207. CTFPlayer *pTFPlayer = static_cast<CTFPlayer *>( pObjects[i] );
  4208. if ( !pTFPlayer )
  4209. continue;
  4210. if ( pTFPlayer == pVictim )
  4211. continue;
  4212. if ( !pTFPlayer->IsBot() )
  4213. continue;
  4214. if ( pVictim->m_Shared.InCond( TF_COND_PHASE ) || pVictim->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  4215. continue;
  4216. if ( pVictim->m_Shared.IsInvulnerable() )
  4217. continue;
  4218. // Stun
  4219. pTFPlayer->m_Shared.StunPlayer( flStunDuration, flStunAmt, TF_STUN_MOVEMENT | TF_STUN_CONTROLS | TF_STUN_NO_EFFECTS, pAttacker );
  4220. // DoT
  4221. pTFPlayer->m_Shared.MakeBleed( ToTFPlayer( pAttacker ), NULL, flStunDuration, 75.f );
  4222. // Shoot a beam at them
  4223. CPVSFilter filter( pTFPlayer->WorldSpaceCenter() );
  4224. Vector vStart = pPrevTFPlayer == NULL ? pVictim->EyePosition() : pPrevTFPlayer->EyePosition();
  4225. Vector vEnd = pTFPlayer->EyePosition();
  4226. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_ABSORIGIN, vEnd };
  4227. TE_TFParticleEffectComplex( filter, 0.0f, "dxhr_arm_muzzleflash", vStart, QAngle( 0, 0, 0 ), NULL, &controlPoint, pTFPlayer, PATTACH_CUSTOMORIGIN );
  4228. pTFPlayer->EmitSound( "Weapon_Upgrade.ExplosiveHeadshot" );
  4229. pPrevTFPlayer = pTFPlayer;
  4230. }
  4231. }
  4232. }
  4233. // Damage bonus on hit
  4234. // Disabled because we have no attributes that use it
  4235. /*
  4236. float flAddDamageDoneBonusOnHit = 0;
  4237. CALL_ATTRIB_HOOK_FLOAT( flAddDamageDoneBonusOnHit, addperc_ondmgdone_tmpbuff );
  4238. if ( flAddDamageDoneBonusOnHit )
  4239. {
  4240. pAttacker->m_Shared.AddTmpDamageBonus( flAddDamageDoneBonusOnHit, 10.0 );
  4241. }
  4242. */
  4243. if ( pVictim )
  4244. {
  4245. int iRageStun = 0;
  4246. CALL_ATTRIB_HOOK_INT_ON_OTHER( pAttacker, iRageStun, generate_rage_on_dmg );
  4247. if ( iRageStun && pAttacker->m_Shared.IsRageDraining() )
  4248. {
  4249. // MvM: Heavies can purchase a rage-based knockback+stun effect
  4250. if ( pAttacker->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  4251. {
  4252. int iStunFlags = TF_STUN_MOVEMENT | TF_STUN_NO_EFFECTS;
  4253. pVictim->m_Shared.StunPlayer( 0.25f, 1.f, iStunFlags, pAttacker );
  4254. }
  4255. }
  4256. // Slow enemy on hit, unless they're being healed by a medic
  4257. if ( !pVictim->m_Shared.InCond( TF_COND_HEALTH_BUFF ) )
  4258. {
  4259. float flSlowEnemy = 0.0;
  4260. CALL_ATTRIB_HOOK_FLOAT( flSlowEnemy, mult_onhit_enemyspeed );
  4261. if ( flSlowEnemy )
  4262. {
  4263. if ( RandomFloat() < flSlowEnemy )
  4264. {
  4265. // Adjust the stun amount based on distance to the target
  4266. // close range full stun, falls off to zero at 1536 (1024 window size)
  4267. Vector vecDistance = pVictim->GetAbsOrigin() - pAttacker->GetAbsOrigin();
  4268. float flStunAmount = RemapValClamped( vecDistance.LengthSqr(), (512.0f * 512.0f), (1536.0f * 1536.0f), 0.60f, 0.0f );
  4269. pVictim->m_Shared.StunPlayer( 0.2, flStunAmount, TF_STUN_MOVEMENT, pAttacker );
  4270. }
  4271. }
  4272. flSlowEnemy = 0.0;
  4273. CALL_ATTRIB_HOOK_FLOAT( flSlowEnemy, mult_onhit_enemyspeed_major );
  4274. if ( flSlowEnemy )
  4275. {
  4276. pVictim->m_Shared.StunPlayer( flSlowEnemy, 0.4, TF_STUN_MOVEMENT, pAttacker );
  4277. }
  4278. }
  4279. // Mark for death on hit.
  4280. int iMarkForDeath = 0;
  4281. CALL_ATTRIB_HOOK_INT( iMarkForDeath, mark_for_death );
  4282. if ( iMarkForDeath )
  4283. {
  4284. // Note: this logic isn't perfect, and can do non-obvious things in certain situations. For example,
  4285. // imagine that we've got two scouts -- if the first scout marks someone, and then the second scout marks
  4286. // the same guy, and then the first scout marks someone else, the original victim will lose his marked-
  4287. // for-death status. Conditions don't have any concept of owner. This could be manually tracked for this
  4288. // condition if it becomes a problem.
  4289. if ( pAttacker->m_pMarkedForDeathTarget != NULL && pAttacker->m_pMarkedForDeathTarget->m_Shared.InCond( TF_COND_MARKEDFORDEATH ) )
  4290. {
  4291. pAttacker->m_pMarkedForDeathTarget->m_Shared.RemoveCond( TF_COND_MARKEDFORDEATH );
  4292. }
  4293. float flDuration = pVictim->IsMiniBoss() ? tf_dev_marked_for_death_lifetime.GetFloat() / 2 : tf_dev_marked_for_death_lifetime.GetFloat();
  4294. pVictim->m_Shared.AddCond( TF_COND_MARKEDFORDEATH, flDuration, pAttacker );
  4295. pAttacker->m_pMarkedForDeathTarget = pVictim;
  4296. // ACHIEVEMENT_TF_MVM_SCOUT_MARK_FOR_DEATH
  4297. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  4298. {
  4299. if ( pAttacker->IsPlayerClass( TF_CLASS_SCOUT ) && ( GetWeaponID() == TF_WEAPON_BAT_WOOD ) )
  4300. {
  4301. if ( pVictim->IsBot() && ( pVictim->GetTeamNumber() == TF_TEAM_PVE_INVADERS ) )
  4302. {
  4303. IGameEvent *event = gameeventmanager->CreateEvent( "mvm_scout_marked_for_death" );
  4304. if ( event )
  4305. {
  4306. event->SetInt( "player", pAttacker->entindex() );
  4307. gameeventmanager->FireEvent( event );
  4308. }
  4309. }
  4310. }
  4311. }
  4312. }
  4313. // Stun airborne enemies who are half a body length higher than attacker
  4314. bool bIsVictimAirborne = !( pVictim->GetFlags() & FL_ONGROUND ) && ( pVictim->GetWaterLevel() == WL_NotInWater );
  4315. int iStunWaistHighAirborne = 0;
  4316. CALL_ATTRIB_HOOK_INT( iStunWaistHighAirborne, stun_waist_high_airborne );
  4317. if ( iStunWaistHighAirborne > 0 && bIsVictimAirborne )
  4318. {
  4319. if ( pVictim->WorldSpaceCenter().z >= pAttacker->EyePosition().z )
  4320. {
  4321. // right in the jimmy!
  4322. pVictim->m_Shared.StunPlayer( iStunWaistHighAirborne, 0.5f, TF_STUN_LOSER_STATE | TF_STUN_BOTH, pAttacker );
  4323. pVictim->EmitSound( "Halloween.PlayerScream" );
  4324. }
  4325. }
  4326. }
  4327. }
  4328. //-----------------------------------------------------------------------------
  4329. // Purpose: When owner of this weapon is hit
  4330. //-----------------------------------------------------------------------------
  4331. void CTFWeaponBase::ApplyOnInjuredAttributes( CTFPlayer *pVictim, CTFPlayer *pAttacker, const CTakeDamageInfo &info )
  4332. {
  4333. if ( CanDeploy() )
  4334. {
  4335. int iBecomeFireproofOnHitByFire = 0;
  4336. CALL_ATTRIB_HOOK_INT( iBecomeFireproofOnHitByFire, become_fireproof_on_hit_by_fire );
  4337. if ( iBecomeFireproofOnHitByFire > 0 && info.GetDamageType() & DMG_BURN )
  4338. {
  4339. pVictim->m_Shared.AddCond( TF_COND_FIRE_IMMUNE, 1.0 );
  4340. if ( pVictim->m_Shared.InCond( TF_COND_BURNING ) )
  4341. {
  4342. pVictim->EmitSound( "TFPlayer.FlameOut" );
  4343. pVictim->m_Shared.RemoveCond( TF_COND_BURNING );
  4344. }
  4345. // STAGING_SPY
  4346. pVictim->m_Shared.AddCond( TF_COND_AFTERBURN_IMMUNE, iBecomeFireproofOnHitByFire );
  4347. }
  4348. }
  4349. }
  4350. //-----------------------------------------------------------------------------
  4351. // Purpose:
  4352. //-----------------------------------------------------------------------------
  4353. void CTFWeaponBase::ApplyPostHitEffects( const CTakeDamageInfo &info, CTFPlayer *pVictim )
  4354. {
  4355. bool bDidDrain = false;
  4356. CTFPlayer *pAttacker = ToTFPlayer( info.GetAttacker() );
  4357. if ( !pAttacker || !pVictim )
  4358. return;
  4359. // only drain a victim once per shot, even with penetrating weapons
  4360. if ( pVictim != m_hLastDrainVictim || m_lastDrainVictimTimer.IsElapsed() )
  4361. {
  4362. // Subtract victim's Medigun charge on hit
  4363. int iSubtractVictimMedigunChargeOnHit = 0;
  4364. CALL_ATTRIB_HOOK_INT( iSubtractVictimMedigunChargeOnHit, subtract_victim_medigun_charge_onhit );
  4365. if ( iSubtractVictimMedigunChargeOnHit > 0 )
  4366. {
  4367. CWeaponMedigun *pMedigun = (CWeaponMedigun *)pVictim->Weapon_OwnsThisID( TF_WEAPON_MEDIGUN );
  4368. if ( pMedigun && !pMedigun->IsReleasingCharge() )
  4369. {
  4370. // STAGING_ENGY
  4371. // Scale drain after 512 Hu to 1536Hu ( 50% drain at 1024, 0 drain at 1536 units )
  4372. Vector toEnt = pVictim->GetAbsOrigin() - pAttacker->GetAbsOrigin();
  4373. if ( toEnt.LengthSqr() > Square( 512.0f ) )
  4374. {
  4375. iSubtractVictimMedigunChargeOnHit *= RemapValClamped( toEnt.LengthSqr(), (512.0f * 512.0f), (1536.0f * 1536.0f), 1.0f, 0.0f );
  4376. }
  4377. pMedigun->SubtractCharge( iSubtractVictimMedigunChargeOnHit / 100.0f );
  4378. bDidDrain = true;
  4379. }
  4380. }
  4381. // Subtract victim's cloak on hit
  4382. int iSubtractVictimCloakOnHit = 0;
  4383. CALL_ATTRIB_HOOK_INT( iSubtractVictimCloakOnHit, subtract_victim_cloak_on_hit );
  4384. if ( iSubtractVictimCloakOnHit > 0 && pVictim->IsPlayerClass( TF_CLASS_SPY ) )
  4385. {
  4386. // STAGING_ENGY
  4387. // Scale drain after 512 Hu to 1536Hu ( 50% drain at 1024, 0 drain at 1536 units )
  4388. Vector toEnt = pVictim->GetAbsOrigin() - pAttacker->GetAbsOrigin();
  4389. if ( toEnt.LengthSqr() > Square( 512.0f ) )
  4390. {
  4391. iSubtractVictimCloakOnHit *= RemapValClamped( toEnt.LengthSqr(), (512.0f * 512.0f), (1536.0f * 1536.0f), 1.0f, 0.0f );
  4392. }
  4393. float flCloak = pVictim->m_Shared.GetSpyCloakMeter();
  4394. flCloak -= iSubtractVictimCloakOnHit;
  4395. if ( flCloak < 0.0f )
  4396. {
  4397. flCloak = 0.0f;
  4398. }
  4399. pVictim->m_Shared.SetSpyCloakMeter( flCloak );
  4400. bDidDrain = true;
  4401. }
  4402. // don't play effects to attacker if he hit a disguised/cloaked spy
  4403. if ( !pVictim->m_Shared.InCond( TF_COND_DISGUISED ) &&
  4404. !pVictim->m_Shared.IsStealthed() )
  4405. {
  4406. if ( bDidDrain )
  4407. {
  4408. DispatchParticleEffect( "drg_pomson_impact_drain", PATTACH_POINT, pVictim, "head", GetParticleColor( 1 ), GetParticleColor( 2 ) );
  4409. if ( pAttacker )
  4410. {
  4411. // play drain sound effect, louder for the attacker
  4412. EmitSound_t params;
  4413. params.m_flSoundTime = 0;
  4414. params.m_pSoundName = "Weapon_Pomson.DrainedVictim";
  4415. params.m_pflSoundDuration = 0;
  4416. CPASFilter filter( pVictim->GetAbsOrigin() );
  4417. filter.RemoveRecipient( pAttacker );
  4418. EmitSound( filter, pVictim->entindex(), params );
  4419. CSingleUserRecipientFilter attackerFilter( pAttacker );
  4420. EmitSound( attackerFilter, pAttacker->entindex(), params );
  4421. }
  4422. m_hLastDrainVictim = pVictim;
  4423. m_lastDrainVictimTimer.Start( 0.3f );
  4424. }
  4425. }
  4426. }
  4427. }
  4428. //-----------------------------------------------------------------------------
  4429. // Purpose:
  4430. //-----------------------------------------------------------------------------
  4431. void CTFWeaponBase::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  4432. {
  4433. // Deliberately disabled to prevent players picking up fallen weapons.
  4434. return;
  4435. }
  4436. //-----------------------------------------------------------------------------
  4437. // Purpose:
  4438. //-----------------------------------------------------------------------------
  4439. void CTFWeaponBase::DisguiseWeaponThink( void )
  4440. {
  4441. // Periodically check to make sure we are valid.
  4442. // Disguise weapons are attached to a player, but not managed through the owned weapons list.
  4443. CTFPlayer *pTFOwner = ToTFPlayer( GetOwner() );
  4444. if ( !pTFOwner )
  4445. {
  4446. // We must have an owner to be valid.
  4447. Drop( Vector( 0,0,0 ) );
  4448. return;
  4449. }
  4450. if ( pTFOwner->m_Shared.GetDisguiseWeapon() != this )
  4451. {
  4452. // The owner's disguise weapon must be us, otherwise we are invalid.
  4453. Drop( Vector( 0,0,0 ) );
  4454. return;
  4455. }
  4456. SetContextThink( &CTFWeaponBase::DisguiseWeaponThink, gpGlobals->curtime + 0.5, "DisguiseWeaponThink" );
  4457. }
  4458. #endif // GAME_DLL
  4459. //-----------------------------------------------------------------------------
  4460. // Purpose:
  4461. //-----------------------------------------------------------------------------
  4462. bool CTFWeaponBase::IsViewModelFlipped( void )
  4463. {
  4464. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  4465. if ( !pPlayer )
  4466. return false;
  4467. #ifdef GAME_DLL
  4468. if ( m_bFlipViewModel != pPlayer->m_bFlipViewModels )
  4469. {
  4470. return true;
  4471. }
  4472. #else
  4473. if ( m_bFlipViewModel != cl_flipviewmodels.GetBool() )
  4474. {
  4475. return true;
  4476. }
  4477. #endif
  4478. return false;
  4479. }
  4480. //-----------------------------------------------------------------------------
  4481. // Purpose:
  4482. //-----------------------------------------------------------------------------
  4483. void CTFWeaponBase::ReapplyProvision( void )
  4484. {
  4485. // Disguise items never provide
  4486. if ( m_bDisguiseWeapon )
  4487. {
  4488. #ifdef GAME_DLL
  4489. UpdateModelToClass();
  4490. #endif
  4491. return;
  4492. }
  4493. int iProvideMode = 0;
  4494. CALL_ATTRIB_HOOK_INT( iProvideMode, provide_on_active );
  4495. if ( 1 == iProvideMode )
  4496. {
  4497. if ( m_iState == WEAPON_IS_ACTIVE )
  4498. {
  4499. // We are active, provide to our owner.
  4500. BaseClass::ReapplyProvision();
  4501. }
  4502. else
  4503. {
  4504. // We aren't active so stop providing to our owner.
  4505. GetAttributeManager()->StopProvidingTo( GetPlayerOwner() );
  4506. m_hOldProvidee = NULL;
  4507. }
  4508. }
  4509. else
  4510. {
  4511. BaseClass::ReapplyProvision();
  4512. }
  4513. }
  4514. //-----------------------------------------------------------------------------
  4515. // Purpose: Return the origin & angles for a projectile fired from the player's gun
  4516. //-----------------------------------------------------------------------------
  4517. void CTFWeaponBase::GetProjectileFireSetup( CTFPlayer *pPlayer, Vector vecOffset, Vector *vecSrc, QAngle *angForward, bool bHitTeammates /* = true */, float flEndDist /* = 2000 */)
  4518. {
  4519. // @todo third person code!!
  4520. // Flip the firing offset if our view model is flipped.
  4521. if ( IsViewModelFlipped() )
  4522. {
  4523. vecOffset.y *= -1;
  4524. }
  4525. int iCenterFireProjectile = 0;
  4526. CALL_ATTRIB_HOOK_INT( iCenterFireProjectile, centerfire_projectile );
  4527. if ( iCenterFireProjectile == 1 )
  4528. {
  4529. vecOffset.y = 0;
  4530. }
  4531. QAngle angSpread = GetSpreadAngles();
  4532. Vector vecForward, vecRight, vecUp;
  4533. AngleVectors( angSpread, &vecForward, &vecRight, &vecUp );
  4534. Vector vecShootPos = pPlayer->Weapon_ShootPosition();
  4535. // Estimate end point
  4536. Vector endPos = vecShootPos + vecForward * flEndDist;
  4537. // Trace forward and find what's in front of us, and aim at that
  4538. trace_t tr;
  4539. if ( bHitTeammates )
  4540. {
  4541. CTraceFilterSimple traceFilter( pPlayer, COLLISION_GROUP_NONE );
  4542. ITraceFilter *pFilterChain = NULL;
  4543. CTraceFilterIgnoreFriendlyCombatItems traceFilterCombatItem( pPlayer, COLLISION_GROUP_NONE, GetTeamNumber() );
  4544. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  4545. {
  4546. // Ignore teammates and their (physical) upgrade items in MvM
  4547. pFilterChain = &traceFilterCombatItem;
  4548. }
  4549. CTraceFilterChain traceFilterChain( &traceFilter, pFilterChain );
  4550. UTIL_TraceLine( vecShootPos, endPos, MASK_SOLID, &traceFilterChain, &tr );
  4551. }
  4552. else
  4553. {
  4554. CTraceFilterIgnoreTeammates filter( pPlayer, COLLISION_GROUP_NONE, pPlayer->GetTeamNumber() );
  4555. UTIL_TraceLine( vecShootPos, endPos, MASK_SOLID, &filter, &tr );
  4556. }
  4557. // Offset actual start point
  4558. *vecSrc = vecShootPos + (vecForward * vecOffset.x) + (vecRight * vecOffset.y) + (vecUp * vecOffset.z);
  4559. // Find angles that will get us to our desired end point
  4560. // Only use the trace end if it wasn't too close, which results
  4561. // in visually bizarre forward angles
  4562. if ( tr.fraction > 0.1 )
  4563. {
  4564. VectorAngles( tr.endpos - *vecSrc, *angForward );
  4565. }
  4566. else
  4567. {
  4568. VectorAngles( endPos - *vecSrc, *angForward );
  4569. }
  4570. }
  4571. //-----------------------------------------------------------------------------
  4572. // Purpose:
  4573. //-----------------------------------------------------------------------------
  4574. QAngle CTFWeaponBase::GetSpreadAngles( void )
  4575. {
  4576. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  4577. Assert( pOwner );
  4578. QAngle angEyes = pOwner->EyeAngles();
  4579. float flSpreadAngle = 0.0f;
  4580. CALL_ATTRIB_HOOK_FLOAT( flSpreadAngle, projectile_spread_angle );
  4581. if ( flSpreadAngle )
  4582. {
  4583. QAngle angSpread = RandomAngle( -flSpreadAngle, flSpreadAngle );
  4584. angSpread.z = 0.0f;
  4585. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  4586. {
  4587. if ( CanOverload() && AutoFiresFullClip() && Clip1() == 1 && !m_bFiringWholeClip )
  4588. {
  4589. float flTimeSinceLastAttack = gpGlobals->curtime - GetLastPrimaryAttackTime();
  4590. if ( flTimeSinceLastAttack < 0.9f )
  4591. {
  4592. // Punish upgraded single-fire spam for this class of weapon
  4593. float flPenaltyAngle = RemapValClamped( flTimeSinceLastAttack, 0.4f, 0.9f, 6.f, 1.f );
  4594. angSpread += RandomAngle( -flPenaltyAngle, flPenaltyAngle );
  4595. }
  4596. }
  4597. }
  4598. angEyes += angSpread;
  4599. }
  4600. return angEyes;
  4601. }
  4602. //-----------------------------------------------------------------------------
  4603. // Purpose:
  4604. //-----------------------------------------------------------------------------
  4605. bool CTFWeaponBase::CanPerformSecondaryAttack() const
  4606. {
  4607. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  4608. // Demo shields are allowed to charge whenever
  4609. if ( pOwner->m_Shared.HasDemoShieldEquipped() )
  4610. return true;
  4611. return BaseClass::CanPerformSecondaryAttack();
  4612. }
  4613. //-----------------------------------------------------------------------------
  4614. // Purpose:
  4615. //-----------------------------------------------------------------------------
  4616. bool CTFWeaponBase::AreRandomCritsEnabled( void )
  4617. {
  4618. if ( TFGameRules() )
  4619. {
  4620. if ( TFGameRules()->IsPowerupMode() )
  4621. return false;
  4622. const IMatchGroupDescription *pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  4623. if ( pMatchDesc )
  4624. return pMatchDesc->m_params.m_bRandomWeaponCrits;
  4625. }
  4626. return tf_weapon_criticals.GetBool();
  4627. }
  4628. #ifdef GAME_DLL
  4629. //-----------------------------------------------------------------------------
  4630. // Purpose:
  4631. //-----------------------------------------------------------------------------
  4632. void CTFWeaponBase::ChangeTeam( int iTeamNum )
  4633. {
  4634. BaseClass::ChangeTeam( iTeamNum );
  4635. // We need to set the team for our econ item view as well
  4636. if ( GetAttributeContainer() && GetAttributeContainer()->GetItem() )
  4637. {
  4638. GetAttributeContainer()->GetItem()->SetTeamNumber( GetTeamNumber() );
  4639. }
  4640. }
  4641. //-----------------------------------------------------------------------------
  4642. // Purpose:
  4643. //-----------------------------------------------------------------------------
  4644. bool CTFWeaponBase::DeflectProjectiles()
  4645. {
  4646. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  4647. if ( !pOwner )
  4648. return false;
  4649. if ( pOwner->GetWaterLevel() == WL_Eyes )
  4650. return false;
  4651. lagcompensation->StartLagCompensation( pOwner, pOwner->GetCurrentCommand() );
  4652. Vector vecEye = pOwner->EyePosition();
  4653. Vector vecForward, vecRight, vecUp;
  4654. AngleVectors( pOwner->EyeAngles(), &vecForward, &vecRight, &vecUp );
  4655. Vector vecSize = GetDeflectionSize();
  4656. float flMaxElement = 0.0f;
  4657. for ( int i = 0; i < 3; ++i )
  4658. {
  4659. flMaxElement = MAX( flMaxElement, vecSize[i] );
  4660. }
  4661. Vector vecCenter = vecEye + vecForward * flMaxElement;
  4662. // Get a list of entities in the box defined by vecSize at VecCenter.
  4663. // We will then try to deflect everything in the box.
  4664. const int maxCollectedEntities = 64;
  4665. CBaseEntity *pObjects[ maxCollectedEntities ];
  4666. int count = UTIL_EntitiesInBox( pObjects, maxCollectedEntities, vecCenter - vecSize, vecCenter + vecSize, FL_CLIENT | FL_GRENADE );
  4667. // NDebugOverlay::Box( vecCenter, -vecSize, vecSize, 0, 255, 0, 40, 3 );
  4668. bool bDeflected = false;
  4669. bool bDeflectedPlayer = false;
  4670. int iEnemyTeam = GetEnemyTeam( pOwner->GetTeamNumber() );
  4671. bool bTruce = TFGameRules() && TFGameRules()->IsTruceActive() && pOwner->IsTruceValidForEnt();
  4672. for ( int i = 0; i < count; i++ )
  4673. {
  4674. if ( pObjects[i] == pOwner )
  4675. continue;
  4676. if ( pObjects[i]->IsPlayer() && pObjects[i]->GetTeamNumber() == TEAM_SPECTATOR )
  4677. continue;
  4678. if ( !pObjects[i]->IsDeflectable() && !FClassnameIs( pObjects[i], "prop_physics" ) )
  4679. continue;
  4680. if ( pOwner->FVisible( pObjects[i], MASK_SOLID ) == false )
  4681. continue;
  4682. if ( bTruce && ( pObjects[i]->GetTeamNumber() == iEnemyTeam ) )
  4683. continue;
  4684. if ( pObjects[i]->IsPlayer() == true )
  4685. {
  4686. CTFPlayer *pTarget = ToTFPlayer( pObjects[i] );
  4687. if ( pTarget )
  4688. {
  4689. bool bRes = DeflectPlayer( pTarget, pOwner, vecForward, vecCenter, vecSize );
  4690. bDeflectedPlayer |= bRes;
  4691. bDeflected |= bRes;
  4692. }
  4693. }
  4694. else
  4695. {
  4696. bDeflected |= DeflectEntity( pObjects[i], pOwner, vecForward, vecCenter, vecSize );
  4697. }
  4698. }
  4699. if ( bDeflected )
  4700. {
  4701. pOwner->SpeakConceptIfAllowed( MP_CONCEPT_DEFLECTED, "victim:0" );
  4702. PlayDeflectionSound( bDeflectedPlayer );
  4703. }
  4704. lagcompensation->FinishLagCompensation( pOwner );
  4705. return true;
  4706. }
  4707. //-----------------------------------------------------------------------------
  4708. // Purpose:
  4709. //-----------------------------------------------------------------------------
  4710. bool CTFWeaponBase::DeflectPlayer( CTFPlayer *pTarget, CTFPlayer *pOwner, Vector &vecForward, Vector &vecCenter, Vector &vecSize )
  4711. {
  4712. return true;
  4713. }
  4714. //-----------------------------------------------------------------------------
  4715. // This filter checks against friendly players, buildings, shields
  4716. //-----------------------------------------------------------------------------
  4717. class CTraceFilterDeflection : public CTraceFilterSimple
  4718. {
  4719. public:
  4720. DECLARE_CLASS( CTraceFilterDeflection, CTraceFilterSimple );
  4721. CTraceFilterDeflection( const IHandleEntity *passentity, int collisionGroup, int iIgnoreTeam )
  4722. : CTraceFilterSimple( passentity, collisionGroup ), m_iIgnoreTeam( iIgnoreTeam )
  4723. {
  4724. }
  4725. virtual bool ShouldHitEntity( IHandleEntity *passentity, int contentsMask ) OVERRIDE
  4726. {
  4727. CBaseEntity *pEntity = EntityFromEntityHandle( passentity );
  4728. if ( !pEntity )
  4729. return false;
  4730. if ( pEntity->IsPlayer() )
  4731. return false;
  4732. if ( pEntity->IsBaseObject() )
  4733. return false;
  4734. if ( pEntity->IsCombatItem() )
  4735. return false;
  4736. return BaseClass::ShouldHitEntity( passentity, contentsMask );
  4737. }
  4738. int m_iIgnoreTeam;
  4739. };
  4740. //-----------------------------------------------------------------------------
  4741. // Purpose:
  4742. //-----------------------------------------------------------------------------
  4743. bool CTFWeaponBase::DeflectEntity( CBaseEntity *pTarget, CTFPlayer *pOwner, Vector &vecForward, Vector &vecCenter, Vector &vecSize )
  4744. {
  4745. Assert( pTarget );
  4746. Assert( pOwner );
  4747. Vector vecEye = pOwner->EyePosition();
  4748. Vector vecVel = pTarget->GetAbsVelocity();
  4749. // apply an impulse instead if this is a prop physics object
  4750. if ( FClassnameIs( pTarget, "prop_physics" ) )
  4751. {
  4752. IPhysicsObject *pPhysicsObject = pTarget->VPhysicsGetObject();
  4753. if ( pPhysicsObject && pTarget->CollisionProp() )
  4754. {
  4755. Vector vecDir = pTarget->WorldSpaceCenter() - vecEye;
  4756. VectorNormalize( vecDir );
  4757. float flVel = 50.0f * CTFWeaponBase::DeflectionForce( pTarget->CollisionProp()->OBBSize(), 90, 12.0f );
  4758. pPhysicsObject->ApplyForceOffset( vecDir * flVel, vecEye );
  4759. }
  4760. return true;
  4761. }
  4762. AngularImpulse angularimp;
  4763. CTraceFilterDeflection filter( pOwner, COLLISION_GROUP_NONE, pOwner->GetTeamNumber() );
  4764. trace_t tr;
  4765. UTIL_TraceLine( vecEye, vecEye + vecForward * MAX_TRACE_LENGTH, MASK_SOLID, &filter, &tr );
  4766. Vector vecDir = pTarget->WorldSpaceCenter() - tr.endpos;
  4767. VectorNormalize( vecDir );
  4768. // Send the entity back where it came.
  4769. // If we want per-entity physical deflection behavior this could move into ::Deflected
  4770. IPhysicsObject *pPhysicsObject = pTarget->VPhysicsGetObject();
  4771. if ( pPhysicsObject )
  4772. {
  4773. pPhysicsObject->GetVelocity( &vecVel, &angularimp );
  4774. }
  4775. float flVel = vecVel.Length();
  4776. vecVel = -flVel * vecDir;
  4777. if ( pPhysicsObject )
  4778. {
  4779. if ( pPhysicsObject->IsMotionEnabled() == false )
  4780. {
  4781. vecDir = pOwner->WorldSpaceCenter() - pTarget->WorldSpaceCenter();
  4782. VectorNormalize( vecDir );
  4783. vecVel = -flVel * vecDir;
  4784. }
  4785. pPhysicsObject->EnableMotion( true );
  4786. pPhysicsObject->SetVelocity( &vecVel, &angularimp );
  4787. }
  4788. else
  4789. {
  4790. pTarget->SetAbsVelocity( vecVel );
  4791. }
  4792. // Perform entity specific deflection behavior like team changing.
  4793. pTarget->Deflected( pOwner, vecDir );
  4794. QAngle newAngles;
  4795. VectorAngles( -vecDir, newAngles );
  4796. pTarget->SetAbsAngles( newAngles );
  4797. pOwner->AwardAchievement( ACHIEVEMENT_TF_PYRO_REFLECT_PROJECTILES );
  4798. CDisablePredictionFiltering disabler;
  4799. DispatchParticleEffect( "deflect_fx", PATTACH_ABSORIGIN_FOLLOW, pTarget );
  4800. return true;
  4801. }
  4802. //-----------------------------------------------------------------------------
  4803. // Purpose: Static deflection helper.
  4804. //-----------------------------------------------------------------------------
  4805. float CTFWeaponBase::DeflectionForce( const Vector &size, float damage, float scale )
  4806. {
  4807. float force = damage * ((48 * 48 * 82.0) / (size.x * size.y * size.z)) * scale;
  4808. if ( force > 1000.0)
  4809. {
  4810. force = 1000.0;
  4811. }
  4812. return force;
  4813. }
  4814. //-----------------------------------------------------------------------------
  4815. // Purpose: Static deflection helper.
  4816. //-----------------------------------------------------------------------------
  4817. void CTFWeaponBase::SendObjectDeflectedEvent( CTFPlayer *pNewOwner, CTFPlayer *pPrevOwner, int iWeaponID, CBaseAnimating *pObject )
  4818. {
  4819. if ( pNewOwner && pPrevOwner )
  4820. {
  4821. IGameEvent * event = gameeventmanager->CreateEvent( "object_deflected" );
  4822. if ( event )
  4823. {
  4824. event->SetInt( "userid", pNewOwner->GetUserID() );
  4825. event->SetInt( "ownerid", pPrevOwner->GetUserID() );
  4826. event->SetInt( "weaponid", iWeaponID );
  4827. // Community request. We don't use object_entindex, but some server plugins do.
  4828. event->SetInt( "object_entindex", pObject ? pObject->entindex() : 0 );
  4829. gameeventmanager->FireEvent( event );
  4830. }
  4831. }
  4832. }
  4833. //-----------------------------------------------------------------------------
  4834. // Purpose: Separate Regen function to handle item-specific cases
  4835. //-----------------------------------------------------------------------------
  4836. void CTFWeaponBase::ApplyItemRegen( void )
  4837. {
  4838. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  4839. if ( !pOwner )
  4840. return;
  4841. m_flRegenTime += gpGlobals->frametime;
  4842. if ( m_flRegenTime > 1.0f )
  4843. {
  4844. m_flRegenTime -= 1.0;
  4845. float flRegenAmount = 0;
  4846. CALL_ATTRIB_HOOK_FLOAT( flRegenAmount, active_item_health_regen );
  4847. if ( (int)flRegenAmount != 0 )
  4848. {
  4849. pOwner->TakeDamage( CTakeDamageInfo( pOwner, pOwner, vec3_origin, WorldSpaceCenter(), (int)flRegenAmount * -1, DMG_GENERIC ) );
  4850. IGameEvent *event = gameeventmanager->CreateEvent( "player_healonhit" );
  4851. if ( event )
  4852. {
  4853. event->SetInt( "amount", (int)flRegenAmount );
  4854. event->SetInt( "entindex", pOwner->entindex() );
  4855. item_definition_index_t healingItemDef = INVALID_ITEM_DEF_INDEX;
  4856. if ( GetAttributeContainer() && GetAttributeContainer()->GetItem() )
  4857. {
  4858. healingItemDef = GetAttributeContainer()->GetItem()->GetItemDefIndex();
  4859. }
  4860. event->SetInt( "weapon_def_index", healingItemDef );
  4861. gameeventmanager->FireEvent( event );
  4862. }
  4863. }
  4864. }
  4865. }
  4866. kill_eater_event_t CTFWeaponBase::GetKillEaterKillEventType() const
  4867. {
  4868. uint32 unEventType = kKillEaterEvent_PlayerKill;
  4869. CALL_ATTRIB_HOOK_INT( unEventType, kill_eater_kill_type );
  4870. return (kill_eater_event_t)unEventType;
  4871. }
  4872. #endif // GAME_DLL
  4873. bool CTFWeaponBase::IsSilentKiller()
  4874. {
  4875. int iSilentKiller = 0;
  4876. CALL_ATTRIB_HOOK_INT( iSilentKiller, set_silent_killer );
  4877. if ( iSilentKiller == 1 )
  4878. return true;
  4879. else
  4880. return false;
  4881. }
  4882. //-----------------------------------------------------------------------------
  4883. // Purpose: Ensures that a player's correct body groups are enabled on client respawn.
  4884. //-----------------------------------------------------------------------------
  4885. void CTFWeaponBase::UpdateWeaponBodyGroups( CTFPlayer* pPlayer, bool bHandleDeployedBodygroups )
  4886. {
  4887. if ( !pPlayer )
  4888. return;
  4889. for ( int i = 0; i < pPlayer->WeaponCount(); i++)
  4890. {
  4891. CTFWeaponBase *pWpn = ( CTFWeaponBase *) pPlayer->GetWeapon(i);
  4892. if ( !pWpn )
  4893. continue;
  4894. // If this weapon if repurposed for a taunt, dont modify bodygroups. This is so
  4895. // things like the Heavy's boxing gloves can change to a different model (ie. a guitar)
  4896. // and then his hands will draw like normal
  4897. if( pWpn->IsBeingRepurposedForTaunt() )
  4898. continue;
  4899. // Dynamic models which are not yet rendering do not modify bodygroups
  4900. if ( pWpn->IsDynamicModelLoading() )
  4901. continue;
  4902. // These are updated later or have already been updated.
  4903. CEconItemView *pScriptItem = pWpn->GetAttributeContainer()->GetItem();
  4904. const bool bHideBodygroupsDeployedOnly = pScriptItem ? pScriptItem->GetStaticData()->GetHideBodyGroupsDeployedOnly() : false;
  4905. if ( bHideBodygroupsDeployedOnly != bHandleDeployedBodygroups )
  4906. continue;
  4907. // If we're supposed to hide bodygroups when deployed and we aren't deployed, don't do anything.
  4908. if ( bHideBodygroupsDeployedOnly && pPlayer->GetActiveWeapon() != pWpn )
  4909. continue;
  4910. pWpn->UpdateBodygroups( pPlayer, 1 );
  4911. }
  4912. }
  4913. #ifdef CLIENT_DLL
  4914. //-----------------------------------------------------------------------------
  4915. // Purpose: Weapon Level Notification
  4916. //-----------------------------------------------------------------------------
  4917. class CTFKillEaterNotification : public CEconNotification
  4918. {
  4919. public:
  4920. CTFKillEaterNotification( const CSteamID& KillerID, const wchar_t *wszWeaponName, const wchar_t *wszLevelName )
  4921. : CEconNotification()
  4922. {
  4923. SetLifetime( 20.0f );
  4924. SetSteamID( KillerID );
  4925. SetText( "#TF_HUD_Event_KillEater_Leveled" );
  4926. AddStringToken( "weapon_name", wszWeaponName );
  4927. AddStringToken( "rank_name", wszLevelName );
  4928. SetSoundFilename( "misc/happy_birthday.wav" );
  4929. }
  4930. virtual EType NotificationType() { return eType_Basic; }
  4931. };
  4932. //-----------------------------------------------------------------------------
  4933. // Purpose: GC Msg handler to receive the server response that we've killed a player.
  4934. //-----------------------------------------------------------------------------
  4935. class CGCPlayerKilledResponse : public GCSDK::CGCClientJob
  4936. {
  4937. public:
  4938. CGCPlayerKilledResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  4939. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  4940. {
  4941. GCSDK::CProtoBufMsg<CMsgGCIncrementKillCountResponse> msg( pNetPacket );
  4942. if ( !steamapicontext || !steamapicontext->SteamFriends() || !steamapicontext->SteamUser() || !steamapicontext->SteamUtils() )
  4943. return true;
  4944. item_definition_index_t unItemDef = msg.Body().item_def();
  4945. const CEconItemDefinition* pItemDef = ItemSystem()->GetStaticDataForItemByDefIndex( unItemDef );
  4946. if ( !pItemDef )
  4947. return true;
  4948. const char *pszKillerName = InventoryManager()->PersonaName_Get( msg.Body().killer_account_id() );
  4949. if ( !pszKillerName )
  4950. return true;
  4951. wchar_t wszPlayerName[1024];
  4952. g_pVGuiLocalize->ConvertANSIToUnicode( pszKillerName, wszPlayerName, sizeof(wszPlayerName) );
  4953. wchar_t* wszWeaponName = g_pVGuiLocalize->Find( pItemDef->GetItemBaseName() );
  4954. uint32 unLevelBlock = msg.Body().level_type();
  4955. const char *pszLevelBlockName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( unLevelBlock );
  4956. const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( pszLevelBlockName, msg.Body().num_kills() );
  4957. if ( !pLevelDef )
  4958. return true;
  4959. wchar_t* wszLevelName = g_pVGuiLocalize->Find( pLevelDef->GetNameLocalizationKey() );
  4960. // Kyle says: the notifications were annoying people so instead of doing a full
  4961. // flashy thing we display a HUD message for everyone except the guy whose
  4962. // weapon it is. Basically, *you* get the flashy notification that your
  4963. // weapon leveled up, but everyone else just gets the HUD text.
  4964. if ( steamapicontext->SteamUser()->GetSteamID().GetAccountID() == msg.Body().killer_account_id() )
  4965. {
  4966. NotificationQueue_Add( new CTFKillEaterNotification( CSteamID( msg.Body().killer_account_id(), GetUniverse(), k_EAccountTypeIndividual ), wszWeaponName, wszLevelName ) );
  4967. }
  4968. // Everyone gets the HUD notification text.
  4969. CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat );
  4970. if ( pHUDChat )
  4971. {
  4972. wchar_t wszNotification[1024]=L"";
  4973. g_pVGuiLocalize->ConstructString_safe( wszNotification,
  4974. g_pVGuiLocalize->Find( "#TF_HUD_Event_KillEater_Leveled_Chat" ),
  4975. 3, wszPlayerName, wszWeaponName, wszLevelName );
  4976. char szAnsi[1024];
  4977. g_pVGuiLocalize->ConvertUnicodeToANSI( wszNotification, szAnsi, sizeof(szAnsi) );
  4978. pHUDChat->Printf( CHAT_FILTER_NONE, "%s", szAnsi );
  4979. }
  4980. return true;
  4981. }
  4982. };
  4983. GC_REG_JOB( GCSDK::CGCClient, CGCPlayerKilledResponse, "CGCPlayerKilledResponse", k_EMsgGC_IncrementKillCountResponse, GCSDK::k_EServerTypeGCClient );
  4984. #endif
  4985. bool WeaponID_IsSniperRifle( int iWeaponID )
  4986. {
  4987. #ifdef STAGING_ONLY
  4988. if ( iWeaponID == TF_WEAPON_SNIPERRIFLE ||
  4989. iWeaponID == TF_WEAPON_SNIPERRIFLE_DECAP ||
  4990. iWeaponID == TF_WEAPON_SNIPERRIFLE_CLASSIC ||
  4991. iWeaponID == TF_WEAPON_SNIPERRIFLE_REVOLVER )
  4992. #else
  4993. if ( iWeaponID == TF_WEAPON_SNIPERRIFLE ||
  4994. iWeaponID == TF_WEAPON_SNIPERRIFLE_DECAP ||
  4995. iWeaponID == TF_WEAPON_SNIPERRIFLE_CLASSIC )
  4996. #endif
  4997. return true;
  4998. else
  4999. return false;
  5000. }
  5001. bool WeaponID_IsSniperRifleOrBow( int iWeaponID )
  5002. {
  5003. if ( iWeaponID == TF_WEAPON_COMPOUND_BOW )
  5004. return true;
  5005. else
  5006. return WeaponID_IsSniperRifle( iWeaponID );
  5007. }
  5008. //-----------------------------------------------------------------------------
  5009. // Purpose:
  5010. //-----------------------------------------------------------------------------
  5011. float CTFWeaponBase::Energy_GetMaxEnergy( void ) const
  5012. {
  5013. // This is a terrible hack to support clip size upgrades.
  5014. // Basically -- figure out the desired number of shots,
  5015. // and return the amount of energy required for that.
  5016. int iNumShots = ENERGY_WEAPON_MAX_CHARGE / Energy_GetShotCost();
  5017. CALL_ATTRIB_HOOK_FLOAT( iNumShots, mult_clipsize_upgrade );
  5018. return ( iNumShots * Energy_GetShotCost() );
  5019. }
  5020. //-----------------------------------------------------------------------------
  5021. // Purpose:
  5022. //-----------------------------------------------------------------------------
  5023. bool CTFWeaponBase::Energy_FullyCharged( void ) const
  5024. {
  5025. if ( m_flEnergy >= Energy_GetMaxEnergy() )
  5026. return true;
  5027. else
  5028. return false;
  5029. }
  5030. //-----------------------------------------------------------------------------
  5031. // Purpose:
  5032. //-----------------------------------------------------------------------------
  5033. bool CTFWeaponBase::Energy_HasEnergy( void )
  5034. {
  5035. if ( m_flEnergy >= Energy_GetShotCost() )
  5036. return true;
  5037. else
  5038. return false;
  5039. }
  5040. //-----------------------------------------------------------------------------
  5041. // Purpose:
  5042. //-----------------------------------------------------------------------------
  5043. void CTFWeaponBase::Energy_DrainEnergy( void )
  5044. {
  5045. Energy_DrainEnergy( Energy_GetShotCost() );
  5046. }
  5047. //-----------------------------------------------------------------------------
  5048. // Purpose:
  5049. //-----------------------------------------------------------------------------
  5050. void CTFWeaponBase::Energy_DrainEnergy( float flDrain )
  5051. {
  5052. m_flEnergy -= flDrain;
  5053. }
  5054. //-----------------------------------------------------------------------------
  5055. // Purpose:
  5056. //-----------------------------------------------------------------------------
  5057. bool CTFWeaponBase::Energy_Recharge( void )
  5058. {
  5059. m_flEnergy += Energy_GetRechargeCost();
  5060. if ( Energy_FullyCharged() )
  5061. {
  5062. m_flEnergy = Energy_GetMaxEnergy();
  5063. return true;
  5064. }
  5065. else
  5066. return false;
  5067. }
  5068. //-----------------------------------------------------------------------------
  5069. // Purpose:
  5070. //-----------------------------------------------------------------------------
  5071. void CTFWeaponBase::WeaponRegenerate( void )
  5072. {
  5073. m_flEnergy = Energy_GetMaxEnergy();
  5074. }
  5075. //-----------------------------------------------------------------------------
  5076. // Purpose:
  5077. //-----------------------------------------------------------------------------
  5078. void CTFWeaponBase::FinishReload( void )
  5079. {
  5080. if ( IsEnergyWeapon() )
  5081. {
  5082. m_bInReload = false;
  5083. return;
  5084. }
  5085. BaseClass::FinishReload();
  5086. CTFPlayer *pPlayer = GetTFPlayerOwner();
  5087. if ( pPlayer )
  5088. {
  5089. int iAttr = 0;
  5090. CALL_ATTRIB_HOOK_INT( iAttr, last_shot_crits );
  5091. if ( iAttr )
  5092. {
  5093. if ( m_iClip1 == 1 )
  5094. {
  5095. pPlayer->m_Shared.AddCond( TF_COND_CRITBOOSTED );
  5096. }
  5097. else
  5098. {
  5099. pPlayer->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  5100. }
  5101. }
  5102. }
  5103. }
  5104. //-----------------------------------------------------------------------------
  5105. // Purpose:
  5106. //-----------------------------------------------------------------------------
  5107. void CTFWeaponBase::CheckReload( void )
  5108. {
  5109. if ( IsEnergyWeapon() )
  5110. {
  5111. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  5112. if ( !pOwner )
  5113. return;
  5114. if ( !Energy_HasEnergy() )
  5115. {
  5116. Reload();
  5117. return;
  5118. }
  5119. if ((m_bInReload) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
  5120. {
  5121. if ( pOwner->m_nButtons & (IN_ATTACK | IN_ATTACK2) && Energy_HasEnergy() )
  5122. {
  5123. m_bInReload = false;
  5124. return;
  5125. }
  5126. if ( !Energy_FullyCharged() )
  5127. {
  5128. Reload();
  5129. }
  5130. else
  5131. {
  5132. FinishReload();
  5133. m_flNextPrimaryAttack = gpGlobals->curtime;
  5134. m_flNextSecondaryAttack = gpGlobals->curtime;
  5135. }
  5136. }
  5137. }
  5138. else
  5139. {
  5140. BaseClass::CheckReload();
  5141. }
  5142. }
  5143. //-----------------------------------------------------------------------------
  5144. // Purpose: Get the current bar state (will return a value from 0.0 to 1.0)
  5145. //-----------------------------------------------------------------------------
  5146. float CTFWeaponBase::GetEffectBarProgress( void )
  5147. {
  5148. CTFPlayer *pPlayer = GetTFPlayerOwner();
  5149. if ( pPlayer && (pPlayer->GetAmmoCount( GetEffectBarAmmo() ) < pPlayer->GetMaxAmmo( GetEffectBarAmmo() )) )
  5150. {
  5151. float flTime = GetEffectBarRechargeTime();
  5152. float flProgress = (flTime - (m_flEffectBarRegenTime - gpGlobals->curtime)) / flTime;
  5153. return flProgress;
  5154. }
  5155. return 1.f;
  5156. }
  5157. //-----------------------------------------------------------------------------
  5158. // Purpose: Start the regeneration bar charging from this moment in time
  5159. //-----------------------------------------------------------------------------
  5160. void CTFWeaponBase::StartEffectBarRegen( void )
  5161. {
  5162. // Only reset regen if its less then curr time or we were full
  5163. CTFPlayer *pPlayer = GetTFPlayerOwner();
  5164. bool bWasFull = false;
  5165. if ( pPlayer && (pPlayer->GetAmmoCount( GetEffectBarAmmo() ) + 1 == pPlayer->GetMaxAmmo( GetEffectBarAmmo() ) ) )
  5166. {
  5167. bWasFull = true;
  5168. }
  5169. if ( m_flEffectBarRegenTime < gpGlobals->curtime || bWasFull )
  5170. {
  5171. m_flEffectBarRegenTime = gpGlobals->curtime + GetEffectBarRechargeTime();
  5172. }
  5173. }
  5174. //-----------------------------------------------------------------------------
  5175. // Purpose:
  5176. //-----------------------------------------------------------------------------
  5177. void CTFWeaponBase::CheckEffectBarRegen( void )
  5178. {
  5179. if ( !m_flEffectBarRegenTime )
  5180. return;
  5181. // If we're full stop the timer. Fixes a bug with "double" throws after respawning or touching a supply cab
  5182. CTFPlayer *pPlayer = GetTFPlayerOwner();
  5183. if ( pPlayer->GetAmmoCount( GetEffectBarAmmo() ) == pPlayer->GetMaxAmmo( GetEffectBarAmmo() ) )
  5184. {
  5185. m_flEffectBarRegenTime = 0;
  5186. return;
  5187. }
  5188. if ( m_flEffectBarRegenTime < gpGlobals->curtime )
  5189. {
  5190. m_flEffectBarRegenTime = 0;
  5191. EffectBarRegenFinished();
  5192. }
  5193. }
  5194. //-----------------------------------------------------------------------------
  5195. // Purpose:
  5196. //-----------------------------------------------------------------------------
  5197. void CTFWeaponBase::EffectBarRegenFinished( void )
  5198. {
  5199. CTFPlayer *pPlayer = GetTFPlayerOwner();
  5200. if ( pPlayer && (pPlayer->GetAmmoCount( GetEffectBarAmmo() ) < pPlayer->GetMaxAmmo( GetEffectBarAmmo() )) )
  5201. {
  5202. #ifdef GAME_DLL
  5203. pPlayer->GiveAmmo( 1, GetEffectBarAmmo(), true );
  5204. #endif
  5205. #ifdef GAME_DLL
  5206. // If we still have more ammo space, recharge
  5207. if ( pPlayer->GetAmmoCount( GetEffectBarAmmo() ) < pPlayer->GetMaxAmmo( GetEffectBarAmmo() ) )
  5208. #else
  5209. // On the client, we assume we'll get 1 more ammo as soon as the server updates us, so only restart if that still won't make us full.
  5210. if ( pPlayer->GetAmmoCount( GetEffectBarAmmo() ) + 1 < pPlayer->GetMaxAmmo( GetEffectBarAmmo() ) )
  5211. #endif
  5212. {
  5213. StartEffectBarRegen();
  5214. }
  5215. }
  5216. }
  5217. //-----------------------------------------------------------------------------
  5218. // Purpose:
  5219. //-----------------------------------------------------------------------------
  5220. Vector CTFWeaponBase::GetParticleColor( int iColor )
  5221. {
  5222. CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  5223. if ( !pOwner )
  5224. return Vector(0,0,0);
  5225. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  5226. if ( !pItem->IsValid() )
  5227. return Vector(0,0,0);
  5228. int iModifiedRGB = pItem->GetModifiedRGBValue( pOwner->GetTeamNumber() == TF_TEAM_BLUE );
  5229. if ( iModifiedRGB > 0 )
  5230. {
  5231. Color clr = Color( ((iModifiedRGB & 0xFF0000) >> 16), ((iModifiedRGB & 0xFF00) >> 8), (iModifiedRGB & 0xFF) );
  5232. float fColorMod = 1.f;
  5233. if ( iColor == 2 )
  5234. {
  5235. fColorMod = 0.5f;
  5236. }
  5237. Vector vResult;
  5238. vResult.x = clamp( fColorMod * clr.r() * (1.f/255), 0.f, 1.0f );
  5239. vResult.y = clamp( fColorMod * clr.g() * (1.f/255), 0.f, 1.0f );
  5240. vResult.z = clamp( fColorMod * clr.b() * (1.f/255), 0.f, 1.0f );
  5241. return vResult;
  5242. }
  5243. if ( iColor == 1 )
  5244. {
  5245. if ( pOwner->GetTeamNumber() == TF_TEAM_RED )
  5246. return TF_PARTICLE_WEAPON_RED_1;
  5247. else
  5248. return TF_PARTICLE_WEAPON_BLUE_1;
  5249. }
  5250. else
  5251. {
  5252. if ( pOwner->GetTeamNumber() == TF_TEAM_RED )
  5253. return TF_PARTICLE_WEAPON_RED_2;
  5254. else
  5255. return TF_PARTICLE_WEAPON_BLUE_2;
  5256. }
  5257. }
  5258. //-----------------------------------------------------------------------------
  5259. // Purpose:
  5260. //-----------------------------------------------------------------------------
  5261. bool CTFWeaponBase::CanBeCritBoosted( void )
  5262. {
  5263. int iNoCritBoost = 0;
  5264. CALL_ATTRIB_HOOK_FLOAT( iNoCritBoost, no_crit_boost );
  5265. return iNoCritBoost == 0;
  5266. }
  5267. bool CTFWeaponBase::CanHaveRevengeCrits( void )
  5268. {
  5269. int iSapperCrits = 0;
  5270. CALL_ATTRIB_HOOK_INT( iSapperCrits, sapper_kills_collect_crits );
  5271. if ( iSapperCrits != 0 )
  5272. return true;
  5273. int iExtinguishCrits = 0;
  5274. CALL_ATTRIB_HOOK_INT( iExtinguishCrits, extinguish_revenge );
  5275. if ( iExtinguishCrits != 0 )
  5276. return true;
  5277. int iRevengeCrits = 0;
  5278. CALL_ATTRIB_HOOK_INT( iRevengeCrits, sentry_killed_revenge );
  5279. if ( iRevengeCrits )
  5280. return true;
  5281. return false;
  5282. }
  5283. //-----------------------------------------------------------------------------
  5284. // Purpose: This is an accent sound that plays in addition to the base shoot sound
  5285. //-----------------------------------------------------------------------------
  5286. void CTFWeaponBase::PlayUpgradedShootSound( const char *pszSound )
  5287. {
  5288. if ( TFGameRules()->GameModeUsesUpgrades() )
  5289. {
  5290. CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  5291. if ( pOwner )
  5292. {
  5293. float flDmgMod = 1.f;
  5294. CALL_ATTRIB_HOOK_FLOAT( flDmgMod, mult_dmg );
  5295. if ( flDmgMod > 1.f )
  5296. {
  5297. // This is pretty hacky as it assumes a cap of +100% damage for picking
  5298. // sounds -- anything more and the 1-4 scale below falls apart.
  5299. int nLevel = RemapValClamped( flDmgMod, 1.f, 1.8f, 1.f, 4.f );
  5300. const char *pszSoundname = CFmtStr( "%s%d", pszSound, nLevel );
  5301. CSoundParameters params;
  5302. if ( !GetParametersForSound( pszSoundname, params, NULL ) )
  5303. return;
  5304. CPASAttenuationFilter filter( GetOwner(), params.soundlevel );
  5305. if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
  5306. {
  5307. filter.UsePredictionRules();
  5308. }
  5309. EmitSound( filter, pOwner->entindex(), pszSoundname );
  5310. }
  5311. }
  5312. }
  5313. }
  5314. //-----------------------------------------------------------------------------
  5315. // Purpose: Is this honorbound weapon?
  5316. //-----------------------------------------------------------------------------
  5317. bool CTFWeaponBase::IsHonorBound( void ) const
  5318. {
  5319. int iHonorbound = 0;
  5320. CALL_ATTRIB_HOOK_INT( iHonorbound, honorbound );
  5321. return iHonorbound != 0;
  5322. }
  5323. EWeaponStrangeType_t CTFWeaponBase::GetStrangeType()
  5324. {
  5325. // verify stattrak module and add if necessary
  5326. if ( m_eStrangeType == STRANGE_UNKNOWN )
  5327. {
  5328. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  5329. if ( !pItem )
  5330. return STRANGE_UNKNOWN;
  5331. int iStrangeType = -1;
  5332. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  5333. {
  5334. if ( pItem->FindAttribute( GetKillEaterAttr_Score( i ) ) )
  5335. {
  5336. iStrangeType = i;
  5337. break;
  5338. }
  5339. }
  5340. m_eStrangeType = iStrangeType == -1 ? STRANGE_NOT_STRANGE : STRANGE_IS_STRANGE;
  5341. }
  5342. return m_eStrangeType;
  5343. }
  5344. bool CTFWeaponBase::BHasStatTrakModule()
  5345. {
  5346. if ( m_eStatTrakModuleType == MODULE_UNKNOWN )
  5347. {
  5348. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  5349. if ( !pItem )
  5350. return false;
  5351. EWeaponStrangeType_t eStrangeType = GetStrangeType();
  5352. if ( eStrangeType != STRANGE_IS_STRANGE)
  5353. {
  5354. m_eStatTrakModuleType = MODULE_NONE;
  5355. return false;
  5356. }
  5357. // Does it have a module
  5358. CAttribute_String attrModule;
  5359. static CSchemaAttributeDefHandle pAttr_module( "weapon_uses_stattrak_module" );
  5360. if ( pItem->FindAttribute( pAttr_module, &attrModule ) && attrModule.has_value() )
  5361. {
  5362. m_eStatTrakModuleType = MODULE_FOUND;
  5363. return true;;
  5364. }
  5365. }
  5366. if ( m_eStatTrakModuleType != MODULE_FOUND )
  5367. return false;
  5368. return true;
  5369. }
  5370. #ifdef CLIENT_DLL
  5371. //-----------------------------------------------------------------------------
  5372. void CTFWeaponBase::UpdateAllViewmodelAddons( void )
  5373. {
  5374. C_TFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  5375. // Remove any view model add ons if we're spectating.
  5376. if ( !pPlayer )
  5377. {
  5378. RemoveViewmodelStatTrak();
  5379. return;
  5380. }
  5381. // econ-related addons follow, so bail out if we can't get at the econitemview
  5382. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  5383. if ( !pItem )
  5384. {
  5385. RemoveViewmodelStatTrak();
  5386. return;
  5387. }
  5388. if ( GetStrangeType() > -1 )
  5389. {
  5390. CSteamID HolderSteamID;
  5391. pPlayer->GetSteamID( &HolderSteamID );
  5392. AddStatTrakModel( pItem, m_eStrangeType, HolderSteamID.GetAccountID() );
  5393. }
  5394. else
  5395. {
  5396. RemoveViewmodelStatTrak();
  5397. }
  5398. }
  5399. // StatTrak Module Testing
  5400. void CTFWeaponBase::AddStatTrakModel( CEconItemView *pItem, int nStatTrakType, AccountID_t holderAcctId )
  5401. {
  5402. // Already has module, just early out
  5403. if ( m_viewmodelStatTrakAddon && m_viewmodelStatTrakAddon.Get() && m_viewmodelStatTrakAddon->GetMoveParent() )
  5404. {
  5405. return;
  5406. }
  5407. // Something is missing, remove and return
  5408. if ( !pItem )
  5409. {
  5410. RemoveViewmodelStatTrak();
  5411. RemoveWorldmodelStatTrak();
  5412. return;
  5413. }
  5414. if ( GetStrangeType() != STRANGE_IS_STRANGE )
  5415. {
  5416. RemoveViewmodelStatTrak();
  5417. RemoveWorldmodelStatTrak();
  5418. return;
  5419. }
  5420. if ( !BHasStatTrakModule() )
  5421. {
  5422. RemoveViewmodelStatTrak();
  5423. RemoveWorldmodelStatTrak();
  5424. return;
  5425. }
  5426. // Get Module Data
  5427. CAttribute_String attrModule;
  5428. static CSchemaAttributeDefHandle pAttr_module( "weapon_uses_stattrak_module" );
  5429. if ( !pItem->FindAttribute( pAttr_module, &attrModule ) || !attrModule.has_value() )
  5430. {
  5431. RemoveViewmodelStatTrak();
  5432. RemoveWorldmodelStatTrak();
  5433. return;
  5434. }
  5435. float flScale = 1.0f;
  5436. CALL_ATTRIB_HOOK_FLOAT( flScale, weapon_stattrak_module_scale );
  5437. // Skin
  5438. int nSkin = pItem->GetTeamNumber() - TF_TEAM_RED;
  5439. if ( pItem->GetAccountID() != holderAcctId )
  5440. {
  5441. nSkin += 2; // sad skin
  5442. }
  5443. // View Model / third person
  5444. if ( GetViewmodelAttachment() )
  5445. {
  5446. // Already has a module, early out
  5447. if ( !( m_viewmodelStatTrakAddon && m_viewmodelStatTrakAddon.Get() && m_viewmodelStatTrakAddon->GetMoveParent() ) )
  5448. {
  5449. RemoveViewmodelStatTrak();
  5450. CTFWeaponAttachmentModel *pStatTrakEnt = new class CTFWeaponAttachmentModel;
  5451. if ( pStatTrakEnt )
  5452. {
  5453. pStatTrakEnt->InitializeAsClientEntity( attrModule.value().c_str(), RENDER_GROUP_VIEW_MODEL_OPAQUE );
  5454. pStatTrakEnt->Init( GetViewmodelAttachment(), this, true );
  5455. pStatTrakEnt->m_nSkin = nSkin;
  5456. m_viewmodelStatTrakAddon = pStatTrakEnt;
  5457. if ( cl_flipviewmodels.GetBool() )
  5458. {
  5459. pStatTrakEnt->SetBodygroup( 1, 1 ); // use a special mirror-image stattrak module that appears correct for lefties
  5460. flScale *= -1.0f; // flip scale
  5461. }
  5462. pStatTrakEnt->SetModelScale( flScale );
  5463. //RemoveEffects( EF_NODRAW );
  5464. }
  5465. }
  5466. }
  5467. // World Model
  5468. if ( !(m_worldmodelStatTrakAddon && m_worldmodelStatTrakAddon.Get() && m_worldmodelStatTrakAddon->GetMoveParent() ) )
  5469. {
  5470. RemoveWorldmodelStatTrak();
  5471. CTFWeaponAttachmentModel *pStatTrakEnt = new class CTFWeaponAttachmentModel;
  5472. if ( pStatTrakEnt )
  5473. {
  5474. pStatTrakEnt->InitializeAsClientEntity( attrModule.value().c_str(), RENDER_GROUP_OPAQUE_ENTITY );
  5475. pStatTrakEnt->SetModelScale( flScale );
  5476. pStatTrakEnt->Init( this, this, false );
  5477. pStatTrakEnt->m_nSkin = nSkin;
  5478. m_worldmodelStatTrakAddon = pStatTrakEnt;
  5479. // //if ( !cl_flipviewmodels.GetBool() )
  5480. // //{
  5481. // // pStatTrakEnt->SetBodygroup( 0, 1 ); // use a special mirror-image stattrak module that appears correct for lefties
  5482. // //}
  5483. //RemoveEffects( EF_NODRAW );
  5484. }
  5485. }
  5486. }
  5487. //-----------------------------------------------------------------------------
  5488. void CTFWeaponBase::RemoveViewmodelStatTrak( void )
  5489. {
  5490. if ( m_viewmodelStatTrakAddon.Get() )
  5491. {
  5492. m_viewmodelStatTrakAddon->Remove();
  5493. m_viewmodelStatTrakAddon = NULL;
  5494. }
  5495. }
  5496. //-----------------------------------------------------------------------------
  5497. void CTFWeaponBase::RemoveWorldmodelStatTrak( void )
  5498. {
  5499. if ( m_worldmodelStatTrakAddon )
  5500. {
  5501. m_worldmodelStatTrakAddon->Remove();
  5502. m_worldmodelStatTrakAddon = NULL;
  5503. }
  5504. }
  5505. //-----------------------------------------------------------------------------
  5506. const Vector& CTFWeaponBase::GetViewmodelOffset()
  5507. {
  5508. if ( !m_bInitViewmodelOffset )
  5509. {
  5510. CAttribute_String attr_min_viewmodel_offset;
  5511. CALL_ATTRIB_HOOK_STRING( attr_min_viewmodel_offset, min_viewmodel_offset );
  5512. const char* pszMinViewmodelOffset = attr_min_viewmodel_offset.value().c_str();
  5513. if ( pszMinViewmodelOffset && *pszMinViewmodelOffset )
  5514. {
  5515. UTIL_StringToVector( m_vecViewmodelOffset.Base(), pszMinViewmodelOffset );
  5516. }
  5517. m_bInitViewmodelOffset = true;
  5518. }
  5519. return m_vecViewmodelOffset;
  5520. }
  5521. //-----------------------------------------------------------------------------
  5522. // CTFWeaponAttachmentModel
  5523. //-----------------------------------------------------------------------------
  5524. void CTFWeaponAttachmentModel::Init( CBaseEntity *pParent, CTFWeaponBase *pAssociatedWeapon, bool bIsViewModel )
  5525. {
  5526. SetParent( pParent );
  5527. SetLocalOrigin( vec3_origin );
  5528. UpdatePartitionListEntry();
  5529. CollisionProp()->MarkPartitionHandleDirty();
  5530. //UpdateVisibility();
  5531. SetWeaponAssociatedWith( pAssociatedWeapon );
  5532. AddEffects( EF_BONEMERGE );
  5533. AddEffects( EF_BONEMERGE_FASTCULL );
  5534. AddEffects( EF_NODRAW );
  5535. m_bIsViewModelAttachment = bIsViewModel;
  5536. }
  5537. //-----------------------------------------------------------------------------
  5538. bool CTFWeaponAttachmentModel::ShouldDraw( void )
  5539. {
  5540. // Follow my associated weapon
  5541. if ( !m_hWeaponAssociatedWith.Get() )
  5542. return false;
  5543. // some code is overriding the weapon model (taunt), don't show the attachment model
  5544. if ( m_hWeaponAssociatedWith->IsUsingOverrideModel() )
  5545. return false;
  5546. if ( m_hWeaponAssociatedWith->IsFirstPersonView() && !m_bIsViewModelAttachment )
  5547. {
  5548. return false;
  5549. }
  5550. bool bShouldDraw = m_hWeaponAssociatedWith->ShouldDraw();
  5551. if ( bShouldDraw )
  5552. {
  5553. return !m_bIsViewModelAttachment;
  5554. }
  5555. return false;
  5556. //if ( pWeapon )
  5557. //{
  5558. // // If the weapon isn't active, don't draw
  5559. // if ( pOwner && pOwner->GetActiveWeapon() != pWeapon )
  5560. // {
  5561. // return false;
  5562. // }
  5563. // if ( !IsViewModelWearable() )
  5564. // {
  5565. // // If it's the 3rd person wearable, don't draw it when the weapon is hidden
  5566. // if ( !pWeapon->ShouldDraw() )
  5567. // {
  5568. // return false;
  5569. // }
  5570. // }
  5571. // // If the weapon is being repurposed for a taunt dont draw.
  5572. // // The Brutal Legend taunt changes your weapon's model to be the guitar,
  5573. // // but we dont want things like bot-killer skulls or festive lights
  5574. // // to continue to draw
  5575. // if ( pWeapon->IsBeingRepurposedForTaunt() )
  5576. // {
  5577. // return false;
  5578. // }
  5579. //}
  5580. //
  5581. }
  5582. #endif // CLIENT_DLL