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.

2376 lines
65 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "basehlcombatweapon.h"
  8. #include "basecombatcharacter.h"
  9. #include "movie_explosion.h"
  10. #include "soundent.h"
  11. #include "player.h"
  12. #include "rope.h"
  13. #include "vstdlib/random.h"
  14. #include "engine/IEngineSound.h"
  15. #include "explode.h"
  16. #include "util.h"
  17. #include "in_buttons.h"
  18. #include "weapon_rpg.h"
  19. #include "shake.h"
  20. #include "ai_basenpc.h"
  21. #include "ai_squad.h"
  22. #include "te_effect_dispatch.h"
  23. #include "triggers.h"
  24. #include "smoke_trail.h"
  25. #include "collisionutils.h"
  26. #include "hl2_shareddefs.h"
  27. #include "rumble_shared.h"
  28. #include "gamestats.h"
  29. #ifdef PORTAL
  30. #include "portal_util_shared.h"
  31. #endif
  32. #ifdef HL2_DLL
  33. extern int g_interactionPlayerLaunchedRPG;
  34. #endif
  35. // memdbgon must be the last include file in a .cpp file!!!
  36. #include "tier0/memdbgon.h"
  37. #define RPG_SPEED 1500
  38. static ConVar sk_apc_missile_damage("sk_apc_missile_damage", "15");
  39. ConVar rpg_missle_use_custom_detonators( "rpg_missle_use_custom_detonators", "1" );
  40. #define APC_MISSILE_DAMAGE sk_apc_missile_damage.GetFloat()
  41. const char *g_pLaserDotThink = "LaserThinkContext";
  42. //-----------------------------------------------------------------------------
  43. // Laser Dot
  44. //-----------------------------------------------------------------------------
  45. class CLaserDot : public CSprite
  46. {
  47. DECLARE_CLASS( CLaserDot, CSprite );
  48. public:
  49. CLaserDot( void );
  50. ~CLaserDot( void );
  51. static CLaserDot *Create( const Vector &origin, CBaseEntity *pOwner = NULL, bool bVisibleDot = true );
  52. void SetTargetEntity( CBaseEntity *pTarget ) { m_hTargetEnt = pTarget; }
  53. CBaseEntity *GetTargetEntity( void ) { return m_hTargetEnt; }
  54. void SetLaserPosition( const Vector &origin, const Vector &normal );
  55. Vector GetChasePosition();
  56. void TurnOn( void );
  57. void TurnOff( void );
  58. bool IsOn() const { return m_bIsOn; }
  59. void Toggle( void );
  60. void LaserThink( void );
  61. int ObjectCaps() { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
  62. void MakeInvisible( void );
  63. protected:
  64. Vector m_vecSurfaceNormal;
  65. EHANDLE m_hTargetEnt;
  66. bool m_bVisibleLaserDot;
  67. bool m_bIsOn;
  68. DECLARE_DATADESC();
  69. public:
  70. CLaserDot *m_pNext;
  71. };
  72. // a list of laser dots to search quickly
  73. CEntityClassList<CLaserDot> g_LaserDotList;
  74. template <> CLaserDot *CEntityClassList<CLaserDot>::m_pClassList = NULL;
  75. CLaserDot *GetLaserDotList()
  76. {
  77. return g_LaserDotList.m_pClassList;
  78. }
  79. BEGIN_DATADESC( CMissile )
  80. DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
  81. DEFINE_FIELD( m_hRocketTrail, FIELD_EHANDLE ),
  82. DEFINE_FIELD( m_flAugerTime, FIELD_TIME ),
  83. DEFINE_FIELD( m_flMarkDeadTime, FIELD_TIME ),
  84. DEFINE_FIELD( m_flGracePeriodEndsAt, FIELD_TIME ),
  85. DEFINE_FIELD( m_flDamage, FIELD_FLOAT ),
  86. DEFINE_FIELD( m_bCreateDangerSounds, FIELD_BOOLEAN ),
  87. // Function Pointers
  88. DEFINE_FUNCTION( MissileTouch ),
  89. DEFINE_FUNCTION( AccelerateThink ),
  90. DEFINE_FUNCTION( AugerThink ),
  91. DEFINE_FUNCTION( IgniteThink ),
  92. DEFINE_FUNCTION( SeekThink ),
  93. END_DATADESC()
  94. LINK_ENTITY_TO_CLASS( rpg_missile, CMissile );
  95. class CWeaponRPG;
  96. //-----------------------------------------------------------------------------
  97. // Constructor
  98. //-----------------------------------------------------------------------------
  99. CMissile::CMissile()
  100. {
  101. m_hRocketTrail = NULL;
  102. m_bCreateDangerSounds = false;
  103. }
  104. CMissile::~CMissile()
  105. {
  106. }
  107. //-----------------------------------------------------------------------------
  108. // Purpose:
  109. //
  110. //
  111. //-----------------------------------------------------------------------------
  112. void CMissile::Precache( void )
  113. {
  114. PrecacheModel( "models/weapons/w_missile.mdl" );
  115. PrecacheModel( "models/weapons/w_missile_launch.mdl" );
  116. PrecacheModel( "models/weapons/w_missile_closed.mdl" );
  117. }
  118. //-----------------------------------------------------------------------------
  119. // Purpose:
  120. //
  121. //
  122. //-----------------------------------------------------------------------------
  123. void CMissile::Spawn( void )
  124. {
  125. Precache();
  126. SetSolid( SOLID_BBOX );
  127. SetModel("models/weapons/w_missile_launch.mdl");
  128. UTIL_SetSize( this, -Vector(4,4,4), Vector(4,4,4) );
  129. SetTouch( &CMissile::MissileTouch );
  130. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  131. SetThink( &CMissile::IgniteThink );
  132. SetNextThink( gpGlobals->curtime + 0.3f );
  133. SetDamage( 200.0f );
  134. m_takedamage = DAMAGE_YES;
  135. m_iHealth = m_iMaxHealth = 100;
  136. m_bloodColor = DONT_BLEED;
  137. m_flGracePeriodEndsAt = 0;
  138. AddFlag( FL_OBJECT );
  139. }
  140. //---------------------------------------------------------
  141. //---------------------------------------------------------
  142. void CMissile::Event_Killed( const CTakeDamageInfo &info )
  143. {
  144. m_takedamage = DAMAGE_NO;
  145. ShotDown();
  146. }
  147. unsigned int CMissile::PhysicsSolidMaskForEntity( void ) const
  148. {
  149. return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX;
  150. }
  151. //---------------------------------------------------------
  152. //---------------------------------------------------------
  153. int CMissile::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  154. {
  155. if ( ( info.GetDamageType() & (DMG_MISSILEDEFENSE | DMG_AIRBOAT) ) == false )
  156. return 0;
  157. bool bIsDamaged;
  158. if( m_iHealth <= AugerHealth() )
  159. {
  160. // This missile is already damaged (i.e., already running AugerThink)
  161. bIsDamaged = true;
  162. }
  163. else
  164. {
  165. // This missile isn't damaged enough to wobble in flight yet
  166. bIsDamaged = false;
  167. }
  168. int nRetVal = BaseClass::OnTakeDamage_Alive( info );
  169. if( !bIsDamaged )
  170. {
  171. if ( m_iHealth <= AugerHealth() )
  172. {
  173. ShotDown();
  174. }
  175. }
  176. return nRetVal;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose: Stops any kind of tracking and shoots dumb
  180. //-----------------------------------------------------------------------------
  181. void CMissile::DumbFire( void )
  182. {
  183. SetThink( NULL );
  184. SetMoveType( MOVETYPE_FLY );
  185. SetModel("models/weapons/w_missile.mdl");
  186. UTIL_SetSize( this, vec3_origin, vec3_origin );
  187. EmitSound( "Missile.Ignite" );
  188. // Smoke trail.
  189. CreateSmokeTrail();
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose:
  193. //-----------------------------------------------------------------------------
  194. void CMissile::SetGracePeriod( float flGracePeriod )
  195. {
  196. m_flGracePeriodEndsAt = gpGlobals->curtime + flGracePeriod;
  197. // Go non-solid until the grace period ends
  198. AddSolidFlags( FSOLID_NOT_SOLID );
  199. }
  200. //---------------------------------------------------------
  201. //---------------------------------------------------------
  202. void CMissile::AccelerateThink( void )
  203. {
  204. Vector vecForward;
  205. // !!!UNDONE - make this work exactly the same as HL1 RPG, lest we have looping sound bugs again!
  206. EmitSound( "Missile.Accelerate" );
  207. // SetEffects( EF_LIGHT );
  208. AngleVectors( GetLocalAngles(), &vecForward );
  209. SetAbsVelocity( vecForward * RPG_SPEED );
  210. SetThink( &CMissile::SeekThink );
  211. SetNextThink( gpGlobals->curtime + 0.1f );
  212. }
  213. #define AUGER_YDEVIANCE 20.0f
  214. #define AUGER_XDEVIANCEUP 8.0f
  215. #define AUGER_XDEVIANCEDOWN 1.0f
  216. //---------------------------------------------------------
  217. //---------------------------------------------------------
  218. void CMissile::AugerThink( void )
  219. {
  220. // If we've augered long enough, then just explode
  221. if ( m_flAugerTime < gpGlobals->curtime )
  222. {
  223. Explode();
  224. return;
  225. }
  226. if ( m_flMarkDeadTime < gpGlobals->curtime )
  227. {
  228. m_lifeState = LIFE_DYING;
  229. }
  230. QAngle angles = GetLocalAngles();
  231. angles.y += random->RandomFloat( -AUGER_YDEVIANCE, AUGER_YDEVIANCE );
  232. angles.x += random->RandomFloat( -AUGER_XDEVIANCEDOWN, AUGER_XDEVIANCEUP );
  233. SetLocalAngles( angles );
  234. Vector vecForward;
  235. AngleVectors( GetLocalAngles(), &vecForward );
  236. SetAbsVelocity( vecForward * 1000.0f );
  237. SetNextThink( gpGlobals->curtime + 0.05f );
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: Causes the missile to spiral to the ground and explode, due to damage
  241. //-----------------------------------------------------------------------------
  242. void CMissile::ShotDown( void )
  243. {
  244. CEffectData data;
  245. data.m_vOrigin = GetAbsOrigin();
  246. DispatchEffect( "RPGShotDown", data );
  247. if ( m_hRocketTrail != NULL )
  248. {
  249. m_hRocketTrail->m_bDamaged = true;
  250. }
  251. SetThink( &CMissile::AugerThink );
  252. SetNextThink( gpGlobals->curtime );
  253. m_flAugerTime = gpGlobals->curtime + 1.5f;
  254. m_flMarkDeadTime = gpGlobals->curtime + 0.75;
  255. // Let the RPG start reloading immediately
  256. if ( m_hOwner != NULL )
  257. {
  258. m_hOwner->NotifyRocketDied();
  259. m_hOwner = NULL;
  260. }
  261. }
  262. //-----------------------------------------------------------------------------
  263. // The actual explosion
  264. //-----------------------------------------------------------------------------
  265. void CMissile::DoExplosion( void )
  266. {
  267. // Explode
  268. ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), GetDamage(), CMissile::EXPLOSION_RADIUS,
  269. SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this);
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Purpose:
  273. //-----------------------------------------------------------------------------
  274. void CMissile::Explode( void )
  275. {
  276. // Don't explode against the skybox. Just pretend that
  277. // the missile flies off into the distance.
  278. Vector forward;
  279. GetVectors( &forward, NULL, NULL );
  280. trace_t tr;
  281. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + forward * 16, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  282. m_takedamage = DAMAGE_NO;
  283. SetSolid( SOLID_NONE );
  284. if( tr.fraction == 1.0 || !(tr.surface.flags & SURF_SKY) )
  285. {
  286. DoExplosion();
  287. }
  288. if( m_hRocketTrail )
  289. {
  290. m_hRocketTrail->SetLifetime(0.1f);
  291. m_hRocketTrail = NULL;
  292. }
  293. if ( m_hOwner != NULL )
  294. {
  295. m_hOwner->NotifyRocketDied();
  296. m_hOwner = NULL;
  297. }
  298. StopSound( "Missile.Ignite" );
  299. UTIL_Remove( this );
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Purpose:
  303. // Input : *pOther -
  304. //-----------------------------------------------------------------------------
  305. void CMissile::MissileTouch( CBaseEntity *pOther )
  306. {
  307. Assert( pOther );
  308. // Don't touch triggers (but DO hit weapons)
  309. if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON )
  310. {
  311. // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them.
  312. if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) )
  313. return;
  314. }
  315. Explode();
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Purpose:
  319. //-----------------------------------------------------------------------------
  320. void CMissile::CreateSmokeTrail( void )
  321. {
  322. if ( m_hRocketTrail )
  323. return;
  324. // Smoke trail.
  325. if ( (m_hRocketTrail = RocketTrail::CreateRocketTrail()) != NULL )
  326. {
  327. m_hRocketTrail->m_Opacity = 0.2f;
  328. m_hRocketTrail->m_SpawnRate = 100;
  329. m_hRocketTrail->m_ParticleLifetime = 0.5f;
  330. m_hRocketTrail->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
  331. m_hRocketTrail->m_EndColor.Init( 0.0, 0.0, 0.0 );
  332. m_hRocketTrail->m_StartSize = 8;
  333. m_hRocketTrail->m_EndSize = 32;
  334. m_hRocketTrail->m_SpawnRadius = 4;
  335. m_hRocketTrail->m_MinSpeed = 2;
  336. m_hRocketTrail->m_MaxSpeed = 16;
  337. m_hRocketTrail->SetLifetime( 999 );
  338. m_hRocketTrail->FollowEntity( this, "0" );
  339. }
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose:
  343. //-----------------------------------------------------------------------------
  344. void CMissile::IgniteThink( void )
  345. {
  346. SetMoveType( MOVETYPE_FLY );
  347. SetModel("models/weapons/w_missile.mdl");
  348. UTIL_SetSize( this, vec3_origin, vec3_origin );
  349. RemoveSolidFlags( FSOLID_NOT_SOLID );
  350. //TODO: Play opening sound
  351. Vector vecForward;
  352. EmitSound( "Missile.Ignite" );
  353. AngleVectors( GetLocalAngles(), &vecForward );
  354. SetAbsVelocity( vecForward * RPG_SPEED );
  355. SetThink( &CMissile::SeekThink );
  356. SetNextThink( gpGlobals->curtime );
  357. if ( m_hOwner && m_hOwner->GetOwner() )
  358. {
  359. CBasePlayer *pPlayer = ToBasePlayer( m_hOwner->GetOwner() );
  360. if ( pPlayer )
  361. {
  362. color32 white = { 255,225,205,64 };
  363. UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN );
  364. pPlayer->RumbleEffect( RUMBLE_RPG_MISSILE, 0, RUMBLE_FLAG_RESTART );
  365. }
  366. }
  367. CreateSmokeTrail();
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Gets the shooting position
  371. //-----------------------------------------------------------------------------
  372. void CMissile::GetShootPosition( CLaserDot *pLaserDot, Vector *pShootPosition )
  373. {
  374. if ( pLaserDot->GetOwnerEntity() != NULL )
  375. {
  376. //FIXME: Do we care this isn't exactly the muzzle position?
  377. *pShootPosition = pLaserDot->GetOwnerEntity()->WorldSpaceCenter();
  378. }
  379. else
  380. {
  381. *pShootPosition = pLaserDot->GetChasePosition();
  382. }
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose:
  386. //-----------------------------------------------------------------------------
  387. #define RPG_HOMING_SPEED 0.125f
  388. void CMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed )
  389. {
  390. *pHomingSpeed = RPG_HOMING_SPEED;
  391. if ( pLaserDot->GetTargetEntity() )
  392. {
  393. *pActualDotPosition = pLaserDot->GetChasePosition();
  394. return;
  395. }
  396. Vector vLaserStart;
  397. GetShootPosition( pLaserDot, &vLaserStart );
  398. //Get the laser's vector
  399. Vector vLaserDir;
  400. VectorSubtract( pLaserDot->GetChasePosition(), vLaserStart, vLaserDir );
  401. //Find the length of the current laser
  402. float flLaserLength = VectorNormalize( vLaserDir );
  403. //Find the length from the missile to the laser's owner
  404. float flMissileLength = GetAbsOrigin().DistTo( vLaserStart );
  405. //Find the length from the missile to the laser's position
  406. Vector vecTargetToMissile;
  407. VectorSubtract( GetAbsOrigin(), pLaserDot->GetChasePosition(), vecTargetToMissile );
  408. float flTargetLength = VectorNormalize( vecTargetToMissile );
  409. // See if we should chase the line segment nearest us
  410. if ( ( flMissileLength < flLaserLength ) || ( flTargetLength <= 512.0f ) )
  411. {
  412. *pActualDotPosition = UTIL_PointOnLineNearestPoint( vLaserStart, pLaserDot->GetChasePosition(), GetAbsOrigin() );
  413. *pActualDotPosition += ( vLaserDir * 256.0f );
  414. }
  415. else
  416. {
  417. // Otherwise chase the dot
  418. *pActualDotPosition = pLaserDot->GetChasePosition();
  419. }
  420. // NDebugOverlay::Line( pLaserDot->GetChasePosition(), vLaserStart, 0, 255, 0, true, 0.05f );
  421. // NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );
  422. // NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose:
  426. //-----------------------------------------------------------------------------
  427. void CMissile::SeekThink( void )
  428. {
  429. CBaseEntity *pBestDot = NULL;
  430. float flBestDist = MAX_TRACE_LENGTH;
  431. float dotDist;
  432. // If we have a grace period, go solid when it ends
  433. if ( m_flGracePeriodEndsAt )
  434. {
  435. if ( m_flGracePeriodEndsAt < gpGlobals->curtime )
  436. {
  437. RemoveSolidFlags( FSOLID_NOT_SOLID );
  438. m_flGracePeriodEndsAt = 0;
  439. }
  440. }
  441. //Search for all dots relevant to us
  442. for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext )
  443. {
  444. if ( !pEnt->IsOn() )
  445. continue;
  446. if ( pEnt->GetOwnerEntity() != GetOwnerEntity() )
  447. continue;
  448. dotDist = (GetAbsOrigin() - pEnt->GetAbsOrigin()).Length();
  449. //Find closest
  450. if ( dotDist < flBestDist )
  451. {
  452. pBestDot = pEnt;
  453. flBestDist = dotDist;
  454. }
  455. }
  456. if( hl2_episodic.GetBool() )
  457. {
  458. if( flBestDist <= ( GetAbsVelocity().Length() * 2.5f ) && FVisible( pBestDot->GetAbsOrigin() ) )
  459. {
  460. // Scare targets
  461. CSoundEnt::InsertSound( SOUND_DANGER, pBestDot->GetAbsOrigin(), CMissile::EXPLOSION_RADIUS, 0.2f, pBestDot, SOUNDENT_CHANNEL_REPEATED_DANGER, NULL );
  462. }
  463. }
  464. if ( rpg_missle_use_custom_detonators.GetBool() )
  465. {
  466. for ( int i = gm_CustomDetonators.Count() - 1; i >=0; --i )
  467. {
  468. CustomDetonator_t &detonator = gm_CustomDetonators[i];
  469. if ( !detonator.hEntity )
  470. {
  471. gm_CustomDetonators.FastRemove( i );
  472. }
  473. else
  474. {
  475. const Vector &vPos = detonator.hEntity->CollisionProp()->WorldSpaceCenter();
  476. if ( detonator.halfHeight > 0 )
  477. {
  478. if ( fabsf( vPos.z - GetAbsOrigin().z ) < detonator.halfHeight )
  479. {
  480. if ( ( GetAbsOrigin().AsVector2D() - vPos.AsVector2D() ).LengthSqr() < detonator.radiusSq )
  481. {
  482. Explode();
  483. return;
  484. }
  485. }
  486. }
  487. else
  488. {
  489. if ( ( GetAbsOrigin() - vPos ).LengthSqr() < detonator.radiusSq )
  490. {
  491. Explode();
  492. return;
  493. }
  494. }
  495. }
  496. }
  497. }
  498. //If we have a dot target
  499. if ( pBestDot == NULL )
  500. {
  501. //Think as soon as possible
  502. SetNextThink( gpGlobals->curtime );
  503. return;
  504. }
  505. CLaserDot *pLaserDot = (CLaserDot *)pBestDot;
  506. Vector targetPos;
  507. float flHomingSpeed;
  508. Vector vecLaserDotPosition;
  509. ComputeActualDotPosition( pLaserDot, &targetPos, &flHomingSpeed );
  510. if ( IsSimulatingOnAlternateTicks() )
  511. flHomingSpeed *= 2;
  512. Vector vTargetDir;
  513. VectorSubtract( targetPos, GetAbsOrigin(), vTargetDir );
  514. float flDist = VectorNormalize( vTargetDir );
  515. if( pLaserDot->GetTargetEntity() != NULL && flDist <= 240.0f && hl2_episodic.GetBool() )
  516. {
  517. // Prevent the missile circling the Strider like a Halo in ep1_c17_06. If the missile gets within 20
  518. // feet of a Strider, tighten up the turn speed of the missile so it can break the halo and strike. (sjb 4/27/2006)
  519. if( pLaserDot->GetTargetEntity()->ClassMatches( "npc_strider" ) )
  520. {
  521. flHomingSpeed *= 1.75f;
  522. }
  523. }
  524. Vector vDir = GetAbsVelocity();
  525. float flSpeed = VectorNormalize( vDir );
  526. Vector vNewVelocity = vDir;
  527. if ( gpGlobals->frametime > 0.0f )
  528. {
  529. if ( flSpeed != 0 )
  530. {
  531. vNewVelocity = ( flHomingSpeed * vTargetDir ) + ( ( 1 - flHomingSpeed ) * vDir );
  532. // This computation may happen to cancel itself out exactly. If so, slam to targetdir.
  533. if ( VectorNormalize( vNewVelocity ) < 1e-3 )
  534. {
  535. vNewVelocity = (flDist != 0) ? vTargetDir : vDir;
  536. }
  537. }
  538. else
  539. {
  540. vNewVelocity = vTargetDir;
  541. }
  542. }
  543. QAngle finalAngles;
  544. VectorAngles( vNewVelocity, finalAngles );
  545. SetAbsAngles( finalAngles );
  546. vNewVelocity *= flSpeed;
  547. SetAbsVelocity( vNewVelocity );
  548. if( GetAbsVelocity() == vec3_origin )
  549. {
  550. // Strange circumstances have brought this missile to halt. Just blow it up.
  551. Explode();
  552. return;
  553. }
  554. // Think as soon as possible
  555. SetNextThink( gpGlobals->curtime );
  556. #ifdef HL2_EPISODIC
  557. if ( m_bCreateDangerSounds == true )
  558. {
  559. trace_t tr;
  560. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 0.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  561. CSoundEnt::InsertSound( SOUND_DANGER, tr.endpos, 100, 0.2, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
  562. }
  563. #endif
  564. }
  565. //-----------------------------------------------------------------------------
  566. // Purpose:
  567. //
  568. // Input : &vecOrigin -
  569. // &vecAngles -
  570. // NULL -
  571. //
  572. // Output : CMissile
  573. //-----------------------------------------------------------------------------
  574. CMissile *CMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner = NULL )
  575. {
  576. //CMissile *pMissile = (CMissile *)CreateEntityByName("rpg_missile" );
  577. CMissile *pMissile = (CMissile *) CBaseEntity::Create( "rpg_missile", vecOrigin, vecAngles, CBaseEntity::Instance( pentOwner ) );
  578. pMissile->SetOwnerEntity( Instance( pentOwner ) );
  579. pMissile->Spawn();
  580. pMissile->AddEffects( EF_NOSHADOW );
  581. Vector vecForward;
  582. AngleVectors( vecAngles, &vecForward );
  583. pMissile->SetAbsVelocity( vecForward * 300 + Vector( 0,0, 128 ) );
  584. return pMissile;
  585. }
  586. //-----------------------------------------------------------------------------
  587. //-----------------------------------------------------------------------------
  588. CUtlVector<CMissile::CustomDetonator_t> CMissile::gm_CustomDetonators;
  589. void CMissile::AddCustomDetonator( CBaseEntity *pEntity, float radius, float height )
  590. {
  591. int i = gm_CustomDetonators.AddToTail();
  592. gm_CustomDetonators[i].hEntity = pEntity;
  593. gm_CustomDetonators[i].radiusSq = Square( radius );
  594. gm_CustomDetonators[i].halfHeight = height * 0.5f;
  595. }
  596. //-----------------------------------------------------------------------------
  597. //-----------------------------------------------------------------------------
  598. void CMissile::RemoveCustomDetonator( CBaseEntity *pEntity )
  599. {
  600. for ( int i = 0; i < gm_CustomDetonators.Count(); i++ )
  601. {
  602. if ( gm_CustomDetonators[i].hEntity == pEntity )
  603. {
  604. gm_CustomDetonators.FastRemove( i );
  605. break;
  606. }
  607. }
  608. }
  609. //-----------------------------------------------------------------------------
  610. // This entity is used to create little force boxes that the helicopter
  611. // should avoid.
  612. //-----------------------------------------------------------------------------
  613. class CInfoAPCMissileHint : public CBaseEntity
  614. {
  615. DECLARE_DATADESC();
  616. public:
  617. DECLARE_CLASS( CInfoAPCMissileHint, CBaseEntity );
  618. virtual void Spawn( );
  619. virtual void Activate();
  620. virtual void UpdateOnRemove();
  621. static CBaseEntity *FindAimTarget( CBaseEntity *pMissile, const char *pTargetName,
  622. const Vector &vecCurrentTargetPos, const Vector &vecCurrentTargetVel );
  623. private:
  624. EHANDLE m_hTarget;
  625. typedef CHandle<CInfoAPCMissileHint> APCMissileHintHandle_t;
  626. static CUtlVector< APCMissileHintHandle_t > s_APCMissileHints;
  627. };
  628. //-----------------------------------------------------------------------------
  629. //
  630. // This entity is used to create little force boxes that the helicopters should avoid.
  631. //
  632. //-----------------------------------------------------------------------------
  633. CUtlVector< CInfoAPCMissileHint::APCMissileHintHandle_t > CInfoAPCMissileHint::s_APCMissileHints;
  634. LINK_ENTITY_TO_CLASS( info_apc_missile_hint, CInfoAPCMissileHint );
  635. BEGIN_DATADESC( CInfoAPCMissileHint )
  636. DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
  637. END_DATADESC()
  638. //-----------------------------------------------------------------------------
  639. // Spawn, remove
  640. //-----------------------------------------------------------------------------
  641. void CInfoAPCMissileHint::Spawn( )
  642. {
  643. SetModel( STRING( GetModelName() ) );
  644. SetSolid( SOLID_BSP );
  645. AddSolidFlags( FSOLID_NOT_SOLID );
  646. AddEffects( EF_NODRAW );
  647. }
  648. void CInfoAPCMissileHint::Activate( )
  649. {
  650. BaseClass::Activate();
  651. m_hTarget = gEntList.FindEntityByName( NULL, m_target );
  652. if ( m_hTarget == NULL )
  653. {
  654. DevWarning( "%s: Could not find target '%s'!\n", GetClassname(), STRING( m_target ) );
  655. }
  656. else
  657. {
  658. s_APCMissileHints.AddToTail( this );
  659. }
  660. }
  661. void CInfoAPCMissileHint::UpdateOnRemove( )
  662. {
  663. s_APCMissileHints.FindAndRemove( this );
  664. BaseClass::UpdateOnRemove();
  665. }
  666. //-----------------------------------------------------------------------------
  667. // Where are how should we avoid?
  668. //-----------------------------------------------------------------------------
  669. #define HINT_PREDICTION_TIME 3.0f
  670. CBaseEntity *CInfoAPCMissileHint::FindAimTarget( CBaseEntity *pMissile, const char *pTargetName,
  671. const Vector &vecCurrentEnemyPos, const Vector &vecCurrentEnemyVel )
  672. {
  673. if ( !pTargetName )
  674. return NULL;
  675. float flOOSpeed = pMissile->GetAbsVelocity().Length();
  676. if ( flOOSpeed != 0.0f )
  677. {
  678. flOOSpeed = 1.0f / flOOSpeed;
  679. }
  680. for ( int i = s_APCMissileHints.Count(); --i >= 0; )
  681. {
  682. CInfoAPCMissileHint *pHint = s_APCMissileHints[i];
  683. if ( !pHint->NameMatches( pTargetName ) )
  684. continue;
  685. if ( !pHint->m_hTarget )
  686. continue;
  687. Vector vecMissileToHint, vecMissileToEnemy;
  688. VectorSubtract( pHint->m_hTarget->WorldSpaceCenter(), pMissile->GetAbsOrigin(), vecMissileToHint );
  689. VectorSubtract( vecCurrentEnemyPos, pMissile->GetAbsOrigin(), vecMissileToEnemy );
  690. float flDistMissileToHint = VectorNormalize( vecMissileToHint );
  691. VectorNormalize( vecMissileToEnemy );
  692. if ( DotProduct( vecMissileToHint, vecMissileToEnemy ) < 0.866f )
  693. continue;
  694. // Determine when the target will be inside the volume.
  695. // Project at most 3 seconds in advance
  696. Vector vecRayDelta;
  697. VectorMultiply( vecCurrentEnemyVel, HINT_PREDICTION_TIME, vecRayDelta );
  698. BoxTraceInfo_t trace;
  699. if ( !IntersectRayWithOBB( vecCurrentEnemyPos, vecRayDelta, pHint->CollisionProp()->CollisionToWorldTransform(),
  700. pHint->CollisionProp()->OBBMins(), pHint->CollisionProp()->OBBMaxs(), 0.0f, &trace ))
  701. {
  702. continue;
  703. }
  704. // Determine the amount of time it would take the missile to reach the target
  705. // If we can reach the target within the time it takes for the enemy to reach the
  706. float tSqr = flDistMissileToHint * flOOSpeed / HINT_PREDICTION_TIME;
  707. if ( (tSqr < (trace.t1 * trace.t1)) || (tSqr > (trace.t2 * trace.t2)) )
  708. continue;
  709. return pHint->m_hTarget;
  710. }
  711. return NULL;
  712. }
  713. //-----------------------------------------------------------------------------
  714. // a list of missiles to search quickly
  715. //-----------------------------------------------------------------------------
  716. CEntityClassList<CAPCMissile> g_APCMissileList;
  717. template <> CAPCMissile *CEntityClassList<CAPCMissile>::m_pClassList = NULL;
  718. CAPCMissile *GetAPCMissileList()
  719. {
  720. return g_APCMissileList.m_pClassList;
  721. }
  722. //-----------------------------------------------------------------------------
  723. // Finds apc missiles in cone
  724. //-----------------------------------------------------------------------------
  725. CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDirection, float flAngle )
  726. {
  727. float flCosAngle = cos( DEG2RAD( flAngle ) );
  728. for( CAPCMissile *pEnt = GetAPCMissileList(); pEnt != NULL; pEnt = pEnt->m_pNext )
  729. {
  730. if ( !pEnt->IsSolid() )
  731. continue;
  732. Vector vecDelta;
  733. VectorSubtract( pEnt->GetAbsOrigin(), vecOrigin, vecDelta );
  734. VectorNormalize( vecDelta );
  735. float flDot = DotProduct( vecDelta, vecDirection );
  736. if ( flDot > flCosAngle )
  737. return pEnt;
  738. }
  739. return NULL;
  740. }
  741. //-----------------------------------------------------------------------------
  742. //
  743. // Specialized version of the missile
  744. //
  745. //-----------------------------------------------------------------------------
  746. #define MAX_HOMING_DISTANCE 2250.0f
  747. #define MIN_HOMING_DISTANCE 1250.0f
  748. #define MAX_NEAR_HOMING_DISTANCE 1750.0f
  749. #define MIN_NEAR_HOMING_DISTANCE 1000.0f
  750. #define DOWNWARD_BLEND_TIME_START 0.2f
  751. #define MIN_HEIGHT_DIFFERENCE 250.0f
  752. #define MAX_HEIGHT_DIFFERENCE 550.0f
  753. #define CORRECTION_TIME 0.2f
  754. #define APC_LAUNCH_HOMING_SPEED 0.1f
  755. #define APC_HOMING_SPEED 0.025f
  756. #define HOMING_SPEED_ACCEL 0.01f
  757. BEGIN_DATADESC( CAPCMissile )
  758. DEFINE_FIELD( m_flReachedTargetTime, FIELD_TIME ),
  759. DEFINE_FIELD( m_flIgnitionTime, FIELD_TIME ),
  760. DEFINE_FIELD( m_bGuidingDisabled, FIELD_BOOLEAN ),
  761. DEFINE_FIELD( m_hSpecificTarget, FIELD_EHANDLE ),
  762. DEFINE_FIELD( m_strHint, FIELD_STRING ),
  763. DEFINE_FIELD( m_flLastHomingSpeed, FIELD_FLOAT ),
  764. // DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ),
  765. DEFINE_THINKFUNC( BeginSeekThink ),
  766. DEFINE_THINKFUNC( AugerStartThink ),
  767. DEFINE_THINKFUNC( ExplodeThink ),
  768. DEFINE_THINKFUNC( APCSeekThink ),
  769. DEFINE_FUNCTION( APCMissileTouch ),
  770. END_DATADESC()
  771. LINK_ENTITY_TO_CLASS( apc_missile, CAPCMissile );
  772. CAPCMissile *CAPCMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, CBaseEntity *pOwner )
  773. {
  774. CAPCMissile *pMissile = (CAPCMissile *)CBaseEntity::Create( "apc_missile", vecOrigin, vecAngles, pOwner );
  775. pMissile->SetOwnerEntity( pOwner );
  776. pMissile->Spawn();
  777. pMissile->SetAbsVelocity( vecVelocity );
  778. pMissile->AddFlag( FL_NOTARGET );
  779. pMissile->AddEffects( EF_NOSHADOW );
  780. return pMissile;
  781. }
  782. //-----------------------------------------------------------------------------
  783. // Constructor, destructor
  784. //-----------------------------------------------------------------------------
  785. CAPCMissile::CAPCMissile()
  786. {
  787. g_APCMissileList.Insert( this );
  788. }
  789. CAPCMissile::~CAPCMissile()
  790. {
  791. g_APCMissileList.Remove( this );
  792. }
  793. //-----------------------------------------------------------------------------
  794. // Shared initialization code
  795. //-----------------------------------------------------------------------------
  796. void CAPCMissile::Init()
  797. {
  798. SetMoveType( MOVETYPE_FLY );
  799. SetModel("models/weapons/w_missile.mdl");
  800. UTIL_SetSize( this, vec3_origin, vec3_origin );
  801. CreateSmokeTrail();
  802. SetTouch( &CAPCMissile::APCMissileTouch );
  803. m_flLastHomingSpeed = APC_HOMING_SPEED;
  804. CreateDangerSounds( true );
  805. if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
  806. {
  807. AddFlag( FL_AIMTARGET );
  808. }
  809. }
  810. //-----------------------------------------------------------------------------
  811. // For hitting a specific target
  812. //-----------------------------------------------------------------------------
  813. void CAPCMissile::AimAtSpecificTarget( CBaseEntity *pTarget )
  814. {
  815. m_hSpecificTarget = pTarget;
  816. }
  817. //-----------------------------------------------------------------------------
  818. // Purpose:
  819. // Input : *pOther -
  820. //-----------------------------------------------------------------------------
  821. void CAPCMissile::APCMissileTouch( CBaseEntity *pOther )
  822. {
  823. Assert( pOther );
  824. if ( !pOther->IsSolid() && !pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
  825. return;
  826. Explode();
  827. }
  828. //-----------------------------------------------------------------------------
  829. // Specialized version of the missile
  830. //-----------------------------------------------------------------------------
  831. void CAPCMissile::IgniteDelay( void )
  832. {
  833. m_flIgnitionTime = gpGlobals->curtime + 0.3f;
  834. SetThink( &CAPCMissile::BeginSeekThink );
  835. SetNextThink( m_flIgnitionTime );
  836. Init();
  837. AddSolidFlags( FSOLID_NOT_SOLID );
  838. }
  839. void CAPCMissile::AugerDelay( float flDelay )
  840. {
  841. m_flIgnitionTime = gpGlobals->curtime;
  842. SetThink( &CAPCMissile::AugerStartThink );
  843. SetNextThink( gpGlobals->curtime + flDelay );
  844. Init();
  845. DisableGuiding();
  846. }
  847. void CAPCMissile::AugerStartThink()
  848. {
  849. if ( m_hRocketTrail != NULL )
  850. {
  851. m_hRocketTrail->m_bDamaged = true;
  852. }
  853. m_flAugerTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );
  854. SetThink( &CAPCMissile::AugerThink );
  855. SetNextThink( gpGlobals->curtime );
  856. }
  857. void CAPCMissile::ExplodeDelay( float flDelay )
  858. {
  859. m_flIgnitionTime = gpGlobals->curtime;
  860. SetThink( &CAPCMissile::ExplodeThink );
  861. SetNextThink( gpGlobals->curtime + flDelay );
  862. Init();
  863. DisableGuiding();
  864. }
  865. void CAPCMissile::BeginSeekThink( void )
  866. {
  867. RemoveSolidFlags( FSOLID_NOT_SOLID );
  868. SetThink( &CAPCMissile::APCSeekThink );
  869. SetNextThink( gpGlobals->curtime );
  870. }
  871. void CAPCMissile::APCSeekThink( void )
  872. {
  873. BaseClass::SeekThink();
  874. bool bFoundDot = false;
  875. //If we can't find a dot to follow around then just send me wherever I'm facing so I can blow up in peace.
  876. for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext )
  877. {
  878. if ( !pEnt->IsOn() )
  879. continue;
  880. if ( pEnt->GetOwnerEntity() != GetOwnerEntity() )
  881. continue;
  882. bFoundDot = true;
  883. }
  884. if ( bFoundDot == false )
  885. {
  886. Vector vDir = GetAbsVelocity();
  887. VectorNormalize ( vDir );
  888. SetAbsVelocity( vDir * 800 );
  889. SetThink( NULL );
  890. }
  891. }
  892. void CAPCMissile::ExplodeThink()
  893. {
  894. DoExplosion();
  895. }
  896. //-----------------------------------------------------------------------------
  897. // Health lost at which augering starts
  898. //-----------------------------------------------------------------------------
  899. int CAPCMissile::AugerHealth()
  900. {
  901. return m_iMaxHealth - 25;
  902. }
  903. //-----------------------------------------------------------------------------
  904. // Health lost at which augering starts
  905. //-----------------------------------------------------------------------------
  906. void CAPCMissile::DisableGuiding()
  907. {
  908. m_bGuidingDisabled = true;
  909. }
  910. //-----------------------------------------------------------------------------
  911. // Guidance hints
  912. //-----------------------------------------------------------------------------
  913. void CAPCMissile::SetGuidanceHint( const char *pHintName )
  914. {
  915. m_strHint = MAKE_STRING( pHintName );
  916. }
  917. //-----------------------------------------------------------------------------
  918. // The actual explosion
  919. //-----------------------------------------------------------------------------
  920. void CAPCMissile::DoExplosion( void )
  921. {
  922. if ( GetWaterLevel() != 0 )
  923. {
  924. CEffectData data;
  925. data.m_vOrigin = WorldSpaceCenter();
  926. data.m_flMagnitude = 128;
  927. data.m_flScale = 128;
  928. data.m_fFlags = 0;
  929. DispatchEffect( "WaterSurfaceExplosion", data );
  930. }
  931. else
  932. {
  933. #ifdef HL2_EPISODIC
  934. ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, APC_MISSILE_DAMAGE, 100, true, 20000 );
  935. #else
  936. ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), APC_MISSILE_DAMAGE, 100, true, 20000 );
  937. #endif
  938. }
  939. }
  940. //-----------------------------------------------------------------------------
  941. // Purpose:
  942. //-----------------------------------------------------------------------------
  943. void CAPCMissile::ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition )
  944. {
  945. Vector vecTarget = pTarget->BodyTarget( vecShootPosition, false );
  946. float flShotSpeed = GetAbsVelocity().Length();
  947. if ( flShotSpeed == 0 )
  948. {
  949. *pLeadPosition = vecTarget;
  950. return;
  951. }
  952. Vector vecVelocity = pTarget->GetSmoothedVelocity();
  953. vecVelocity.z = 0.0f;
  954. float flTargetSpeed = VectorNormalize( vecVelocity );
  955. Vector vecDelta;
  956. VectorSubtract( vecShootPosition, vecTarget, vecDelta );
  957. float flTargetToShooter = VectorNormalize( vecDelta );
  958. float flCosTheta = DotProduct( vecDelta, vecVelocity );
  959. // Law of cosines... z^2 = x^2 + y^2 - 2xy cos Theta
  960. // where z = flShooterToPredictedTargetPosition = flShotSpeed * predicted time
  961. // x = flTargetSpeed * predicted time
  962. // y = flTargetToShooter
  963. // solve for predicted time using at^2 + bt + c = 0, t = (-b +/- sqrt( b^2 - 4ac )) / 2a
  964. float a = flTargetSpeed * flTargetSpeed - flShotSpeed * flShotSpeed;
  965. float b = -2.0f * flTargetToShooter * flCosTheta * flTargetSpeed;
  966. float c = flTargetToShooter * flTargetToShooter;
  967. float flDiscrim = b*b - 4*a*c;
  968. if (flDiscrim < 0)
  969. {
  970. *pLeadPosition = vecTarget;
  971. return;
  972. }
  973. flDiscrim = sqrt(flDiscrim);
  974. float t = (-b + flDiscrim) / (2.0f * a);
  975. float t2 = (-b - flDiscrim) / (2.0f * a);
  976. if ( t < t2 )
  977. {
  978. t = t2;
  979. }
  980. if ( t <= 0.0f )
  981. {
  982. *pLeadPosition = vecTarget;
  983. return;
  984. }
  985. VectorMA( vecTarget, flTargetSpeed * t, vecVelocity, *pLeadPosition );
  986. }
  987. //-----------------------------------------------------------------------------
  988. // Purpose:
  989. //-----------------------------------------------------------------------------
  990. void CAPCMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed )
  991. {
  992. if ( m_bGuidingDisabled )
  993. {
  994. *pActualDotPosition = GetAbsOrigin();
  995. *pHomingSpeed = 0.0f;
  996. m_flLastHomingSpeed = *pHomingSpeed;
  997. return;
  998. }
  999. if ( ( m_strHint != NULL_STRING ) && (!m_hSpecificTarget) )
  1000. {
  1001. Vector vecOrigin, vecVelocity;
  1002. CBaseEntity *pTarget = pLaserDot->GetTargetEntity();
  1003. if ( pTarget )
  1004. {
  1005. vecOrigin = pTarget->BodyTarget( GetAbsOrigin(), false );
  1006. vecVelocity = pTarget->GetSmoothedVelocity();
  1007. }
  1008. else
  1009. {
  1010. vecOrigin = pLaserDot->GetChasePosition();
  1011. vecVelocity = vec3_origin;
  1012. }
  1013. m_hSpecificTarget = CInfoAPCMissileHint::FindAimTarget( this, STRING( m_strHint ), vecOrigin, vecVelocity );
  1014. }
  1015. CBaseEntity *pLaserTarget = m_hSpecificTarget ? m_hSpecificTarget.Get() : pLaserDot->GetTargetEntity();
  1016. if ( !pLaserTarget )
  1017. {
  1018. BaseClass::ComputeActualDotPosition( pLaserDot, pActualDotPosition, pHomingSpeed );
  1019. m_flLastHomingSpeed = *pHomingSpeed;
  1020. return;
  1021. }
  1022. if ( pLaserTarget->ClassMatches( "npc_bullseye" ) )
  1023. {
  1024. if ( m_flLastHomingSpeed != RPG_HOMING_SPEED )
  1025. {
  1026. if (m_flLastHomingSpeed > RPG_HOMING_SPEED)
  1027. {
  1028. m_flLastHomingSpeed -= HOMING_SPEED_ACCEL * UTIL_GetSimulationInterval();
  1029. if ( m_flLastHomingSpeed < RPG_HOMING_SPEED )
  1030. {
  1031. m_flLastHomingSpeed = RPG_HOMING_SPEED;
  1032. }
  1033. }
  1034. else
  1035. {
  1036. m_flLastHomingSpeed += HOMING_SPEED_ACCEL * UTIL_GetSimulationInterval();
  1037. if ( m_flLastHomingSpeed > RPG_HOMING_SPEED )
  1038. {
  1039. m_flLastHomingSpeed = RPG_HOMING_SPEED;
  1040. }
  1041. }
  1042. }
  1043. *pHomingSpeed = m_flLastHomingSpeed;
  1044. *pActualDotPosition = pLaserTarget->WorldSpaceCenter();
  1045. return;
  1046. }
  1047. Vector vLaserStart;
  1048. GetShootPosition( pLaserDot, &vLaserStart );
  1049. *pHomingSpeed = APC_LAUNCH_HOMING_SPEED;
  1050. //Get the laser's vector
  1051. Vector vecTargetPosition = pLaserTarget->BodyTarget( GetAbsOrigin(), false );
  1052. // Compute leading position
  1053. Vector vecLeadPosition;
  1054. ComputeLeadingPosition( GetAbsOrigin(), pLaserTarget, &vecLeadPosition );
  1055. Vector vecTargetToMissile, vecTargetToShooter;
  1056. VectorSubtract( GetAbsOrigin(), vecTargetPosition, vecTargetToMissile );
  1057. VectorSubtract( vLaserStart, vecTargetPosition, vecTargetToShooter );
  1058. *pActualDotPosition = vecLeadPosition;
  1059. float flMinHomingDistance = MIN_HOMING_DISTANCE;
  1060. float flMaxHomingDistance = MAX_HOMING_DISTANCE;
  1061. float flBlendTime = gpGlobals->curtime - m_flIgnitionTime;
  1062. if ( flBlendTime > DOWNWARD_BLEND_TIME_START )
  1063. {
  1064. if ( m_flReachedTargetTime != 0.0f )
  1065. {
  1066. *pHomingSpeed = APC_HOMING_SPEED;
  1067. float flDeltaTime = clamp( gpGlobals->curtime - m_flReachedTargetTime, 0.0f, CORRECTION_TIME );
  1068. *pHomingSpeed = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, 0.2f, *pHomingSpeed );
  1069. flMinHomingDistance = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, MIN_NEAR_HOMING_DISTANCE, flMinHomingDistance );
  1070. flMaxHomingDistance = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, MAX_NEAR_HOMING_DISTANCE, flMaxHomingDistance );
  1071. }
  1072. else
  1073. {
  1074. flMinHomingDistance = MIN_NEAR_HOMING_DISTANCE;
  1075. flMaxHomingDistance = MAX_NEAR_HOMING_DISTANCE;
  1076. Vector vecDelta;
  1077. VectorSubtract( GetAbsOrigin(), *pActualDotPosition, vecDelta );
  1078. if ( vecDelta.z > MIN_HEIGHT_DIFFERENCE )
  1079. {
  1080. float flClampedHeight = clamp( vecDelta.z, MIN_HEIGHT_DIFFERENCE, MAX_HEIGHT_DIFFERENCE );
  1081. float flHeightAdjustFactor = SimpleSplineRemapVal( flClampedHeight, MIN_HEIGHT_DIFFERENCE, MAX_HEIGHT_DIFFERENCE, 0.0f, 1.0f );
  1082. vecDelta.z = 0.0f;
  1083. float flDist = VectorNormalize( vecDelta );
  1084. float flForwardOffset = 2000.0f;
  1085. if ( flDist > flForwardOffset )
  1086. {
  1087. Vector vecNewPosition;
  1088. VectorMA( GetAbsOrigin(), -flForwardOffset, vecDelta, vecNewPosition );
  1089. vecNewPosition.z = pActualDotPosition->z;
  1090. VectorLerp( *pActualDotPosition, vecNewPosition, flHeightAdjustFactor, *pActualDotPosition );
  1091. }
  1092. }
  1093. else
  1094. {
  1095. m_flReachedTargetTime = gpGlobals->curtime;
  1096. }
  1097. }
  1098. // Allows for players right at the edge of rocket range to be threatened
  1099. if ( flBlendTime > 0.6f )
  1100. {
  1101. float flTargetLength = GetAbsOrigin().DistTo( pLaserTarget->WorldSpaceCenter() );
  1102. flTargetLength = clamp( flTargetLength, flMinHomingDistance, flMaxHomingDistance );
  1103. *pHomingSpeed = SimpleSplineRemapVal( flTargetLength, flMaxHomingDistance, flMinHomingDistance, *pHomingSpeed, 0.01f );
  1104. }
  1105. }
  1106. float flDot = DotProduct2D( vecTargetToShooter.AsVector2D(), vecTargetToMissile.AsVector2D() );
  1107. if ( ( flDot < 0 ) || m_bGuidingDisabled )
  1108. {
  1109. *pHomingSpeed = 0.0f;
  1110. }
  1111. m_flLastHomingSpeed = *pHomingSpeed;
  1112. // NDebugOverlay::Line( vecLeadPosition, GetAbsOrigin(), 0, 255, 0, true, 0.05f );
  1113. // NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );
  1114. // NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );
  1115. }
  1116. #define RPG_BEAM_SPRITE "effects/laser1_noz.vmt"
  1117. #define RPG_LASER_SPRITE "sprites/redglow1.vmt"
  1118. //=============================================================================
  1119. // RPG
  1120. //=============================================================================
  1121. BEGIN_DATADESC( CWeaponRPG )
  1122. DEFINE_FIELD( m_bInitialStateUpdate,FIELD_BOOLEAN ),
  1123. DEFINE_FIELD( m_bGuiding, FIELD_BOOLEAN ),
  1124. DEFINE_FIELD( m_vecNPCLaserDot, FIELD_POSITION_VECTOR ),
  1125. DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ),
  1126. DEFINE_FIELD( m_hMissile, FIELD_EHANDLE ),
  1127. DEFINE_FIELD( m_hLaserMuzzleSprite, FIELD_EHANDLE ),
  1128. DEFINE_FIELD( m_hLaserBeam, FIELD_EHANDLE ),
  1129. DEFINE_FIELD( m_bHideGuiding, FIELD_BOOLEAN ),
  1130. END_DATADESC()
  1131. IMPLEMENT_SERVERCLASS_ST(CWeaponRPG, DT_WeaponRPG)
  1132. END_SEND_TABLE()
  1133. LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG );
  1134. PRECACHE_WEAPON_REGISTER(weapon_rpg);
  1135. acttable_t CWeaponRPG::m_acttable[] =
  1136. {
  1137. { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true },
  1138. { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true },
  1139. { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true },
  1140. { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_RPG, true },
  1141. { ACT_IDLE, ACT_IDLE_RPG, true },
  1142. { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_RPG, true },
  1143. { ACT_WALK, ACT_WALK_RPG, true },
  1144. { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RPG, true },
  1145. { ACT_RUN, ACT_RUN_RPG, true },
  1146. { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true },
  1147. { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true },
  1148. };
  1149. IMPLEMENT_ACTTABLE(CWeaponRPG);
  1150. //-----------------------------------------------------------------------------
  1151. // Purpose:
  1152. //-----------------------------------------------------------------------------
  1153. CWeaponRPG::CWeaponRPG()
  1154. {
  1155. m_bReloadsSingly = true;
  1156. m_bInitialStateUpdate= false;
  1157. m_bHideGuiding = false;
  1158. m_bGuiding = false;
  1159. m_fMinRange1 = m_fMinRange2 = 40*12;
  1160. m_fMaxRange1 = m_fMaxRange2 = 500*12;
  1161. }
  1162. //-----------------------------------------------------------------------------
  1163. // Purpose:
  1164. //-----------------------------------------------------------------------------
  1165. CWeaponRPG::~CWeaponRPG()
  1166. {
  1167. if ( m_hLaserDot != NULL )
  1168. {
  1169. UTIL_Remove( m_hLaserDot );
  1170. m_hLaserDot = NULL;
  1171. }
  1172. if ( m_hLaserMuzzleSprite )
  1173. {
  1174. UTIL_Remove( m_hLaserMuzzleSprite );
  1175. }
  1176. if ( m_hLaserBeam )
  1177. {
  1178. UTIL_Remove( m_hLaserBeam );
  1179. }
  1180. }
  1181. //-----------------------------------------------------------------------------
  1182. // Purpose:
  1183. //-----------------------------------------------------------------------------
  1184. void CWeaponRPG::Precache( void )
  1185. {
  1186. BaseClass::Precache();
  1187. PrecacheScriptSound( "Missile.Ignite" );
  1188. PrecacheScriptSound( "Missile.Accelerate" );
  1189. // Laser dot...
  1190. PrecacheModel( "sprites/redglow1.vmt" );
  1191. PrecacheModel( RPG_LASER_SPRITE );
  1192. PrecacheModel( RPG_BEAM_SPRITE );
  1193. UTIL_PrecacheOther( "rpg_missile" );
  1194. }
  1195. //-----------------------------------------------------------------------------
  1196. // Purpose:
  1197. //-----------------------------------------------------------------------------
  1198. void CWeaponRPG::Activate( void )
  1199. {
  1200. BaseClass::Activate();
  1201. // Restore the laser pointer after transition
  1202. if ( m_bGuiding )
  1203. {
  1204. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1205. if ( pOwner == NULL )
  1206. return;
  1207. if ( pOwner->GetActiveWeapon() == this )
  1208. {
  1209. StartGuiding();
  1210. }
  1211. }
  1212. }
  1213. //-----------------------------------------------------------------------------
  1214. // Purpose:
  1215. // Input : *pEvent -
  1216. // *pOperator -
  1217. //-----------------------------------------------------------------------------
  1218. void CWeaponRPG::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  1219. {
  1220. switch( pEvent->event )
  1221. {
  1222. case EVENT_WEAPON_SMG1:
  1223. {
  1224. if ( m_hMissile != NULL )
  1225. return;
  1226. Vector muzzlePoint;
  1227. QAngle vecAngles;
  1228. muzzlePoint = GetOwner()->Weapon_ShootPosition();
  1229. CAI_BaseNPC *npc = pOperator->MyNPCPointer();
  1230. ASSERT( npc != NULL );
  1231. Vector vecShootDir = npc->GetActualShootTrajectory( muzzlePoint );
  1232. // look for a better launch location
  1233. Vector altLaunchPoint;
  1234. if (GetAttachment( "missile", altLaunchPoint ))
  1235. {
  1236. // check to see if it's relativly free
  1237. trace_t tr;
  1238. AI_TraceHull( altLaunchPoint, altLaunchPoint + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
  1239. if( tr.fraction == 1.0)
  1240. {
  1241. muzzlePoint = altLaunchPoint;
  1242. }
  1243. }
  1244. VectorAngles( vecShootDir, vecAngles );
  1245. m_hMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
  1246. m_hMissile->m_hOwner = this;
  1247. // NPCs always get a grace period
  1248. m_hMissile->SetGracePeriod( 0.5 );
  1249. pOperator->DoMuzzleFlash();
  1250. WeaponSound( SINGLE_NPC );
  1251. // Make sure our laserdot is off
  1252. m_bGuiding = false;
  1253. if ( m_hLaserDot )
  1254. {
  1255. m_hLaserDot->TurnOff();
  1256. }
  1257. }
  1258. break;
  1259. default:
  1260. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  1261. break;
  1262. }
  1263. }
  1264. //-----------------------------------------------------------------------------
  1265. // Purpose:
  1266. //-----------------------------------------------------------------------------
  1267. bool CWeaponRPG::HasAnyAmmo( void )
  1268. {
  1269. if ( m_hMissile != NULL )
  1270. return true;
  1271. return BaseClass::HasAnyAmmo();
  1272. }
  1273. //-----------------------------------------------------------------------------
  1274. // Purpose:
  1275. // Output : Returns true on success, false on failure.
  1276. //-----------------------------------------------------------------------------
  1277. bool CWeaponRPG::WeaponShouldBeLowered( void )
  1278. {
  1279. // Lower us if we're out of ammo
  1280. if ( !HasAnyAmmo() )
  1281. return true;
  1282. return BaseClass::WeaponShouldBeLowered();
  1283. }
  1284. //-----------------------------------------------------------------------------
  1285. // Purpose:
  1286. //-----------------------------------------------------------------------------
  1287. void CWeaponRPG::PrimaryAttack( void )
  1288. {
  1289. // Can't have an active missile out
  1290. if ( m_hMissile != NULL )
  1291. return;
  1292. // Can't be reloading
  1293. if ( GetActivity() == ACT_VM_RELOAD )
  1294. return;
  1295. Vector vecOrigin;
  1296. Vector vecForward;
  1297. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
  1298. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1299. if ( pOwner == NULL )
  1300. return;
  1301. Vector vForward, vRight, vUp;
  1302. pOwner->EyeVectors( &vForward, &vRight, &vUp );
  1303. Vector muzzlePoint = pOwner->Weapon_ShootPosition() + vForward * 12.0f + vRight * 6.0f + vUp * -3.0f;
  1304. QAngle vecAngles;
  1305. VectorAngles( vForward, vecAngles );
  1306. m_hMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
  1307. m_hMissile->m_hOwner = this;
  1308. // If the shot is clear to the player, give the missile a grace period
  1309. trace_t tr;
  1310. Vector vecEye = pOwner->EyePosition();
  1311. UTIL_TraceLine( vecEye, vecEye + vForward * 128, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  1312. if ( tr.fraction == 1.0 )
  1313. {
  1314. m_hMissile->SetGracePeriod( 0.3 );
  1315. }
  1316. DecrementAmmo( GetOwner() );
  1317. // Register a muzzleflash for the AI
  1318. pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
  1319. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  1320. WeaponSound( SINGLE );
  1321. pOwner->RumbleEffect( RUMBLE_SHOTGUN_SINGLE, 0, RUMBLE_FLAG_RESTART );
  1322. m_iPrimaryAttacks++;
  1323. gamestats->Event_WeaponFired( pOwner, true, GetClassname() );
  1324. CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON );
  1325. // Check to see if we should trigger any RPG firing triggers
  1326. int iCount = g_hWeaponFireTriggers.Count();
  1327. for ( int i = 0; i < iCount; i++ )
  1328. {
  1329. if ( g_hWeaponFireTriggers[i]->IsTouching( pOwner ) )
  1330. {
  1331. if ( FClassnameIs( g_hWeaponFireTriggers[i], "trigger_rpgfire" ) )
  1332. {
  1333. g_hWeaponFireTriggers[i]->ActivateMultiTrigger( pOwner );
  1334. }
  1335. }
  1336. }
  1337. if( hl2_episodic.GetBool() )
  1338. {
  1339. CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  1340. int nAIs = g_AI_Manager.NumAIs();
  1341. string_t iszStriderClassname = AllocPooledString( "npc_strider" );
  1342. for ( int i = 0; i < nAIs; i++ )
  1343. {
  1344. if( ppAIs[ i ]->m_iClassname == iszStriderClassname )
  1345. {
  1346. ppAIs[ i ]->DispatchInteraction( g_interactionPlayerLaunchedRPG, NULL, m_hMissile );
  1347. }
  1348. }
  1349. }
  1350. }
  1351. //-----------------------------------------------------------------------------
  1352. // Purpose:
  1353. // Input : *pOwner -
  1354. //-----------------------------------------------------------------------------
  1355. void CWeaponRPG::DecrementAmmo( CBaseCombatCharacter *pOwner )
  1356. {
  1357. // Take away our primary ammo type
  1358. pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
  1359. }
  1360. //-----------------------------------------------------------------------------
  1361. // Purpose:
  1362. // Input : state -
  1363. //-----------------------------------------------------------------------------
  1364. void CWeaponRPG::SuppressGuiding( bool state )
  1365. {
  1366. m_bHideGuiding = state;
  1367. if ( m_hLaserDot == NULL )
  1368. {
  1369. StartGuiding();
  1370. //STILL!?
  1371. if ( m_hLaserDot == NULL )
  1372. return;
  1373. }
  1374. if ( state )
  1375. {
  1376. m_hLaserDot->TurnOff();
  1377. }
  1378. else
  1379. {
  1380. m_hLaserDot->TurnOn();
  1381. }
  1382. }
  1383. //-----------------------------------------------------------------------------
  1384. // Purpose: Override this if we're guiding a missile currently
  1385. // Output : Returns true on success, false on failure.
  1386. //-----------------------------------------------------------------------------
  1387. bool CWeaponRPG::Lower( void )
  1388. {
  1389. if ( m_hMissile != NULL )
  1390. return false;
  1391. return BaseClass::Lower();
  1392. }
  1393. //-----------------------------------------------------------------------------
  1394. // Purpose:
  1395. //-----------------------------------------------------------------------------
  1396. void CWeaponRPG::ItemPostFrame( void )
  1397. {
  1398. BaseClass::ItemPostFrame();
  1399. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  1400. if ( pPlayer == NULL )
  1401. return;
  1402. //If we're pulling the weapon out for the first time, wait to draw the laser
  1403. if ( ( m_bInitialStateUpdate ) && ( GetActivity() != ACT_VM_DRAW ) )
  1404. {
  1405. StartGuiding();
  1406. m_bInitialStateUpdate = false;
  1407. }
  1408. // Supress our guiding effects if we're lowered
  1409. if ( GetIdealActivity() == ACT_VM_IDLE_LOWERED || GetIdealActivity() == ACT_VM_RELOAD )
  1410. {
  1411. SuppressGuiding();
  1412. }
  1413. else
  1414. {
  1415. SuppressGuiding( false );
  1416. }
  1417. //Player has toggled guidance state
  1418. //Adrian: Players are not allowed to remove the laser guide in single player anymore, bye!
  1419. if ( g_pGameRules->IsMultiplayer() == true )
  1420. {
  1421. if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
  1422. {
  1423. ToggleGuiding();
  1424. }
  1425. }
  1426. //Move the laser
  1427. UpdateLaserPosition();
  1428. UpdateLaserEffects();
  1429. }
  1430. //-----------------------------------------------------------------------------
  1431. // Purpose:
  1432. // Output : Vector
  1433. //-----------------------------------------------------------------------------
  1434. Vector CWeaponRPG::GetLaserPosition( void )
  1435. {
  1436. CreateLaserPointer();
  1437. if ( m_hLaserDot != NULL )
  1438. return m_hLaserDot->GetAbsOrigin();
  1439. //FIXME: The laser dot sprite is not active, this code should not be allowed!
  1440. assert(0);
  1441. return vec3_origin;
  1442. }
  1443. //-----------------------------------------------------------------------------
  1444. // Purpose: NPC RPG users cheat and directly set the laser pointer's origin
  1445. // Input : &vecTarget -
  1446. //-----------------------------------------------------------------------------
  1447. void CWeaponRPG::UpdateNPCLaserPosition( const Vector &vecTarget )
  1448. {
  1449. CreateLaserPointer();
  1450. // Turn the laserdot on
  1451. m_bGuiding = true;
  1452. m_hLaserDot->TurnOn();
  1453. Vector muzzlePoint = GetOwner()->Weapon_ShootPosition();
  1454. Vector vecDir = (vecTarget - muzzlePoint);
  1455. VectorNormalize( vecDir );
  1456. vecDir = muzzlePoint + ( vecDir * MAX_TRACE_LENGTH );
  1457. UpdateLaserPosition( muzzlePoint, vecDir );
  1458. SetNPCLaserPosition( vecTarget );
  1459. }
  1460. //-----------------------------------------------------------------------------
  1461. // Purpose:
  1462. //-----------------------------------------------------------------------------
  1463. void CWeaponRPG::SetNPCLaserPosition( const Vector &vecTarget )
  1464. {
  1465. m_vecNPCLaserDot = vecTarget;
  1466. //NDebugOverlay::Box( m_vecNPCLaserDot, -Vector(10,10,10), Vector(10,10,10), 255,0,0, 8, 3 );
  1467. }
  1468. //-----------------------------------------------------------------------------
  1469. // Purpose:
  1470. //-----------------------------------------------------------------------------
  1471. const Vector &CWeaponRPG::GetNPCLaserPosition( void )
  1472. {
  1473. return m_vecNPCLaserDot;
  1474. }
  1475. //-----------------------------------------------------------------------------
  1476. // Purpose:
  1477. // Output : Returns true if the rocket is being guided, false if it's dumb
  1478. //-----------------------------------------------------------------------------
  1479. bool CWeaponRPG::IsGuiding( void )
  1480. {
  1481. return m_bGuiding;
  1482. }
  1483. //-----------------------------------------------------------------------------
  1484. // Purpose:
  1485. // Output : Returns true on success, false on failure.
  1486. //-----------------------------------------------------------------------------
  1487. bool CWeaponRPG::Deploy( void )
  1488. {
  1489. m_bInitialStateUpdate = true;
  1490. return BaseClass::Deploy();
  1491. }
  1492. //-----------------------------------------------------------------------------
  1493. // Purpose:
  1494. //-----------------------------------------------------------------------------
  1495. bool CWeaponRPG::Holster( CBaseCombatWeapon *pSwitchingTo )
  1496. {
  1497. //Can't have an active missile out
  1498. if ( m_hMissile != NULL )
  1499. return false;
  1500. StopGuiding();
  1501. return BaseClass::Holster( pSwitchingTo );
  1502. }
  1503. //-----------------------------------------------------------------------------
  1504. // Purpose: Turn on the guiding laser
  1505. //-----------------------------------------------------------------------------
  1506. void CWeaponRPG::StartGuiding( void )
  1507. {
  1508. // Don't start back up if we're overriding this
  1509. if ( m_bHideGuiding )
  1510. return;
  1511. m_bGuiding = true;
  1512. WeaponSound(SPECIAL1);
  1513. CreateLaserPointer();
  1514. StartLaserEffects();
  1515. }
  1516. //-----------------------------------------------------------------------------
  1517. // Purpose: Turn off the guiding laser
  1518. //-----------------------------------------------------------------------------
  1519. void CWeaponRPG::StopGuiding( void )
  1520. {
  1521. m_bGuiding = false;
  1522. WeaponSound( SPECIAL2 );
  1523. StopLaserEffects();
  1524. // Kill the dot completely
  1525. if ( m_hLaserDot != NULL )
  1526. {
  1527. m_hLaserDot->TurnOff();
  1528. UTIL_Remove( m_hLaserDot );
  1529. m_hLaserDot = NULL;
  1530. }
  1531. }
  1532. //-----------------------------------------------------------------------------
  1533. // Purpose: Toggle the guiding laser
  1534. //-----------------------------------------------------------------------------
  1535. void CWeaponRPG::ToggleGuiding( void )
  1536. {
  1537. if ( IsGuiding() )
  1538. {
  1539. StopGuiding();
  1540. }
  1541. else
  1542. {
  1543. StartGuiding();
  1544. }
  1545. }
  1546. //-----------------------------------------------------------------------------
  1547. // Purpose:
  1548. //-----------------------------------------------------------------------------
  1549. void CWeaponRPG::Drop( const Vector &vecVelocity )
  1550. {
  1551. StopGuiding();
  1552. BaseClass::Drop( vecVelocity );
  1553. }
  1554. //-----------------------------------------------------------------------------
  1555. // Purpose:
  1556. //-----------------------------------------------------------------------------
  1557. void CWeaponRPG::UpdateLaserPosition( Vector vecMuzzlePos, Vector vecEndPos )
  1558. {
  1559. if ( vecMuzzlePos == vec3_origin || vecEndPos == vec3_origin )
  1560. {
  1561. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  1562. if ( !pPlayer )
  1563. return;
  1564. vecMuzzlePos = pPlayer->Weapon_ShootPosition();
  1565. Vector forward;
  1566. if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
  1567. {
  1568. forward = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
  1569. }
  1570. else
  1571. {
  1572. pPlayer->EyeVectors( &forward );
  1573. }
  1574. vecEndPos = vecMuzzlePos + ( forward * MAX_TRACE_LENGTH );
  1575. }
  1576. //Move the laser dot, if active
  1577. trace_t tr;
  1578. // Trace out for the endpoint
  1579. #ifdef PORTAL
  1580. g_bBulletPortalTrace = true;
  1581. Ray_t rayLaser;
  1582. rayLaser.Init( vecMuzzlePos, vecEndPos );
  1583. UTIL_Portal_TraceRay( rayLaser, (MASK_SHOT & ~CONTENTS_WINDOW), this, COLLISION_GROUP_NONE, &tr );
  1584. g_bBulletPortalTrace = false;
  1585. #else
  1586. UTIL_TraceLine( vecMuzzlePos, vecEndPos, (MASK_SHOT & ~CONTENTS_WINDOW), this, COLLISION_GROUP_NONE, &tr );
  1587. #endif
  1588. // Move the laser sprite
  1589. if ( m_hLaserDot != NULL )
  1590. {
  1591. Vector laserPos = tr.endpos;
  1592. m_hLaserDot->SetLaserPosition( laserPos, tr.plane.normal );
  1593. if ( tr.DidHitNonWorldEntity() )
  1594. {
  1595. CBaseEntity *pHit = tr.m_pEnt;
  1596. if ( ( pHit != NULL ) && ( pHit->m_takedamage ) )
  1597. {
  1598. m_hLaserDot->SetTargetEntity( pHit );
  1599. }
  1600. else
  1601. {
  1602. m_hLaserDot->SetTargetEntity( NULL );
  1603. }
  1604. }
  1605. else
  1606. {
  1607. m_hLaserDot->SetTargetEntity( NULL );
  1608. }
  1609. }
  1610. }
  1611. //-----------------------------------------------------------------------------
  1612. // Purpose:
  1613. //-----------------------------------------------------------------------------
  1614. void CWeaponRPG::CreateLaserPointer( void )
  1615. {
  1616. if ( m_hLaserDot != NULL )
  1617. return;
  1618. m_hLaserDot = CLaserDot::Create( GetAbsOrigin(), GetOwnerEntity() );
  1619. m_hLaserDot->TurnOff();
  1620. UpdateLaserPosition();
  1621. }
  1622. //-----------------------------------------------------------------------------
  1623. // Purpose:
  1624. //-----------------------------------------------------------------------------
  1625. void CWeaponRPG::NotifyRocketDied( void )
  1626. {
  1627. m_hMissile = NULL;
  1628. Reload();
  1629. }
  1630. //-----------------------------------------------------------------------------
  1631. // Purpose:
  1632. //-----------------------------------------------------------------------------
  1633. bool CWeaponRPG::Reload( void )
  1634. {
  1635. CBaseCombatCharacter *pOwner = GetOwner();
  1636. if ( pOwner == NULL )
  1637. return false;
  1638. if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
  1639. return false;
  1640. WeaponSound( RELOAD );
  1641. SendWeaponAnim( ACT_VM_RELOAD );
  1642. return true;
  1643. }
  1644. //-----------------------------------------------------------------------------
  1645. //-----------------------------------------------------------------------------
  1646. bool CWeaponRPG::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions )
  1647. {
  1648. bool bResult = BaseClass::WeaponLOSCondition( ownerPos, targetPos, bSetConditions );
  1649. if( bResult )
  1650. {
  1651. CAI_BaseNPC* npcOwner = GetOwner()->MyNPCPointer();
  1652. if( npcOwner )
  1653. {
  1654. trace_t tr;
  1655. Vector vecRelativeShootPosition;
  1656. VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition );
  1657. Vector vecMuzzle = ownerPos + vecRelativeShootPosition;
  1658. Vector vecShootDir = npcOwner->GetActualShootTrajectory( vecMuzzle );
  1659. // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out.
  1660. AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
  1661. if( tr.fraction != 1.0f )
  1662. bResult = false;
  1663. }
  1664. }
  1665. return bResult;
  1666. }
  1667. //-----------------------------------------------------------------------------
  1668. // Purpose:
  1669. // Input : flDot -
  1670. // flDist -
  1671. // Output : int
  1672. //-----------------------------------------------------------------------------
  1673. int CWeaponRPG::WeaponRangeAttack1Condition( float flDot, float flDist )
  1674. {
  1675. if ( m_hMissile != NULL )
  1676. return 0;
  1677. // Ignore vertical distance when doing our RPG distance calculations
  1678. CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
  1679. if ( pNPC )
  1680. {
  1681. CBaseEntity *pEnemy = pNPC->GetEnemy();
  1682. Vector vecToTarget = (pEnemy->GetAbsOrigin() - pNPC->GetAbsOrigin());
  1683. vecToTarget.z = 0;
  1684. flDist = vecToTarget.Length();
  1685. }
  1686. if ( flDist < MIN( m_fMinRange1, m_fMinRange2 ) )
  1687. return COND_TOO_CLOSE_TO_ATTACK;
  1688. if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  1689. return 0;
  1690. // See if there's anyone in the way!
  1691. CAI_BaseNPC *pOwner = GetOwner()->MyNPCPointer();
  1692. ASSERT( pOwner != NULL );
  1693. if( pOwner )
  1694. {
  1695. // Make sure I don't shoot the world!
  1696. trace_t tr;
  1697. Vector vecMuzzle = pOwner->Weapon_ShootPosition();
  1698. Vector vecShootDir = pOwner->GetActualShootTrajectory( vecMuzzle );
  1699. // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out.
  1700. AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
  1701. if( tr.fraction != 1.0 )
  1702. {
  1703. return COND_WEAPON_SIGHT_OCCLUDED;
  1704. }
  1705. }
  1706. return COND_CAN_RANGE_ATTACK1;
  1707. }
  1708. //-----------------------------------------------------------------------------
  1709. // Purpose: Start the effects on the viewmodel of the RPG
  1710. //-----------------------------------------------------------------------------
  1711. void CWeaponRPG::StartLaserEffects( void )
  1712. {
  1713. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1714. if ( pOwner == NULL )
  1715. return;
  1716. CBaseViewModel *pBeamEnt = static_cast<CBaseViewModel *>(pOwner->GetViewModel());
  1717. if ( m_hLaserBeam == NULL )
  1718. {
  1719. m_hLaserBeam = CBeam::BeamCreate( RPG_BEAM_SPRITE, 1.0f );
  1720. if ( m_hLaserBeam == NULL )
  1721. {
  1722. // We were unable to create the beam
  1723. Assert(0);
  1724. return;
  1725. }
  1726. m_hLaserBeam->EntsInit( pBeamEnt, pBeamEnt );
  1727. int startAttachment = LookupAttachment( "laser" );
  1728. int endAttachment = LookupAttachment( "laser_end" );
  1729. m_hLaserBeam->FollowEntity( pBeamEnt );
  1730. m_hLaserBeam->SetStartAttachment( startAttachment );
  1731. m_hLaserBeam->SetEndAttachment( endAttachment );
  1732. m_hLaserBeam->SetNoise( 0 );
  1733. m_hLaserBeam->SetColor( 255, 0, 0 );
  1734. m_hLaserBeam->SetScrollRate( 0 );
  1735. m_hLaserBeam->SetWidth( 0.5f );
  1736. m_hLaserBeam->SetEndWidth( 0.5f );
  1737. m_hLaserBeam->SetBrightness( 128 );
  1738. m_hLaserBeam->SetBeamFlags( SF_BEAM_SHADEIN );
  1739. #ifdef PORTAL
  1740. m_hLaserBeam->m_bDrawInMainRender = true;
  1741. m_hLaserBeam->m_bDrawInPortalRender = false;
  1742. #endif
  1743. }
  1744. else
  1745. {
  1746. m_hLaserBeam->SetBrightness( 128 );
  1747. }
  1748. if ( m_hLaserMuzzleSprite == NULL )
  1749. {
  1750. m_hLaserMuzzleSprite = CSprite::SpriteCreate( RPG_LASER_SPRITE, GetAbsOrigin(), false );
  1751. if ( m_hLaserMuzzleSprite == NULL )
  1752. {
  1753. // We were unable to create the sprite
  1754. Assert(0);
  1755. return;
  1756. }
  1757. #ifdef PORTAL
  1758. m_hLaserMuzzleSprite->m_bDrawInMainRender = true;
  1759. m_hLaserMuzzleSprite->m_bDrawInPortalRender = false;
  1760. #endif
  1761. m_hLaserMuzzleSprite->SetAttachment( pOwner->GetViewModel(), LookupAttachment( "laser" ) );
  1762. m_hLaserMuzzleSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
  1763. m_hLaserMuzzleSprite->SetBrightness( 255, 0.5f );
  1764. m_hLaserMuzzleSprite->SetScale( 0.25f, 0.5f );
  1765. m_hLaserMuzzleSprite->TurnOn();
  1766. }
  1767. else
  1768. {
  1769. m_hLaserMuzzleSprite->TurnOn();
  1770. m_hLaserMuzzleSprite->SetScale( 0.25f, 0.25f );
  1771. m_hLaserMuzzleSprite->SetBrightness( 255 );
  1772. }
  1773. }
  1774. //-----------------------------------------------------------------------------
  1775. // Purpose: Stop the effects on the viewmodel of the RPG
  1776. //-----------------------------------------------------------------------------
  1777. void CWeaponRPG::StopLaserEffects( void )
  1778. {
  1779. if ( m_hLaserBeam != NULL )
  1780. {
  1781. m_hLaserBeam->SetBrightness( 0 );
  1782. }
  1783. if ( m_hLaserMuzzleSprite != NULL )
  1784. {
  1785. m_hLaserMuzzleSprite->SetScale( 0.01f );
  1786. m_hLaserMuzzleSprite->SetBrightness( 0, 0.5f );
  1787. }
  1788. }
  1789. //-----------------------------------------------------------------------------
  1790. // Purpose: Pulse all the effects to make them more... well, laser-like
  1791. //-----------------------------------------------------------------------------
  1792. void CWeaponRPG::UpdateLaserEffects( void )
  1793. {
  1794. if ( !m_bGuiding )
  1795. return;
  1796. if ( m_hLaserBeam != NULL )
  1797. {
  1798. m_hLaserBeam->SetBrightness( 128 + random->RandomInt( -8, 8 ) );
  1799. }
  1800. if ( m_hLaserMuzzleSprite != NULL )
  1801. {
  1802. m_hLaserMuzzleSprite->SetScale( 0.1f + random->RandomFloat( -0.025f, 0.025f ) );
  1803. }
  1804. }
  1805. //=============================================================================
  1806. // Laser Dot
  1807. //=============================================================================
  1808. LINK_ENTITY_TO_CLASS( env_laserdot, CLaserDot );
  1809. BEGIN_DATADESC( CLaserDot )
  1810. DEFINE_FIELD( m_vecSurfaceNormal, FIELD_VECTOR ),
  1811. DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
  1812. DEFINE_FIELD( m_bVisibleLaserDot, FIELD_BOOLEAN ),
  1813. DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ),
  1814. //DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ), // don't save - regenerated by constructor
  1815. DEFINE_THINKFUNC( LaserThink ),
  1816. END_DATADESC()
  1817. //-----------------------------------------------------------------------------
  1818. // Finds missiles in cone
  1819. //-----------------------------------------------------------------------------
  1820. CBaseEntity *CreateLaserDot( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
  1821. {
  1822. return CLaserDot::Create( origin, pOwner, bVisibleDot );
  1823. }
  1824. void SetLaserDotTarget( CBaseEntity *pLaserDot, CBaseEntity *pTarget )
  1825. {
  1826. CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot );
  1827. pDot->SetTargetEntity( pTarget );
  1828. }
  1829. void EnableLaserDot( CBaseEntity *pLaserDot, bool bEnable )
  1830. {
  1831. CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot );
  1832. if ( bEnable )
  1833. {
  1834. pDot->TurnOn();
  1835. }
  1836. else
  1837. {
  1838. pDot->TurnOff();
  1839. }
  1840. }
  1841. CLaserDot::CLaserDot( void )
  1842. {
  1843. m_hTargetEnt = NULL;
  1844. m_bIsOn = true;
  1845. g_LaserDotList.Insert( this );
  1846. }
  1847. CLaserDot::~CLaserDot( void )
  1848. {
  1849. g_LaserDotList.Remove( this );
  1850. }
  1851. //-----------------------------------------------------------------------------
  1852. // Purpose:
  1853. // Input : &origin -
  1854. // Output : CLaserDot
  1855. //-----------------------------------------------------------------------------
  1856. CLaserDot *CLaserDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
  1857. {
  1858. CLaserDot *pLaserDot = (CLaserDot *) CBaseEntity::Create( "env_laserdot", origin, QAngle(0,0,0) );
  1859. if ( pLaserDot == NULL )
  1860. return NULL;
  1861. pLaserDot->m_bVisibleLaserDot = bVisibleDot;
  1862. pLaserDot->SetMoveType( MOVETYPE_NONE );
  1863. pLaserDot->AddSolidFlags( FSOLID_NOT_SOLID );
  1864. pLaserDot->AddEffects( EF_NOSHADOW );
  1865. UTIL_SetSize( pLaserDot, vec3_origin, vec3_origin );
  1866. //Create the graphic
  1867. pLaserDot->SpriteInit( "sprites/redglow1.vmt", origin );
  1868. pLaserDot->SetName( AllocPooledString("TEST") );
  1869. pLaserDot->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
  1870. pLaserDot->SetScale( 0.5f );
  1871. pLaserDot->SetOwnerEntity( pOwner );
  1872. pLaserDot->SetContextThink( &CLaserDot::LaserThink, gpGlobals->curtime + 0.1f, g_pLaserDotThink );
  1873. pLaserDot->SetSimulatedEveryTick( true );
  1874. if ( !bVisibleDot )
  1875. {
  1876. pLaserDot->MakeInvisible();
  1877. }
  1878. return pLaserDot;
  1879. }
  1880. //-----------------------------------------------------------------------------
  1881. // Purpose:
  1882. //-----------------------------------------------------------------------------
  1883. void CLaserDot::LaserThink( void )
  1884. {
  1885. SetNextThink( gpGlobals->curtime + 0.05f, g_pLaserDotThink );
  1886. if ( GetOwnerEntity() == NULL )
  1887. return;
  1888. Vector viewDir = GetAbsOrigin() - GetOwnerEntity()->GetAbsOrigin();
  1889. float dist = VectorNormalize( viewDir );
  1890. float scale = RemapVal( dist, 32, 1024, 0.01f, 0.5f );
  1891. float scaleOffs = random->RandomFloat( -scale * 0.25f, scale * 0.25f );
  1892. scale = clamp( scale + scaleOffs, 0.1f, 32.0f );
  1893. SetScale( scale );
  1894. }
  1895. void CLaserDot::SetLaserPosition( const Vector &origin, const Vector &normal )
  1896. {
  1897. SetAbsOrigin( origin );
  1898. m_vecSurfaceNormal = normal;
  1899. }
  1900. Vector CLaserDot::GetChasePosition()
  1901. {
  1902. return GetAbsOrigin() - m_vecSurfaceNormal * 10;
  1903. }
  1904. //-----------------------------------------------------------------------------
  1905. // Purpose:
  1906. //-----------------------------------------------------------------------------
  1907. void CLaserDot::TurnOn( void )
  1908. {
  1909. m_bIsOn = true;
  1910. if ( m_bVisibleLaserDot )
  1911. {
  1912. BaseClass::TurnOn();
  1913. }
  1914. }
  1915. //-----------------------------------------------------------------------------
  1916. // Purpose:
  1917. //-----------------------------------------------------------------------------
  1918. void CLaserDot::TurnOff( void )
  1919. {
  1920. m_bIsOn = false;
  1921. if ( m_bVisibleLaserDot )
  1922. {
  1923. BaseClass::TurnOff();
  1924. }
  1925. }
  1926. //-----------------------------------------------------------------------------
  1927. // Purpose:
  1928. //-----------------------------------------------------------------------------
  1929. void CLaserDot::MakeInvisible( void )
  1930. {
  1931. BaseClass::TurnOff();
  1932. }