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.

573 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Tripmine
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npcevent.h"
  9. #include "hl1_basecombatweapon_shared.h"
  10. #include "basecombatcharacter.h"
  11. #include "ai_basenpc.h"
  12. #include "player.h"
  13. #include "gamerules.h"
  14. #include "in_buttons.h"
  15. #include "soundent.h"
  16. #include "game.h"
  17. #include "vstdlib/random.h"
  18. #include "engine/IEngineSound.h"
  19. #include "hl1_player.h"
  20. #include "hl1_basegrenade.h"
  21. #include "beam_shared.h"
  22. extern ConVar sk_plr_dmg_tripmine;
  23. //-----------------------------------------------------------------------------
  24. // CWeaponTripMine
  25. //-----------------------------------------------------------------------------
  26. #define TRIPMINE_MODEL "models/w_tripmine.mdl"
  27. class CWeaponTripMine : public CBaseHL1CombatWeapon
  28. {
  29. DECLARE_CLASS( CWeaponTripMine, CBaseHL1CombatWeapon );
  30. public:
  31. CWeaponTripMine( void );
  32. void Spawn( void );
  33. void Precache( void );
  34. void Equip( CBaseCombatCharacter *pOwner );
  35. void PrimaryAttack( void );
  36. void WeaponIdle( void );
  37. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  38. DECLARE_SERVERCLASS();
  39. DECLARE_DATADESC();
  40. private:
  41. int m_iGroundIndex;
  42. int m_iPickedUpIndex;
  43. };
  44. LINK_ENTITY_TO_CLASS( weapon_tripmine, CWeaponTripMine );
  45. PRECACHE_WEAPON_REGISTER( weapon_tripmine );
  46. IMPLEMENT_SERVERCLASS_ST( CWeaponTripMine, DT_WeaponTripMine )
  47. END_SEND_TABLE()
  48. BEGIN_DATADESC( CWeaponTripMine )
  49. END_DATADESC()
  50. //-----------------------------------------------------------------------------
  51. // Purpose: Constructor
  52. //-----------------------------------------------------------------------------
  53. CWeaponTripMine::CWeaponTripMine( void )
  54. {
  55. m_bReloadsSingly = false;
  56. m_bFiresUnderwater = true;
  57. }
  58. void CWeaponTripMine::Spawn( void )
  59. {
  60. BaseClass::Spawn();
  61. m_iWorldModelIndex = m_iGroundIndex;
  62. SetModel( TRIPMINE_MODEL );
  63. SetActivity( ACT_TRIPMINE_GROUND );
  64. ResetSequenceInfo( );
  65. m_flPlaybackRate = 0;
  66. if ( !g_pGameRules->IsDeathmatch() )
  67. {
  68. UTIL_SetSize( this, Vector(-16, -16, 0), Vector(16, 16, 28) );
  69. }
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Purpose:
  73. //-----------------------------------------------------------------------------
  74. void CWeaponTripMine::Precache( void )
  75. {
  76. BaseClass::Precache();
  77. m_iGroundIndex = PrecacheModel( TRIPMINE_MODEL );
  78. m_iPickedUpIndex = PrecacheModel( GetWorldModel() );
  79. UTIL_PrecacheOther( "monster_tripmine" );
  80. }
  81. void CWeaponTripMine::Equip( CBaseCombatCharacter *pOwner )
  82. {
  83. m_iWorldModelIndex = m_iPickedUpIndex;
  84. BaseClass::Equip( pOwner );
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose:
  88. //-----------------------------------------------------------------------------
  89. void CWeaponTripMine::PrimaryAttack( void )
  90. {
  91. CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
  92. if ( !pPlayer )
  93. {
  94. return;
  95. }
  96. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  97. return;
  98. Vector vecAiming = pPlayer->GetAutoaimVector( 0 );
  99. Vector vecSrc = pPlayer->Weapon_ShootPosition( );
  100. trace_t tr;
  101. UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 64, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr );
  102. if ( tr.fraction < 1.0 )
  103. {
  104. CBaseEntity *pEntity = tr.m_pEnt;
  105. if ( pEntity && !( pEntity->GetFlags() & FL_CONVEYOR ) )
  106. {
  107. QAngle angles;
  108. VectorAngles( tr.plane.normal, angles );
  109. CBaseEntity::Create( "monster_tripmine", tr.endpos + tr.plane.normal * 2, angles, pPlayer );
  110. pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
  111. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  112. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  113. {
  114. if ( !pPlayer->SwitchToNextBestWeapon( pPlayer->GetActiveWeapon() ) )
  115. Holster();
  116. }
  117. else
  118. {
  119. SendWeaponAnim( ACT_VM_DRAW );
  120. SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );
  121. }
  122. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  123. SetWeaponIdleTime( gpGlobals->curtime ); // MO curtime correct ?
  124. }
  125. }
  126. else
  127. {
  128. SetWeaponIdleTime( m_flTimeWeaponIdle = gpGlobals->curtime + random->RandomFloat( 10, 15 ) );
  129. }
  130. }
  131. void CWeaponTripMine::WeaponIdle( void )
  132. {
  133. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  134. if ( !pPlayer )
  135. {
  136. return;
  137. }
  138. if ( !HasWeaponIdleTimeElapsed() )
  139. return;
  140. int iAnim;
  141. if ( random->RandomFloat( 0, 1 ) <= 0.75 )
  142. {
  143. iAnim = ACT_VM_IDLE;
  144. }
  145. else
  146. {
  147. iAnim = ACT_VM_FIDGET;
  148. }
  149. SendWeaponAnim( iAnim );
  150. }
  151. bool CWeaponTripMine::Holster( CBaseCombatWeapon *pSwitchingTo )
  152. {
  153. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  154. if ( !pPlayer )
  155. {
  156. return false;
  157. }
  158. if ( !BaseClass::Holster( pSwitchingTo ) )
  159. {
  160. return false;
  161. }
  162. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  163. {
  164. SetThink( &CWeaponTripMine::DestroyItem );
  165. SetNextThink( gpGlobals->curtime + 0.1 );
  166. }
  167. pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 );
  168. return true;
  169. }
  170. //-----------------------------------------------------------------------------
  171. // CTripmineGrenade
  172. //-----------------------------------------------------------------------------
  173. #define TRIPMINE_BEAM_SPRITE "sprites/laserbeam.vmt"
  174. class CTripmineGrenade : public CHL1BaseGrenade
  175. {
  176. DECLARE_CLASS( CTripmineGrenade, CHL1BaseGrenade );
  177. public:
  178. CTripmineGrenade();
  179. void Spawn( void );
  180. void Precache( void );
  181. int OnTakeDamage_Alive( const CTakeDamageInfo &info );
  182. void WarningThink( void );
  183. void PowerupThink( void );
  184. void BeamBreakThink( void );
  185. void DelayDeathThink( void );
  186. void Event_Killed( const CTakeDamageInfo &info );
  187. DECLARE_DATADESC();
  188. private:
  189. void MakeBeam( void );
  190. void KillBeam( void );
  191. private:
  192. float m_flPowerUp;
  193. Vector m_vecDir;
  194. Vector m_vecEnd;
  195. float m_flBeamLength;
  196. CHandle<CBaseEntity> m_hRealOwner;
  197. CHandle<CBeam> m_hBeam;
  198. CHandle<CBaseEntity> m_hStuckOn;
  199. Vector m_posStuckOn;
  200. QAngle m_angStuckOn;
  201. int m_iLaserModel;
  202. };
  203. LINK_ENTITY_TO_CLASS( monster_tripmine, CTripmineGrenade );
  204. BEGIN_DATADESC( CTripmineGrenade )
  205. DEFINE_FIELD( m_flPowerUp, FIELD_TIME ),
  206. DEFINE_FIELD( m_vecDir, FIELD_VECTOR ),
  207. DEFINE_FIELD( m_vecEnd, FIELD_POSITION_VECTOR ),
  208. DEFINE_FIELD( m_flBeamLength, FIELD_FLOAT ),
  209. // DEFINE_FIELD( m_hBeam, FIELD_EHANDLE ),
  210. DEFINE_FIELD( m_hRealOwner, FIELD_EHANDLE ),
  211. DEFINE_FIELD( m_hStuckOn, FIELD_EHANDLE ),
  212. DEFINE_FIELD( m_posStuckOn, FIELD_POSITION_VECTOR ),
  213. DEFINE_FIELD( m_angStuckOn, FIELD_VECTOR ),
  214. //DEFINE_FIELD( m_iLaserModel, FIELD_INTEGER ),
  215. // Function Pointers
  216. DEFINE_THINKFUNC( WarningThink ),
  217. DEFINE_THINKFUNC( PowerupThink ),
  218. DEFINE_THINKFUNC( BeamBreakThink ),
  219. DEFINE_THINKFUNC( DelayDeathThink ),
  220. END_DATADESC()
  221. CTripmineGrenade::CTripmineGrenade()
  222. {
  223. m_vecDir.Init();
  224. m_vecEnd.Init();
  225. }
  226. void CTripmineGrenade::Spawn( void )
  227. {
  228. Precache( );
  229. // motor
  230. SetMoveType( MOVETYPE_FLY );
  231. SetSolid( SOLID_BBOX );
  232. AddSolidFlags( FSOLID_NOT_SOLID );
  233. SetModel( TRIPMINE_MODEL );
  234. // Don't collide with the player (the beam will still be tripped by one, however)
  235. SetCollisionGroup( COLLISION_GROUP_WEAPON );
  236. SetCycle( 0 );
  237. SetSequence( SelectWeightedSequence( ACT_TRIPMINE_WORLD ) );
  238. ResetSequenceInfo();
  239. m_flPlaybackRate = 0;
  240. UTIL_SetSize( this, Vector( -8, -8, -8), Vector(8, 8, 8) );
  241. m_flDamage = sk_plr_dmg_tripmine.GetFloat();
  242. m_DmgRadius = m_flDamage * 2.5;
  243. if ( m_spawnflags & 1 )
  244. {
  245. // power up quickly
  246. m_flPowerUp = gpGlobals->curtime + 1.0;
  247. }
  248. else
  249. {
  250. // power up in 2.5 seconds
  251. m_flPowerUp = gpGlobals->curtime + 2.5;
  252. }
  253. SetThink( &CTripmineGrenade::PowerupThink );
  254. SetNextThink( gpGlobals->curtime + 0.2 );
  255. m_takedamage = DAMAGE_YES;
  256. m_iHealth = 1;
  257. if ( GetOwnerEntity() != NULL )
  258. {
  259. // play deploy sound
  260. EmitSound( "TripmineGrenade.Deploy" );
  261. EmitSound( "TripmineGrenade.Charge" );
  262. m_hRealOwner = GetOwnerEntity();
  263. }
  264. AngleVectors( GetAbsAngles(), &m_vecDir );
  265. m_vecEnd = GetAbsOrigin() + m_vecDir * MAX_TRACE_LENGTH;
  266. }
  267. void CTripmineGrenade::Precache( void )
  268. {
  269. PrecacheModel( TRIPMINE_MODEL );
  270. m_iLaserModel = PrecacheModel( TRIPMINE_BEAM_SPRITE );
  271. PrecacheScriptSound( "TripmineGrenade.Deploy" );
  272. PrecacheScriptSound( "TripmineGrenade.Charge" );
  273. PrecacheScriptSound( "TripmineGrenade.Activate" );
  274. }
  275. void CTripmineGrenade::WarningThink( void )
  276. {
  277. // set to power up
  278. SetThink( &CTripmineGrenade::PowerupThink );
  279. SetNextThink( gpGlobals->curtime + 1.0f );
  280. }
  281. void CTripmineGrenade::PowerupThink( void )
  282. {
  283. if ( m_hStuckOn == NULL )
  284. {
  285. trace_t tr;
  286. CBaseEntity *pOldOwner = GetOwnerEntity();
  287. // don't explode if the player is standing in front of the laser
  288. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vecDir * 32, MASK_SHOT, NULL, COLLISION_GROUP_NONE, &tr );
  289. if( tr.m_pEnt && pOldOwner &&
  290. ( tr.m_pEnt == pOldOwner ) && pOldOwner->IsPlayer() )
  291. {
  292. m_flPowerUp += 0.1; //delay the arming
  293. SetNextThink( gpGlobals->curtime + 0.1f );
  294. return;
  295. }
  296. // find out what we've been stuck on
  297. SetOwnerEntity( NULL );
  298. UTIL_TraceLine( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 32, MASK_SHOT, pOldOwner, COLLISION_GROUP_NONE, &tr );
  299. if ( tr.startsolid )
  300. {
  301. SetOwnerEntity( pOldOwner );
  302. m_flPowerUp += 0.1;
  303. SetNextThink( gpGlobals->curtime + 0.1f );
  304. return;
  305. }
  306. if ( tr.fraction < 1.0 )
  307. {
  308. SetOwnerEntity( tr.m_pEnt );
  309. m_hStuckOn = tr.m_pEnt;
  310. m_posStuckOn = m_hStuckOn->GetAbsOrigin();
  311. m_angStuckOn = m_hStuckOn->GetAbsAngles();
  312. }
  313. else
  314. {
  315. // somehow we've been deployed on nothing, or something that was there, but now isn't.
  316. // remove ourselves
  317. StopSound( "TripmineGrenade.Deploy" );
  318. StopSound( "TripmineGrenade.Charge" );
  319. SetThink( &CBaseEntity::SUB_Remove );
  320. SetNextThink( gpGlobals->curtime + 0.1f );
  321. // ALERT( at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", pev->origin.x, pev->origin.y, pev->origin.z );
  322. KillBeam();
  323. return;
  324. }
  325. }
  326. else if ( (m_posStuckOn != m_hStuckOn->GetAbsOrigin()) || (m_angStuckOn != m_hStuckOn->GetAbsAngles()) )
  327. {
  328. // what we were stuck on has moved, or rotated. Create a tripmine weapon and drop to ground
  329. StopSound( "TripmineGrenade.Deploy" );
  330. StopSound( "TripmineGrenade.Charge" );
  331. CBaseEntity *pMine = Create( "weapon_tripmine", GetAbsOrigin() + m_vecDir * 24, GetAbsAngles() );
  332. pMine->AddSpawnFlags( SF_NORESPAWN );
  333. SetThink( &CBaseEntity::SUB_Remove );
  334. SetNextThink( gpGlobals->curtime + 0.1f );
  335. KillBeam();
  336. return;
  337. }
  338. if ( gpGlobals->curtime > m_flPowerUp )
  339. {
  340. MakeBeam( );
  341. RemoveSolidFlags( FSOLID_NOT_SOLID );
  342. m_bIsLive = true;
  343. // play enabled sound
  344. EmitSound( "TripmineGrenade.Activate" );
  345. }
  346. SetNextThink( gpGlobals->curtime + 0.1f );
  347. }
  348. void CTripmineGrenade::KillBeam( void )
  349. {
  350. if ( m_hBeam )
  351. {
  352. UTIL_Remove( m_hBeam );
  353. m_hBeam = NULL;
  354. }
  355. }
  356. void CTripmineGrenade::MakeBeam( void )
  357. {
  358. trace_t tr;
  359. UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  360. m_flBeamLength = tr.fraction;
  361. // set to follow laser spot
  362. SetThink( &CTripmineGrenade::BeamBreakThink );
  363. SetNextThink( gpGlobals->curtime + 1.0f );
  364. Vector vecTmpEnd = GetAbsOrigin() + m_vecDir * MAX_TRACE_LENGTH * m_flBeamLength;
  365. m_hBeam = CBeam::BeamCreate( TRIPMINE_BEAM_SPRITE, 1.0 );
  366. m_hBeam->PointEntInit( vecTmpEnd, this );
  367. m_hBeam->SetColor( 0, 214, 198 );
  368. m_hBeam->SetScrollRate( 25.5 );
  369. m_hBeam->SetBrightness( 64 );
  370. m_hBeam->AddSpawnFlags( SF_BEAM_TEMPORARY ); // so it won't save and come back to haunt us later..
  371. }
  372. void CTripmineGrenade::BeamBreakThink( void )
  373. {
  374. bool bBlowup = false;
  375. trace_t tr;
  376. // NOT MASK_SHOT because we want only simple hit boxes
  377. UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  378. // ALERT( at_console, "%f : %f\n", tr.flFraction, m_flBeamLength );
  379. // respawn detect.
  380. if ( !m_hBeam )
  381. {
  382. MakeBeam();
  383. trace_t stuckOnTrace;
  384. Vector forward;
  385. GetVectors( &forward, NULL, NULL );
  386. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - forward * 12.0f, MASK_SOLID, this, COLLISION_GROUP_NONE, &stuckOnTrace );
  387. if ( stuckOnTrace.m_pEnt )
  388. {
  389. m_hStuckOn = stuckOnTrace.m_pEnt; // reset stuck on ent too
  390. }
  391. }
  392. CBaseEntity *pEntity = tr.m_pEnt;
  393. CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
  394. if ( pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001 )
  395. {
  396. bBlowup = true;
  397. }
  398. else
  399. {
  400. if ( m_hStuckOn == NULL )
  401. bBlowup = true;
  402. else if ( m_posStuckOn != m_hStuckOn->GetAbsOrigin() )
  403. bBlowup = true;
  404. else if ( m_angStuckOn != m_hStuckOn->GetAbsAngles() )
  405. bBlowup = true;
  406. }
  407. if ( bBlowup )
  408. {
  409. SetOwnerEntity( m_hRealOwner );
  410. m_iHealth = 0;
  411. Event_Killed( CTakeDamageInfo( this, m_hRealOwner, 100, GIB_NORMAL ) );
  412. return;
  413. }
  414. SetNextThink( gpGlobals->curtime + 0.1 );
  415. }
  416. /*
  417. int CTripmineGrenade::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  418. {
  419. if (gpGlobals->curtime < m_flPowerUp && info.GetDamage() < m_iHealth)
  420. {
  421. // disable
  422. // Create( "weapon_tripmine", GetLocalOrigin() + m_vecDir * 24, GetAngles() );
  423. SetThink( &CBaseEntity::SUB_Remove );
  424. SetNextThink( gpGlobals->curtime + 0.1f );
  425. KillBeam();
  426. return 0;
  427. }
  428. return BaseClass::OnTakeDamage_Alive( info );
  429. }*/
  430. void CTripmineGrenade::Event_Killed( const CTakeDamageInfo &info )
  431. {
  432. m_takedamage = DAMAGE_NO;
  433. if ( info.GetAttacker() && ( info.GetAttacker()->GetFlags() & FL_CLIENT ) )
  434. {
  435. // some client has destroyed this mine, he'll get credit for any kills
  436. SetOwnerEntity( info.GetAttacker() );
  437. }
  438. SetThink( &CTripmineGrenade::DelayDeathThink );
  439. SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.3 ) );
  440. StopSound( "TripmineGrenade.Charge" );
  441. }
  442. void CTripmineGrenade::DelayDeathThink( void )
  443. {
  444. KillBeam();
  445. trace_t tr;
  446. UTIL_TraceLine ( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, & tr);
  447. Explode( &tr, DMG_BLAST );
  448. }