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.

2283 lines
61 KiB

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