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.

443 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Projectile shot by mortar synth.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "grenade_beam.h"
  9. #include "beam_shared.h"
  10. #include "ndebugoverlay.h"
  11. #include "decals.h"
  12. #include "engine/IEngineSound.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. #define GRENADEBEAM_DEFAULTWIDTH 2.0
  16. // ==============================================================================
  17. // > CGrenadeBeamChaser
  18. // ==============================================================================
  19. BEGIN_DATADESC( CGrenadeBeamChaser )
  20. DEFINE_FIELD( m_pTarget, FIELD_CLASSPTR ),
  21. // Function pointers
  22. DEFINE_FUNCTION( ChaserThink ),
  23. END_DATADESC()
  24. LINK_ENTITY_TO_CLASS( grenade_beam_chaser, CGrenadeBeamChaser );
  25. //------------------------------------------------------------------------------
  26. // Purpose :
  27. // Input :
  28. // Output :
  29. //------------------------------------------------------------------------------
  30. void CGrenadeBeamChaser::Spawn( void )
  31. {
  32. SetSolid( SOLID_NONE );
  33. SetMoveType( MOVETYPE_FLY );
  34. SetThink(&CGrenadeBeamChaser::ChaserThink);
  35. SetNextThink( gpGlobals->curtime );
  36. }
  37. //------------------------------------------------------------------------------
  38. // Purpose :
  39. // Input :
  40. // Output :
  41. //------------------------------------------------------------------------------
  42. void CGrenadeBeamChaser::ChaserThink( void )
  43. {
  44. Vector vTargetPos;
  45. m_pTarget->GetChaserTargetPos(&vTargetPos);
  46. Vector vTargetDir = (vTargetPos - GetLocalOrigin());
  47. // -------------------------------------------------
  48. // Check to see if we'll pass our target this frame
  49. // If so get the next target
  50. // -------------------------------------------------
  51. float flTargetDist = vTargetDir.Length();
  52. if ((gpGlobals->frametime * m_pTarget->m_flBeamSpeed) > flTargetDist)
  53. {
  54. m_pTarget->GetNextTargetPos(&vTargetPos);
  55. vTargetDir = (vTargetPos - GetLocalOrigin());
  56. flTargetDist = vTargetDir.Length();
  57. }
  58. if (flTargetDist != 0)
  59. {
  60. //--------------------------------------
  61. // Set our velocity to chase the target
  62. //--------------------------------------
  63. VectorNormalize(vTargetDir);
  64. SetAbsVelocity( vTargetDir * m_pTarget->m_flBeamSpeed );
  65. }
  66. SetNextThink( gpGlobals->curtime );
  67. }
  68. //------------------------------------------------------------------------------
  69. // Purpose :
  70. // Input :
  71. // Output :
  72. //------------------------------------------------------------------------------
  73. CGrenadeBeamChaser* CGrenadeBeamChaser::ChaserCreate( CGrenadeBeam *pTarget )
  74. {
  75. CGrenadeBeamChaser *pChaser = (CGrenadeBeamChaser *)CreateEntityByName( "grenade_beam_chaser" );
  76. pChaser->SetLocalOrigin( pTarget->GetLocalOrigin() );
  77. pChaser->m_pTarget = pTarget;
  78. pChaser->Spawn();
  79. return pChaser;
  80. }
  81. // ==============================================================================
  82. // > CGrenadeBeam
  83. // ==============================================================================
  84. BEGIN_DATADESC( CGrenadeBeam )
  85. DEFINE_FIELD( m_vLaunchPos, FIELD_POSITION_VECTOR ),
  86. DEFINE_FIELD( m_flBeamWidth, FIELD_FLOAT ),
  87. DEFINE_FIELD( m_flBeamSpeed, FIELD_FLOAT ),
  88. DEFINE_FIELD( m_flBeamLag, FIELD_FLOAT ),
  89. DEFINE_FIELD( m_flLaunchTime, FIELD_TIME ),
  90. DEFINE_FIELD( m_flLastTouchTime, FIELD_TIME ),
  91. DEFINE_FIELD( m_hBeamChaser, FIELD_EHANDLE ),
  92. DEFINE_FIELD( m_nNumHits, FIELD_INTEGER ),
  93. DEFINE_ARRAY( m_pHitLocation, FIELD_VECTOR, GRENADEBEAM_MAXHITS ),
  94. DEFINE_ARRAY( m_pBeam, FIELD_CLASSPTR, GRENADEBEAM_MAXBEAMS ),
  95. // Function pointers
  96. DEFINE_ENTITYFUNC( GrenadeBeamTouch ),
  97. DEFINE_THINKFUNC( KillBeam ),
  98. END_DATADESC()
  99. LINK_ENTITY_TO_CLASS( grenade_beam, CGrenadeBeam );
  100. //------------------------------------------------------------------------------
  101. // Purpose :
  102. // Input :
  103. // Output :
  104. //------------------------------------------------------------------------------
  105. void CGrenadeBeam::Spawn( void )
  106. {
  107. Precache( );
  108. SetSolid( SOLID_BBOX );
  109. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  110. //UNDONE/HACK: this model is never used but one is needed
  111. SetModel( "Models/weapons/flare.mdl" );
  112. AddEffects( EF_NODRAW );
  113. SetTouch( &CGrenadeBeam::GrenadeBeamTouch );
  114. SetNextThink( gpGlobals->curtime );
  115. m_takedamage = DAMAGE_NO;
  116. m_iHealth = 1;
  117. SetGravity( 0.0001 );
  118. m_nNumHits = 0;
  119. UTIL_SetSize( this, vec3_origin, vec3_origin );
  120. }
  121. //------------------------------------------------------------------------------
  122. // Purpose :
  123. // Input :
  124. // Output :
  125. //------------------------------------------------------------------------------
  126. CGrenadeBeam* CGrenadeBeam::Create( CBaseEntity* pOwner, const Vector &vStart)
  127. {
  128. CGrenadeBeam *pEnergy = (CGrenadeBeam *)CreateEntityByName( "grenade_beam" );
  129. pEnergy->Spawn();
  130. pEnergy->SetOwnerEntity( pOwner );
  131. pEnergy->SetRenderColor( 255, 0, 0, 0 );
  132. pEnergy->m_flBeamWidth = GRENADEBEAM_DEFAULTWIDTH;
  133. UTIL_SetOrigin( pEnergy, vStart );
  134. return pEnergy;
  135. }
  136. //------------------------------------------------------------------------------
  137. // Purpose :
  138. // Input :
  139. // Output :
  140. //------------------------------------------------------------------------------
  141. void CGrenadeBeam::Format(color32 clrColor, float flWidth)
  142. {
  143. m_clrRender = clrColor;
  144. m_flBeamWidth = flWidth;
  145. }
  146. //------------------------------------------------------------------------------
  147. // Purpose :
  148. // Input :
  149. // Output :
  150. //------------------------------------------------------------------------------
  151. void CGrenadeBeam::Shoot(Vector vDirection, float flSpeed, float flLifetime, float flLag, float flDamage )
  152. {
  153. SetThink ( &CGrenadeBeam::KillBeam );
  154. SetNextThink( gpGlobals->curtime + flLifetime );
  155. m_hBeamChaser = CGrenadeBeamChaser::ChaserCreate(this);
  156. m_flBeamSpeed = flSpeed;
  157. SetAbsVelocity( vDirection * flSpeed );
  158. m_flBeamLag = flLag;
  159. m_flDamage = flDamage;
  160. m_flLaunchTime = gpGlobals->curtime;
  161. m_vLaunchPos = GetAbsOrigin();
  162. m_flLastTouchTime = 0;
  163. CreateBeams();
  164. UpdateBeams();
  165. }
  166. //------------------------------------------------------------------------------
  167. // Purpose :
  168. // Input :
  169. // Output :
  170. //------------------------------------------------------------------------------
  171. void CGrenadeBeam::KillBeam(void)
  172. {
  173. SetThink(NULL);
  174. SetTouch(NULL);
  175. m_hBeamChaser->SetThink(NULL);
  176. UTIL_Remove(m_hBeamChaser);
  177. UTIL_Remove(this);
  178. for (int i=0;i<GRENADEBEAM_MAXBEAMS;i++)
  179. {
  180. if (m_pBeam[i])
  181. {
  182. UTIL_Remove(m_pBeam[i]);
  183. }
  184. }
  185. }
  186. //------------------------------------------------------------------------------
  187. // Purpose :
  188. // Input :
  189. // Output :
  190. //------------------------------------------------------------------------------
  191. void CGrenadeBeam::GrenadeBeamTouch( CBaseEntity *pOther )
  192. {
  193. //---------------------------------------------------------
  194. // Make sure I'm not caught in a corner, if so remove me
  195. //---------------------------------------------------------
  196. if (gpGlobals->curtime - m_flLastTouchTime < 0.01)
  197. {
  198. KillBeam();
  199. return;
  200. }
  201. m_flLastTouchTime = gpGlobals->curtime;
  202. // ---------------------------------------
  203. // If I have room for another hit, add it
  204. // ---------------------------------------
  205. if (m_nNumHits < GRENADEBEAM_MAXHITS)
  206. {
  207. m_pHitLocation[m_nNumHits] = GetLocalOrigin();
  208. m_nNumHits++;
  209. }
  210. // Otherwise copy over old hit, and force chaser into last hit position
  211. else
  212. {
  213. m_hBeamChaser->SetLocalOrigin( m_pHitLocation[0] );
  214. for (int i=0;i<m_nNumHits-1;i++)
  215. {
  216. m_pHitLocation[i] = m_pHitLocation[i+1];
  217. }
  218. m_pHitLocation[m_nNumHits-1]=GetLocalOrigin();
  219. }
  220. UpdateBeams();
  221. // --------------------------------------
  222. // Smoke or bubbles effect
  223. // --------------------------------------
  224. if (UTIL_PointContents ( GetAbsOrigin() ) & MASK_WATER)
  225. {
  226. UTIL_Bubbles(GetAbsOrigin()-Vector(3,3,3),GetAbsOrigin()+Vector(3,3,3),10);
  227. }
  228. else
  229. {
  230. UTIL_Smoke(GetAbsOrigin(), random->RandomInt(5, 10), 10);
  231. }
  232. // --------------------------------------------
  233. // Play burn sounds
  234. // --------------------------------------------
  235. if (pOther->m_takedamage)
  236. {
  237. pOther->TakeDamage( CTakeDamageInfo( this, this, m_flDamage, DMG_BURN ) );
  238. KillBeam();
  239. return;
  240. }
  241. EmitSound( "GrenadeBeam.HitSound" );
  242. trace_t tr;
  243. Vector vDirection = GetAbsVelocity();
  244. VectorNormalize(vDirection);
  245. UTIL_TraceLine( GetAbsOrigin()-vDirection, GetAbsOrigin()+vDirection, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
  246. UTIL_DecalTrace( &tr, "RedGlowFade" );
  247. UTIL_ImpactTrace( &tr, DMG_ENERGYBEAM );
  248. }
  249. //------------------------------------------------------------------------------
  250. // Purpose :
  251. // Input :
  252. // Output :
  253. //------------------------------------------------------------------------------
  254. void CGrenadeBeam::GetNextTargetPos(Vector *vPosition)
  255. {
  256. // Only advance if tail launch time has passed
  257. if (gpGlobals->curtime - m_flLaunchTime > m_flBeamLag)
  258. {
  259. if (m_nNumHits > 0)
  260. {
  261. for (int i=0;i<m_nNumHits-1;i++)
  262. {
  263. m_pHitLocation[i] = m_pHitLocation[i+1];
  264. }
  265. m_nNumHits--;
  266. UpdateBeams();
  267. }
  268. }
  269. GetChaserTargetPos(vPosition);
  270. }
  271. //------------------------------------------------------------------------------
  272. // Purpose :
  273. // Input :
  274. // Output :
  275. //------------------------------------------------------------------------------
  276. void CGrenadeBeam::GetChaserTargetPos(Vector *vPosition)
  277. {
  278. // -----------------------------
  279. // Launch chaser after a delay
  280. // -----------------------------
  281. if (gpGlobals->curtime - m_flLaunchTime < m_flBeamLag)
  282. {
  283. *vPosition = m_vLaunchPos;
  284. }
  285. else if (m_nNumHits > 0)
  286. {
  287. *vPosition = m_pHitLocation[0];
  288. }
  289. else
  290. {
  291. *vPosition = GetLocalOrigin();
  292. }
  293. }
  294. //------------------------------------------------------------------------------
  295. // Purpose :
  296. // Input :
  297. // Output :
  298. //------------------------------------------------------------------------------
  299. void CGrenadeBeam::CreateBeams(void)
  300. {
  301. for ( int i=0; i < GRENADEBEAM_MAXBEAMS; ++i )
  302. {
  303. m_pBeam[i] = CBeam::BeamCreate( "sprites/laser.vmt", m_flBeamWidth );
  304. m_pBeam[i]->SetColor( m_clrRender->r, m_clrRender->g, m_clrRender->b );
  305. m_pBeam[i]->EntsInit( this, m_hBeamChaser );
  306. m_pBeam[i]->SetBrightness( 255 );
  307. m_pBeam[i]->SetNoise( 1 );
  308. m_pBeam[i]->SetBeamFlag( FBEAM_SHADEIN );
  309. m_pBeam[i]->SetBeamFlag( FBEAM_SHADEOUT );
  310. }
  311. }
  312. /*
  313. void CGrenadeBeam::DebugBeams(void)
  314. {
  315. if (m_nNumHits > 0)
  316. {
  317. NDebugOverlay::Line(GetLocalOrigin(), m_pHitLocation[m_nNumHits-1], 255,255,25, true, 0.1);
  318. NDebugOverlay::Line(m_hBeamChaser->GetLocalOrigin(), m_pHitLocation[0], 255,255,25, true, 0.1);
  319. for (int i=0;i<m_nNumHits-1;i++)
  320. {
  321. NDebugOverlay::Line(m_pHitLocation[i], m_pHitLocation[i+1], 255,255,25, true, 0.1);
  322. }
  323. }
  324. else
  325. {
  326. NDebugOverlay::Line(GetLocalOrigin(), m_hBeamChaser->GetLocalOrigin(), 255,255,25, true, 0.1);
  327. }
  328. for (int i=0;i<m_nNumHits;i++)
  329. {
  330. NDebugOverlay::Cross3D(m_pHitLocation[i], Vector(-8,-8,-8),Vector(8,8,8),0,255,0,true,0.1);
  331. }
  332. }
  333. */
  334. //------------------------------------------------------------------------------
  335. // Purpose :
  336. // Input :
  337. // Output :
  338. //------------------------------------------------------------------------------
  339. void CGrenadeBeam::UpdateBeams(void)
  340. {
  341. // ------------------------------------------------------------------
  342. // If no hits, draw a single beam between the grenade and the chaser
  343. // ------------------------------------------------------------------
  344. if (m_nNumHits == 0)
  345. {
  346. m_pBeam[0]->EntsInit( this, m_hBeamChaser );
  347. for (int i=1;i<GRENADEBEAM_MAXBEAMS;i++)
  348. {
  349. m_pBeam[i]->SetBrightness(0);
  350. }
  351. }
  352. // ------------------------------------------------------------------
  353. // Otherwise draw beams between hits
  354. // ------------------------------------------------------------------
  355. else
  356. {
  357. m_pBeam[0]->PointEntInit( m_pHitLocation[0], m_hBeamChaser );
  358. for (int i=1;i<GRENADEBEAM_MAXBEAMS-1;i++)
  359. {
  360. if (i<m_nNumHits)
  361. {
  362. m_pBeam[i]->PointsInit(m_pHitLocation[i-1],m_pHitLocation[i]);
  363. m_pBeam[i]->SetBrightness(255);
  364. }
  365. else
  366. {
  367. m_pBeam[i]->SetBrightness(0);
  368. }
  369. }
  370. m_pBeam[GRENADEBEAM_MAXBEAMS-1]->PointEntInit( m_pHitLocation[m_nNumHits-1], this );
  371. m_pBeam[GRENADEBEAM_MAXBEAMS-1]->SetBrightness(255);
  372. }
  373. }
  374. //------------------------------------------------------------------------------
  375. // Purpose :
  376. // Input :
  377. // Output :
  378. //------------------------------------------------------------------------------
  379. void CGrenadeBeam::Precache( void )
  380. {
  381. PrecacheModel("sprites/laser.vmt");
  382. //UNDONE/HACK: this model is never used but one is needed
  383. PrecacheModel("Models/weapons/flare.mdl");
  384. PrecacheScriptSound( "GrenadeBeam.HitSound" );
  385. }
  386. //------------------------------------------------------------------------------
  387. // Purpose : Send even though we don't have a model
  388. // Input :
  389. // Output :
  390. //------------------------------------------------------------------------------
  391. int CGrenadeBeam::UpdateTransmitState(void)
  392. {
  393. return SetTransmitState( FL_EDICT_PVSCHECK );
  394. }