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.

512 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "basehlcombatweapon.h"
  8. #include "npcevent.h"
  9. #include "basecombatcharacter.h"
  10. #include "ai_basenpc.h"
  11. #include "player.h"
  12. #include "game.h"
  13. #include "in_buttons.h"
  14. #include "grenade_ar2.h"
  15. #include "ai_memory.h"
  16. #include "soundent.h"
  17. #include "rumble_shared.h"
  18. #include "gamestats.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. extern ConVar sk_plr_dmg_smg1_grenade;
  22. class CWeaponSMG1 : public CHLSelectFireMachineGun
  23. {
  24. DECLARE_DATADESC();
  25. public:
  26. DECLARE_CLASS( CWeaponSMG1, CHLSelectFireMachineGun );
  27. CWeaponSMG1();
  28. DECLARE_SERVERCLASS();
  29. void Precache( void );
  30. void AddViewKick( void );
  31. void SecondaryAttack( void );
  32. int GetMinBurst() { return 2; }
  33. int GetMaxBurst() { return 5; }
  34. virtual void Equip( CBaseCombatCharacter *pOwner );
  35. bool Reload( void );
  36. float GetFireRate( void ) { return 0.075f; } // 13.3hz
  37. int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
  38. int WeaponRangeAttack2Condition( float flDot, float flDist );
  39. Activity GetPrimaryAttackActivity( void );
  40. virtual const Vector& GetBulletSpread( void )
  41. {
  42. static const Vector cone = VECTOR_CONE_5DEGREES;
  43. return cone;
  44. }
  45. const WeaponProficiencyInfo_t *GetProficiencyValues();
  46. void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir );
  47. void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary );
  48. void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
  49. DECLARE_ACTTABLE();
  50. protected:
  51. Vector m_vecTossVelocity;
  52. float m_flNextGrenadeCheck;
  53. };
  54. IMPLEMENT_SERVERCLASS_ST(CWeaponSMG1, DT_WeaponSMG1)
  55. END_SEND_TABLE()
  56. LINK_ENTITY_TO_CLASS( weapon_smg1, CWeaponSMG1 );
  57. PRECACHE_WEAPON_REGISTER(weapon_smg1);
  58. BEGIN_DATADESC( CWeaponSMG1 )
  59. DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ),
  60. DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ),
  61. END_DATADESC()
  62. acttable_t CWeaponSMG1::m_acttable[] =
  63. {
  64. { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true },
  65. { ACT_RELOAD, ACT_RELOAD_SMG1, true },
  66. { ACT_IDLE, ACT_IDLE_SMG1, true },
  67. { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG1, true },
  68. { ACT_WALK, ACT_WALK_RIFLE, true },
  69. { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true },
  70. // Readiness activities (not aiming)
  71. { ACT_IDLE_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims
  72. { ACT_IDLE_STIMULATED, ACT_IDLE_SMG1_STIMULATED, false },
  73. { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims
  74. { ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims
  75. { ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false },
  76. { ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims
  77. { ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims
  78. { ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false },
  79. { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
  80. // Readiness activities (aiming)
  81. { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims
  82. { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false },
  83. { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims
  84. { ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims
  85. { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false },
  86. { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims
  87. { ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims
  88. { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false },
  89. { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
  90. //End readiness activities
  91. { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true },
  92. { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true },
  93. { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true },
  94. { ACT_RUN, ACT_RUN_RIFLE, true },
  95. { ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, true },
  96. { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true },
  97. { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true },
  98. { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SMG1, true },
  99. { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SMG1_LOW, true },
  100. { ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false },
  101. { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false },
  102. { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false },
  103. { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true },
  104. };
  105. IMPLEMENT_ACTTABLE(CWeaponSMG1);
  106. //=========================================================
  107. CWeaponSMG1::CWeaponSMG1( )
  108. {
  109. m_fMinRange1 = 0;// No minimum range.
  110. m_fMaxRange1 = 1400;
  111. m_bAltFiresUnderwater = false;
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose:
  115. //-----------------------------------------------------------------------------
  116. void CWeaponSMG1::Precache( void )
  117. {
  118. UTIL_PrecacheOther("grenade_ar2");
  119. BaseClass::Precache();
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Give this weapon longer range when wielded by an ally NPC.
  123. //-----------------------------------------------------------------------------
  124. void CWeaponSMG1::Equip( CBaseCombatCharacter *pOwner )
  125. {
  126. if( pOwner->Classify() == CLASS_PLAYER_ALLY )
  127. {
  128. m_fMaxRange1 = 3000;
  129. }
  130. else
  131. {
  132. m_fMaxRange1 = 1400;
  133. }
  134. BaseClass::Equip( pOwner );
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose:
  138. //-----------------------------------------------------------------------------
  139. void CWeaponSMG1::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir )
  140. {
  141. // FIXME: use the returned number of bullets to account for >10hz firerate
  142. WeaponSoundRealtime( SINGLE_NPC );
  143. CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() );
  144. pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED,
  145. MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), 0 );
  146. pOperator->DoMuzzleFlash();
  147. m_iClip1 = m_iClip1 - 1;
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose:
  151. //-----------------------------------------------------------------------------
  152. void CWeaponSMG1::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary )
  153. {
  154. // Ensure we have enough rounds in the clip
  155. m_iClip1++;
  156. Vector vecShootOrigin, vecShootDir;
  157. QAngle angShootDir;
  158. GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir );
  159. AngleVectors( angShootDir, &vecShootDir );
  160. FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir );
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Purpose:
  164. //-----------------------------------------------------------------------------
  165. void CWeaponSMG1::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  166. {
  167. switch( pEvent->event )
  168. {
  169. case EVENT_WEAPON_SMG1:
  170. {
  171. Vector vecShootOrigin, vecShootDir;
  172. QAngle angDiscard;
  173. // Support old style attachment point firing
  174. if ((pEvent->options == NULL) || (pEvent->options[0] == '\0') || (!pOperator->GetAttachment(pEvent->options, vecShootOrigin, angDiscard)))
  175. {
  176. vecShootOrigin = pOperator->Weapon_ShootPosition();
  177. }
  178. CAI_BaseNPC *npc = pOperator->MyNPCPointer();
  179. ASSERT( npc != NULL );
  180. vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin );
  181. FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir );
  182. }
  183. break;
  184. /*//FIXME: Re-enable
  185. case EVENT_WEAPON_AR2_GRENADE:
  186. {
  187. CAI_BaseNPC *npc = pOperator->MyNPCPointer();
  188. Vector vecShootOrigin, vecShootDir;
  189. vecShootOrigin = pOperator->Weapon_ShootPosition();
  190. vecShootDir = npc->GetShootEnemyDir( vecShootOrigin );
  191. Vector vecThrow = m_vecTossVelocity;
  192. CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecShootOrigin, vec3_angle, npc );
  193. pGrenade->SetAbsVelocity( vecThrow );
  194. pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) );
  195. pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY );
  196. pGrenade->m_hOwner = npc;
  197. pGrenade->m_pMyWeaponAR2 = this;
  198. pGrenade->SetDamage(sk_npc_dmg_ar2_grenade.GetFloat());
  199. // FIXME: arrgg ,this is hard coded into the weapon???
  200. m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
  201. m_iClip2--;
  202. }
  203. break;
  204. */
  205. default:
  206. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  207. break;
  208. }
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose:
  212. // Output : Activity
  213. //-----------------------------------------------------------------------------
  214. Activity CWeaponSMG1::GetPrimaryAttackActivity( void )
  215. {
  216. if ( m_nShotsFired < 2 )
  217. return ACT_VM_PRIMARYATTACK;
  218. if ( m_nShotsFired < 3 )
  219. return ACT_VM_RECOIL1;
  220. if ( m_nShotsFired < 4 )
  221. return ACT_VM_RECOIL2;
  222. return ACT_VM_RECOIL3;
  223. }
  224. //-----------------------------------------------------------------------------
  225. //-----------------------------------------------------------------------------
  226. bool CWeaponSMG1::Reload( void )
  227. {
  228. bool fRet;
  229. float fCacheTime = m_flNextSecondaryAttack;
  230. fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
  231. if ( fRet )
  232. {
  233. // Undo whatever the reload process has done to our secondary
  234. // attack timer. We allow you to interrupt reloading to fire
  235. // a grenade.
  236. m_flNextSecondaryAttack = GetOwner()->m_flNextAttack = fCacheTime;
  237. WeaponSound( RELOAD );
  238. }
  239. return fRet;
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose:
  243. //-----------------------------------------------------------------------------
  244. void CWeaponSMG1::AddViewKick( void )
  245. {
  246. #define EASY_DAMPEN 0.5f
  247. #define MAX_VERTICAL_KICK 1.0f //Degrees
  248. #define SLIDE_LIMIT 2.0f //Seconds
  249. //Get the view kick
  250. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  251. if ( pPlayer == NULL )
  252. return;
  253. DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK, m_fFireDuration, SLIDE_LIMIT );
  254. }
  255. //-----------------------------------------------------------------------------
  256. // Purpose:
  257. //-----------------------------------------------------------------------------
  258. void CWeaponSMG1::SecondaryAttack( void )
  259. {
  260. // Only the player fires this way so we can cast
  261. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  262. if ( pPlayer == NULL )
  263. return;
  264. //Must have ammo
  265. if ( ( pPlayer->GetAmmoCount( m_iSecondaryAmmoType ) <= 0 ) || ( pPlayer->GetWaterLevel() == 3 ) )
  266. {
  267. SendWeaponAnim( ACT_VM_DRYFIRE );
  268. BaseClass::WeaponSound( EMPTY );
  269. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
  270. return;
  271. }
  272. if( m_bInReload )
  273. m_bInReload = false;
  274. // MUST call sound before removing a round from the clip of a CMachineGun
  275. BaseClass::WeaponSound( WPN_DOUBLE );
  276. pPlayer->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAGS_NONE );
  277. Vector vecSrc = pPlayer->Weapon_ShootPosition();
  278. Vector vecThrow;
  279. // Don't autoaim on grenade tosses
  280. AngleVectors( pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), &vecThrow );
  281. VectorScale( vecThrow, 1000.0f, vecThrow );
  282. //Create the grenade
  283. QAngle angles;
  284. VectorAngles( vecThrow, angles );
  285. CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecSrc, angles, pPlayer );
  286. pGrenade->SetAbsVelocity( vecThrow );
  287. pGrenade->SetLocalAngularVelocity( RandomAngle( -400, 400 ) );
  288. pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  289. pGrenade->SetThrower( GetOwner() );
  290. pGrenade->SetDamage( sk_plr_dmg_smg1_grenade.GetFloat() );
  291. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  292. CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON );
  293. // player "shoot" animation
  294. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  295. // Decrease ammo
  296. pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType );
  297. // Can shoot again immediately
  298. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
  299. // Can blow up after a short delay (so have time to release mouse button)
  300. m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
  301. // Register a muzzleflash for the AI.
  302. pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
  303. m_iSecondaryAttacks++;
  304. gamestats->Event_WeaponFired( pPlayer, false, GetClassname() );
  305. }
  306. #define COMBINE_MIN_GRENADE_CLEAR_DIST 256
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. // Input : flDot -
  310. // flDist -
  311. // Output : int
  312. //-----------------------------------------------------------------------------
  313. int CWeaponSMG1::WeaponRangeAttack2Condition( float flDot, float flDist )
  314. {
  315. CAI_BaseNPC *npcOwner = GetOwner()->MyNPCPointer();
  316. return COND_NONE;
  317. /*
  318. // --------------------------------------------------------
  319. // Assume things haven't changed too much since last time
  320. // --------------------------------------------------------
  321. if (gpGlobals->curtime < m_flNextGrenadeCheck )
  322. return m_lastGrenadeCondition;
  323. */
  324. // -----------------------
  325. // If moving, don't check.
  326. // -----------------------
  327. if ( npcOwner->IsMoving())
  328. return COND_NONE;
  329. CBaseEntity *pEnemy = npcOwner->GetEnemy();
  330. if (!pEnemy)
  331. return COND_NONE;
  332. Vector vecEnemyLKP = npcOwner->GetEnemyLKP();
  333. if ( !( pEnemy->GetFlags() & FL_ONGROUND ) && pEnemy->GetWaterLevel() == 0 && vecEnemyLKP.z > (GetAbsOrigin().z + WorldAlignMaxs().z) )
  334. {
  335. //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to
  336. // be grenaded.
  337. // don't throw grenades at anything that isn't on the ground!
  338. return COND_NONE;
  339. }
  340. // --------------------------------------
  341. // Get target vector
  342. // --------------------------------------
  343. Vector vecTarget;
  344. if (random->RandomInt(0,1))
  345. {
  346. // magically know where they are
  347. vecTarget = pEnemy->WorldSpaceCenter();
  348. }
  349. else
  350. {
  351. // toss it to where you last saw them
  352. vecTarget = vecEnemyLKP;
  353. }
  354. // vecTarget = m_vecEnemyLKP + (pEnemy->BodyTarget( GetLocalOrigin() ) - pEnemy->GetLocalOrigin());
  355. // estimate position
  356. // vecTarget = vecTarget + pEnemy->m_vecVelocity * 2;
  357. if ( ( vecTarget - npcOwner->GetLocalOrigin() ).Length2D() <= COMBINE_MIN_GRENADE_CLEAR_DIST )
  358. {
  359. // crap, I don't want to blow myself up
  360. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  361. return (COND_NONE);
  362. }
  363. // ---------------------------------------------------------------------
  364. // Are any friendlies near the intended grenade impact area?
  365. // ---------------------------------------------------------------------
  366. CBaseEntity *pTarget = NULL;
  367. while ( ( pTarget = gEntList.FindEntityInSphere( pTarget, vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST ) ) != NULL )
  368. {
  369. //Check to see if the default relationship is hatred, and if so intensify that
  370. if ( npcOwner->IRelationType( pTarget ) == D_LI )
  371. {
  372. // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while.
  373. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  374. return (COND_WEAPON_BLOCKED_BY_FRIEND);
  375. }
  376. }
  377. // ---------------------------------------------------------------------
  378. // Check that throw is legal and clear
  379. // ---------------------------------------------------------------------
  380. // FIXME: speed is based on difficulty...
  381. Vector vecToss = VecCheckThrow( this, npcOwner->GetLocalOrigin() + Vector(0,0,60), vecTarget, 600.0, 0.5 );
  382. if ( vecToss != vec3_origin )
  383. {
  384. m_vecTossVelocity = vecToss;
  385. // don't check again for a while.
  386. // JAY: HL1 keeps checking - test?
  387. //m_flNextGrenadeCheck = gpGlobals->curtime;
  388. m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second.
  389. return COND_CAN_RANGE_ATTACK2;
  390. }
  391. else
  392. {
  393. // don't check again for a while.
  394. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  395. return COND_WEAPON_SIGHT_OCCLUDED;
  396. }
  397. }
  398. //-----------------------------------------------------------------------------
  399. const WeaponProficiencyInfo_t *CWeaponSMG1::GetProficiencyValues()
  400. {
  401. static WeaponProficiencyInfo_t proficiencyTable[] =
  402. {
  403. { 7.0, 0.75 },
  404. { 5.00, 0.75 },
  405. { 10.0/3.0, 0.75 },
  406. { 5.0/3.0, 0.75 },
  407. { 1.00, 1.0 },
  408. };
  409. COMPILE_TIME_ASSERT( ARRAYSIZE(proficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1);
  410. return proficiencyTable;
  411. }