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.

373 lines
8.9 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "basehlcombatweapon.h"
  9. #include "basecombatcharacter.h"
  10. #include "player.h"
  11. #include "soundent.h"
  12. #include "te_particlesystem.h"
  13. #include "ndebugoverlay.h"
  14. #include "in_buttons.h"
  15. #include "ai_basenpc.h"
  16. #include "ai_memory.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. #define MAX_BURN_RADIUS 256
  20. #define RADIUS_GROW_RATE 50.0 // units/sec
  21. #define IMMOLATOR_TARGET_INVALID Vector( FLT_MAX, FLT_MAX, FLT_MAX )
  22. class CWeaponImmolator : public CBaseHLCombatWeapon
  23. {
  24. public:
  25. DECLARE_CLASS( CWeaponImmolator, CBaseHLCombatWeapon );
  26. DECLARE_SERVERCLASS();
  27. CWeaponImmolator( void );
  28. void Precache( void );
  29. void PrimaryAttack( void );
  30. void ItemPostFrame( void );
  31. int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
  32. void ImmolationDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore );
  33. virtual bool WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions );
  34. virtual int WeaponRangeAttack1Condition( float flDot, float flDist );
  35. void Update();
  36. void UpdateThink();
  37. void StartImmolating();
  38. void StopImmolating();
  39. bool IsImmolating() { return m_flBurnRadius != 0.0; }
  40. DECLARE_ACTTABLE();
  41. DECLARE_DATADESC();
  42. int m_beamIndex;
  43. float m_flBurnRadius;
  44. float m_flTimeLastUpdatedRadius;
  45. Vector m_vecImmolatorTarget;
  46. };
  47. IMPLEMENT_SERVERCLASS_ST(CWeaponImmolator, DT_WeaponImmolator)
  48. END_SEND_TABLE()
  49. LINK_ENTITY_TO_CLASS( info_target_immolator, CPointEntity );
  50. LINK_ENTITY_TO_CLASS( weapon_immolator, CWeaponImmolator );
  51. PRECACHE_WEAPON_REGISTER( weapon_immolator );
  52. BEGIN_DATADESC( CWeaponImmolator )
  53. DEFINE_FIELD( m_beamIndex, FIELD_INTEGER ),
  54. DEFINE_FIELD( m_flBurnRadius, FIELD_FLOAT ),
  55. DEFINE_FIELD( m_flTimeLastUpdatedRadius, FIELD_TIME ),
  56. DEFINE_FIELD( m_vecImmolatorTarget, FIELD_VECTOR ),
  57. DEFINE_ENTITYFUNC( UpdateThink ),
  58. END_DATADESC()
  59. //-----------------------------------------------------------------------------
  60. // Maps base activities to weapons-specific ones so our characters do the right things.
  61. //-----------------------------------------------------------------------------
  62. acttable_t CWeaponImmolator::m_acttable[] =
  63. {
  64. { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SNIPER_RIFLE, true }
  65. };
  66. IMPLEMENT_ACTTABLE( CWeaponImmolator );
  67. //-----------------------------------------------------------------------------
  68. // Constructor
  69. //-----------------------------------------------------------------------------
  70. CWeaponImmolator::CWeaponImmolator( void )
  71. {
  72. m_fMaxRange1 = 4096;
  73. StopImmolating();
  74. }
  75. void CWeaponImmolator::StartImmolating()
  76. {
  77. // Start the radius really tiny because we use radius == 0.0 to
  78. // determine whether the immolator is operating or not.
  79. m_flBurnRadius = 0.1;
  80. m_flTimeLastUpdatedRadius = gpGlobals->curtime;
  81. SetThink( UpdateThink );
  82. SetNextThink( gpGlobals->curtime );
  83. CSoundEnt::InsertSound( SOUND_DANGER, m_vecImmolatorTarget, 256, 5.0, GetOwner() );
  84. }
  85. void CWeaponImmolator::StopImmolating()
  86. {
  87. m_flBurnRadius = 0.0;
  88. SetThink( NULL );
  89. m_vecImmolatorTarget= IMMOLATOR_TARGET_INVALID;
  90. m_flNextPrimaryAttack = gpGlobals->curtime + 5.0;
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Purpose:
  94. //-----------------------------------------------------------------------------
  95. void CWeaponImmolator::Precache( void )
  96. {
  97. m_beamIndex = PrecacheModel( "sprites/bluelaser1.vmt" );
  98. BaseClass::Precache();
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose:
  102. //-----------------------------------------------------------------------------
  103. void CWeaponImmolator::PrimaryAttack( void )
  104. {
  105. WeaponSound( SINGLE );
  106. if( !IsImmolating() )
  107. {
  108. StartImmolating();
  109. }
  110. }
  111. //-----------------------------------------------------------------------------
  112. // This weapon is said to have Line of Sight when it CAN'T see the target, but
  113. // can see a place near the target than can.
  114. //-----------------------------------------------------------------------------
  115. bool CWeaponImmolator::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions )
  116. {
  117. CAI_BaseNPC* npcOwner = GetOwner()->MyNPCPointer();
  118. if( !npcOwner )
  119. {
  120. return false;
  121. }
  122. if( IsImmolating() )
  123. {
  124. // Don't update while Immolating. This is a committed attack.
  125. return false;
  126. }
  127. // Assume we won't find a target.
  128. m_vecImmolatorTarget = targetPos;
  129. return true;
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Purpose: Weapon firing conditions
  133. //-----------------------------------------------------------------------------
  134. int CWeaponImmolator::WeaponRangeAttack1Condition( float flDot, float flDist )
  135. {
  136. if( m_flNextPrimaryAttack > gpGlobals->curtime )
  137. {
  138. // Too soon to attack!
  139. return COND_NONE;
  140. }
  141. if( IsImmolating() )
  142. {
  143. // Once is enough!
  144. return COND_NONE;
  145. }
  146. if( m_vecImmolatorTarget == IMMOLATOR_TARGET_INVALID )
  147. {
  148. // No target!
  149. return COND_NONE;
  150. }
  151. if ( flDist > m_fMaxRange1 )
  152. {
  153. return COND_TOO_FAR_TO_ATTACK;
  154. }
  155. else if ( flDot < 0.5f ) // UNDONE: Why check this here? Isn't the AI checking this already?
  156. {
  157. return COND_NOT_FACING_ATTACK;
  158. }
  159. return COND_CAN_RANGE_ATTACK1;
  160. }
  161. void CWeaponImmolator::UpdateThink( void )
  162. {
  163. CBaseCombatCharacter *pOwner = GetOwner();
  164. if( pOwner && !pOwner->IsAlive() )
  165. {
  166. StopImmolating();
  167. return;
  168. }
  169. Update();
  170. SetNextThink( gpGlobals->curtime + 0.05 );
  171. }
  172. //-----------------------------------------------------------------------------
  173. //-----------------------------------------------------------------------------
  174. void CWeaponImmolator::Update()
  175. {
  176. float flDuration = gpGlobals->curtime - m_flTimeLastUpdatedRadius;
  177. if( flDuration != 0.0 )
  178. {
  179. m_flBurnRadius += RADIUS_GROW_RATE * flDuration;
  180. }
  181. // Clamp
  182. m_flBurnRadius = MIN( m_flBurnRadius, MAX_BURN_RADIUS );
  183. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  184. Vector vecSrc;
  185. Vector vecAiming;
  186. if( pOwner )
  187. {
  188. vecSrc = pOwner->Weapon_ShootPosition( );
  189. vecAiming = pOwner->GetAutoaimVector(AUTOAIM_2DEGREES);
  190. }
  191. else
  192. {
  193. CBaseCombatCharacter *pOwner = GetOwner();
  194. vecSrc = pOwner->Weapon_ShootPosition( );
  195. vecAiming = m_vecImmolatorTarget - vecSrc;
  196. VectorNormalize( vecAiming );
  197. }
  198. trace_t tr;
  199. UTIL_TraceLine( vecSrc, vecSrc + vecAiming * MAX_TRACE_LENGTH, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
  200. int brightness;
  201. brightness = 255 * (m_flBurnRadius/MAX_BURN_RADIUS);
  202. UTIL_Beam( vecSrc,
  203. tr.endpos,
  204. m_beamIndex,
  205. 0, //halo index
  206. 0, //frame start
  207. 2.0f, //framerate
  208. 0.1f, //life
  209. 20, // width
  210. 1, // endwidth
  211. 0, // fadelength,
  212. 1, // noise
  213. 0, // red
  214. 255, // green
  215. 0, // blue,
  216. brightness, // bright
  217. 100 // speed
  218. );
  219. if( tr.DidHitWorld() )
  220. {
  221. int beams;
  222. for( beams = 0 ; beams < 5 ; beams++ )
  223. {
  224. Vector vecDest;
  225. // Random unit vector
  226. vecDest.x = random->RandomFloat( -1, 1 );
  227. vecDest.y = random->RandomFloat( -1, 1 );
  228. vecDest.z = random->RandomFloat( 0, 1 );
  229. // Push out to radius dist.
  230. vecDest = tr.endpos + vecDest * m_flBurnRadius;
  231. UTIL_Beam( tr.endpos,
  232. vecDest,
  233. m_beamIndex,
  234. 0, //halo index
  235. 0, //frame start
  236. 2.0f, //framerate
  237. 0.15f, //life
  238. 20, // width
  239. 1.75, // endwidth
  240. 0.75, // fadelength,
  241. 15, // noise
  242. 0, // red
  243. 255, // green
  244. 0, // blue,
  245. 128, // bright
  246. 100 // speed
  247. );
  248. }
  249. // Immolator starts to hurt a few seconds after the effect is seen
  250. if( m_flBurnRadius > 64.0 )
  251. {
  252. ImmolationDamage( CTakeDamageInfo( this, this, 1, DMG_BURN ), tr.endpos, m_flBurnRadius, CLASS_NONE );
  253. }
  254. }
  255. else
  256. {
  257. // The attack beam struck some kind of entity directly.
  258. }
  259. m_flTimeLastUpdatedRadius = gpGlobals->curtime;
  260. if( m_flBurnRadius >= MAX_BURN_RADIUS )
  261. {
  262. StopImmolating();
  263. }
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Purpose:
  267. //-----------------------------------------------------------------------------
  268. void CWeaponImmolator::ItemPostFrame( void )
  269. {
  270. BaseClass::ItemPostFrame();
  271. }
  272. void CWeaponImmolator::ImmolationDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore )
  273. {
  274. CBaseEntity *pEntity = NULL;
  275. trace_t tr;
  276. Vector vecSpot;
  277. Vector vecSrc = vecSrcIn;
  278. // iterate on all entities in the vicinity.
  279. for ( CEntitySphereQuery sphere( vecSrc, flRadius ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
  280. {
  281. CBaseCombatCharacter *pBCC;
  282. pBCC = pEntity->MyCombatCharacterPointer();
  283. if ( pBCC && !pBCC->IsOnFire() )
  284. {
  285. // UNDONE: this should check a damage mask, not an ignore
  286. if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
  287. {
  288. continue;
  289. }
  290. if( pEntity == GetOwner() )
  291. {
  292. continue;
  293. }
  294. pBCC->Ignite( random->RandomFloat( 15, 20 ) );
  295. }
  296. }
  297. }