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.

529 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Egon
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npcevent.h"
  9. #include "hl1mp_basecombatweapon_shared.h"
  10. #include "Sprite.h"
  11. #include "beam_shared.h"
  12. #include "takedamageinfo.h"
  13. //#include "basecombatcharacter.h"
  14. //#include "AI_BaseNPC.h"
  15. #ifdef CLIENT_DLL
  16. #include "c_baseplayer.h"
  17. #include "hl1/hl1_c_player.h"
  18. #else
  19. #include "player.h"
  20. #include "hl1_player.h"
  21. #endif
  22. //#include "player.h"
  23. #include "gamerules.h"
  24. #include "in_buttons.h"
  25. #ifdef CLIENT_DLL
  26. #else
  27. #include "soundent.h"
  28. #include "game.h"
  29. #endif
  30. #include "vstdlib/random.h"
  31. #include "engine/IEngineSound.h"
  32. #include "IEffects.h"
  33. #ifdef CLIENT_DLL
  34. #include "c_te_effect_dispatch.h"
  35. #else
  36. #include "te_effect_dispatch.h"
  37. #endif
  38. enum EGON_FIRESTATE { FIRE_OFF, FIRE_STARTUP, FIRE_CHARGE };
  39. #define EGON_PULSE_INTERVAL 0.1
  40. #define EGON_DISCHARGE_INTERVAL 0.1
  41. #define EGON_BEAM_SPRITE "sprites/xbeam1.vmt"
  42. #define EGON_FLARE_SPRITE "sprites/XSpark1.vmt"
  43. extern ConVar sk_plr_dmg_egon_wide;
  44. //-----------------------------------------------------------------------------
  45. // CWeaponEgon
  46. //-----------------------------------------------------------------------------
  47. #ifdef CLIENT_DLL
  48. #define CWeaponEgon C_WeaponEgon
  49. #endif
  50. class CWeaponEgon : public CBaseHL1MPCombatWeapon
  51. {
  52. DECLARE_CLASS( CWeaponEgon, CBaseHL1MPCombatWeapon );
  53. public:
  54. DECLARE_NETWORKCLASS();
  55. DECLARE_PREDICTABLE();
  56. CWeaponEgon(void);
  57. virtual bool Deploy( void );
  58. void PrimaryAttack( void );
  59. virtual void Precache( void );
  60. void SecondaryAttack( void )
  61. {
  62. PrimaryAttack();
  63. }
  64. void WeaponIdle( void );
  65. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  66. // DECLARE_SERVERCLASS();
  67. // DECLARE_DATADESC();
  68. private:
  69. bool HasAmmo( void );
  70. void UseAmmo( int count );
  71. void Attack( void );
  72. void EndAttack( void );
  73. void Fire( const Vector &vecOrigSrc, const Vector &vecDir );
  74. void UpdateEffect( const Vector &startPoint, const Vector &endPoint );
  75. void CreateEffect( void );
  76. void DestroyEffect( void );
  77. EGON_FIRESTATE m_fireState;
  78. float m_flAmmoUseTime; // since we use < 1 point of ammo per update, we subtract ammo on a timer.
  79. float m_flShakeTime;
  80. float m_flStartFireTime;
  81. float m_flDmgTime;
  82. CHandle<CSprite> m_hSprite;
  83. CHandle<CBeam> m_hBeam;
  84. CHandle<CBeam> m_hNoise;
  85. };
  86. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponEgon, DT_WeaponEgon );
  87. BEGIN_NETWORK_TABLE( CWeaponEgon, DT_WeaponEgon )
  88. END_NETWORK_TABLE()
  89. BEGIN_PREDICTION_DATA( CWeaponEgon )
  90. END_PREDICTION_DATA()
  91. LINK_ENTITY_TO_CLASS( weapon_egon, CWeaponEgon );
  92. PRECACHE_WEAPON_REGISTER( weapon_egon );
  93. /*
  94. IMPLEMENT_SERVERCLASS_ST( CWeaponEgon, DT_WeaponEgon )
  95. END_SEND_TABLE()
  96. */
  97. /*
  98. BEGIN_DATADESC( CWeaponEgon )
  99. DEFINE_FIELD( m_fireState, FIELD_INTEGER ),
  100. DEFINE_FIELD( m_flAmmoUseTime, FIELD_TIME ),
  101. DEFINE_FIELD( m_flShakeTime, FIELD_TIME ),
  102. DEFINE_FIELD( m_flStartFireTime, FIELD_TIME ),
  103. DEFINE_FIELD( m_flDmgTime, FIELD_TIME ),
  104. DEFINE_FIELD( m_hSprite, FIELD_EHANDLE ),
  105. DEFINE_FIELD( m_hBeam, FIELD_EHANDLE ),
  106. DEFINE_FIELD( m_hNoise, FIELD_EHANDLE ),
  107. END_DATADESC()
  108. */
  109. //-----------------------------------------------------------------------------
  110. // Purpose: Constructor
  111. //-----------------------------------------------------------------------------
  112. CWeaponEgon::CWeaponEgon( void )
  113. {
  114. m_bReloadsSingly = false;
  115. m_bFiresUnderwater = true;
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose:
  119. //-----------------------------------------------------------------------------
  120. void CWeaponEgon::Precache( void )
  121. {
  122. PrecacheScriptSound( "Weapon_Gluon.Start" );
  123. PrecacheScriptSound( "Weapon_Gluon.Run" );
  124. PrecacheScriptSound( "Weapon_Gluon.Off" );
  125. PrecacheModel( EGON_BEAM_SPRITE );
  126. PrecacheModel( EGON_FLARE_SPRITE );
  127. BaseClass::Precache();
  128. }
  129. bool CWeaponEgon::Deploy( void )
  130. {
  131. m_fireState = FIRE_OFF;
  132. return BaseClass::Deploy();
  133. }
  134. bool CWeaponEgon::HasAmmo( void )
  135. {
  136. CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
  137. if ( !pPlayer )
  138. {
  139. return false;
  140. }
  141. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
  142. return false;
  143. return true;
  144. }
  145. void CWeaponEgon::UseAmmo( int count )
  146. {
  147. CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
  148. if ( !pPlayer )
  149. {
  150. return;
  151. }
  152. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) >= count )
  153. pPlayer->RemoveAmmo( count, m_iPrimaryAmmoType );
  154. else
  155. pPlayer->RemoveAmmo( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ), m_iPrimaryAmmoType );
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose:
  159. //-----------------------------------------------------------------------------
  160. void CWeaponEgon::PrimaryAttack( void )
  161. {
  162. CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
  163. if ( !pPlayer )
  164. {
  165. return;
  166. }
  167. // don't fire underwater
  168. if ( pPlayer->GetWaterLevel() == 3 )
  169. {
  170. if ( m_fireState != FIRE_OFF || m_hBeam )
  171. {
  172. EndAttack();
  173. }
  174. else
  175. {
  176. WeaponSound( EMPTY );
  177. }
  178. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  179. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  180. return;
  181. }
  182. Vector vecAiming = pPlayer->GetAutoaimVector( 0 );
  183. Vector vecSrc = pPlayer->Weapon_ShootPosition( );
  184. switch( m_fireState )
  185. {
  186. case FIRE_OFF:
  187. {
  188. if ( !HasAmmo() )
  189. {
  190. m_flNextPrimaryAttack = gpGlobals->curtime + 0.25;
  191. m_flNextSecondaryAttack = gpGlobals->curtime + 0.25;
  192. WeaponSound( EMPTY );
  193. return;
  194. }
  195. m_flAmmoUseTime = gpGlobals->curtime;// start using ammo ASAP.
  196. EmitSound( "Weapon_Gluon.Start" );
  197. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  198. m_flShakeTime = 0;
  199. m_flStartFireTime = gpGlobals->curtime;
  200. SetWeaponIdleTime( gpGlobals->curtime + 0.1 );
  201. m_flDmgTime = gpGlobals->curtime + EGON_PULSE_INTERVAL;
  202. m_fireState = FIRE_STARTUP;
  203. }
  204. break;
  205. case FIRE_STARTUP:
  206. {
  207. Fire( vecSrc, vecAiming );
  208. if ( gpGlobals->curtime >= ( m_flStartFireTime + 2.0 ) )
  209. {
  210. EmitSound( "Weapon_Gluon.Run" );
  211. m_fireState = FIRE_CHARGE;
  212. }
  213. if ( !HasAmmo() )
  214. {
  215. EndAttack();
  216. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  217. m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
  218. }
  219. }
  220. case FIRE_CHARGE:
  221. {
  222. Fire( vecSrc, vecAiming );
  223. if ( !HasAmmo() )
  224. {
  225. EndAttack();
  226. m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
  227. m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
  228. }
  229. }
  230. break;
  231. }
  232. }
  233. void CWeaponEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir )
  234. {
  235. CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
  236. if ( !pPlayer )
  237. {
  238. return;
  239. }
  240. //CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 450, 0.1 );
  241. WeaponSound( SINGLE );
  242. Vector vecDest = vecOrigSrc + (vecDir * MAX_TRACE_LENGTH);
  243. trace_t tr;
  244. UTIL_TraceLine( vecOrigSrc, vecDest, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr );
  245. if ( tr.allsolid )
  246. return;
  247. CBaseEntity *pEntity = tr.m_pEnt;
  248. if ( pEntity == NULL )
  249. return;
  250. if ( g_pGameRules->IsMultiplayer() )
  251. {
  252. if ( m_hSprite )
  253. {
  254. if ( pEntity->m_takedamage != DAMAGE_NO )
  255. {
  256. m_hSprite->TurnOn();
  257. }
  258. else
  259. {
  260. m_hSprite->TurnOff();
  261. }
  262. }
  263. }
  264. if ( m_flDmgTime < gpGlobals->curtime )
  265. {
  266. // wide mode does damage to the ent, and radius damage
  267. if ( pEntity->m_takedamage != DAMAGE_NO )
  268. {
  269. ClearMultiDamage();
  270. CTakeDamageInfo info( this, pPlayer, sk_plr_dmg_egon_wide.GetFloat() * g_pGameRules->GetDamageMultiplier(), DMG_ENERGYBEAM | DMG_ALWAYSGIB );
  271. CalculateMeleeDamageForce( &info, vecDir, tr.endpos );
  272. pEntity->DispatchTraceAttack( info, vecDir, &tr );
  273. ApplyMultiDamage();
  274. }
  275. if ( g_pGameRules->IsMultiplayer() )
  276. {
  277. // radius damage a little more potent in multiplayer.
  278. #ifndef CLIENT_DLL
  279. RadiusDamage( CTakeDamageInfo( this, pPlayer, sk_plr_dmg_egon_wide.GetFloat() * g_pGameRules->GetDamageMultiplier() / 4, DMG_ENERGYBEAM | DMG_BLAST | DMG_ALWAYSGIB ), tr.endpos, 128, CLASS_NONE, NULL );
  280. #endif
  281. }
  282. if ( !pPlayer->IsAlive() )
  283. return;
  284. if ( g_pGameRules->IsMultiplayer() )
  285. {
  286. //multiplayer uses 5 ammo/second
  287. if ( gpGlobals->curtime >= m_flAmmoUseTime )
  288. {
  289. UseAmmo( 1 );
  290. m_flAmmoUseTime = gpGlobals->curtime + 0.2;
  291. }
  292. }
  293. else
  294. {
  295. // Wide mode uses 10 charges per second in single player
  296. if ( gpGlobals->curtime >= m_flAmmoUseTime )
  297. {
  298. UseAmmo( 1 );
  299. m_flAmmoUseTime = gpGlobals->curtime + 0.1;
  300. }
  301. }
  302. m_flDmgTime = gpGlobals->curtime + EGON_DISCHARGE_INTERVAL;
  303. if ( m_flShakeTime < gpGlobals->curtime )
  304. {
  305. #ifndef CLIENT_DLL
  306. UTIL_ScreenShake( tr.endpos, 5.0, 150.0, 0.75, 250.0, SHAKE_START );
  307. #endif
  308. m_flShakeTime = gpGlobals->curtime + 1.5;
  309. }
  310. }
  311. Vector vecUp, vecRight;
  312. QAngle angDir;
  313. VectorAngles( vecDir, angDir );
  314. AngleVectors( angDir, NULL, &vecRight, &vecUp );
  315. Vector tmpSrc = vecOrigSrc + (vecUp * -8) + (vecRight * 3);
  316. UpdateEffect( tmpSrc, tr.endpos );
  317. }
  318. void CWeaponEgon::UpdateEffect( const Vector &startPoint, const Vector &endPoint )
  319. {
  320. if ( !m_hBeam )
  321. {
  322. CreateEffect();
  323. }
  324. if ( m_hBeam )
  325. {
  326. m_hBeam->SetStartPos( endPoint );
  327. }
  328. if ( m_hSprite )
  329. {
  330. m_hSprite->SetAbsOrigin( endPoint );
  331. m_hSprite->m_flFrame += 8 * gpGlobals->frametime;
  332. if ( m_hSprite->m_flFrame > m_hSprite->Frames() )
  333. m_hSprite->m_flFrame = 0;
  334. }
  335. if ( m_hNoise )
  336. {
  337. m_hNoise->SetStartPos( endPoint );
  338. }
  339. }
  340. void CWeaponEgon::CreateEffect( void )
  341. {
  342. #ifndef CLIENT_DLL
  343. CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
  344. if ( !pPlayer )
  345. {
  346. return;
  347. }
  348. DestroyEffect();
  349. m_hBeam = CBeam::BeamCreate( EGON_BEAM_SPRITE, 3.5 );
  350. m_hBeam->PointEntInit( GetAbsOrigin(), this );
  351. m_hBeam->SetBeamFlags( FBEAM_SINENOISE );
  352. m_hBeam->SetEndAttachment( 1 );
  353. m_hBeam->AddSpawnFlags( SF_BEAM_TEMPORARY ); // Flag these to be destroyed on save/restore or level transition
  354. m_hBeam->SetOwnerEntity( pPlayer );
  355. m_hBeam->SetScrollRate( 10 );
  356. m_hBeam->SetBrightness( 200 );
  357. m_hBeam->SetColor( 50, 50, 255 );
  358. m_hBeam->SetNoise( 0.2 );
  359. m_hNoise = CBeam::BeamCreate( EGON_BEAM_SPRITE, 5.0 );
  360. m_hNoise->PointEntInit( GetAbsOrigin(), this );
  361. m_hNoise->SetEndAttachment( 1 );
  362. m_hNoise->AddSpawnFlags( SF_BEAM_TEMPORARY );
  363. m_hNoise->SetOwnerEntity( pPlayer );
  364. m_hNoise->SetScrollRate( 25 );
  365. m_hNoise->SetBrightness( 200 );
  366. m_hNoise->SetColor( 50, 50, 255 );
  367. m_hNoise->SetNoise( 0.8 );
  368. m_hSprite = CSprite::SpriteCreate( EGON_FLARE_SPRITE, GetAbsOrigin(), false );
  369. m_hSprite->SetScale( 1.0 );
  370. m_hSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
  371. m_hSprite->AddSpawnFlags( SF_SPRITE_TEMPORARY );
  372. m_hSprite->SetOwnerEntity( pPlayer );
  373. #endif
  374. }
  375. void CWeaponEgon::DestroyEffect( void )
  376. {
  377. #ifndef CLIENT_DLL
  378. if ( m_hBeam )
  379. {
  380. UTIL_Remove( m_hBeam );
  381. m_hBeam = NULL;
  382. }
  383. if ( m_hNoise )
  384. {
  385. UTIL_Remove( m_hNoise );
  386. m_hNoise = NULL;
  387. }
  388. if ( m_hSprite )
  389. {
  390. m_hSprite->Expand( 10, 500 );
  391. m_hSprite = NULL;
  392. }
  393. #endif
  394. }
  395. void CWeaponEgon::EndAttack( void )
  396. {
  397. StopSound( "Weapon_Gluon.Run" );
  398. if ( m_fireState != FIRE_OFF )
  399. {
  400. EmitSound( "Weapon_Gluon.Off" );
  401. }
  402. SetWeaponIdleTime( gpGlobals->curtime + 2.0 );
  403. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  404. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  405. m_fireState = FIRE_OFF;
  406. DestroyEffect();
  407. }
  408. bool CWeaponEgon::Holster( CBaseCombatWeapon *pSwitchingTo )
  409. {
  410. EndAttack();
  411. return BaseClass::Holster( pSwitchingTo );
  412. }
  413. void CWeaponEgon::WeaponIdle( void )
  414. {
  415. if ( !HasWeaponIdleTimeElapsed() )
  416. return;
  417. if ( m_fireState != FIRE_OFF )
  418. EndAttack();
  419. int iAnim;
  420. float flRand = random->RandomFloat( 0,1 );
  421. float flIdleTime;
  422. if ( flRand <= 0.5 )
  423. {
  424. iAnim = ACT_VM_IDLE;
  425. flIdleTime = gpGlobals->curtime + random->RandomFloat( 10, 15 );
  426. }
  427. else
  428. {
  429. iAnim = ACT_VM_FIDGET;
  430. flIdleTime = gpGlobals->curtime + 3.0;
  431. }
  432. SendWeaponAnim( iAnim );
  433. SetWeaponIdleTime( flIdleTime );
  434. }