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.

600 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Satchel charge
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npcevent.h"
  9. #include "hl1_basecombatweapon_shared.h"
  10. //#include "basecombatweapon.h"
  11. //#include "basecombatcharacter.h"
  12. #ifdef CLIENT_DLL
  13. #include "c_baseplayer.h"
  14. #else
  15. #include "player.h"
  16. #endif
  17. //#include "AI_BaseNPC.h"
  18. //#include "player.h"
  19. #include "gamerules.h"
  20. #include "in_buttons.h"
  21. #ifndef CLIENT_DLL
  22. #include "soundent.h"
  23. #include "game.h"
  24. #endif
  25. #include "vstdlib/random.h"
  26. #include "engine/IEngineSound.h"
  27. #include "hl1mp_weapon_satchel.h"
  28. //-----------------------------------------------------------------------------
  29. // CWeaponSatchel
  30. //-----------------------------------------------------------------------------
  31. #define SATCHEL_VIEW_MODEL "models/v_satchel.mdl"
  32. #define SATCHEL_WORLD_MODEL "models/w_satchel.mdl"
  33. #define SATCHELRADIO_VIEW_MODEL "models/v_satchel_radio.mdl"
  34. #define SATCHELRADIO_WORLD_MODEL "models/w_satchel.mdl" // this needs fixing if we do multiplayer
  35. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSatchel, DT_WeaponSatchel );
  36. BEGIN_NETWORK_TABLE( CWeaponSatchel, DT_WeaponSatchel )
  37. #ifdef CLIENT_DLL
  38. RecvPropInt( RECVINFO( m_iRadioViewIndex ) ),
  39. RecvPropInt( RECVINFO( m_iRadioWorldIndex ) ),
  40. RecvPropInt( RECVINFO( m_iSatchelViewIndex ) ),
  41. RecvPropInt( RECVINFO( m_iSatchelWorldIndex ) ),
  42. RecvPropInt( RECVINFO( m_iChargeReady ) ),
  43. #else
  44. SendPropInt( SENDINFO( m_iRadioViewIndex ) ),
  45. SendPropInt( SENDINFO( m_iRadioWorldIndex ) ),
  46. SendPropInt( SENDINFO( m_iSatchelViewIndex ) ),
  47. SendPropInt( SENDINFO( m_iSatchelWorldIndex ) ),
  48. SendPropInt( SENDINFO( m_iChargeReady ) ),
  49. #endif
  50. END_NETWORK_TABLE()
  51. LINK_ENTITY_TO_CLASS( weapon_satchel, CWeaponSatchel );
  52. PRECACHE_WEAPON_REGISTER( weapon_satchel );
  53. //IMPLEMENT_SERVERCLASS_ST( CWeaponSatchel, DT_WeaponSatchel )
  54. //END_SEND_TABLE()
  55. BEGIN_PREDICTION_DATA( CWeaponSatchel )
  56. #ifdef CLIENT_DLL
  57. DEFINE_PRED_FIELD( m_iRadioViewIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
  58. DEFINE_PRED_FIELD( m_iRadioWorldIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
  59. DEFINE_PRED_FIELD( m_iSatchelViewIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
  60. DEFINE_PRED_FIELD( m_iSatchelWorldIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
  61. DEFINE_PRED_FIELD( m_iChargeReady, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  62. #endif
  63. END_PREDICTION_DATA()
  64. BEGIN_DATADESC( CWeaponSatchel )
  65. DEFINE_FIELD( m_iChargeReady, FIELD_INTEGER ),
  66. // DEFINE_FIELD( m_iRadioViewIndex, FIELD_INTEGER ),
  67. // DEFINE_FIELD( m_iRadioWorldIndex, FIELD_INTEGER ),
  68. // DEFINE_FIELD( m_iSatchelViewIndex, FIELD_INTEGER ),
  69. // DEFINE_FIELD( m_iSatchelWorldIndex, FIELD_INTEGER ),
  70. END_DATADESC()
  71. //-----------------------------------------------------------------------------
  72. // Purpose: Constructor
  73. //-----------------------------------------------------------------------------
  74. CWeaponSatchel::CWeaponSatchel( void )
  75. {
  76. m_bReloadsSingly = false;
  77. m_bFiresUnderwater = true;
  78. }
  79. void CWeaponSatchel::Equip( CBaseCombatCharacter *pOwner )
  80. {
  81. BaseClass::Equip( pOwner );
  82. m_iChargeReady = 0; // this satchel charge weapon now forgets that any satchels are deployed by it.
  83. }
  84. bool CWeaponSatchel::HasAnyAmmo( void )
  85. {
  86. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  87. if ( !pPlayer )
  88. {
  89. return false;
  90. }
  91. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  92. {
  93. // player is carrying some satchels
  94. return true;
  95. }
  96. if ( HasChargeDeployed() )
  97. {
  98. // player isn't carrying any satchels, but has some out
  99. return true;
  100. }
  101. return BaseClass::HasAnyAmmo();
  102. }
  103. bool CWeaponSatchel::CanDeploy( void )
  104. {
  105. if ( HasAnyAmmo() )
  106. {
  107. return true;
  108. }
  109. else
  110. {
  111. return false;
  112. }
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose:
  116. //-----------------------------------------------------------------------------
  117. void CWeaponSatchel::Precache( void )
  118. {
  119. m_iSatchelViewIndex = PrecacheModel( SATCHEL_VIEW_MODEL );
  120. m_iSatchelWorldIndex = PrecacheModel( SATCHEL_WORLD_MODEL );
  121. m_iRadioViewIndex = PrecacheModel( SATCHELRADIO_VIEW_MODEL );
  122. m_iRadioWorldIndex = PrecacheModel( SATCHELRADIO_WORLD_MODEL );
  123. #ifndef CLIENT_DLL
  124. UTIL_PrecacheOther( "monster_satchel" );
  125. #endif
  126. BaseClass::Precache();
  127. }
  128. void CWeaponSatchel::ItemPostFrame( void )
  129. {
  130. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  131. if (!pOwner)
  132. {
  133. return;
  134. }
  135. if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
  136. {
  137. // If the firing button was just pressed, reset the firing time
  138. if ( pOwner->m_afButtonPressed & IN_ATTACK )
  139. {
  140. m_flNextPrimaryAttack = gpGlobals->curtime;
  141. }
  142. PrimaryAttack();
  143. }
  144. BaseClass::ItemPostFrame();
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Purpose:
  148. //-----------------------------------------------------------------------------
  149. void CWeaponSatchel::PrimaryAttack( void )
  150. {
  151. switch ( m_iChargeReady )
  152. {
  153. case 0:
  154. {
  155. Throw();
  156. }
  157. break;
  158. case 1:
  159. {
  160. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  161. if ( !pPlayer )
  162. {
  163. return;
  164. }
  165. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  166. #ifndef CLIENT_DLL
  167. CBaseEntity *pSatchel = NULL;
  168. while ( (pSatchel = gEntList.FindEntityByClassname( pSatchel, "monster_satchel" ) ) != NULL)
  169. {
  170. if ( pSatchel->GetOwnerEntity() == pPlayer )
  171. {
  172. pSatchel->Use( pPlayer, pPlayer, USE_ON, 0 );
  173. }
  174. }
  175. #endif
  176. m_iChargeReady = 2;
  177. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  178. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  179. SetWeaponIdleTime( gpGlobals->curtime + 0.5 );
  180. break;
  181. }
  182. case 2:
  183. // we're reloading, don't allow fire
  184. break;
  185. }
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose:
  189. //-----------------------------------------------------------------------------
  190. void CWeaponSatchel::SecondaryAttack( void )
  191. {
  192. if ( m_iChargeReady != 2 )
  193. {
  194. Throw();
  195. }
  196. }
  197. void CWeaponSatchel::Throw( void )
  198. {
  199. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  200. if ( !pPlayer )
  201. {
  202. return;
  203. }
  204. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  205. {
  206. Vector vecForward;
  207. pPlayer->EyeVectors( &vecForward );
  208. Vector vecSrc = pPlayer->WorldSpaceCenter();
  209. Vector vecThrow = vecForward * 274 + pPlayer->GetAbsVelocity();
  210. #ifndef CLIENT_DLL
  211. CBaseEntity *pSatchel = Create( "monster_satchel", vecSrc, QAngle( 0, 0, 90 ), pPlayer );
  212. if ( pSatchel )
  213. {
  214. pSatchel->SetAbsVelocity( vecThrow );
  215. QAngle angVel = pSatchel->GetLocalAngularVelocity();
  216. angVel.y = 400;
  217. pSatchel->SetLocalAngularVelocity( angVel );
  218. ActivateRadioModel();
  219. SendWeaponAnim( ACT_VM_DRAW );
  220. // player "shoot" animation
  221. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  222. m_iChargeReady = 1;
  223. pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
  224. }
  225. #endif
  226. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  227. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  228. }
  229. }
  230. void CWeaponSatchel::WeaponIdle( void )
  231. {
  232. if ( !HasWeaponIdleTimeElapsed() )
  233. return;
  234. switch( m_iChargeReady )
  235. {
  236. case 0:
  237. case 1:
  238. SendWeaponAnim( ACT_VM_FIDGET );
  239. break;
  240. case 2:
  241. {
  242. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  243. if ( pPlayer && (pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0) )
  244. {
  245. m_iChargeReady = 0;
  246. if ( !pPlayer->SwitchToNextBestWeapon( pPlayer->GetActiveWeapon() ) )
  247. {
  248. Holster();
  249. }
  250. return;
  251. }
  252. ActivateSatchelModel();
  253. SendWeaponAnim( ACT_VM_DRAW );
  254. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  255. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  256. m_iChargeReady = 0;
  257. break;
  258. }
  259. }
  260. SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );// how long till we do this again.
  261. }
  262. bool CWeaponSatchel::Deploy( void )
  263. {
  264. SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );
  265. if ( HasChargeDeployed() )
  266. {
  267. ActivateRadioModel();
  268. }
  269. else
  270. {
  271. ActivateSatchelModel();
  272. }
  273. bool bRet = BaseClass::Deploy();
  274. if ( bRet )
  275. {
  276. //
  277. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  278. if ( pPlayer )
  279. {
  280. pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 );
  281. }
  282. }
  283. return bRet;
  284. }
  285. bool CWeaponSatchel::Holster( CBaseCombatWeapon *pSwitchingTo )
  286. {
  287. if ( !BaseClass::Holster( pSwitchingTo ) )
  288. {
  289. return false;
  290. }
  291. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  292. if ( pPlayer )
  293. {
  294. pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 );
  295. if ( (pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0) && !HasChargeDeployed() )
  296. {
  297. #ifndef CLIENT_DLL
  298. SetThink( &CWeaponSatchel::DestroyItem );
  299. SetNextThink( gpGlobals->curtime + 0.1 );
  300. #endif
  301. }
  302. }
  303. return true;
  304. }
  305. void CWeaponSatchel::ActivateSatchelModel( void )
  306. {
  307. m_iViewModelIndex = m_iSatchelViewIndex;
  308. m_iWorldModelIndex = m_iSatchelWorldIndex;
  309. SetModel( GetViewModel() );
  310. }
  311. void CWeaponSatchel::ActivateRadioModel( void )
  312. {
  313. m_iViewModelIndex = m_iRadioViewIndex;
  314. m_iWorldModelIndex = m_iRadioWorldIndex;
  315. SetModel( GetViewModel() );
  316. }
  317. const char *CWeaponSatchel::GetViewModel( int ) const
  318. {
  319. if ( m_iViewModelIndex == m_iSatchelViewIndex )
  320. {
  321. return SATCHEL_VIEW_MODEL;
  322. }
  323. else
  324. {
  325. return SATCHELRADIO_VIEW_MODEL;
  326. }
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose:
  330. //-----------------------------------------------------------------------------
  331. const char *CWeaponSatchel::GetWorldModel( void ) const
  332. {
  333. if ( m_iViewModelIndex == m_iSatchelViewIndex )
  334. {
  335. return SATCHEL_WORLD_MODEL;
  336. }
  337. else
  338. {
  339. return SATCHELRADIO_WORLD_MODEL;
  340. }
  341. }
  342. void CWeaponSatchel::OnRestore( void )
  343. {
  344. BaseClass::OnRestore();
  345. if ( HasChargeDeployed() )
  346. {
  347. ActivateRadioModel();
  348. }
  349. else
  350. {
  351. ActivateSatchelModel();
  352. }
  353. }
  354. #ifndef CLIENT_DLL
  355. //-----------------------------------------------------------------------------
  356. // CSatchelCharge
  357. //-----------------------------------------------------------------------------
  358. #define SATCHEL_CHARGE_MODEL "models/w_satchel.mdl"
  359. extern ConVar sk_plr_dmg_satchel;
  360. BEGIN_DATADESC( CSatchelCharge )
  361. DEFINE_FIELD( m_flNextBounceSoundTime, FIELD_TIME ),
  362. DEFINE_FIELD( m_bInAir, FIELD_BOOLEAN ),
  363. DEFINE_FIELD( m_vLastPosition, FIELD_POSITION_VECTOR ),
  364. // Function Pointers
  365. DEFINE_ENTITYFUNC( SatchelTouch ),
  366. DEFINE_THINKFUNC( SatchelThink ),
  367. DEFINE_USEFUNC( SatchelUse ),
  368. END_DATADESC()
  369. LINK_ENTITY_TO_CLASS( monster_satchel, CSatchelCharge );
  370. //=========================================================
  371. // Deactivate - do whatever it is we do to an orphaned
  372. // satchel when we don't want it in the world anymore.
  373. //=========================================================
  374. void CSatchelCharge::Deactivate( void )
  375. {
  376. AddSolidFlags( FSOLID_NOT_SOLID );
  377. UTIL_Remove( this );
  378. }
  379. void CSatchelCharge::Spawn( void )
  380. {
  381. Precache( );
  382. // motor
  383. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_SLIDE );
  384. SetSolid( SOLID_BBOX );
  385. SetModel( SATCHEL_CHARGE_MODEL );
  386. UTIL_SetSize( this, Vector( -4, -4, 0), Vector(4, 4, 8) );
  387. SetTouch( &CSatchelCharge::SatchelTouch );
  388. SetUse( &CSatchelCharge::SatchelUse );
  389. SetThink( &CSatchelCharge::SatchelThink );
  390. SetNextThink( gpGlobals->curtime + 0.1f );
  391. m_flDamage = sk_plr_dmg_satchel.GetFloat();
  392. m_DmgRadius = m_flDamage * 2.5;
  393. m_takedamage = DAMAGE_NO;
  394. m_iHealth = 1;
  395. SetGravity( UTIL_ScaleForGravity( 560 ) ); // slightly lower gravity
  396. SetFriction( 0.97 ); //used in SatchelTouch to slow us when sliding
  397. SetSequence( LookupSequence( "onback" ) );
  398. m_bInAir = false;
  399. m_flNextBounceSoundTime = 0;
  400. m_vLastPosition = vec3_origin;
  401. }
  402. //-----------------------------------------------------------------------------
  403. // Purpose:
  404. // Input :
  405. // Output :
  406. //-----------------------------------------------------------------------------
  407. void CSatchelCharge::SatchelUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  408. {
  409. SetThink( &CBaseGrenade::Detonate );
  410. SetNextThink( gpGlobals->curtime );
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Purpose:
  414. // Input :
  415. // Output :
  416. //-----------------------------------------------------------------------------
  417. void CSatchelCharge::SatchelTouch( CBaseEntity *pOther )
  418. {
  419. Assert( pOther );
  420. if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) || GetWaterLevel() > 0 )
  421. return;
  422. StudioFrameAdvance( );
  423. UpdateSlideSound();
  424. if ( m_bInAir )
  425. {
  426. BounceSound();
  427. m_bInAir = false;
  428. }
  429. // add a bit of static friction
  430. SetAbsVelocity( GetAbsVelocity() * GetFriction() );
  431. SetLocalAngularVelocity( GetLocalAngularVelocity() * GetFriction() );
  432. }
  433. void CSatchelCharge::UpdateSlideSound( void )
  434. {
  435. // HACKHACK - On ground isn't always set, so look for ground underneath
  436. trace_t tr;
  437. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  438. if ( !(tr.fraction < 1.0) )
  439. {
  440. m_bInAir = true;
  441. }
  442. }
  443. void CSatchelCharge::SatchelThink( void )
  444. {
  445. UpdateSlideSound();
  446. StudioFrameAdvance( );
  447. SetNextThink( gpGlobals->curtime + 0.1f );
  448. if (!IsInWorld())
  449. {
  450. UTIL_Remove( this );
  451. return;
  452. }
  453. Vector vecNewVel = GetAbsVelocity();
  454. if ( GetWaterLevel() > 0 )
  455. {
  456. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  457. vecNewVel *= 0.8;
  458. SetLocalAngularVelocity( GetLocalAngularVelocity() * 0.9 );
  459. vecNewVel.z = 0;
  460. SetGravity( -0.2 );
  461. }
  462. else if ( GetWaterLevel() == 0 )
  463. {
  464. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_SLIDE );
  465. SetGravity( 1.0 );
  466. }
  467. SetAbsVelocity( vecNewVel );
  468. }
  469. void CSatchelCharge::Precache( void )
  470. {
  471. PrecacheModel( SATCHEL_CHARGE_MODEL );
  472. PrecacheScriptSound( "SatchelCharge.Bounce" );
  473. }
  474. void CSatchelCharge::BounceSound( void )
  475. {
  476. if ( gpGlobals->curtime > m_flNextBounceSoundTime )
  477. {
  478. EmitSound( "SatchelCharge.Bounce" );
  479. m_flNextBounceSoundTime = gpGlobals->curtime + 0.1;
  480. }
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Purpose: Constructor
  484. // Input :
  485. // Output :
  486. //-----------------------------------------------------------------------------
  487. CSatchelCharge::CSatchelCharge(void)
  488. {
  489. m_vLastPosition.Init();
  490. }
  491. #endif