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.

298 lines
8.1 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npc_vehicledriver.h"
  9. #include "vehicle_apc.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. #define SF_APCDRIVER_NO_ROCKET_ATTACK 0x10000
  13. #define SF_APCDRIVER_NO_GUN_ATTACK 0x20000
  14. #define NPC_APCDRIVER_REMEMBER_TIME 4
  15. //-----------------------------------------------------------------------------
  16. // Purpose:
  17. //-----------------------------------------------------------------------------
  18. class CNPC_APCDriver : public CNPC_VehicleDriver
  19. {
  20. DECLARE_CLASS( CNPC_APCDriver, CNPC_VehicleDriver );
  21. public:
  22. DECLARE_DATADESC();
  23. DEFINE_CUSTOM_AI;
  24. virtual void Spawn( void );
  25. virtual void Activate( void );
  26. virtual bool FVisible( CBaseEntity *pTarget, int traceMask, CBaseEntity **ppBlocker );
  27. virtual bool WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions );
  28. virtual Class_T Classify ( void ) { return CLASS_COMBINE; }
  29. virtual void PrescheduleThink( );
  30. virtual Disposition_t IRelationType(CBaseEntity *pTarget);
  31. // AI
  32. virtual int RangeAttack1Conditions( float flDot, float flDist );
  33. virtual int RangeAttack2Conditions( float flDot, float flDist );
  34. private:
  35. // Are we being carried by a dropship?
  36. bool IsBeingCarried();
  37. // Enable, disable firing
  38. void InputEnableFiring( inputdata_t &inputdata );
  39. void InputDisableFiring( inputdata_t &inputdata );
  40. CHandle<CPropAPC> m_hAPC;
  41. float m_flTimeLastSeenEnemy;
  42. bool m_bFiringDisabled;
  43. };
  44. BEGIN_DATADESC( CNPC_APCDriver )
  45. //DEFINE_FIELD( m_hAPC, FIELD_EHANDLE ),
  46. DEFINE_FIELD( m_bFiringDisabled, FIELD_BOOLEAN ),
  47. DEFINE_FIELD( m_flTimeLastSeenEnemy, FIELD_TIME ),
  48. DEFINE_INPUTFUNC( FIELD_VOID, "EnableFiring", InputEnableFiring ),
  49. DEFINE_INPUTFUNC( FIELD_VOID, "DisableFiring", InputDisableFiring ),
  50. END_DATADESC()
  51. LINK_ENTITY_TO_CLASS( npc_apcdriver, CNPC_APCDriver );
  52. //------------------------------------------------------------------------------
  53. // Purpose :
  54. //------------------------------------------------------------------------------
  55. void CNPC_APCDriver::Spawn( void )
  56. {
  57. BaseClass::Spawn();
  58. m_flTimeLastSeenEnemy = -NPC_APCDRIVER_REMEMBER_TIME;
  59. CapabilitiesClear();
  60. CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 );
  61. m_bFiringDisabled = false;
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose:
  65. //-----------------------------------------------------------------------------
  66. void CNPC_APCDriver::Activate( void )
  67. {
  68. BaseClass::Activate();
  69. m_hAPC = dynamic_cast<CPropAPC*>((CBaseEntity*)m_hVehicleEntity);
  70. if ( !m_hAPC )
  71. {
  72. Warning( "npc_apcdriver %s couldn't find his apc named %s.\n", STRING(GetEntityName()), STRING(m_iszVehicleName) );
  73. UTIL_Remove( this );
  74. return;
  75. }
  76. SetParent( m_hAPC );
  77. SetAbsOrigin( m_hAPC->WorldSpaceCenter() );
  78. SetLocalAngles( vec3_angle );
  79. m_flDistTooFar = m_hAPC->MaxAttackRange();
  80. SetDistLook( m_hAPC->MaxAttackRange() );
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Enable, disable firing
  84. //-----------------------------------------------------------------------------
  85. void CNPC_APCDriver::InputEnableFiring( inputdata_t &inputdata )
  86. {
  87. m_bFiringDisabled = false;
  88. }
  89. void CNPC_APCDriver::InputDisableFiring( inputdata_t &inputdata )
  90. {
  91. m_bFiringDisabled = true;
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose: Let's not hate things the APC makes
  95. //-----------------------------------------------------------------------------
  96. Disposition_t CNPC_APCDriver::IRelationType(CBaseEntity *pTarget)
  97. {
  98. if ( pTarget == m_hAPC || (pTarget->GetOwnerEntity() == m_hAPC) )
  99. return D_LI;
  100. return BaseClass::IRelationType(pTarget);
  101. }
  102. //------------------------------------------------------------------------------
  103. // Are we being carried by a dropship?
  104. //------------------------------------------------------------------------------
  105. bool CNPC_APCDriver::IsBeingCarried()
  106. {
  107. // Inert if we're carried...
  108. Vector vecVelocity;
  109. m_hAPC->GetVelocity( &vecVelocity, NULL );
  110. return ( m_hAPC->GetMoveParent() != NULL ) || (fabs(vecVelocity.z) >= 15);
  111. }
  112. //------------------------------------------------------------------------------
  113. // Is the enemy visible?
  114. //------------------------------------------------------------------------------
  115. bool CNPC_APCDriver::WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions)
  116. {
  117. if ( m_hAPC->m_lifeState != LIFE_ALIVE )
  118. return false;
  119. if ( IsBeingCarried() || m_bFiringDisabled )
  120. return false;
  121. float flTargetDist = ownerPos.DistTo( targetPos );
  122. if (flTargetDist > m_hAPC->MaxAttackRange())
  123. return false;
  124. return true;
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Is the enemy visible?
  128. //-----------------------------------------------------------------------------
  129. bool CNPC_APCDriver::FVisible( CBaseEntity *pTarget, int traceMask, CBaseEntity **ppBlocker )
  130. {
  131. if ( m_hAPC->m_lifeState != LIFE_ALIVE )
  132. return false;
  133. if ( IsBeingCarried() || m_bFiringDisabled )
  134. return false;
  135. float flTargetDist = GetAbsOrigin().DistTo( pTarget->GetAbsOrigin() );
  136. if (flTargetDist > m_hAPC->MaxAttackRange())
  137. return false;
  138. bool bVisible = m_hAPC->FVisible( pTarget, traceMask, ppBlocker );
  139. if ( bVisible && (pTarget == GetEnemy()) )
  140. {
  141. m_flTimeLastSeenEnemy = gpGlobals->curtime;
  142. }
  143. if ( pTarget->IsPlayer() || pTarget->Classify() == CLASS_BULLSEYE )
  144. {
  145. if (!bVisible)
  146. {
  147. if ( ( gpGlobals->curtime - m_flTimeLastSeenEnemy ) <= NPC_APCDRIVER_REMEMBER_TIME )
  148. return true;
  149. }
  150. }
  151. return bVisible;
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Purpose:
  155. // Input :
  156. // Output :
  157. //-----------------------------------------------------------------------------
  158. int CNPC_APCDriver::RangeAttack1Conditions( float flDot, float flDist )
  159. {
  160. if ( HasSpawnFlags(SF_APCDRIVER_NO_GUN_ATTACK) )
  161. return COND_NONE;
  162. if ( m_hAPC->m_lifeState != LIFE_ALIVE )
  163. return COND_NONE;
  164. if ( IsBeingCarried() || m_bFiringDisabled )
  165. return COND_NONE;
  166. if ( !HasCondition( COND_SEE_ENEMY ) )
  167. return COND_NONE;
  168. if ( !m_hAPC->IsInPrimaryFiringCone() )
  169. return COND_NONE;
  170. // Vehicle not ready to fire again yet?
  171. if ( m_pVehicleInterface->Weapon_PrimaryCanFireAt() > gpGlobals->curtime + 0.1f )
  172. return COND_NONE;
  173. float flMinDist, flMaxDist;
  174. m_pVehicleInterface->Weapon_PrimaryRanges( &flMinDist, &flMaxDist );
  175. if (flDist < flMinDist)
  176. return COND_NONE;
  177. if (flDist > flMaxDist)
  178. return COND_NONE;
  179. return COND_CAN_RANGE_ATTACK1;
  180. }
  181. int CNPC_APCDriver::RangeAttack2Conditions( float flDot, float flDist )
  182. {
  183. if ( HasSpawnFlags(SF_APCDRIVER_NO_ROCKET_ATTACK) )
  184. return COND_NONE;
  185. if ( m_hAPC->m_lifeState != LIFE_ALIVE )
  186. return COND_NONE;
  187. if ( IsBeingCarried() || m_bFiringDisabled )
  188. return COND_NONE;
  189. if ( !HasCondition( COND_SEE_ENEMY ) )
  190. return COND_NONE;
  191. // Vehicle not ready to fire again yet?
  192. if ( m_pVehicleInterface->Weapon_SecondaryCanFireAt() > gpGlobals->curtime + 0.1f )
  193. return COND_NONE;
  194. float flMinDist, flMaxDist;
  195. m_pVehicleInterface->Weapon_SecondaryRanges( &flMinDist, &flMaxDist );
  196. if (flDist < flMinDist)
  197. return COND_NONE;
  198. if (flDist > flMaxDist)
  199. return COND_NONE;
  200. return COND_CAN_RANGE_ATTACK2;
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Aim the laser dot!
  204. //-----------------------------------------------------------------------------
  205. void CNPC_APCDriver::PrescheduleThink( )
  206. {
  207. BaseClass::PrescheduleThink();
  208. if ( m_hAPC->m_lifeState == LIFE_ALIVE )
  209. {
  210. if ( GetEnemy() )
  211. {
  212. m_hAPC->AimPrimaryWeapon( GetEnemy()->BodyTarget( GetAbsOrigin(), false ) );
  213. }
  214. m_hAPC->AimSecondaryWeaponAt( GetEnemy() );
  215. }
  216. else if ( m_hAPC->m_lifeState == LIFE_DEAD )
  217. {
  218. UTIL_Remove( this );
  219. }
  220. }
  221. //-----------------------------------------------------------------------------
  222. //
  223. // Schedules
  224. //
  225. //-----------------------------------------------------------------------------
  226. AI_BEGIN_CUSTOM_NPC( npc_apcdriver, CNPC_APCDriver )
  227. AI_END_CUSTOM_NPC()