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.

396 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements the tripmine grenade.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "util.h"
  9. #include "shake.h"
  10. #include "grenade_tripwire.h"
  11. #include "grenade_homer.h"
  12. #include "rope.h"
  13. #include "rope_shared.h"
  14. #include "engine/IEngineSound.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. ConVar sk_dmg_tripwire ( "sk_dmg_tripwire","0");
  18. ConVar sk_tripwire_radius ( "sk_tripwire_radius","0");
  19. #define GRENADETRIPWIRE_MISSILEMDL "models/Weapons/ar2_grenade.mdl"
  20. #define TGRENADE_LAUNCH_VEL 1200
  21. #define TGRENADE_SPIN_MAG 50
  22. #define TGRENADE_SPIN_SPEED 100
  23. #define TGRENADE_MISSILE_OFFSET 50
  24. #define TGRENADE_MAX_ROPE_LEN 1500
  25. LINK_ENTITY_TO_CLASS( npc_tripwire, CTripwireGrenade );
  26. BEGIN_DATADESC( CTripwireGrenade )
  27. DEFINE_FIELD( m_flPowerUp, FIELD_TIME ),
  28. DEFINE_FIELD( m_nMissileCount, FIELD_INTEGER ),
  29. DEFINE_FIELD( m_vecDir, FIELD_VECTOR ),
  30. DEFINE_FIELD( m_vTargetPos, FIELD_POSITION_VECTOR ),
  31. DEFINE_FIELD( m_vTargetOffset, FIELD_VECTOR ),
  32. DEFINE_FIELD( m_pRope, FIELD_CLASSPTR ),
  33. DEFINE_FIELD( m_pHook, FIELD_CLASSPTR ),
  34. // Function Pointers
  35. DEFINE_FUNCTION( WarningThink ),
  36. DEFINE_FUNCTION( PowerupThink ),
  37. DEFINE_FUNCTION( RopeBreakThink ),
  38. DEFINE_FUNCTION( FireThink ),
  39. END_DATADESC()
  40. CTripwireGrenade::CTripwireGrenade()
  41. {
  42. m_vecDir.Init();
  43. }
  44. void CTripwireGrenade::Spawn( void )
  45. {
  46. Precache( );
  47. SetMoveType( MOVETYPE_FLY );
  48. SetSolid( SOLID_BBOX );
  49. AddSolidFlags( FSOLID_NOT_SOLID );
  50. SetModel( "models/Weapons/w_slam.mdl" );
  51. m_nMissileCount = 0;
  52. UTIL_SetSize(this, Vector( -4, -4, -2), Vector(4, 4, 2));
  53. m_flPowerUp = gpGlobals->curtime + 1.2;//<<CHECK>>get rid of this
  54. SetThink( WarningThink );
  55. SetNextThink( gpGlobals->curtime + 1.0f );
  56. m_takedamage = DAMAGE_YES;
  57. m_iHealth = 1;
  58. m_pRope = NULL;
  59. m_pHook = NULL;
  60. // Tripwire grenade sits at 90 on wall so rotate back to get m_vecDir
  61. QAngle angles = GetLocalAngles();
  62. angles.x -= 90;
  63. AngleVectors( angles, &m_vecDir );
  64. }
  65. void CTripwireGrenade::Precache( void )
  66. {
  67. PrecacheModel("models/Weapons/w_slam.mdl");
  68. PrecacheModel(GRENADETRIPWIRE_MISSILEMDL);
  69. }
  70. void CTripwireGrenade::WarningThink( void )
  71. {
  72. // play activate sound
  73. EmitSound( "TripwireGrenade.Activate" );
  74. // set to power up
  75. SetThink( PowerupThink );
  76. SetNextThink( gpGlobals->curtime + 1.0f );
  77. }
  78. void CTripwireGrenade::PowerupThink( void )
  79. {
  80. if (gpGlobals->curtime > m_flPowerUp)
  81. {
  82. MakeRope( );
  83. RemoveSolidFlags( FSOLID_NOT_SOLID );
  84. m_bIsLive = true;
  85. }
  86. SetNextThink( gpGlobals->curtime + 0.1f );
  87. }
  88. void CTripwireGrenade::BreakRope( void )
  89. {
  90. if (m_pRope)
  91. {
  92. m_pRope->m_RopeFlags |= ROPE_COLLIDE;
  93. m_pRope->DetachPoint(0);
  94. Vector vVelocity;
  95. m_pHook->GetVelocity( &vVelocity, NULL );
  96. if (vVelocity.Length() > 1)
  97. {
  98. m_pRope->DetachPoint(1);
  99. }
  100. }
  101. }
  102. void CTripwireGrenade::MakeRope( void )
  103. {
  104. SetThink( RopeBreakThink );
  105. // Delay first think slightly so rope has time
  106. // to appear if person right in front of it
  107. SetNextThink( gpGlobals->curtime + 1.0f );
  108. // Create hook for end of tripwire
  109. m_pHook = (CTripwireHook*)CBaseEntity::Create( "tripwire_hook", GetLocalOrigin(), GetLocalAngles() );
  110. if (m_pHook)
  111. {
  112. Vector vShootVel = 800*(m_vecDir + Vector(0,0,0.3)+RandomVector(-0.01,0.01));
  113. m_pHook->SetVelocity( vShootVel, vec3_origin);
  114. m_pHook->SetOwnerEntity( this );
  115. m_pHook->m_hGrenade = this;
  116. m_pRope = CRopeKeyframe::Create(this,m_pHook,0,0);
  117. if (m_pRope)
  118. {
  119. m_pRope->m_Width = 1;
  120. m_pRope->m_RopeLength = 3;
  121. m_pRope->m_Slack = 100;
  122. CPASAttenuationFilter filter( this,"TripwireGrenade.ShootRope" );
  123. EmitSound( filter, entindex(),"TripwireGrenade.ShootRope" );
  124. }
  125. }
  126. }
  127. void CTripwireGrenade::Attach( void )
  128. {
  129. StopSound( "TripwireGrenade.ShootRope" );
  130. }
  131. void CTripwireGrenade::RopeBreakThink( void )
  132. {
  133. // See if I can go solid yet (has dropper moved out of way?)
  134. if (IsSolidFlagSet(FSOLID_NOT_SOLID))
  135. {
  136. trace_t tr;
  137. Vector vUpBit = GetAbsOrigin();
  138. vUpBit.z += 5.0;
  139. UTIL_TraceEntity( this, GetAbsOrigin(), vUpBit, MASK_SHOT, &tr );
  140. if ( !tr.startsolid && (tr.fraction == 1.0) )
  141. {
  142. RemoveSolidFlags( FSOLID_NOT_SOLID );
  143. }
  144. }
  145. // Check if rope had gotten beyond it's max length
  146. float flRopeLength = (GetAbsOrigin()-m_pHook->GetAbsOrigin()).Length();
  147. if (flRopeLength > TGRENADE_MAX_ROPE_LEN)
  148. {
  149. // Shoot missiles at hook
  150. m_iHealth = 0;
  151. BreakRope();
  152. m_vTargetPos = m_pHook->GetAbsOrigin();
  153. CrossProduct ( m_vecDir, Vector(0,0,1), m_vTargetOffset );
  154. m_vTargetOffset *=TGRENADE_MISSILE_OFFSET;
  155. SetThink(FireThink);
  156. FireThink();
  157. }
  158. // Check to see if can see hook
  159. // NOT MASK_SHOT because we want only simple hit boxes
  160. trace_t tr;
  161. UTIL_TraceLine( GetAbsOrigin(), m_pHook->GetAbsOrigin(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  162. // If can't see hook
  163. CBaseEntity *pEntity = tr.m_pEnt;
  164. if (tr.fraction != 1.0 && pEntity != m_pHook)
  165. {
  166. // Shoot missiles at place where rope was intersected
  167. m_iHealth = 0;
  168. BreakRope();
  169. m_vTargetPos = tr.endpos;
  170. CrossProduct ( m_vecDir, Vector(0,0,1), m_vTargetOffset );
  171. m_vTargetOffset *=TGRENADE_MISSILE_OFFSET;
  172. SetThink(FireThink);
  173. FireThink();
  174. return;
  175. }
  176. SetNextThink( gpGlobals->curtime + 0.1f );
  177. }
  178. //------------------------------------------------------------------------------
  179. // Purpose : Die if I take any damage
  180. // Input :
  181. // Output :
  182. //------------------------------------------------------------------------------
  183. int CTripwireGrenade::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  184. {
  185. // Killed upon any damage
  186. Event_Killed( info );
  187. return 0;
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose: If someone damaged, me shoot of my missiles and die
  191. // Input :
  192. // Output :
  193. //-----------------------------------------------------------------------------
  194. void CTripwireGrenade::Event_Killed( const CTakeDamageInfo &info )
  195. {
  196. if (m_iHealth > 0)
  197. {
  198. // Fire missiles and blow up
  199. for (int i=0;i<6;i++)
  200. {
  201. Vector vTargetPos = GetAbsOrigin() + RandomVector(-600,600);
  202. FireMissile(vTargetPos);
  203. }
  204. BreakRope();
  205. UTIL_Remove(this);
  206. }
  207. }
  208. //------------------------------------------------------------------------------
  209. // Purpose : Fire a missile at the target position
  210. // Input :
  211. // Output :
  212. //------------------------------------------------------------------------------
  213. void CTripwireGrenade::FireMissile(const Vector &vTargetPos)
  214. {
  215. Vector vTargetDir = (vTargetPos - GetAbsOrigin());
  216. VectorNormalize(vTargetDir);
  217. float flGravity = 0.0001; // No gravity on the missiles
  218. bool bSmokeTrail = true;
  219. float flHomingSpeed = 0;
  220. Vector vLaunchVelocity = TGRENADE_LAUNCH_VEL*vTargetDir;
  221. float flSpinMagnitude = TGRENADE_SPIN_MAG;
  222. float flSpinSpeed = TGRENADE_SPIN_SPEED;
  223. //<<CHECK>> hold in string_t
  224. CGrenadeHomer *pGrenade = CGrenadeHomer::CreateGrenadeHomer( MAKE_STRING(GRENADETRIPWIRE_MISSILEMDL), MAKE_STRING("TripwireGrenade.FlySound"), GetAbsOrigin(), vec3_angle, edict() );
  225. pGrenade->Spawn( );
  226. pGrenade->SetSpin(flSpinMagnitude,flSpinSpeed);
  227. pGrenade->SetHoming(0,0,0,0,0);
  228. pGrenade->SetDamage(sk_dmg_tripwire.GetFloat());
  229. pGrenade->SetDamageRadius(sk_tripwire_radius.GetFloat());
  230. pGrenade->Launch(this,NULL,vLaunchVelocity,flHomingSpeed,flGravity,bSmokeTrail);
  231. // Calculate travel time
  232. float flTargetDist = (GetAbsOrigin() - vTargetPos).Length();
  233. pGrenade->m_flDetonateTime = gpGlobals->curtime + flTargetDist/TGRENADE_LAUNCH_VEL;
  234. }
  235. //------------------------------------------------------------------------------
  236. // Purpose : Shoot off a series of missiles over time, then go intert
  237. // Input :
  238. // Output :
  239. //------------------------------------------------------------------------------
  240. void CTripwireGrenade::FireThink()
  241. {
  242. SetNextThink( gpGlobals->curtime + 0.16f );
  243. Vector vTargetPos = m_vTargetPos + (m_vTargetOffset * m_nMissileCount);
  244. FireMissile(vTargetPos);
  245. vTargetPos = m_vTargetPos - (m_vTargetOffset * m_nMissileCount);
  246. FireMissile(vTargetPos);
  247. m_nMissileCount++;
  248. if (m_nMissileCount > 4)
  249. {
  250. m_iHealth = -1;
  251. SetThink( NULL );
  252. }
  253. }
  254. // ####################################################################
  255. // CTripwireHook
  256. //
  257. // This is what the tripwire shoots out at the end of the rope
  258. // ####################################################################
  259. LINK_ENTITY_TO_CLASS( tripwire_hook, CTripwireHook );
  260. //---------------------------------------------------------
  261. // Save/Restore
  262. //---------------------------------------------------------
  263. BEGIN_DATADESC( CTripwireHook )
  264. DEFINE_FIELD( m_hGrenade, FIELD_EHANDLE ),
  265. DEFINE_FIELD( m_bAttached, FIELD_BOOLEAN ),
  266. END_DATADESC()
  267. void CTripwireHook::Spawn( void )
  268. {
  269. Precache( );
  270. SetModel( "models/Weapons/w_grenade.mdl" );//<<CHECK>>
  271. UTIL_SetSize(this, Vector( -1, -1, -1), Vector(1,1, 1));
  272. m_takedamage = DAMAGE_NO;
  273. m_bAttached = false;
  274. CreateVPhysics();
  275. }
  276. bool CTripwireHook::CreateVPhysics()
  277. {
  278. // Create the object in the physics system
  279. IPhysicsObject *pPhysicsObject = VPhysicsInitNormal( SOLID_BBOX, 0, false );
  280. // Make sure I get touch called for static geometry
  281. if ( pPhysicsObject )
  282. {
  283. int flags = pPhysicsObject->GetCallbackFlags();
  284. flags |= CALLBACK_GLOBAL_TOUCH_STATIC;
  285. pPhysicsObject->SetCallbackFlags(flags);
  286. }
  287. return true;
  288. }
  289. void CTripwireHook::Precache( void )
  290. {
  291. PrecacheModel("models/Weapons/w_grenade.mdl"); //<<CHECK>>
  292. }
  293. void CTripwireHook::EndTouch( CBaseEntity *pOther )
  294. {
  295. //<<CHECK>>do instead by clearing touch function
  296. if (!m_bAttached)
  297. {
  298. m_bAttached = true;
  299. SetVelocity(vec3_origin, vec3_origin);
  300. SetMoveType( MOVETYPE_NONE );
  301. EmitSound( "TripwireGrenade.Hook" );
  302. // Let the tripwire grenade know that I've attached
  303. CTripwireGrenade* pGrenade = dynamic_cast<CTripwireGrenade*>((CBaseEntity*)m_hGrenade);
  304. if (pGrenade)
  305. {
  306. pGrenade->Attach();
  307. }
  308. }
  309. }
  310. void CTripwireHook::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity )
  311. {
  312. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  313. if ( pPhysicsObject )
  314. {
  315. pPhysicsObject->AddVelocity( &velocity, &angVelocity );
  316. }
  317. }