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.

335 lines
9.1 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "BasePropDoor.h"
  8. #include "ai_basehumanoid.h"
  9. #include "ai_blended_movement.h"
  10. #include "ai_navigator.h"
  11. #include "ai_memory.h"
  12. #ifdef HL2_DLL
  13. #include "ai_interactions.h"
  14. #endif
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. //-----------------------------------------------------------------------------
  18. // Purpose: This is a generic function (to be implemented by sub-classes) to
  19. // handle specific interactions between different types of characters
  20. // (For example the barnacle grabbing an NPC)
  21. // Input : Constant for the type of interaction
  22. // Output : true - if sub-class has a response for the interaction
  23. // false - if sub-class has no response
  24. //-----------------------------------------------------------------------------
  25. bool CAI_BaseHumanoid::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
  26. {
  27. #ifdef HL2_DLL
  28. // Annoying to ifdef this out. Copy it into all the HL2 specific humanoid NPC's instead?
  29. if ( interactionType == g_interactionBarnacleVictimDangle )
  30. {
  31. // Force choosing of a new schedule
  32. ClearSchedule( "Grabbed by a barnacle" );
  33. return true;
  34. }
  35. else if ( interactionType == g_interactionBarnacleVictimReleased )
  36. {
  37. // Destroy the entity, the barnacle is going to use the ragdoll that it is releasing
  38. // as the corpse.
  39. UTIL_Remove( this );
  40. return true;
  41. }
  42. #endif
  43. return BaseClass::HandleInteraction( interactionType, data, sourceEnt);
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Purpose: check ammo
  47. //-----------------------------------------------------------------------------
  48. void CAI_BaseHumanoid::CheckAmmo( void )
  49. {
  50. BaseClass::CheckAmmo();
  51. // FIXME: put into GatherConditions()?
  52. // FIXME: why isn't this a baseclass function?
  53. if (!GetActiveWeapon())
  54. return;
  55. // Don't do this while holstering / unholstering
  56. if ( IsWeaponStateChanging() )
  57. return;
  58. if (GetActiveWeapon()->UsesPrimaryAmmo())
  59. {
  60. if (!GetActiveWeapon()->HasPrimaryAmmo() )
  61. {
  62. SetCondition(COND_NO_PRIMARY_AMMO);
  63. }
  64. else if (GetActiveWeapon()->UsesClipsForAmmo1() && GetActiveWeapon()->Clip1() < (GetActiveWeapon()->GetMaxClip1() / 4 + 1))
  65. {
  66. // don't check for low ammo if you're near the max range of the weapon
  67. SetCondition(COND_LOW_PRIMARY_AMMO);
  68. }
  69. }
  70. if (!GetActiveWeapon()->HasSecondaryAmmo() )
  71. {
  72. if ( GetActiveWeapon()->UsesClipsForAmmo2() )
  73. {
  74. SetCondition(COND_NO_SECONDARY_AMMO);
  75. }
  76. }
  77. }
  78. //-----------------------------------------------------------------------------
  79. // TASK_RANGE_ATTACK1
  80. //-----------------------------------------------------------------------------
  81. void CAI_BaseHumanoid::BuildScheduleTestBits( )
  82. {
  83. BaseClass::BuildScheduleTestBits();
  84. if ( CapabilitiesGet() & bits_CAP_USE_SHOT_REGULATOR )
  85. {
  86. if ( GetShotRegulator()->IsInRestInterval() )
  87. {
  88. ClearCustomInterruptCondition( COND_CAN_RANGE_ATTACK1 );
  89. }
  90. }
  91. }
  92. //-----------------------------------------------------------------------------
  93. //-----------------------------------------------------------------------------
  94. static bool IsSmall( CBaseEntity *pBlocker )
  95. {
  96. CCollisionProperty *pCollisionProp = pBlocker->CollisionProp();
  97. int nSmaller = 0;
  98. Vector vecSize = pCollisionProp->OBBMaxs() - pCollisionProp->OBBMins();
  99. for ( int i = 0; i < 3; i++ )
  100. {
  101. if ( vecSize[i] >= 42 )
  102. return false;
  103. if ( vecSize[i] <= 30 )
  104. {
  105. nSmaller++;
  106. }
  107. }
  108. return ( nSmaller >= 2 );
  109. }
  110. bool CAI_BaseHumanoid::OnMoveBlocked( AIMoveResult_t *pResult )
  111. {
  112. if ( *pResult != AIMR_BLOCKED_NPC && GetNavigator()->GetBlockingEntity() && !GetNavigator()->GetBlockingEntity()->IsNPC() )
  113. {
  114. CBaseEntity *pBlocker = GetNavigator()->GetBlockingEntity();
  115. float massBonus = ( IsNavigationUrgent() ) ? 40.0 : 0;
  116. if ( pBlocker->GetMoveType() == MOVETYPE_VPHYSICS &&
  117. pBlocker != GetGroundEntity() &&
  118. !pBlocker->IsNavIgnored() &&
  119. !dynamic_cast<CBasePropDoor *>(pBlocker) &&
  120. pBlocker->VPhysicsGetObject() &&
  121. pBlocker->VPhysicsGetObject()->IsMoveable() &&
  122. ( pBlocker->VPhysicsGetObject()->GetMass() <= 35.0 + massBonus + 0.1 ||
  123. ( pBlocker->VPhysicsGetObject()->GetMass() <= 50.0 + massBonus + 0.1 && IsSmall( pBlocker ) ) ) )
  124. {
  125. DbgNavMsg1( this, "Setting ignore on object %s", pBlocker->GetDebugName() );
  126. pBlocker->SetNavIgnore( 2.5 );
  127. }
  128. #if 0
  129. else
  130. {
  131. CPhysicsProp *pProp = dynamic_cast<CPhysicsProp*>( pBlocker );
  132. if ( pProp && pProp->GetHealth() && pProp->GetExplosiveDamage() == 0.0 && GetActiveWeapon() && !GetActiveWeapon()->ClassMatches( "weapon_rpg" ) )
  133. {
  134. Msg( "!\n" );
  135. // Destroy!
  136. }
  137. }
  138. #endif
  139. }
  140. return BaseClass::OnMoveBlocked( pResult );
  141. }
  142. //-----------------------------------------------------------------------------
  143. //-----------------------------------------------------------------------------
  144. #define SNEAK_ATTACK_DIST 360.0f // 30 feet
  145. void CAI_BaseHumanoid::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  146. {
  147. bool bSneakAttacked = false;
  148. if( ptr->hitgroup == HITGROUP_HEAD )
  149. {
  150. if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && info.GetAttacker() != GetEnemy() && !IsInAScript() )
  151. {
  152. // Shot in the head by a player I've never seen. In this case the player
  153. // has gotten the drop on this enemy and such an attack is always lethal (at close range)
  154. bSneakAttacked = true;
  155. AIEnemiesIter_t iter;
  156. for( AI_EnemyInfo_t *pMemory = GetEnemies()->GetFirst(&iter); pMemory != NULL; pMemory = GetEnemies()->GetNext(&iter) )
  157. {
  158. if ( pMemory->hEnemy == info.GetAttacker() )
  159. {
  160. bSneakAttacked = false;
  161. break;
  162. }
  163. }
  164. float flDist;
  165. flDist = (info.GetAttacker()->GetAbsOrigin() - GetAbsOrigin()).Length();
  166. if( flDist > SNEAK_ATTACK_DIST )
  167. {
  168. bSneakAttacked = false;
  169. }
  170. }
  171. }
  172. if( bSneakAttacked )
  173. {
  174. CTakeDamageInfo newInfo = info;
  175. newInfo.SetDamage( GetHealth() );
  176. BaseClass::TraceAttack( newInfo, vecDir, ptr, pAccumulator );
  177. return;
  178. }
  179. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  180. }
  181. //-----------------------------------------------------------------------------
  182. // TASK_RANGE_ATTACK1
  183. //-----------------------------------------------------------------------------
  184. void CAI_BaseHumanoid::StartTaskRangeAttack1( const Task_t *pTask )
  185. {
  186. if ( ( CapabilitiesGet() & bits_CAP_USE_SHOT_REGULATOR ) == 0 )
  187. {
  188. BaseClass::StartTask( pTask );
  189. return;
  190. }
  191. // Can't shoot if we're in the rest interval; fail the schedule
  192. if ( GetShotRegulator()->IsInRestInterval() )
  193. {
  194. TaskFail( "Shot regulator in rest interval" );
  195. return;
  196. }
  197. if ( GetShotRegulator()->ShouldShoot() )
  198. {
  199. OnRangeAttack1();
  200. ResetIdealActivity( ACT_RANGE_ATTACK1 );
  201. }
  202. else
  203. {
  204. // This can happen if we start while in the middle of a burst
  205. // which shouldn't happen, but given the chaotic nature of our AI system,
  206. // does occasionally happen.
  207. ResetIdealActivity( ACT_IDLE_ANGRY );
  208. }
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Starting Tasks
  212. //-----------------------------------------------------------------------------
  213. void CAI_BaseHumanoid::StartTask( const Task_t *pTask )
  214. {
  215. switch( pTask->iTask )
  216. {
  217. case TASK_RANGE_ATTACK1:
  218. StartTaskRangeAttack1( pTask );
  219. break;
  220. default:
  221. BaseClass::StartTask( pTask );
  222. }
  223. }
  224. //-----------------------------------------------------------------------------
  225. // TASK_RANGE_ATTACK1 / TASK_RANGE_ATTACK2 / etc.
  226. //-----------------------------------------------------------------------------
  227. void CAI_BaseHumanoid::RunTaskRangeAttack1( const Task_t *pTask )
  228. {
  229. if ( ( CapabilitiesGet() & bits_CAP_USE_SHOT_REGULATOR ) == 0 )
  230. {
  231. BaseClass::RunTask( pTask );
  232. return;
  233. }
  234. AutoMovement( );
  235. Vector vecEnemyLKP = GetEnemyLKP();
  236. // If our enemy was killed, but I'm not done animating, the last known position comes
  237. // back as the origin and makes the me face the world origin if my attack schedule
  238. // doesn't break when my enemy dies. (sjb)
  239. if( vecEnemyLKP != vec3_origin )
  240. {
  241. if ( ( pTask->iTask == TASK_RANGE_ATTACK1 || pTask->iTask == TASK_RELOAD ) &&
  242. ( CapabilitiesGet() & bits_CAP_AIM_GUN ) &&
  243. FInAimCone( vecEnemyLKP ) )
  244. {
  245. // Arms will aim, so leave body yaw as is
  246. GetMotor()->SetIdealYawAndUpdate( GetMotor()->GetIdealYaw(), AI_KEEP_YAW_SPEED );
  247. }
  248. else
  249. {
  250. GetMotor()->SetIdealYawToTargetAndUpdate( vecEnemyLKP, AI_KEEP_YAW_SPEED );
  251. }
  252. }
  253. if ( IsActivityFinished() )
  254. {
  255. if ( !GetEnemy() || !GetEnemy()->IsAlive() )
  256. {
  257. TaskComplete();
  258. return;
  259. }
  260. if ( !GetShotRegulator()->IsInRestInterval() )
  261. {
  262. if ( GetShotRegulator()->ShouldShoot() )
  263. {
  264. OnRangeAttack1();
  265. ResetIdealActivity( ACT_RANGE_ATTACK1 );
  266. }
  267. return;
  268. }
  269. TaskComplete();
  270. }
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Running Tasks
  274. //-----------------------------------------------------------------------------
  275. void CAI_BaseHumanoid::RunTask( const Task_t *pTask )
  276. {
  277. switch( pTask->iTask )
  278. {
  279. case TASK_RANGE_ATTACK1:
  280. RunTaskRangeAttack1( pTask );
  281. break;
  282. default:
  283. BaseClass::RunTask( pTask );
  284. }
  285. }