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.

579 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weapon_wrench.h"
  8. #include "decals.h"
  9. #include "baseobject_shared.h"
  10. #include "tf_viewmodel.h"
  11. // Client specific.
  12. #ifdef CLIENT_DLL
  13. #include "c_tf_player.h"
  14. #include "in_buttons.h"
  15. #include "tf_hud_menu_eureka_teleport.h"
  16. // NVNT haptics system interface
  17. #include "haptics/ihaptics.h"
  18. // Server specific.
  19. #else
  20. #include "tf_player.h"
  21. #include "variant_t.h"
  22. #include "tf_gamerules.h"
  23. #include "particle_parse.h"
  24. #include "tf_fx.h"
  25. #include "tf_obj_sentrygun.h"
  26. #endif
  27. // Maximum time between robo arm hits to maintain the three-hit-combo
  28. #define ROBOARM_COMBO_TIMEOUT 1.0f
  29. //=============================================================================
  30. //
  31. // Weapon Wrench tables.
  32. //
  33. IMPLEMENT_NETWORKCLASS_ALIASED( TFWrench, DT_TFWeaponWrench )
  34. BEGIN_NETWORK_TABLE( CTFWrench, DT_TFWeaponWrench )
  35. END_NETWORK_TABLE()
  36. BEGIN_PREDICTION_DATA( CTFWrench )
  37. END_PREDICTION_DATA()
  38. LINK_ENTITY_TO_CLASS( tf_weapon_wrench, CTFWrench );
  39. PRECACHE_WEAPON_REGISTER( tf_weapon_wrench );
  40. //=============================================================================
  41. //
  42. // Robot Arm tables.
  43. //
  44. IMPLEMENT_NETWORKCLASS_ALIASED( TFRobotArm, DT_TFWeaponRobotArm )
  45. BEGIN_NETWORK_TABLE( CTFRobotArm, DT_TFWeaponRobotArm )
  46. #ifdef GAME_DLL
  47. SendPropEHandle(SENDINFO(m_hRobotArm)),
  48. #else
  49. RecvPropEHandle(RECVINFO(m_hRobotArm)),
  50. #endif
  51. END_NETWORK_TABLE()
  52. #ifdef CLIENT_DLL
  53. BEGIN_PREDICTION_DATA( CTFRobotArm )
  54. // DEFINE_PRED_FIELD( name, fieldtype, flags )
  55. DEFINE_PRED_FIELD( m_iComboCount, FIELD_INTEGER, 0 ),
  56. DEFINE_PRED_FIELD( m_flLastComboHit, FIELD_FLOAT, 0 ),
  57. END_PREDICTION_DATA()
  58. #endif
  59. LINK_ENTITY_TO_CLASS( tf_weapon_robot_arm, CTFRobotArm );
  60. PRECACHE_WEAPON_REGISTER( tf_weapon_robot_arm );
  61. IMPLEMENT_NETWORKCLASS_ALIASED( TFWearableRobotArm, DT_TFWearableRobotArm )
  62. BEGIN_NETWORK_TABLE( CTFWearableRobotArm, DT_TFWearableRobotArm )
  63. END_NETWORK_TABLE()
  64. LINK_ENTITY_TO_CLASS( tf_wearable_robot_arm, CTFWearableRobotArm );
  65. //=============================================================================
  66. //
  67. // Weapon Wrench functions.
  68. //
  69. //-----------------------------------------------------------------------------
  70. // Purpose:
  71. //-----------------------------------------------------------------------------
  72. CTFWrench::CTFWrench()
  73. : m_bReloadDown( false )
  74. {}
  75. //-----------------------------------------------------------------------------
  76. // Purpose:
  77. //-----------------------------------------------------------------------------
  78. void CTFWrench::Spawn()
  79. {
  80. BaseClass::Spawn();
  81. }
  82. #ifdef GAME_DLL
  83. void CTFWrench::OnFriendlyBuildingHit( CBaseObject *pObject, CTFPlayer *pPlayer, Vector hitLoc )
  84. {
  85. bool bHelpTeammateBuildStructure = pObject->IsBuilding() && pObject->GetOwner() != GetOwner();
  86. // Did this object hit do any work? repair or upgrade?
  87. bool bUsefulHit = pObject->InputWrenchHit( pPlayer, this, hitLoc );
  88. // award achievement if we helped a teammate build a structure
  89. if ( bUsefulHit && bHelpTeammateBuildStructure )
  90. {
  91. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  92. if ( pOwner && pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) )
  93. {
  94. pOwner->AwardAchievement( ACHIEVEMENT_TF_ENGINEER_HELP_BUILD_STRUCTURE );
  95. }
  96. }
  97. CDisablePredictionFiltering disabler;
  98. if ( pObject->IsDisposableBuilding() )
  99. {
  100. CSingleUserRecipientFilter singleFilter( pPlayer );
  101. EmitSound( singleFilter, pObject->entindex(), "Player.UseDeny" );
  102. }
  103. else
  104. {
  105. if ( bUsefulHit )
  106. {
  107. // play success sound
  108. WeaponSound( SPECIAL1 );
  109. }
  110. else
  111. {
  112. // play failure sound
  113. WeaponSound( SPECIAL2 );
  114. }
  115. }
  116. }
  117. #endif
  118. void CTFWrench::Smack( void )
  119. {
  120. // see if we can hit an object with a higher range
  121. // Get the current player.
  122. CTFPlayer *pPlayer = GetTFPlayerOwner();
  123. if ( !pPlayer )
  124. return;
  125. if ( !CanAttack() )
  126. return;
  127. // Setup a volume for the melee weapon to be swung - approx size, so all melee behave the same.
  128. static Vector vecSwingMins( -18, -18, -18 );
  129. static Vector vecSwingMaxs( 18, 18, 18 );
  130. // Setup the swing range.
  131. Vector vecForward;
  132. AngleVectors( pPlayer->EyeAngles(), &vecForward );
  133. Vector vecSwingStart = pPlayer->Weapon_ShootPosition();
  134. Vector vecSwingEnd = vecSwingStart + vecForward * 70;
  135. // only trace against objects
  136. // See if we hit anything.
  137. trace_t trace;
  138. CTraceFilterIgnorePlayers traceFilter( NULL, COLLISION_GROUP_NONE );
  139. UTIL_TraceLine( vecSwingStart, vecSwingEnd, MASK_SOLID, &traceFilter, &trace );
  140. if ( trace.fraction >= 1.0 )
  141. {
  142. UTIL_TraceHull( vecSwingStart, vecSwingEnd, vecSwingMins, vecSwingMaxs, MASK_SOLID, &traceFilter, &trace );
  143. }
  144. // We hit, setup the smack.
  145. if ( trace.fraction < 1.0f &&
  146. trace.m_pEnt &&
  147. trace.m_pEnt->IsBaseObject() &&
  148. trace.m_pEnt->GetTeamNumber() == pPlayer->GetTeamNumber() )
  149. {
  150. #ifdef GAME_DLL
  151. OnFriendlyBuildingHit( dynamic_cast< CBaseObject * >( trace.m_pEnt ), pPlayer, trace.endpos );
  152. #else
  153. // NVNT if the local player is the owner of this wrench
  154. // Notify the haptics system we just repaired something.
  155. if(pPlayer==C_TFPlayer::GetLocalTFPlayer() && haptics)
  156. haptics->ProcessHapticEvent(2,"Weapons","tf_weapon_wrench_fix");
  157. #endif
  158. }
  159. else
  160. {
  161. // if we cannot, Smack as usual for player hits
  162. BaseClass::Smack();
  163. }
  164. }
  165. #ifdef CLIENT_DLL
  166. //-----------------------------------------------------------------------------
  167. // Purpose:
  168. //-----------------------------------------------------------------------------
  169. void CTFWrench::ItemPostFrame()
  170. {
  171. BaseClass::ItemPostFrame();
  172. if ( !CanAttack() )
  173. {
  174. return;
  175. }
  176. CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  177. if ( !pOwner )
  178. {
  179. return;
  180. }
  181. // Just pressed reload?
  182. if ( pOwner->m_nButtons & IN_RELOAD && !m_bReloadDown )
  183. {
  184. m_bReloadDown = true;
  185. int iAltFireTeleportToSpawn = 0;
  186. CALL_ATTRIB_HOOK_INT( iAltFireTeleportToSpawn, alt_fire_teleport_to_spawn );
  187. if ( iAltFireTeleportToSpawn )
  188. {
  189. // Tell the teleport menu to show
  190. CHudEurekaEffectTeleportMenu *pTeleportMenu = ( CHudEurekaEffectTeleportMenu * )GET_HUDELEMENT( CHudEurekaEffectTeleportMenu );
  191. if ( pTeleportMenu )
  192. {
  193. pTeleportMenu->WantsToTeleport();
  194. }
  195. }
  196. }
  197. else if ( !(pOwner->m_nButtons & IN_RELOAD) && m_bReloadDown )
  198. {
  199. m_bReloadDown = false;
  200. }
  201. }
  202. #endif
  203. //-----------------------------------------------------------------------------
  204. // Purpose: Kill all buildings when wrench is changed.
  205. //-----------------------------------------------------------------------------
  206. #ifdef GAME_DLL
  207. void CTFWrench::Equip( CBaseCombatCharacter *pOwner )
  208. {
  209. // STAGING_ENGY
  210. CTFPlayer *pPlayer = ToTFPlayer( pOwner );
  211. if ( pPlayer )
  212. {
  213. // if switching too gunslinger, blow up other sentry
  214. int iMiniSentry = 0;
  215. CALL_ATTRIB_HOOK_INT( iMiniSentry, wrench_builds_minisentry );
  216. if ( iMiniSentry )
  217. {
  218. // Just detonate Sentries
  219. CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( pPlayer->GetObjectOfType( OBJ_SENTRYGUN ) );
  220. if ( pSentry )
  221. {
  222. pSentry->DetonateObject();
  223. }
  224. }
  225. }
  226. BaseClass::Equip( pOwner );
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Purpose: Kill all buildings when wrench is changed.
  230. //-----------------------------------------------------------------------------
  231. void CTFWrench::Detach( void )
  232. {
  233. // STAGING_ENGY
  234. CTFPlayer *pPlayer = GetTFPlayerOwner();
  235. if ( pPlayer )
  236. {
  237. bool bDetonateObjects = true;
  238. // In MvM mode, leave engineer's buildings after he dies
  239. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  240. {
  241. if ( pPlayer->GetTeamNumber() != TF_TEAM_PVE_DEFENDERS )
  242. {
  243. bDetonateObjects = false;
  244. }
  245. }
  246. // Only detonate if we are unequipping gunslinger
  247. if ( bDetonateObjects )
  248. {
  249. // if switching off of gunslinger detonate
  250. int iMiniSentry = 0;
  251. CALL_ATTRIB_HOOK_INT( iMiniSentry, wrench_builds_minisentry );
  252. if ( iMiniSentry )
  253. {
  254. // Just detonate Sentries
  255. CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( pPlayer->GetObjectOfType( OBJ_SENTRYGUN ) );
  256. if ( pSentry )
  257. {
  258. pSentry->DetonateObject();
  259. }
  260. }
  261. }
  262. }
  263. BaseClass::Detach();
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Purpose: Apply health upgrade to our existing buildings
  267. //-----------------------------------------------------------------------------
  268. void CTFWrench::ApplyBuildingHealthUpgrade( void )
  269. {
  270. CTFPlayer *pPlayer = GetTFPlayerOwner();
  271. if ( !pPlayer )
  272. return;
  273. for ( int i = pPlayer->GetObjectCount()-1; i >= 0; i-- )
  274. {
  275. CBaseObject *pObj = pPlayer->GetObject(i);
  276. if ( pObj )
  277. {
  278. pObj->ApplyHealthUpgrade();
  279. }
  280. }
  281. }
  282. #endif
  283. // STAGING_ENGY
  284. ConVar tf_construction_build_rate_multiplier( "tf_construction_build_rate_multiplier", "1.5f", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  285. float CTFWrench::GetConstructionValue( void )
  286. {
  287. float flValue = tf_construction_build_rate_multiplier.GetFloat();
  288. CALL_ATTRIB_HOOK_FLOAT( flValue, mult_construction_value );
  289. return flValue;
  290. }
  291. float CTFWrench::GetRepairValue( void )
  292. {
  293. float flValue = 1.0;
  294. CALL_ATTRIB_HOOK_FLOAT( flValue, mult_repair_value );
  295. #ifdef GAME_DLL
  296. if ( GetOwner() )
  297. {
  298. CBaseCombatWeapon* pWpn = GetOwner()->Weapon_GetSlot( TF_WPN_TYPE_PRIMARY );
  299. if ( pWpn )
  300. {
  301. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWpn, flValue, mult_repair_value );
  302. }
  303. }
  304. #endif
  305. return flValue;
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. //-----------------------------------------------------------------------------
  310. bool CTFWrench::Holster( CBaseCombatWeapon *pSwitchingTo )
  311. {
  312. return BaseClass::Holster( pSwitchingTo );
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. //-----------------------------------------------------------------------------
  317. CTFRobotArm::CTFRobotArm()
  318. {
  319. m_iComboCount = 0;
  320. m_flLastComboHit = 0.f;
  321. m_bBigIdle = false;
  322. m_bBigHit = false;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose:
  326. //-----------------------------------------------------------------------------
  327. void CTFRobotArm::Precache()
  328. {
  329. BaseClass::Precache();
  330. extern const char *g_HACK_GunslingerEngineerArmsOverride;
  331. PrecacheModel( g_HACK_GunslingerEngineerArmsOverride );
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Purpose:
  335. //-----------------------------------------------------------------------------
  336. #ifdef GAME_DLL
  337. void CTFRobotArm::Equip( CBaseCombatCharacter* pOwner )
  338. {
  339. BaseClass::Equip( pOwner );
  340. if ( !IsPDQ() )
  341. return;
  342. CTFWearable* pArmItem = dynamic_cast<CTFWearable*>( CreateEntityByName( "tf_wearable_robot_arm" ) );
  343. if ( pArmItem )
  344. {
  345. pArmItem->AddSpawnFlags( SF_NORESPAWN );
  346. pArmItem->SetAlwaysAllow( true );
  347. DispatchSpawn( pArmItem );
  348. pArmItem->GiveTo( pOwner );
  349. pArmItem->AddHiddenBodyGroup( "rightarm" );
  350. pArmItem->SetOwnerEntity( pOwner );
  351. m_hRobotArm.Set( pArmItem );
  352. }
  353. }
  354. //-----------------------------------------------------------------------------
  355. // Purpose:
  356. //-----------------------------------------------------------------------------
  357. void CTFRobotArm::Drop( const Vector &vecVelocity )
  358. {
  359. RemoveRobotArm();
  360. BaseClass::Drop( vecVelocity );
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose:
  364. //-----------------------------------------------------------------------------
  365. void CTFRobotArm::UpdateOnRemove( void )
  366. {
  367. RemoveRobotArm();
  368. BaseClass::UpdateOnRemove();
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Purpose:
  372. //-----------------------------------------------------------------------------
  373. void CTFRobotArm::RemoveRobotArm( void )
  374. {
  375. if ( m_hRobotArm )
  376. {
  377. m_hRobotArm->RemoveFrom( GetOwnerEntity() );
  378. m_hRobotArm = NULL;
  379. }
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose:
  383. //-----------------------------------------------------------------------------
  384. void CTFRobotArm::OnActiveStateChanged( int iOldState )
  385. {
  386. if ( m_iState == WEAPON_NOT_CARRIED )
  387. {
  388. RemoveRobotArm();
  389. }
  390. }
  391. #endif
  392. // -----------------------------------------------------------------------------
  393. // Purpose:
  394. // -----------------------------------------------------------------------------
  395. void CTFRobotArm::PrimaryAttack()
  396. {
  397. CTFPlayer *pPlayer = GetTFPlayerOwner();
  398. if ( !pPlayer )
  399. return;
  400. if ( gpGlobals->curtime - m_flLastComboHit > ROBOARM_COMBO_TIMEOUT )
  401. {
  402. m_iComboCount = 0;
  403. }
  404. if ( m_iComboCount == 2 && CanAttack() )
  405. {
  406. pPlayer->m_Shared.SetNextMeleeCrit( MELEE_CRIT );
  407. }
  408. BaseClass::PrimaryAttack();
  409. }
  410. //-----------------------------------------------------------------------------
  411. // Purpose:
  412. //-----------------------------------------------------------------------------
  413. void CTFRobotArm::Smack( void )
  414. {
  415. CTFPlayer *pPlayer = GetTFPlayerOwner();
  416. if ( !pPlayer )
  417. return;
  418. trace_t trace;
  419. bool btrace = DoSwingTrace( trace );
  420. if ( btrace && trace.DidHitNonWorldEntity() && trace.m_pEnt && trace.m_pEnt->IsPlayer() &&
  421. trace.m_pEnt->GetTeamNumber() != pPlayer->GetTeamNumber() )
  422. {
  423. m_iComboCount++;
  424. m_flLastComboHit = gpGlobals->curtime;
  425. if ( m_iComboCount == 3 )
  426. {
  427. m_iComboCount = 0;
  428. m_bBigIdle = true;
  429. m_bBigHit = true;
  430. }
  431. }
  432. else
  433. {
  434. m_iComboCount = 0;
  435. }
  436. BaseClass::Smack();
  437. m_bBigHit = false;
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose:
  441. //-----------------------------------------------------------------------------
  442. void CTFRobotArm::DoViewModelAnimation( void )
  443. {
  444. if ( m_iComboCount == 2 )
  445. {
  446. SendWeaponAnim( ACT_ITEM2_VM_SWINGHARD );
  447. }
  448. else
  449. {
  450. SendWeaponAnim( ACT_ITEM2_VM_HITCENTER );
  451. }
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Purpose:
  455. //-----------------------------------------------------------------------------
  456. #ifdef GAME_DLL
  457. int CTFRobotArm::GetDamageCustom()
  458. {
  459. if ( m_bBigHit )
  460. {
  461. return TF_DMG_CUSTOM_COMBO_PUNCH;
  462. }
  463. else
  464. {
  465. return BaseClass::GetDamageCustom();
  466. }
  467. }
  468. //-----------------------------------------------------------------------------
  469. // Purpose:
  470. //-----------------------------------------------------------------------------
  471. float CTFRobotArm::GetForceScale( void )
  472. {
  473. if ( m_bBigHit )
  474. {
  475. return 500.f;
  476. }
  477. else
  478. {
  479. return BaseClass::GetForceScale();
  480. }
  481. }
  482. #endif
  483. //-----------------------------------------------------------------------------
  484. // Purpose:
  485. //-----------------------------------------------------------------------------
  486. void CTFRobotArm::WeaponIdle( void )
  487. {
  488. #ifdef GAME_DLL
  489. if ( m_bBigIdle )
  490. {
  491. m_bBigIdle = false;
  492. SendWeaponAnim( ACT_ITEM2_VM_IDLE_2 );
  493. m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
  494. return;
  495. }
  496. #endif
  497. BaseClass::WeaponIdle();
  498. }