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.

581 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Gravity well device
  4. //
  5. //=====================================================================================//
  6. #include "cbase.h"
  7. #include "grenade_hopwire.h"
  8. #include "rope.h"
  9. #include "rope_shared.h"
  10. #include "beam_shared.h"
  11. #include "physics.h"
  12. #include "physics_saverestore.h"
  13. #include "explode.h"
  14. #include "physics_prop_ragdoll.h"
  15. #include "movevars_shared.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. ConVar hopwire_vortex( "hopwire_vortex", "0" );
  19. ConVar hopwire_trap( "hopwire_trap", "1" );
  20. ConVar hopwire_strider_kill_dist_h( "hopwire_strider_kill_dist_h", "300" );
  21. ConVar hopwire_strider_kill_dist_v( "hopwire_strider_kill_dist_v", "256" );
  22. ConVar hopwire_strider_hits( "hopwire_strider_hits", "1" );
  23. ConVar hopwire_hopheight( "hopwire_hopheight", "400" );
  24. ConVar g_debug_hopwire( "g_debug_hopwire", "0" );
  25. #define DENSE_BALL_MODEL "models/props_junk/metal_paintcan001b.mdl"
  26. #define MAX_HOP_HEIGHT (hopwire_hopheight.GetFloat()) // Maximum amount the grenade will "hop" upwards when detonated
  27. class CGravityVortexController : public CBaseEntity
  28. {
  29. DECLARE_CLASS( CGravityVortexController, CBaseEntity );
  30. DECLARE_DATADESC();
  31. public:
  32. CGravityVortexController( void ) : m_flEndTime( 0.0f ), m_flRadius( 256 ), m_flStrength( 256 ), m_flMass( 0.0f ) {}
  33. float GetConsumedMass( void ) const;
  34. static CGravityVortexController *Create( const Vector &origin, float radius, float strength, float duration );
  35. private:
  36. void ConsumeEntity( CBaseEntity *pEnt );
  37. void PullPlayersInRange( void );
  38. bool KillNPCInRange( CBaseEntity *pVictim, IPhysicsObject **pPhysObj );
  39. void CreateDenseBall( void );
  40. void PullThink( void );
  41. void StartPull( const Vector &origin, float radius, float strength, float duration );
  42. float m_flMass; // Mass consumed by the vortex
  43. float m_flEndTime; // Time when the vortex will stop functioning
  44. float m_flRadius; // Area of effect for the vortex
  45. float m_flStrength; // Pulling strength of the vortex
  46. };
  47. //-----------------------------------------------------------------------------
  48. // Purpose: Returns the amount of mass consumed by the vortex
  49. //-----------------------------------------------------------------------------
  50. float CGravityVortexController::GetConsumedMass( void ) const
  51. {
  52. return m_flMass;
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Purpose: Adds the entity's mass to the aggregate mass consumed
  56. //-----------------------------------------------------------------------------
  57. void CGravityVortexController::ConsumeEntity( CBaseEntity *pEnt )
  58. {
  59. // Get our base physics object
  60. IPhysicsObject *pPhysObject = pEnt->VPhysicsGetObject();
  61. if ( pPhysObject == NULL )
  62. return;
  63. // Ragdolls need to report the sum of all their parts
  64. CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pEnt );
  65. if ( pRagdoll != NULL )
  66. {
  67. // Find the aggregate mass of the whole ragdoll
  68. ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll();
  69. for ( int j = 0; j < pRagdollPhys->listCount; ++j )
  70. {
  71. m_flMass += pRagdollPhys->list[j].pObject->GetMass();
  72. }
  73. }
  74. else
  75. {
  76. // Otherwise we just take the normal mass
  77. m_flMass += pPhysObject->GetMass();
  78. }
  79. // Destroy the entity
  80. UTIL_Remove( pEnt );
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Purpose: Causes players within the radius to be sucked in
  84. //-----------------------------------------------------------------------------
  85. void CGravityVortexController::PullPlayersInRange( void )
  86. {
  87. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  88. Vector vecForce = GetAbsOrigin() - pPlayer->WorldSpaceCenter();
  89. float dist = VectorNormalize( vecForce );
  90. // FIXME: Need a more deterministic method here
  91. if ( dist < 128.0f )
  92. {
  93. // Kill the player (with falling death sound and effects)
  94. CTakeDamageInfo deathInfo( this, this, GetAbsOrigin(), GetAbsOrigin(), 200, DMG_FALL );
  95. pPlayer->TakeDamage( deathInfo );
  96. if ( pPlayer->IsAlive() == false )
  97. {
  98. color32 black = { 0, 0, 0, 255 };
  99. UTIL_ScreenFade( pPlayer, black, 0.1f, 0.0f, (FFADE_OUT|FFADE_STAYOUT) );
  100. return;
  101. }
  102. }
  103. // Must be within the radius
  104. if ( dist > m_flRadius )
  105. return;
  106. float mass = pPlayer->VPhysicsGetObject()->GetMass();
  107. float playerForce = m_flStrength * 0.05f;
  108. // Find the pull force
  109. // NOTE: We might want to make this non-linear to give more of a "grace distance"
  110. vecForce *= ( 1.0f - ( dist / m_flRadius ) ) * playerForce * mass;
  111. vecForce[2] *= 0.025f;
  112. pPlayer->SetBaseVelocity( vecForce );
  113. pPlayer->AddFlag( FL_BASEVELOCITY );
  114. // Make sure the player moves
  115. if ( vecForce.z > 0 && ( pPlayer->GetFlags() & FL_ONGROUND) )
  116. {
  117. pPlayer->SetGroundEntity( NULL );
  118. }
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose: Attempts to kill an NPC if it's within range and other criteria
  122. // Input : *pVictim - NPC to assess
  123. // **pPhysObj - pointer to the ragdoll created if the NPC is killed
  124. // Output : bool - whether or not the NPC was killed and the returned pointer is valid
  125. //-----------------------------------------------------------------------------
  126. bool CGravityVortexController::KillNPCInRange( CBaseEntity *pVictim, IPhysicsObject **pPhysObj )
  127. {
  128. CBaseCombatCharacter *pBCC = pVictim->MyCombatCharacterPointer();
  129. // See if we can ragdoll
  130. if ( pBCC != NULL && pBCC->CanBecomeRagdoll() )
  131. {
  132. // Don't bother with striders
  133. if ( FClassnameIs( pBCC, "npc_strider" ) )
  134. return false;
  135. // TODO: Make this an interaction between the NPC and the vortex
  136. // Become ragdoll
  137. CTakeDamageInfo info( this, this, 1.0f, DMG_GENERIC );
  138. CBaseEntity *pRagdoll = CreateServerRagdoll( pBCC, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
  139. pRagdoll->SetCollisionBounds( pVictim->CollisionProp()->OBBMins(), pVictim->CollisionProp()->OBBMaxs() );
  140. // Necessary to cause it to do the appropriate death cleanup
  141. CTakeDamageInfo ragdollInfo( this, this, 10000.0, DMG_GENERIC | DMG_REMOVENORAGDOLL );
  142. pVictim->TakeDamage( ragdollInfo );
  143. // Return the pointer to the ragdoll
  144. *pPhysObj = pRagdoll->VPhysicsGetObject();
  145. return true;
  146. }
  147. // Wasn't able to ragdoll this target
  148. *pPhysObj = NULL;
  149. return false;
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Purpose: Creates a dense ball with a mass equal to the aggregate mass consumed by the vortex
  153. //-----------------------------------------------------------------------------
  154. void CGravityVortexController::CreateDenseBall( void )
  155. {
  156. CBaseEntity *pBall = CreateEntityByName( "prop_physics" );
  157. pBall->SetModel( DENSE_BALL_MODEL );
  158. pBall->SetAbsOrigin( GetAbsOrigin() );
  159. pBall->Spawn();
  160. IPhysicsObject *pObj = pBall->VPhysicsGetObject();
  161. if ( pObj != NULL )
  162. {
  163. pObj->SetMass( GetConsumedMass() );
  164. }
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Pulls physical objects towards the vortex center, killing them if they come too near
  168. //-----------------------------------------------------------------------------
  169. void CGravityVortexController::PullThink( void )
  170. {
  171. // Pull any players close enough to us
  172. PullPlayersInRange();
  173. Vector mins, maxs;
  174. mins = GetAbsOrigin() - Vector( m_flRadius, m_flRadius, m_flRadius );
  175. maxs = GetAbsOrigin() + Vector( m_flRadius, m_flRadius, m_flRadius );
  176. // Draw debug information
  177. if ( g_debug_hopwire.GetBool() )
  178. {
  179. NDebugOverlay::Box( GetAbsOrigin(), mins - GetAbsOrigin(), maxs - GetAbsOrigin(), 0, 255, 0, 16, 4.0f );
  180. }
  181. CBaseEntity *pEnts[128];
  182. int numEnts = UTIL_EntitiesInBox( pEnts, 128, mins, maxs, 0 );
  183. for ( int i = 0; i < numEnts; i++ )
  184. {
  185. IPhysicsObject *pPhysObject = NULL;
  186. // Attempt to kill and ragdoll any victims in range
  187. if ( KillNPCInRange( pEnts[i], &pPhysObject ) == false )
  188. {
  189. // If we didn't have a valid victim, see if we can just get the vphysics object
  190. pPhysObject = pEnts[i]->VPhysicsGetObject();
  191. if ( pPhysObject == NULL )
  192. continue;
  193. }
  194. float mass;
  195. CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pEnts[i] );
  196. if ( pRagdoll != NULL )
  197. {
  198. ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll();
  199. mass = 0.0f;
  200. // Find the aggregate mass of the whole ragdoll
  201. for ( int j = 0; j < pRagdollPhys->listCount; ++j )
  202. {
  203. mass += pRagdollPhys->list[j].pObject->GetMass();
  204. }
  205. }
  206. else
  207. {
  208. mass = pPhysObject->GetMass();
  209. }
  210. Vector vecForce = GetAbsOrigin() - pEnts[i]->WorldSpaceCenter();
  211. Vector vecForce2D = vecForce;
  212. vecForce2D[2] = 0.0f;
  213. float dist2D = VectorNormalize( vecForce2D );
  214. float dist = VectorNormalize( vecForce );
  215. // FIXME: Need a more deterministic method here
  216. if ( dist < 48.0f )
  217. {
  218. ConsumeEntity( pEnts[i] );
  219. continue;
  220. }
  221. // Must be within the radius
  222. if ( dist > m_flRadius )
  223. continue;
  224. // Find the pull force
  225. vecForce *= ( 1.0f - ( dist2D / m_flRadius ) ) * m_flStrength * mass;
  226. if ( pEnts[i]->VPhysicsGetObject() )
  227. {
  228. // Pull the object in
  229. pEnts[i]->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, GetAbsOrigin(), m_flStrength, DMG_BLAST ) );
  230. }
  231. }
  232. // Keep going if need-be
  233. if ( m_flEndTime > gpGlobals->curtime )
  234. {
  235. SetThink( &CGravityVortexController::PullThink );
  236. SetNextThink( gpGlobals->curtime + 0.1f );
  237. }
  238. else
  239. {
  240. //Msg( "Consumed %.2f kilograms\n", m_flMass );
  241. //CreateDenseBall();
  242. }
  243. }
  244. //-----------------------------------------------------------------------------
  245. // Purpose: Starts the vortex working
  246. //-----------------------------------------------------------------------------
  247. void CGravityVortexController::StartPull( const Vector &origin, float radius, float strength, float duration )
  248. {
  249. SetAbsOrigin( origin );
  250. m_flEndTime = gpGlobals->curtime + duration;
  251. m_flRadius = radius;
  252. m_flStrength= strength;
  253. SetThink( &CGravityVortexController::PullThink );
  254. SetNextThink( gpGlobals->curtime + 0.1f );
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: Creation utility
  258. //-----------------------------------------------------------------------------
  259. CGravityVortexController *CGravityVortexController::Create( const Vector &origin, float radius, float strength, float duration )
  260. {
  261. // Create an instance of the vortex
  262. CGravityVortexController *pVortex = (CGravityVortexController *) CreateEntityByName( "vortex_controller" );
  263. if ( pVortex == NULL )
  264. return NULL;
  265. // Start the vortex working
  266. pVortex->StartPull( origin, radius, strength, duration );
  267. return pVortex;
  268. }
  269. BEGIN_DATADESC( CGravityVortexController )
  270. DEFINE_FIELD( m_flMass, FIELD_FLOAT ),
  271. DEFINE_FIELD( m_flEndTime, FIELD_TIME ),
  272. DEFINE_FIELD( m_flRadius, FIELD_FLOAT ),
  273. DEFINE_FIELD( m_flStrength, FIELD_FLOAT ),
  274. DEFINE_THINKFUNC( PullThink ),
  275. END_DATADESC()
  276. LINK_ENTITY_TO_CLASS( vortex_controller, CGravityVortexController );
  277. #define GRENADE_MODEL_CLOSED "models/roller.mdl"
  278. #define GRENADE_MODEL_OPEN "models/roller_spikes.mdl"
  279. BEGIN_DATADESC( CGrenadeHopwire )
  280. DEFINE_FIELD( m_hVortexController, FIELD_EHANDLE ),
  281. DEFINE_THINKFUNC( EndThink ),
  282. DEFINE_THINKFUNC( CombatThink ),
  283. END_DATADESC()
  284. LINK_ENTITY_TO_CLASS( npc_grenade_hopwire, CGrenadeHopwire );
  285. IMPLEMENT_SERVERCLASS_ST( CGrenadeHopwire, DT_GrenadeHopwire )
  286. END_SEND_TABLE()
  287. //-----------------------------------------------------------------------------
  288. // Purpose:
  289. //-----------------------------------------------------------------------------
  290. void CGrenadeHopwire::Spawn( void )
  291. {
  292. Precache();
  293. SetModel( GRENADE_MODEL_CLOSED );
  294. SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
  295. CreateVPhysics();
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Purpose:
  299. // Output : Returns true on success, false on failure.
  300. //-----------------------------------------------------------------------------
  301. bool CGrenadeHopwire::CreateVPhysics()
  302. {
  303. // Create the object in the physics system
  304. VPhysicsInitNormal( SOLID_BBOX, 0, false );
  305. return true;
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. //-----------------------------------------------------------------------------
  310. void CGrenadeHopwire::Precache( void )
  311. {
  312. // FIXME: Replace
  313. //PrecacheSound("NPC_Strider.Shoot");
  314. //PrecacheSound("d3_citadel.weapon_zapper_beam_loop2");
  315. PrecacheModel( GRENADE_MODEL_OPEN );
  316. PrecacheModel( GRENADE_MODEL_CLOSED );
  317. PrecacheModel( DENSE_BALL_MODEL );
  318. BaseClass::Precache();
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose:
  322. // Input : timer -
  323. //-----------------------------------------------------------------------------
  324. void CGrenadeHopwire::SetTimer( float timer )
  325. {
  326. SetThink( &CBaseGrenade::PreDetonate );
  327. SetNextThink( gpGlobals->curtime + timer );
  328. }
  329. #define MAX_STRIDER_KILL_DISTANCE_HORZ (hopwire_strider_kill_dist_h.GetFloat()) // Distance a Strider will be killed if within
  330. #define MAX_STRIDER_KILL_DISTANCE_VERT (hopwire_strider_kill_dist_v.GetFloat()) // Distance a Strider will be killed if within
  331. #define MAX_STRIDER_STUN_DISTANCE_HORZ (MAX_STRIDER_KILL_DISTANCE_HORZ*2) // Distance a Strider will be stunned if within
  332. #define MAX_STRIDER_STUN_DISTANCE_VERT (MAX_STRIDER_KILL_DISTANCE_VERT*2) // Distance a Strider will be stunned if within
  333. //-----------------------------------------------------------------------------
  334. // Purpose:
  335. //-----------------------------------------------------------------------------
  336. void CGrenadeHopwire::KillStriders( void )
  337. {
  338. CBaseEntity *pEnts[128];
  339. Vector mins, maxs;
  340. ClearBounds( mins, maxs );
  341. AddPointToBounds( -Vector( MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ ), mins, maxs );
  342. AddPointToBounds( Vector( MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ ), mins, maxs );
  343. AddPointToBounds( -Vector( MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT ), mins, maxs );
  344. AddPointToBounds( Vector( MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT ), mins, maxs );
  345. // FIXME: It's probably much faster to simply iterate over the striders in the map, rather than any entity in the radius - jdw
  346. // Find any striders in range of us
  347. int numTargets = UTIL_EntitiesInBox( pEnts, ARRAYSIZE( pEnts ), GetAbsOrigin()+mins, GetAbsOrigin()+maxs, FL_NPC );
  348. float targetDistHorz, targetDistVert;
  349. for ( int i = 0; i < numTargets; i++ )
  350. {
  351. // Only affect striders
  352. if ( FClassnameIs( pEnts[i], "npc_strider" ) == false )
  353. continue;
  354. // We categorize our spatial relation to the strider in horizontal and vertical terms, so that we can specify both parameters separately
  355. targetDistHorz = UTIL_DistApprox2D( pEnts[i]->GetAbsOrigin(), GetAbsOrigin() );
  356. targetDistVert = fabs( pEnts[i]->GetAbsOrigin()[2] - GetAbsOrigin()[2] );
  357. if ( targetDistHorz < MAX_STRIDER_KILL_DISTANCE_HORZ && targetDistHorz < MAX_STRIDER_KILL_DISTANCE_VERT )
  358. {
  359. // Kill the strider
  360. float fracDamage = ( pEnts[i]->GetMaxHealth() / hopwire_strider_hits.GetFloat() ) + 1.0f;
  361. CTakeDamageInfo killInfo( this, this, fracDamage, DMG_GENERIC );
  362. Vector killDir = pEnts[i]->GetAbsOrigin() - GetAbsOrigin();
  363. VectorNormalize( killDir );
  364. killInfo.SetDamageForce( killDir * -1000.0f );
  365. killInfo.SetDamagePosition( GetAbsOrigin() );
  366. pEnts[i]->TakeDamage( killInfo );
  367. }
  368. else if ( targetDistHorz < MAX_STRIDER_STUN_DISTANCE_HORZ && targetDistHorz < MAX_STRIDER_STUN_DISTANCE_VERT )
  369. {
  370. // Stun the strider
  371. CTakeDamageInfo killInfo( this, this, 200.0f, DMG_GENERIC );
  372. pEnts[i]->TakeDamage( killInfo );
  373. }
  374. }
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose:
  378. //-----------------------------------------------------------------------------
  379. void CGrenadeHopwire::EndThink( void )
  380. {
  381. if ( hopwire_vortex.GetBool() )
  382. {
  383. EntityMessageBegin( this, true );
  384. WRITE_BYTE( 1 );
  385. MessageEnd();
  386. }
  387. SetThink( &CBaseEntity::SUB_Remove );
  388. SetNextThink( gpGlobals->curtime + 1.0f );
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose:
  392. //-----------------------------------------------------------------------------
  393. void CGrenadeHopwire::CombatThink( void )
  394. {
  395. // Stop the grenade from moving
  396. AddEFlags( EF_NODRAW );
  397. AddFlag( FSOLID_NOT_SOLID );
  398. VPhysicsDestroyObject();
  399. SetAbsVelocity( vec3_origin );
  400. SetMoveType( MOVETYPE_NONE );
  401. // Do special behaviors if there are any striders in the area
  402. KillStriders();
  403. // FIXME: Replace
  404. //EmitSound("NPC_Strider.Shoot");
  405. //EmitSound("d3_citadel.weapon_zapper_beam_loop2");
  406. // Quick screen flash
  407. CBasePlayer *pPlayer = ToBasePlayer( GetThrower() );
  408. color32 white = { 255,255,255,255 };
  409. UTIL_ScreenFade( pPlayer, white, 0.2f, 0.0f, FFADE_IN );
  410. // Create the vortex controller to pull entities towards us
  411. if ( hopwire_vortex.GetBool() )
  412. {
  413. m_hVortexController = CGravityVortexController::Create( GetAbsOrigin(), 512, 150, 3.0f );
  414. // Start our client-side effect
  415. EntityMessageBegin( this, true );
  416. WRITE_BYTE( 0 );
  417. MessageEnd();
  418. // Begin to stop in two seconds
  419. SetThink( &CGrenadeHopwire::EndThink );
  420. SetNextThink( gpGlobals->curtime + 2.0f );
  421. }
  422. else
  423. {
  424. // Remove us immediately
  425. SetThink( &CBaseEntity::SUB_Remove );
  426. SetNextThink( gpGlobals->curtime + 0.1f );
  427. }
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Purpose:
  431. //-----------------------------------------------------------------------------
  432. void CGrenadeHopwire::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity )
  433. {
  434. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  435. if ( pPhysicsObject != NULL )
  436. {
  437. pPhysicsObject->AddVelocity( &velocity, &angVelocity );
  438. }
  439. }
  440. //-----------------------------------------------------------------------------
  441. // Purpose: Hop off the ground to start deployment
  442. //-----------------------------------------------------------------------------
  443. void CGrenadeHopwire::Detonate( void )
  444. {
  445. SetModel( GRENADE_MODEL_OPEN );
  446. AngularImpulse hopAngle = RandomAngularImpulse( -300, 300 );
  447. //Find out how tall the ceiling is and always try to hop halfway
  448. trace_t tr;
  449. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, MAX_HOP_HEIGHT*2 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  450. // Jump half the height to the found ceiling
  451. float hopHeight = MIN( MAX_HOP_HEIGHT, (MAX_HOP_HEIGHT*tr.fraction) );
  452. //Add upwards velocity for the "hop"
  453. Vector hopVel( 0.0f, 0.0f, hopHeight );
  454. SetVelocity( hopVel, hopAngle );
  455. // Get the time until the apex of the hop
  456. float apexTime = sqrt( hopHeight / GetCurrentGravity() );
  457. // Explode at the apex
  458. SetThink( &CGrenadeHopwire::CombatThink );
  459. SetNextThink( gpGlobals->curtime + apexTime);
  460. }
  461. //-----------------------------------------------------------------------------
  462. // Purpose:
  463. //-----------------------------------------------------------------------------
  464. CBaseGrenade *HopWire_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer )
  465. {
  466. CGrenadeHopwire *pGrenade = (CGrenadeHopwire *) CBaseEntity::Create( "npc_grenade_hopwire", position, angles, pOwner );
  467. // Only set ourselves to detonate on a timer if we're not a trap hopwire
  468. if ( hopwire_trap.GetBool() == false )
  469. {
  470. pGrenade->SetTimer( timer );
  471. }
  472. pGrenade->SetVelocity( velocity, angVelocity );
  473. pGrenade->SetThrower( ToBaseCombatCharacter( pOwner ) );
  474. return pGrenade;
  475. }