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.

1320 lines
37 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The "weapon" used to build objects
  4. //
  5. //
  6. // $Workfile: $
  7. // $Date: $
  8. // $NoKeywords: $
  9. //=============================================================================//
  10. #include "cbase.h"
  11. #include "tf_player.h"
  12. #include "entitylist.h"
  13. #include "in_buttons.h"
  14. #include "SoundEmitterSystem/isoundemittersystembase.h"
  15. #include "engine/IEngineSound.h"
  16. #include "tf_obj.h"
  17. #include "sendproxy.h"
  18. #include "tf_weapon_builder.h"
  19. #include "vguiscreen.h"
  20. #include "tf_gamerules.h"
  21. #include "tf_obj_teleporter.h"
  22. #include "tf_obj_sapper.h"
  23. extern ISoundEmitterSystemBase *soundemitterbase;
  24. extern ConVar tf2_object_hard_limits;
  25. extern ConVar tf_fastbuild;
  26. EXTERN_SEND_TABLE(DT_BaseCombatWeapon)
  27. BEGIN_NETWORK_TABLE_NOBASE( CTFWeaponBuilder, DT_BuilderLocalData )
  28. SendPropInt( SENDINFO( m_iObjectType ), BUILDER_OBJECT_BITS, SPROP_UNSIGNED ),
  29. SendPropEHandle( SENDINFO( m_hObjectBeingBuilt ) ),
  30. SendPropArray3( SENDINFO_ARRAY3( m_aBuildableObjectTypes ), SendPropBool( SENDINFO_ARRAY( m_aBuildableObjectTypes ) ) ),
  31. END_NETWORK_TABLE()
  32. IMPLEMENT_SERVERCLASS_ST(CTFWeaponBuilder, DT_TFWeaponBuilder)
  33. SendPropInt( SENDINFO( m_iBuildState ), 4, SPROP_UNSIGNED ),
  34. SendPropDataTable( "BuilderLocalData", 0, &REFERENCE_SEND_TABLE( DT_BuilderLocalData ), SendProxy_SendLocalWeaponDataTable ),
  35. SendPropInt( SENDINFO( m_iObjectMode ) , 4, SPROP_UNSIGNED ),
  36. SendPropFloat( SENDINFO( m_flWheatleyTalkingUntil) ),
  37. END_SEND_TABLE()
  38. LINK_ENTITY_TO_CLASS( tf_weapon_builder, CTFWeaponBuilder );
  39. PRECACHE_WEAPON_REGISTER( tf_weapon_builder );
  40. //
  41. IMPLEMENT_SERVERCLASS_ST( CTFWeaponSapper, DT_TFWeaponSapper )
  42. SendPropFloat( SENDINFO( m_flChargeBeginTime ) ),
  43. END_SEND_TABLE()
  44. LINK_ENTITY_TO_CLASS( tf_weapon_sapper, CTFWeaponSapper );
  45. PRECACHE_WEAPON_REGISTER( tf_weapon_sapper );
  46. //-----------------------------------------------------------------------------
  47. // Purpose:
  48. //-----------------------------------------------------------------------------
  49. CTFWeaponBuilder::CTFWeaponBuilder()
  50. {
  51. m_iObjectType.Set( BUILDER_INVALID_OBJECT );
  52. m_iObjectMode = 0;
  53. m_bAttack3Down = false;
  54. //Sapper VO Pack stuff
  55. WheatleyReset( true );
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. //-----------------------------------------------------------------------------
  60. CTFWeaponBuilder::~CTFWeaponBuilder()
  61. {
  62. StopPlacement();
  63. if (m_pkvWavList)
  64. {
  65. m_pkvWavList->deleteThis();
  66. }
  67. }
  68. //-----------------------------------------------------------------------------
  69. // Purpose:
  70. //-----------------------------------------------------------------------------
  71. void CTFWeaponBuilder::SetSubType( int iSubType )
  72. {
  73. m_iObjectType = iSubType;
  74. // m_iViewModelIndex is set by the base Precache(), which didn't know what
  75. // type of object we built, so it didn't get the right viewmodel index.
  76. // Now that our data is filled in, go and get the right index.
  77. const char *pszViewModel = GetViewModel(0);
  78. if ( pszViewModel && pszViewModel[0] )
  79. {
  80. m_iViewModelIndex = CBaseEntity::PrecacheModel( pszViewModel );
  81. }
  82. if ( m_iObjectType == OBJ_ATTACHMENT_SAPPER )
  83. {
  84. if ( IsWheatleySapper() )
  85. {
  86. if (m_pkvWavList)
  87. {
  88. m_pkvWavList->deleteThis();
  89. }
  90. m_pkvWavList = new KeyValues("sappervo");
  91. }
  92. }
  93. BaseClass::SetSubType( iSubType );
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose:
  97. //-----------------------------------------------------------------------------
  98. void CTFWeaponBuilder::Precache( void )
  99. {
  100. BaseClass::Precache();
  101. // Precache all the viewmodels we could possibly be building
  102. for ( int iObj=0; iObj < OBJ_LAST; iObj++ )
  103. {
  104. const CObjectInfo *pInfo = GetObjectInfo( iObj );
  105. if ( pInfo )
  106. {
  107. if ( pInfo->m_pViewModel )
  108. {
  109. PrecacheModel( pInfo->m_pViewModel );
  110. }
  111. if ( pInfo->m_pPlayerModel )
  112. {
  113. PrecacheModel( pInfo->m_pPlayerModel );
  114. }
  115. }
  116. }
  117. }
  118. //-----------------------------------------------------------------------------
  119. // Purpose:
  120. //-----------------------------------------------------------------------------
  121. bool CTFWeaponBuilder::CanDeploy( void )
  122. {
  123. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  124. if (!pPlayer)
  125. return false;
  126. if ( pPlayer->m_Shared.IsCarryingObject() )
  127. return BaseClass::CanDeploy();
  128. if ( pPlayer->CanBuild( m_iObjectType, m_iObjectMode ) != CB_CAN_BUILD )
  129. {
  130. return false;
  131. }
  132. return BaseClass::CanDeploy();
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose:
  136. //-----------------------------------------------------------------------------
  137. bool CTFWeaponBuilder::Deploy( void )
  138. {
  139. bool bDeploy = BaseClass::Deploy();
  140. if ( bDeploy )
  141. {
  142. SetCurrentState( BS_PLACING );
  143. StartPlacement();
  144. m_flNextPrimaryAttack = gpGlobals->curtime + 0.35f;
  145. m_flNextSecondaryAttack = gpGlobals->curtime; // asap
  146. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  147. if (!pPlayer)
  148. return false;
  149. pPlayer->SetNextAttack( gpGlobals->curtime );
  150. m_iWorldModelIndex = modelinfo->GetModelIndex( GetWorldModel() );
  151. m_flNextDenySound = 0;
  152. // Set off the hint here, because we don't know until now if our building
  153. // is rotate-able or not.
  154. if ( m_hObjectBeingBuilt && !m_hObjectBeingBuilt->MustBeBuiltOnAttachmentPoint() )
  155. {
  156. // set the alt-fire hint so it gets removed when we holster
  157. m_iAltFireHint = HINT_ALTFIRE_ROTATE_BUILDING;
  158. pPlayer->StartHintTimer( m_iAltFireHint );
  159. }
  160. pPlayer->PlayWearableAnimsForPlaybackEvent( WAP_START_BUILDING );
  161. }
  162. return bDeploy;
  163. }
  164. Activity CTFWeaponBuilder::GetDrawActivity( void )
  165. {
  166. // sapper used to call different draw animations , one when invis and one when not.
  167. // now you can go invis *while* deploying, so let's always use the one-handed deploy.
  168. if ( GetType() == OBJ_ATTACHMENT_SAPPER )
  169. {
  170. return ACT_VM_DRAW_DEPLOYED;
  171. }
  172. return BaseClass::GetDrawActivity();
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Purpose: Stop placement when holstering
  176. //-----------------------------------------------------------------------------
  177. bool CTFWeaponBuilder::Holster( CBaseCombatWeapon *pSwitchingTo )
  178. {
  179. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  180. if ( !pOwner )
  181. return false;
  182. if ( pOwner->m_Shared.IsCarryingObject() )
  183. return false;
  184. if ( m_iObjectType == OBJ_ATTACHMENT_SAPPER )
  185. {
  186. if( IsWheatleySapper() )
  187. {
  188. pOwner->ClearSappingTracking();
  189. if ( pOwner->m_Shared.GetState() == TF_STATE_DYING)
  190. {
  191. if ( RandomInt( 0, 4) == 0 )
  192. {
  193. WheatleyEmitSound( "PSap.DeathLong", true );
  194. }
  195. else
  196. {
  197. WheatleyEmitSound( "PSap.Death", true );
  198. }
  199. }
  200. else
  201. {
  202. float flSoundDuration;
  203. if ( gpGlobals->curtime - m_flWheatleyLastDeploy < 1.5 && gpGlobals->curtime - m_flWheatleyLastDeploy > -1.0 )
  204. {
  205. flSoundDuration = WheatleyEmitSound( "PSap.HolsterFast");
  206. }
  207. else
  208. {
  209. flSoundDuration = WheatleyEmitSound( "PSap.Holster");
  210. }
  211. m_flWheatleyLastHolster = gpGlobals->curtime + flSoundDuration;
  212. }
  213. }
  214. }
  215. m_flNextVoicePakIdleStartTime = -1.0f;
  216. if ( m_iBuildState == BS_PLACING || m_iBuildState == BS_PLACING_INVALID )
  217. {
  218. SetCurrentState( BS_IDLE );
  219. }
  220. StopPlacement();
  221. pOwner->PlayWearableAnimsForPlaybackEvent( WAP_STOP_BUILDING );
  222. return BaseClass::Holster(pSwitchingTo);
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose:
  226. //-----------------------------------------------------------------------------
  227. void CTFWeaponBuilder::ItemPostFrame( void )
  228. {
  229. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  230. if ( !pOwner )
  231. return;
  232. // If we're building, and our team has lost, stop placing the object
  233. if ( m_hObjectBeingBuilt.Get() &&
  234. TFGameRules()->State_Get() == GR_STATE_TEAM_WIN &&
  235. pOwner->GetTeamNumber() != TFGameRules()->GetWinningTeam() )
  236. {
  237. StopPlacement();
  238. return;
  239. }
  240. // Check that I still have enough resources to build this item
  241. if ( pOwner->CanBuild( m_iObjectType, m_iObjectMode ) != CB_CAN_BUILD )
  242. {
  243. SwitchOwnersWeaponToLast();
  244. }
  245. if ( ( pOwner->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
  246. {
  247. PrimaryAttack();
  248. }
  249. if ( pOwner->m_nButtons & IN_ATTACK2 )
  250. {
  251. if ( m_flNextSecondaryAttack <= gpGlobals->curtime )
  252. {
  253. SecondaryAttack();
  254. }
  255. }
  256. else
  257. {
  258. m_bInAttack2 = false;
  259. }
  260. // Attrib
  261. int iMarkForDeathOnPickup = 0;
  262. if ( pOwner->m_Shared.IsCarryingObject () )
  263. {
  264. CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, iMarkForDeathOnPickup, mark_for_death_on_building_pickup );
  265. if ( iMarkForDeathOnPickup )
  266. {
  267. pOwner->m_Shared.AddCond( TF_COND_MARKEDFORDEATH_SILENT, 3.f );
  268. }
  269. }
  270. WeaponIdle();
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose: Start placing or building the currently selected object
  274. //-----------------------------------------------------------------------------
  275. void CTFWeaponBuilder::PrimaryAttack( void )
  276. {
  277. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  278. if ( !pOwner )
  279. return;
  280. if ( !CanAttack() )
  281. return;
  282. // Necessary so that we get the latest building position for the test, otherwise
  283. // we are one frame behind.
  284. UpdatePlacementState();
  285. // What state should we move to?
  286. switch( m_iBuildState )
  287. {
  288. case BS_IDLE:
  289. {
  290. // Idle state starts selection
  291. SetCurrentState( BS_SELECTING );
  292. }
  293. break;
  294. case BS_SELECTING:
  295. {
  296. // Do nothing, client handles selection
  297. return;
  298. }
  299. break;
  300. case BS_PLACING:
  301. {
  302. if ( m_hObjectBeingBuilt )
  303. {
  304. int iFlags = m_hObjectBeingBuilt->GetObjectFlags();
  305. // Tricky, because this can re-calc the object position and change whether its a valid
  306. // pos or not. Best not to do this only in debug, but we can be pretty sure that this
  307. // will give the same result as was calculated in UpdatePlacementState() above.
  308. Assert( IsValidPlacement() );
  309. // If we're placing an attachment, like a sapper, play a placement animation on the owner
  310. if ( m_hObjectBeingBuilt->MustBeBuiltOnAttachmentPoint() )
  311. {
  312. pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_GRENADE );
  313. }
  314. CBaseEntity *pBuiltOnObject = m_hObjectBeingBuilt->GetBuiltOnObject();
  315. if ( pBuiltOnObject && m_iObjectType == OBJ_ATTACHMENT_SAPPER )
  316. {
  317. m_vLastKnownSapPos = pBuiltOnObject->GetAbsOrigin();
  318. m_hLastSappedBuilding = pBuiltOnObject;
  319. }
  320. StartBuilding();
  321. if ( m_iObjectType == OBJ_ATTACHMENT_SAPPER )
  322. {
  323. // tell players a sapper was just placed (so bots can react)
  324. CUtlVector< CTFPlayer * > playerVector;
  325. CollectPlayers( &playerVector, TEAM_ANY, COLLECT_ONLY_LIVING_PLAYERS );
  326. for( int i=0; i<playerVector.Count(); ++i )
  327. playerVector[i]->OnSapperPlaced( pBuiltOnObject );
  328. // if we just placed a sapper on a teleporter...try to sap the match, too?
  329. if ( pBuiltOnObject )
  330. {
  331. CObjectTeleporter *pTeleporter = dynamic_cast<CObjectTeleporter*>( pBuiltOnObject );
  332. if ( pTeleporter && pTeleporter->GetMatchingTeleporter() && !pTeleporter->GetMatchingTeleporter()->HasSapper() )
  333. {
  334. // Start placing another
  335. SetCurrentState( BS_PLACING );
  336. StartPlacement();
  337. if ( m_hObjectBeingBuilt.Get() )
  338. {
  339. m_hObjectBeingBuilt->UpdateAttachmentPlacement( pTeleporter->GetMatchingTeleporter() );
  340. StartBuilding();
  341. }
  342. }
  343. }
  344. }
  345. // Should we switch away?
  346. if ( iFlags & OF_ALLOW_REPEAT_PLACEMENT )
  347. {
  348. // Start placing another
  349. SetCurrentState( BS_PLACING );
  350. StartPlacement();
  351. }
  352. else
  353. {
  354. SwitchOwnersWeaponToLast();
  355. }
  356. }
  357. }
  358. break;
  359. case BS_PLACING_INVALID:
  360. {
  361. if ( m_flNextDenySound < gpGlobals->curtime )
  362. {
  363. CSingleUserRecipientFilter filter( pOwner );
  364. EmitSound( filter, entindex(), "Player.DenyWeaponSelection" );
  365. m_flNextDenySound = gpGlobals->curtime + 0.5;
  366. }
  367. }
  368. break;
  369. }
  370. m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f;
  371. }
  372. void CTFWeaponBuilder::SecondaryAttack( void )
  373. {
  374. if ( m_bInAttack2 )
  375. return;
  376. // require a re-press
  377. m_bInAttack2 = true;
  378. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  379. if ( !pOwner )
  380. return;
  381. UpdatePlacementState();
  382. if ( !pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) && pOwner->DoClassSpecialSkill() )
  383. {
  384. // Spies do the special skill first.
  385. }
  386. else if ( m_iBuildState == BS_PLACING || m_iBuildState == BS_PLACING_INVALID )
  387. {
  388. if ( m_hObjectBeingBuilt )
  389. {
  390. pOwner->StopHintTimer( HINT_ALTFIRE_ROTATE_BUILDING );
  391. m_hObjectBeingBuilt->RotateBuildAngles();
  392. }
  393. }
  394. else if ( pOwner->DoClassSpecialSkill() )
  395. {
  396. // Engineers do the special skill last.
  397. }
  398. m_flNextSecondaryAttack = gpGlobals->curtime + 0.2f;
  399. }
  400. //-----------------------------------------------------------------------------
  401. // Purpose: Set the builder to the specified state
  402. //-----------------------------------------------------------------------------
  403. void CTFWeaponBuilder::SetCurrentState( int iState )
  404. {
  405. m_iBuildState = iState;
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose: Set the owner's weapon and last weapon appropriately when we need to
  409. // switch away from the builder weapon.
  410. //-----------------------------------------------------------------------------
  411. void CTFWeaponBuilder::SwitchOwnersWeaponToLast()
  412. {
  413. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  414. if ( !pOwner )
  415. return;
  416. // for engineer, switch to wrench and set last weapon appropriately
  417. if ( pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) )
  418. {
  419. // Switch to wrench if possible. if not, then best weapon
  420. CBaseCombatWeapon *pWpn = pOwner->Weapon_GetSlot( 2 );
  421. // Don't store last weapon when we autoswitch off builder
  422. CBaseCombatWeapon *pLastWpn = pOwner->GetLastWeapon();
  423. if ( pWpn )
  424. {
  425. pOwner->Weapon_Switch( pWpn );
  426. }
  427. else
  428. {
  429. pOwner->SwitchToNextBestWeapon( NULL );
  430. }
  431. if ( pWpn == pLastWpn )
  432. {
  433. // We had the wrench out before we started building. Go ahead and set out last
  434. // weapon to our primary weapon.
  435. pWpn = pOwner->Weapon_GetSlot( 0 );
  436. pOwner->Weapon_SetLast( pWpn );
  437. }
  438. else
  439. {
  440. pOwner->Weapon_SetLast( pLastWpn );
  441. }
  442. }
  443. else
  444. {
  445. // for all other classes, just switch to last weapon used
  446. pOwner->Weapon_Switch( pOwner->GetLastWeapon() );
  447. }
  448. }
  449. //-----------------------------------------------------------------------------
  450. // Purpose: updates the building postion and checks the new postion
  451. //-----------------------------------------------------------------------------
  452. void CTFWeaponBuilder::UpdatePlacementState( void )
  453. {
  454. // This updates the building position
  455. bool bValidPos = IsValidPlacement();
  456. // If we're in placement mode, update the placement model
  457. switch( m_iBuildState )
  458. {
  459. case BS_PLACING:
  460. case BS_PLACING_INVALID:
  461. {
  462. if ( bValidPos )
  463. {
  464. SetCurrentState( BS_PLACING );
  465. }
  466. else
  467. {
  468. SetCurrentState( BS_PLACING_INVALID );
  469. }
  470. }
  471. break;
  472. default:
  473. break;
  474. }
  475. }
  476. //-----------------------------------------------------------------------------
  477. // Purpose: Idle updates the position of the build placement model
  478. //-----------------------------------------------------------------------------
  479. /*#define SAPPER_VOPAK_DEFAULT_WAIT 0.0f*/
  480. void CTFWeaponBuilder::WeaponIdle( void )
  481. {
  482. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  483. if ( !pOwner )
  484. return;
  485. WheatleySapperIdle( pOwner );
  486. if ( HasWeaponIdleTimeElapsed() )
  487. {
  488. SendWeaponAnim( ACT_VM_IDLE );
  489. }
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose: Special Item Idle
  493. //-----------------------------------------------------------------------------
  494. bool CTFWeaponBuilder::IsWheatleySapper( void )
  495. {
  496. float flVoicePak = 0.0;
  497. CALL_ATTRIB_HOOK_FLOAT( flVoicePak, sapper_voice_pak );
  498. return (flVoicePak == 1.0);
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Purpose: Special Item Reset
  502. //-----------------------------------------------------------------------------
  503. void CTFWeaponBuilder::WheatleyReset( bool bResetIntro )
  504. {
  505. if ( IsWheatleySapper() )
  506. {
  507. WheatleyEmitSound( "PSap.null" );
  508. }
  509. if ( bResetIntro )
  510. {
  511. m_bWheatleyIntroPlayed = false;
  512. }
  513. m_flNextVoicePakIdleStartTime = -1.0f;
  514. SetWheatleyState( TF_PSAPSTATE_IDLE );
  515. m_flWheatleyTalkingUntil = 0.00;
  516. m_flWheatleyLastDamage = 0.00;
  517. m_iWheatleyVOSequenceOffset = 0;
  518. m_flWheatleyLastDeploy = 0.00;
  519. m_flWheatleyLastHolster = 0.00;
  520. }
  521. bool CTFWeaponBuilder::IsWheatleyTalking( void )
  522. {
  523. return gpGlobals->curtime <= m_flWheatleyTalkingUntil;
  524. }
  525. float CTFWeaponBuilder::WheatleyEmitSound( const char *snd, bool bEmitToAll /*= false*/, bool bNoRepeats /*= false */ )
  526. {
  527. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  528. CSoundParameters params;
  529. if ( !soundemitterbase->GetParametersForSound( snd, params, GENDER_NONE ) )
  530. {
  531. return 0.00;
  532. }
  533. //Should we check to see if it's already been played?
  534. if ( bNoRepeats && m_pkvWavList )
  535. {
  536. if ( m_pkvWavList->GetInt( params.soundname , 0 ) )
  537. {
  538. return 0;
  539. }
  540. else
  541. {
  542. m_pkvWavList->SetInt( params.soundname , 1);
  543. }
  544. }
  545. //Look for special cases that require us to pick the next lines from a sequential list
  546. if ( Q_strcmp( params.soundname, "vo/items/wheatley_sapper/wheatley_sapper_idle38.mp3") == NULL )
  547. {
  548. SetWheatleyState( TF_PSAPSTATE_SPECIALIDLE_HARMLESS );
  549. m_iWheatleyVOSequenceOffset = 0;
  550. }
  551. else if ( Q_strcmp( params.soundname, "vo/items/wheatley_sapper/wheatley_sapper_idle41.mp3") == NULL )
  552. {
  553. SetWheatleyState( TF_PSAPSTATE_SPECIALIDLE_HACK );
  554. m_iWheatleyVOSequenceOffset = 0;
  555. }
  556. else if ( Q_strcmp( params.soundname, "vo/items/wheatley_sapper/wheatley_sapper_idle35.mp3") == NULL )
  557. {
  558. SetWheatleyState( TF_PSAPSTATE_SPECIALIDLE_KNIFE );
  559. m_iWheatleyVOSequenceOffset = 0;
  560. }
  561. //Play the sound
  562. // When playing a sound to all players, do it at the last known sapper location
  563. // This is not played on the object itself (building or sapper) cause it may not exist in the case of death and follow up audio
  564. // Also having it played on this entity itself prevents multiple VO playing in the case of mass sapping
  565. if ( bEmitToAll )
  566. {
  567. //int entIndex = 0;
  568. CBroadcastNonOwnerRecipientFilter filter( pOwner );
  569. EmitSound( filter, entindex(), snd, &m_vLastKnownSapPos );
  570. }
  571. // GetSoundDuration is not supported on Linux or for MP3s. So lets just put in a good number
  572. //float flSoundDuration = enginesound->GetSoundDuration( params.soundname );
  573. float flSoundDuration = 3.0f;
  574. CSingleUserRecipientFilter filter( pOwner );
  575. EmitSound( filter, entindex(), params );
  576. m_flWheatleyTalkingUntil = gpGlobals->curtime + flSoundDuration;
  577. return flSoundDuration;
  578. }
  579. //-----------------------------------------------------------------------------
  580. // Purpose: Set Wheatley Sapper State
  581. //-----------------------------------------------------------------------------
  582. void CTFWeaponBuilder::SetWheatleyState( int iNewState )
  583. {
  584. m_iSapState = iNewState;
  585. }
  586. int CTFWeaponBuilder::GetWheatleyIdleWait()
  587. {
  588. return RandomInt( WHEATLEY_IDLE_WAIT_SECS_MIN, WHEATLEY_IDLE_WAIT_SECS_MAX );
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Purpose: Set Wheatley Sapper State
  592. //-----------------------------------------------------------------------------
  593. void CTFWeaponBuilder::WheatleyDamage( void )
  594. {
  595. if ( (gpGlobals->curtime - m_flWheatleyLastDamage) > 10.0)
  596. {
  597. if ( RandomInt(0,2) == 0 )
  598. {
  599. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  600. if (pOwner)
  601. {
  602. pOwner->ClearSappingEvent();
  603. }
  604. SetWheatleyState( TF_PSAPSTATE_IDLE );
  605. m_flWheatleyLastDamage = gpGlobals->curtime;
  606. WheatleyEmitSound( "PSap.Damage" );
  607. }
  608. }
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Purpose: Special Item Idle
  612. //-----------------------------------------------------------------------------
  613. void CTFWeaponBuilder::WheatleySapperIdle( CTFPlayer *pOwner )
  614. {
  615. if ( pOwner && m_iObjectType == OBJ_ATTACHMENT_SAPPER && IsWheatleySapper())
  616. {
  617. //Is Wheatley coming out of the player's pocket?
  618. if( m_flNextVoicePakIdleStartTime < 0.0f )
  619. {
  620. pOwner->ClearSappingTracking();
  621. float flSoundDuration;
  622. if ( gpGlobals->curtime - m_flWheatleyLastHolster < 2.0 && gpGlobals->curtime - m_flWheatleyLastHolster >= -1.00 )
  623. {
  624. flSoundDuration = WheatleyEmitSound( "Psap.DeployAgain" );
  625. }
  626. else
  627. {
  628. flSoundDuration = WheatleyEmitSound( (m_bWheatleyIntroPlayed) ? "Psap.Deploy" : "Psap.DeployIntro" );
  629. }
  630. m_flWheatleyLastDeploy = gpGlobals->curtime + flSoundDuration;
  631. if ( (!m_bWheatleyIntroPlayed) && (RandomInt(0,2) == 0) )
  632. {
  633. SetWheatleyState( TF_PSAPSTATE_INTRO );
  634. m_bWheatleyIntroPlayed = true;
  635. m_iWheatleyVOSequenceOffset = 0;
  636. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + flSoundDuration + 3.0;
  637. }
  638. else
  639. {
  640. m_bWheatleyIntroPlayed = true;
  641. SetWheatleyState( TF_PSAPSTATE_IDLE );
  642. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + flSoundDuration + GetWheatleyIdleWait();
  643. }
  644. }
  645. //Is there a sapper event? (sapper placed / sapper finished)
  646. else if( pOwner->GetSappingEvent() != TF_SAPEVENT_NONE)
  647. {
  648. char *pVoicePakString = NULL;
  649. switch ( pOwner->GetSappingEvent() )
  650. {
  651. case TF_SAPEVENT_PLACED:
  652. if (RandomInt(0,1) == 0)
  653. {
  654. if ( RandomInt(0,3) == 0 )
  655. {
  656. pVoicePakString = "PSap.AttachedPW";
  657. SetWheatleyState( TF_PSAPSTATE_WAITINGHACK );
  658. }
  659. else
  660. {
  661. pVoicePakString = "PSap.Attached";
  662. SetWheatleyState( TF_PSAPSTATE_WAITINGHACKPW );
  663. }
  664. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 0.2;
  665. }
  666. else
  667. {
  668. pVoicePakString = "PSap.Hacking";
  669. SetWheatleyState( TF_PSAPSTATE_IDLE );
  670. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  671. }
  672. break;
  673. case TF_SAPEVENT_DONE:
  674. if ( IsWheatleyTalking() )
  675. {
  676. if ( m_hLastSappedBuilding && m_hLastSappedBuilding.Get() )
  677. {
  678. //Building Alive, Sapper died
  679. pVoicePakString = "PSap.Death";
  680. }
  681. else
  682. {
  683. pVoicePakString = "PSap.HackedLoud";
  684. }
  685. if ( RandomInt( 0, 3 ) == 0 )
  686. {
  687. SetWheatleyState( TF_PSAPSTATE_WAITINGFOLLOWUP);
  688. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 1.3;
  689. }
  690. else
  691. {
  692. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  693. }
  694. }
  695. else
  696. {
  697. SetWheatleyState( TF_PSAPSTATE_WAITINGHACKED );
  698. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 0.5;
  699. }
  700. break;
  701. default:
  702. break;
  703. }
  704. pOwner->ClearSappingEvent();
  705. if ( pVoicePakString )
  706. {
  707. float flSoundDuration = WheatleyEmitSound( pVoicePakString, true );
  708. m_flNextVoicePakIdleStartTime += flSoundDuration;
  709. }
  710. }
  711. //Are we in the intro sequence?
  712. else if ( m_iSapState == TF_PSAPSTATE_INTRO && gpGlobals->curtime > m_flNextVoicePakIdleStartTime )
  713. {
  714. if ( !IsWheatleyTalking() )
  715. {
  716. char szVoicePakString[128];
  717. szVoicePakString[0] = '\0';
  718. if ( m_iWheatleyVOSequenceOffset >= 0 && m_iWheatleyVOSequenceOffset <=3 )
  719. {
  720. V_sprintf_safe( szVoicePakString, "PSap.IdleIntro0%i", ++m_iWheatleyVOSequenceOffset);
  721. float flSoundDuration = WheatleyEmitSound( szVoicePakString );
  722. if ( m_iWheatleyVOSequenceOffset == 4 )
  723. {
  724. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait() + flSoundDuration;
  725. }
  726. else
  727. {
  728. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 1.0 + flSoundDuration;
  729. }
  730. }
  731. else
  732. {
  733. SetWheatleyState( TF_PSAPSTATE_IDLE );
  734. m_iWheatleyVOSequenceOffset = 0;
  735. }
  736. }
  737. }
  738. //Does a generic timed event need to be serviced?
  739. else if( gpGlobals->curtime > m_flNextVoicePakIdleStartTime )
  740. {
  741. bool bNoRepeats = false;
  742. bool bEmitAll = false;
  743. char *pVoicePakString = NULL;
  744. //Sapped! vo
  745. if ( m_iSapState == TF_PSAPSTATE_WAITINGHACKED )
  746. {
  747. bEmitAll = true;
  748. SetWheatleyState( TF_PSAPSTATE_IDLE );
  749. if ( IsWheatleyTalking() )
  750. {
  751. pVoicePakString = "PSap.HackedLoud";
  752. }
  753. else
  754. {
  755. pVoicePakString = "PSap.Hacked";
  756. }
  757. if ( RandomInt( 0, 3 ) == 0 )
  758. {
  759. SetWheatleyState( TF_PSAPSTATE_WAITINGFOLLOWUP);
  760. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 1.3;
  761. }
  762. else
  763. {
  764. SetWheatleyState( TF_PSAPSTATE_IDLE );
  765. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  766. }
  767. }
  768. //Waiting to start the password guessing vo
  769. else if ( m_iSapState == TF_PSAPSTATE_WAITINGHACKPW )
  770. {
  771. bEmitAll = true;
  772. SetWheatleyState( TF_PSAPSTATE_IDLE );
  773. pVoicePakString = "PSap.HackingPW";
  774. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  775. }
  776. //Waiting to start regular hacking vo
  777. else if ( m_iSapState == TF_PSAPSTATE_WAITINGHACK )
  778. {
  779. bEmitAll = true;
  780. SetWheatleyState( TF_PSAPSTATE_IDLE );
  781. if ( RandomInt( 0, 2 ) == 0 )
  782. {
  783. pVoicePakString = "PSap.HackingShort";
  784. }
  785. else
  786. {
  787. pVoicePakString = "PSap.Hacking";
  788. }
  789. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  790. }
  791. //Waiting to start successful hack followup vo
  792. else if ( m_iSapState == TF_PSAPSTATE_WAITINGFOLLOWUP )
  793. {
  794. bEmitAll = true;
  795. SetWheatleyState( TF_PSAPSTATE_IDLE );
  796. pVoicePakString = "PSap.HackedFollowup";
  797. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  798. }
  799. //If Wheatley's talking, skip & check again later
  800. else if ( IsWheatleyTalking() )
  801. {
  802. pVoicePakString = NULL;
  803. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 5.0;
  804. }
  805. //Are we in the SPECIAL IDLE SEQUENCE "HACK"?
  806. else if ( m_iSapState == TF_PSAPSTATE_SPECIALIDLE_HACK )
  807. {
  808. switch ( m_iWheatleyVOSequenceOffset )
  809. {
  810. case 0:
  811. pVoicePakString = "PSap.IdleHack02";
  812. m_iWheatleyVOSequenceOffset++;
  813. break;
  814. default:
  815. SetWheatleyState( TF_PSAPSTATE_IDLE );
  816. m_iWheatleyVOSequenceOffset = 0;
  817. break;
  818. }
  819. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  820. }
  821. //Are we in the SPECIAL IDLE SEQUENCE "KNIFE"?
  822. else if ( m_iSapState == TF_PSAPSTATE_SPECIALIDLE_KNIFE )
  823. {
  824. switch ( m_iWheatleyVOSequenceOffset )
  825. {
  826. case 0:
  827. pVoicePakString = "PSap.IdleKnife02";
  828. m_iWheatleyVOSequenceOffset++;
  829. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 0.3;
  830. break;
  831. case 1:
  832. pVoicePakString = "PSap.IdleKnife03";
  833. m_iWheatleyVOSequenceOffset++;
  834. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  835. break;
  836. default:
  837. SetWheatleyState( TF_PSAPSTATE_IDLE );
  838. m_iWheatleyVOSequenceOffset = 0;
  839. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  840. break;
  841. }
  842. }
  843. //Are we in the SPECIAL IDLE SEQUENCE "HARMLESS"?
  844. else if ( m_iSapState == TF_PSAPSTATE_SPECIALIDLE_HARMLESS )
  845. {
  846. switch ( m_iWheatleyVOSequenceOffset )
  847. {
  848. case 0:
  849. pVoicePakString = "PSap.IdleHarmless02";
  850. m_iWheatleyVOSequenceOffset++;
  851. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  852. break;
  853. default:
  854. SetWheatleyState( TF_PSAPSTATE_IDLE );
  855. m_iWheatleyVOSequenceOffset = 0;
  856. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  857. break;
  858. }
  859. }
  860. //Is the player stealthed?
  861. else if ( pOwner->m_Shared.IsStealthed() )
  862. {
  863. if ( RandomInt(0,1) == 0 )
  864. {
  865. pVoicePakString = "PSap.Sneak";
  866. }
  867. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  868. }
  869. else if ( m_iSapState == TF_PSAPSTATE_IDLE )
  870. {
  871. pVoicePakString = "PSap.Idle";
  872. bNoRepeats = true;
  873. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  874. }
  875. else
  876. {
  877. pVoicePakString = NULL;
  878. }
  879. if (!pVoicePakString)
  880. {
  881. m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait();
  882. return;
  883. }
  884. float flSoundDuration = WheatleyEmitSound( pVoicePakString, bEmitAll, bNoRepeats );
  885. m_flNextVoicePakIdleStartTime += flSoundDuration;
  886. }
  887. }
  888. }
  889. //-----------------------------------------------------------------------------
  890. // Purpose: Start placing the object
  891. //-----------------------------------------------------------------------------
  892. void CTFWeaponBuilder::StartPlacement( void )
  893. {
  894. StopPlacement();
  895. CTFPlayer *pTFPlayer = ToTFPlayer( GetOwner() );
  896. if ( !pTFPlayer )
  897. return;
  898. if ( pTFPlayer->m_Shared.IsCarryingObject() )
  899. {
  900. m_hObjectBeingBuilt = pTFPlayer->m_Shared.GetCarriedObject();
  901. m_hObjectBeingBuilt->StopFollowingEntity();
  902. }
  903. else
  904. {
  905. m_hObjectBeingBuilt = (CBaseObject*)CreateEntityByName( GetObjectInfo( m_iObjectType )->m_pClassName );
  906. }
  907. if ( m_hObjectBeingBuilt )
  908. {
  909. // Set the builder before Spawn() so attributes can hook correctly
  910. m_hObjectBeingBuilt->SetBuilder( pTFPlayer );
  911. bool bIsCarried = m_hObjectBeingBuilt->IsCarried();
  912. // split this off from the block at the bottom because we need to know what type of building
  913. // this is before we spawn so things like the teleporters have the correct placement models
  914. // but we need to set the starting construction health after we've called spawn
  915. if ( !bIsCarried )
  916. {
  917. m_hObjectBeingBuilt->SetObjectMode( m_iObjectMode );
  918. }
  919. m_hObjectBeingBuilt->Spawn();
  920. m_hObjectBeingBuilt->StartPlacement( pTFPlayer );
  921. if ( !bIsCarried )
  922. {
  923. m_hObjectBeingBuilt->m_iHealth = OBJECT_CONSTRUCTION_STARTINGHEALTH;
  924. }
  925. }
  926. }
  927. //-----------------------------------------------------------------------------
  928. // Purpose:
  929. //-----------------------------------------------------------------------------
  930. void CTFWeaponBuilder::StopPlacement( void )
  931. {
  932. if ( m_hObjectBeingBuilt )
  933. {
  934. if ( m_hObjectBeingBuilt->IsCarried() )
  935. {
  936. m_hObjectBeingBuilt->MakeCarriedObject( ToTFPlayer( GetOwner() ) );
  937. }
  938. else
  939. {
  940. m_hObjectBeingBuilt->StopPlacement();
  941. }
  942. m_hObjectBeingBuilt = NULL;
  943. }
  944. }
  945. //-----------------------------------------------------------------------------
  946. // Purpose:
  947. //-----------------------------------------------------------------------------
  948. void CTFWeaponBuilder::WeaponReset( void )
  949. {
  950. //Check to see if the active weapon is the wheatley sapper, and, if so, reset him
  951. if ( m_iObjectType == OBJ_ATTACHMENT_SAPPER )
  952. {
  953. if ( IsWheatleySapper() )
  954. {
  955. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
  956. if ( pPlayer )
  957. {
  958. pPlayer->ClearSappingTracking();
  959. }
  960. WheatleyReset();
  961. }
  962. }
  963. BaseClass::WeaponReset();
  964. StopPlacement();
  965. }
  966. //-----------------------------------------------------------------------------
  967. // Purpose: Move the placement model to the current position. Return false if it's an invalid position
  968. //-----------------------------------------------------------------------------
  969. bool CTFWeaponBuilder::IsValidPlacement( void )
  970. {
  971. if ( !m_hObjectBeingBuilt )
  972. return false;
  973. CBaseObject *pObj = m_hObjectBeingBuilt.Get();
  974. pObj->UpdatePlacement();
  975. return m_hObjectBeingBuilt->IsValidPlacement();
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Purpose: Player holding this weapon has started building something
  979. // Assumes we are in a valid build position
  980. //-----------------------------------------------------------------------------
  981. void CTFWeaponBuilder::StartBuilding( void )
  982. {
  983. CBaseObject *pObj = m_hObjectBeingBuilt.Get();
  984. Assert( pObj );
  985. pObj->StartBuilding( GetOwner() );
  986. m_hObjectBeingBuilt = NULL;
  987. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  988. if ( pOwner )
  989. {
  990. pOwner->RemoveInvisibility();
  991. pOwner->m_Shared.SetCarriedObject( NULL );
  992. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  993. {
  994. if ( pObj->ObjectType() == OBJ_ATTACHMENT_SAPPER )
  995. {
  996. // Let human players place player-targeted sappers in modes that allow upgrades
  997. if ( !pOwner->IsBot() && pObj->GetBuiltOnObject() && pObj->GetBuiltOnObject()->IsPlayer() )
  998. {
  999. int iRoboSapper = 0;
  1000. CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, iRoboSapper, robo_sapper );
  1001. int nMode = iRoboSapper ? MODE_SAPPER_ANTI_ROBOT_RADIUS : MODE_SAPPER_ANTI_ROBOT;
  1002. pObj->SetObjectMode( nMode );
  1003. pOwner->RemoveAmmo( 1, TF_AMMO_GRENADES2 );
  1004. StartEffectBarRegen();
  1005. }
  1006. }
  1007. #ifdef STAGING_ONLY
  1008. // Traps use TF_AMMO_GRENADES1
  1009. else if ( pObj->GetType() == OBJ_SPY_TRAP )
  1010. {
  1011. pOwner->RemoveAmmo( 1, TF_AMMO_GRENADES1 );
  1012. }
  1013. #endif // STAGING_ONLY
  1014. }
  1015. }
  1016. }
  1017. //-----------------------------------------------------------------------------
  1018. // Purpose: Return true if this weapon has some ammo
  1019. //-----------------------------------------------------------------------------
  1020. bool CTFWeaponBuilder::HasAmmo( void )
  1021. {
  1022. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  1023. if ( !pOwner )
  1024. return false;
  1025. int iCost = pOwner->m_Shared.CalculateObjectCost( pOwner, m_iObjectType );
  1026. return ( pOwner->GetBuildResources() >= iCost );
  1027. }
  1028. //-----------------------------------------------------------------------------
  1029. // Purpose:
  1030. //-----------------------------------------------------------------------------
  1031. int CTFWeaponBuilder::GetSlot( void ) const
  1032. {
  1033. return GetObjectInfo( m_iObjectType )->m_SelectionSlot;
  1034. }
  1035. //-----------------------------------------------------------------------------
  1036. // Purpose:
  1037. //-----------------------------------------------------------------------------
  1038. int CTFWeaponBuilder::GetPosition( void ) const
  1039. {
  1040. return GetObjectInfo( m_iObjectType )->m_SelectionPosition;
  1041. }
  1042. //-----------------------------------------------------------------------------
  1043. // Purpose:
  1044. // Output : char const
  1045. //-----------------------------------------------------------------------------
  1046. const char *CTFWeaponBuilder::GetPrintName( void ) const
  1047. {
  1048. return GetObjectInfo( m_iObjectType )->m_pStatusName;
  1049. }
  1050. // -----------------------------------------------------------------------------
  1051. // Purpose:
  1052. // -----------------------------------------------------------------------------
  1053. bool CTFWeaponBuilder::CanBuildObjectType( int iObjectType )
  1054. {
  1055. if ( iObjectType < 0 || iObjectType >= OBJ_LAST )
  1056. return false;
  1057. return m_aBuildableObjectTypes.Get( iObjectType );
  1058. }
  1059. // -----------------------------------------------------------------------------
  1060. // Purpose:
  1061. // -----------------------------------------------------------------------------
  1062. void CTFWeaponBuilder::SetObjectTypeAsBuildable( int iObjectType )
  1063. {
  1064. if ( iObjectType < 0 || iObjectType >= OBJ_LAST )
  1065. return;
  1066. m_aBuildableObjectTypes.Set( iObjectType, true );
  1067. SetSubType( iObjectType );
  1068. }
  1069. // -----------------------------------------------------------------------------
  1070. // Purpose:
  1071. // -----------------------------------------------------------------------------
  1072. Activity CTFWeaponBuilder::TranslateViewmodelHandActivity( Activity actBase )
  1073. {
  1074. if ( GetObjectInfo( m_iObjectType )->m_bUseItemInfo )
  1075. {
  1076. return BaseClass::TranslateViewmodelHandActivity( actBase );
  1077. }
  1078. else
  1079. {
  1080. return actBase;
  1081. }
  1082. }
  1083. // -----------------------------------------------------------------------------
  1084. // Purpose:
  1085. // -----------------------------------------------------------------------------
  1086. const char *CTFWeaponBuilder::GetViewModel( int iViewModel ) const
  1087. {
  1088. if ( m_iObjectType != BUILDER_INVALID_OBJECT )
  1089. {
  1090. if ( GetObjectInfo( m_iObjectType )->m_bUseItemInfo )
  1091. return BaseClass::GetViewModel();
  1092. return GetObjectInfo( m_iObjectType )->m_pViewModel;
  1093. }
  1094. return BaseClass::GetViewModel();
  1095. }
  1096. //-----------------------------------------------------------------------------
  1097. // Purpose:
  1098. //-----------------------------------------------------------------------------
  1099. const char *CTFWeaponBuilder::GetWorldModel( void ) const
  1100. {
  1101. if ( m_iObjectType != BUILDER_INVALID_OBJECT )
  1102. {
  1103. return GetObjectInfo( m_iObjectType )->m_pPlayerModel;
  1104. }
  1105. return BaseClass::GetWorldModel();
  1106. }
  1107. //-----------------------------------------------------------------------------
  1108. // Purpose:
  1109. //-----------------------------------------------------------------------------
  1110. bool CTFWeaponBuilder::AllowsAutoSwitchTo( void ) const
  1111. {
  1112. // ask the object we're building
  1113. return GetObjectInfo( m_iObjectType )->m_bAutoSwitchTo;
  1114. }
  1115. // ****************************************************************************
  1116. // SAPPER
  1117. // ****************************************************************************
  1118. CTFWeaponSapper::CTFWeaponSapper()
  1119. {
  1120. m_flChargeBeginTime = 0;
  1121. m_bAttackDown = false;
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. void CTFWeaponSapper::ItemPostFrame( void )
  1125. {
  1126. #ifdef STAGING_ONLY
  1127. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  1128. if ( pPlayer )
  1129. {
  1130. float flSapperDeployTime = 0;
  1131. CALL_ATTRIB_HOOK_FLOAT( flSapperDeployTime, sapper_deploy_time );
  1132. if ( flSapperDeployTime )
  1133. {
  1134. //IsValidPlacement
  1135. if ( !( pPlayer->m_nButtons & IN_ATTACK ) || !IsValidPlacement() )
  1136. {
  1137. m_bAttackDown = false;
  1138. m_flChargeBeginTime = 0;
  1139. }
  1140. else if ( m_bAttackDown == false && ( pPlayer->m_nButtons & IN_ATTACK ) )
  1141. {
  1142. m_bAttackDown = true;
  1143. m_flChargeBeginTime = gpGlobals->curtime;
  1144. }
  1145. if ( ( m_bAttackDown == true && m_flChargeBeginTime + flSapperDeployTime < gpGlobals->curtime ) || !( pPlayer->m_nButtons & IN_ATTACK ) )
  1146. {
  1147. BaseClass::ItemPostFrame();
  1148. }
  1149. return;
  1150. }
  1151. }
  1152. #endif // STAGING_ONLY
  1153. BaseClass::ItemPostFrame();
  1154. }
  1155. //-----------------------------------------------------------------------------
  1156. const char *CTFWeaponSapper::GetViewModel( int iViewModel ) const
  1157. {
  1158. // Skip over Builder's version
  1159. return CTFWeaponBase::GetViewModel();
  1160. }
  1161. //-----------------------------------------------------------------------------
  1162. const char *CTFWeaponSapper::GetWorldModel( void ) const
  1163. {
  1164. // Skip over Builder's version
  1165. return CTFWeaponBase::GetWorldModel();
  1166. }
  1167. //-----------------------------------------------------------------------------
  1168. Activity CTFWeaponSapper::TranslateViewmodelHandActivity( Activity actBase )
  1169. {
  1170. return BaseClass::TranslateViewmodelHandActivity( actBase );
  1171. // Skip over Builder's version
  1172. //return CTFWeaponBase::TranslateViewmodelHandActivity( actBase );
  1173. }