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.

326 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "weapon_alyxgun.h"
  8. #include "npcevent.h"
  9. #include "ai_basenpc.h"
  10. #include "globalstate.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. IMPLEMENT_SERVERCLASS_ST(CWeaponAlyxGun, DT_WeaponAlyxGun)
  14. END_SEND_TABLE()
  15. LINK_ENTITY_TO_CLASS( weapon_alyxgun, CWeaponAlyxGun );
  16. PRECACHE_WEAPON_REGISTER(weapon_alyxgun);
  17. BEGIN_DATADESC( CWeaponAlyxGun )
  18. END_DATADESC()
  19. acttable_t CWeaponAlyxGun::m_acttable[] =
  20. {
  21. { ACT_IDLE, ACT_IDLE_PISTOL, true },
  22. { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true },
  23. { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true },
  24. { ACT_RELOAD, ACT_RELOAD_PISTOL, true },
  25. { ACT_WALK_AIM, ACT_WALK_AIM_PISTOL, true },
  26. { ACT_RUN_AIM, ACT_RUN_AIM_PISTOL, true },
  27. { ACT_COVER_LOW, ACT_COVER_PISTOL_LOW, true },
  28. { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_PISTOL_LOW, true },
  29. { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_PISTOL,true },
  30. { ACT_RELOAD_LOW, ACT_RELOAD_PISTOL_LOW, true },
  31. { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_PISTOL_LOW, true },
  32. { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, true },
  33. // Readiness activities (not aiming)
  34. { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims
  35. { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false },
  36. { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims
  37. { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false },
  38. { ACT_WALK_RELAXED, ACT_WALK, false },//never aims
  39. { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false },
  40. { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims
  41. { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false },
  42. { ACT_RUN_RELAXED, ACT_RUN, false },//never aims
  43. { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false },
  44. { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims
  45. { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false },
  46. // Readiness activities (aiming)
  47. { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL, false },//never aims
  48. { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_PISTOL, false },
  49. { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims
  50. { ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false },
  51. { ACT_WALK_AIM_RELAXED, ACT_WALK, false },//never aims
  52. { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false },
  53. { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims
  54. { ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims
  55. { ACT_RUN_AIM_RELAXED, ACT_RUN, false },//never aims
  56. { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false },
  57. { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims
  58. { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims
  59. //End readiness activities
  60. // Crouch activities
  61. { ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false },
  62. { ACT_CROUCHIDLE_AIM_STIMULATED,ACT_RANGE_AIM_PISTOL_LOW, false },//always aims
  63. { ACT_CROUCHIDLE_AGITATED, ACT_RANGE_AIM_PISTOL_LOW, false },//always aims
  64. // Readiness translations
  65. { ACT_READINESS_RELAXED_TO_STIMULATED, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED, false },
  66. { ACT_READINESS_RELAXED_TO_STIMULATED_WALK, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED_WALK, false },
  67. { ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false },
  68. { ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false },
  69. // { ACT_ARM, ACT_ARM_PISTOL, true },
  70. // { ACT_DISARM, ACT_DISARM_PISTOL, true },
  71. };
  72. IMPLEMENT_ACTTABLE(CWeaponAlyxGun);
  73. #define TOOCLOSETIMER_OFF 0.0f
  74. #define ALYX_TOOCLOSETIMER 1.0f // Time an enemy must be tooclose before Alyx is allowed to shoot it.
  75. //=========================================================
  76. CWeaponAlyxGun::CWeaponAlyxGun( )
  77. {
  78. m_fMinRange1 = 1;
  79. m_fMaxRange1 = 5000;
  80. m_flTooCloseTimer = TOOCLOSETIMER_OFF;
  81. #ifdef HL2_EPISODIC
  82. m_fMinRange1 = 60;
  83. m_fMaxRange1 = 2048;
  84. #endif//HL2_EPISODIC
  85. }
  86. CWeaponAlyxGun::~CWeaponAlyxGun( )
  87. {
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Purpose:
  91. //-----------------------------------------------------------------------------
  92. void CWeaponAlyxGun::Precache( void )
  93. {
  94. BaseClass::Precache();
  95. }
  96. //-----------------------------------------------------------------------------
  97. //-----------------------------------------------------------------------------
  98. void CWeaponAlyxGun::Equip( CBaseCombatCharacter *pOwner )
  99. {
  100. BaseClass::Equip( pOwner );
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose: Try to encourage Alyx not to use her weapon at point blank range,
  104. // but don't prevent her from defending herself if cornered.
  105. // Input : flDot -
  106. // flDist -
  107. // Output : int
  108. //-----------------------------------------------------------------------------
  109. int CWeaponAlyxGun::WeaponRangeAttack1Condition( float flDot, float flDist )
  110. {
  111. #ifdef HL2_EPISODIC
  112. if( flDist < m_fMinRange1 )
  113. {
  114. // If Alyx is not able to fire because an enemy is too close, start a timer.
  115. // If the condition persists, allow her to ignore it and defend herself. The idea
  116. // is to stop Alyx being content to fire point blank at enemies if she's able to move
  117. // away, without making her defenseless if she's not able to move.
  118. float flTime;
  119. if( m_flTooCloseTimer == TOOCLOSETIMER_OFF )
  120. {
  121. m_flTooCloseTimer = gpGlobals->curtime;
  122. }
  123. flTime = gpGlobals->curtime - m_flTooCloseTimer;
  124. if( flTime > ALYX_TOOCLOSETIMER )
  125. {
  126. // Fake the range to allow Alyx to shoot.
  127. flDist = m_fMinRange1 + 1.0f;
  128. }
  129. }
  130. else
  131. {
  132. m_flTooCloseTimer = TOOCLOSETIMER_OFF;
  133. }
  134. int nBaseCondition = BaseClass::WeaponRangeAttack1Condition( flDot, flDist );
  135. // While in a vehicle, we extend our aiming cone (this relies on COND_NOT_FACING_ATTACK
  136. // TODO: This needs to be rolled in at the animation level
  137. if ( GetOwner()->IsInAVehicle() )
  138. {
  139. Vector vecRoughDirection = ( GetOwner()->GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() );
  140. Vector vecRight;
  141. GetVectors( NULL, &vecRight, NULL );
  142. bool bRightSide = ( DotProduct( vecRoughDirection, vecRight ) > 0.0f );
  143. float flTargetDot = ( bRightSide ) ? -0.7f : 0.0f;
  144. if ( nBaseCondition == COND_NOT_FACING_ATTACK && flDot >= flTargetDot )
  145. {
  146. nBaseCondition = COND_CAN_RANGE_ATTACK1;
  147. }
  148. }
  149. return nBaseCondition;
  150. #else
  151. return BaseClass::WeaponRangeAttack1Condition( flDot, flDist );
  152. #endif//HL2_EPISODIC
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Purpose:
  156. // Input : flDot -
  157. // flDist -
  158. // Output : int
  159. //-----------------------------------------------------------------------------
  160. int CWeaponAlyxGun::WeaponRangeAttack2Condition( float flDot, float flDist )
  161. {
  162. return COND_NONE;
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Purpose:
  166. // Input : *pOperator -
  167. //-----------------------------------------------------------------------------
  168. void CWeaponAlyxGun::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles )
  169. {
  170. Vector vecShootOrigin, vecShootDir;
  171. CAI_BaseNPC *npc = pOperator->MyNPCPointer();
  172. ASSERT( npc != NULL );
  173. if ( bUseWeaponAngles )
  174. {
  175. QAngle angShootDir;
  176. GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir );
  177. AngleVectors( angShootDir, &vecShootDir );
  178. }
  179. else
  180. {
  181. vecShootOrigin = pOperator->Weapon_ShootPosition();
  182. vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin );
  183. }
  184. WeaponSound( SINGLE_NPC );
  185. if( hl2_episodic.GetBool() )
  186. {
  187. pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 1 );
  188. }
  189. else
  190. {
  191. pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2 );
  192. }
  193. pOperator->DoMuzzleFlash();
  194. if( hl2_episodic.GetBool() )
  195. {
  196. // Never fire Alyx's last bullet just in case there's an emergency
  197. // and she needs to be able to shoot without reloading.
  198. if( m_iClip1 > 1 )
  199. {
  200. m_iClip1 = m_iClip1 - 1;
  201. }
  202. }
  203. else
  204. {
  205. m_iClip1 = m_iClip1 - 1;
  206. }
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose:
  210. //-----------------------------------------------------------------------------
  211. void CWeaponAlyxGun::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary )
  212. {
  213. // Ensure we have enough rounds in the clip
  214. m_iClip1++;
  215. // HACK: We need the gun to fire its muzzle flash
  216. if ( bSecondary == false )
  217. {
  218. SetActivity( ACT_RANGE_ATTACK_PISTOL, 0.0f );
  219. }
  220. FireNPCPrimaryAttack( pOperator, true );
  221. }
  222. //-----------------------------------------------------------------------------
  223. void CWeaponAlyxGun::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  224. {
  225. switch( pEvent->event )
  226. {
  227. case EVENT_WEAPON_PISTOL_FIRE:
  228. {
  229. FireNPCPrimaryAttack( pOperator, false );
  230. break;
  231. }
  232. default:
  233. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  234. break;
  235. }
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose: Whether or not Alyx is in the injured mode
  239. //-----------------------------------------------------------------------------
  240. bool IsAlyxInInjuredMode( void )
  241. {
  242. if ( hl2_episodic.GetBool() == false )
  243. return false;
  244. return ( GlobalEntity_GetState("ep2_alyx_injured") == GLOBAL_ON );
  245. }
  246. //-----------------------------------------------------------------------------
  247. const Vector& CWeaponAlyxGun::GetBulletSpread( void )
  248. {
  249. static const Vector cone = VECTOR_CONE_2DEGREES;
  250. static const Vector injuredCone = VECTOR_CONE_6DEGREES;
  251. if ( IsAlyxInInjuredMode() )
  252. return injuredCone;
  253. return cone;
  254. }
  255. //-----------------------------------------------------------------------------
  256. float CWeaponAlyxGun::GetMinRestTime( void )
  257. {
  258. if ( IsAlyxInInjuredMode() )
  259. return 1.5f;
  260. return BaseClass::GetMinRestTime();
  261. }
  262. //-----------------------------------------------------------------------------
  263. float CWeaponAlyxGun::GetMaxRestTime( void )
  264. {
  265. if ( IsAlyxInInjuredMode() )
  266. return 3.0f;
  267. return BaseClass::GetMaxRestTime();
  268. }