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.

5048 lines
152 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Antlion Guard
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_hint.h"
  8. #include "ai_localnavigator.h"
  9. #include "ai_memory.h"
  10. #include "ai_moveprobe.h"
  11. #include "npcevent.h"
  12. #include "IEffects.h"
  13. #include "ndebugoverlay.h"
  14. #include "soundent.h"
  15. #include "soundenvelope.h"
  16. #include "ai_squad.h"
  17. #include "ai_network.h"
  18. #include "ai_pathfinder.h"
  19. #include "ai_navigator.h"
  20. #include "ai_senses.h"
  21. #include "npc_rollermine.h"
  22. #include "ai_blended_movement.h"
  23. #include "physics_prop_ragdoll.h"
  24. #include "iservervehicle.h"
  25. #include "player_pickup.h"
  26. #include "props.h"
  27. #include "antlion_dust.h"
  28. #include "npc_antlion.h"
  29. #include "decals.h"
  30. #include "prop_combine_ball.h"
  31. #include "eventqueue.h"
  32. #include "te_effect_dispatch.h"
  33. #include "Sprite.h"
  34. #include "particle_parse.h"
  35. #include "particle_system.h"
  36. // memdbgon must be the last include file in a .cpp file!!!
  37. #include "tier0/memdbgon.h"
  38. inline void TraceHull_SkipPhysics( const Vector &vecAbsStart, const Vector &vecAbsEnd, const Vector &hullMin,
  39. const Vector &hullMax, unsigned int mask, const CBaseEntity *ignore,
  40. int collisionGroup, trace_t *ptr, float minMass );
  41. ConVar g_debug_antlionguard( "g_debug_antlionguard", "0" );
  42. ConVar sk_antlionguard_dmg_charge( "sk_antlionguard_dmg_charge", "0" );
  43. ConVar sk_antlionguard_dmg_shove( "sk_antlionguard_dmg_shove", "0" );
  44. #if HL2_EPISODIC
  45. // When enabled, add code to have the antlion bleed profusely as it is badly injured.
  46. #define ANTLIONGUARD_BLOOD_EFFECTS 2
  47. ConVar g_antlionguard_hemorrhage( "g_antlionguard_hemorrhage", "1", FCVAR_NONE, "If 1, guard will emit a bleeding particle effect when wounded." );
  48. #endif
  49. // Spawnflags
  50. #define SF_ANTLIONGUARD_SERVERSIDE_RAGDOLL ( 1 << 16 )
  51. #define SF_ANTLIONGUARD_INSIDE_FOOTSTEPS ( 1 << 17 )
  52. #define ENVELOPE_CONTROLLER (CSoundEnvelopeController::GetController())
  53. #define ANTLIONGUARD_MODEL "models/antlion_guard.mdl"
  54. #define MIN_BLAST_DAMAGE 25.0f
  55. #define MIN_CRUSH_DAMAGE 20.0f
  56. //==================================================
  57. //
  58. // Antlion Guard
  59. //
  60. //==================================================
  61. #define ANTLIONGUARD_MAX_OBJECTS 128
  62. #define ANTLIONGUARD_MIN_OBJECT_MASS 8
  63. #define ANTLIONGUARD_MAX_OBJECT_MASS 750
  64. #define ANTLIONGUARD_FARTHEST_PHYSICS_OBJECT 350
  65. #define ANTLIONGUARD_OBJECTFINDING_FOV DOT_45DEGREE // 1/sqrt(2)
  66. //Melee definitions
  67. #define ANTLIONGUARD_MELEE1_RANGE 156.0f
  68. #define ANTLIONGUARD_MELEE1_CONE 0.7f
  69. // Antlion summoning
  70. #define ANTLIONGUARD_SUMMON_COUNT 3
  71. // Sight
  72. #define ANTLIONGUARD_FOV_NORMAL -0.4f
  73. // cavern guard's poisoning behavior
  74. #if HL2_EPISODIC
  75. #define ANTLIONGUARD_POISON_TO 12 // we only poison Gordon down to twelve to give him a chance to regen up to 20 by the next charge
  76. #endif
  77. #define ANTLIONGUARD_CHARGE_MIN 256
  78. #define ANTLIONGUARD_CHARGE_MAX 2048
  79. ConVar sk_antlionguard_health( "sk_antlionguard_health", "0" );
  80. int g_interactionAntlionGuardFoundPhysicsObject = 0; // We're moving to a physics object to shove it, don't all choose the same object
  81. int g_interactionAntlionGuardShovedPhysicsObject = 0; // We've punted an object, it is now clear to be chosen by others
  82. //==================================================
  83. // AntlionGuardSchedules
  84. //==================================================
  85. enum
  86. {
  87. SCHED_ANTLIONGUARD_CHARGE = LAST_SHARED_SCHEDULE,
  88. SCHED_ANTLIONGUARD_CHARGE_CRASH,
  89. SCHED_ANTLIONGUARD_CHARGE_CANCEL,
  90. SCHED_ANTLIONGUARD_PHYSICS_ATTACK,
  91. SCHED_ANTLIONGUARD_PHYSICS_DAMAGE_HEAVY,
  92. SCHED_ANTLIONGUARD_UNBURROW,
  93. SCHED_ANTLIONGUARD_CHARGE_TARGET,
  94. SCHED_ANTLIONGUARD_FIND_CHARGE_POSITION,
  95. SCHED_ANTLIONGUARD_MELEE_ATTACK1,
  96. SCHED_ANTLIONGUARD_SUMMON,
  97. SCHED_ANTLIONGUARD_PATROL_RUN,
  98. SCHED_ANTLIONGUARD_ROAR,
  99. SCHED_ANTLIONGUARD_CHASE_ENEMY_TOLERANCE,
  100. SCHED_FORCE_ANTLIONGUARD_PHYSICS_ATTACK,
  101. SCHED_ANTLIONGUARD_CANT_ATTACK,
  102. SCHED_ANTLIONGUARD_TAKE_COVER_FROM_ENEMY,
  103. SCHED_ANTLIONGUARD_CHASE_ENEMY
  104. };
  105. //==================================================
  106. // AntlionGuardTasks
  107. //==================================================
  108. enum
  109. {
  110. TASK_ANTLIONGUARD_CHARGE = LAST_SHARED_TASK,
  111. TASK_ANTLIONGUARD_GET_PATH_TO_PHYSOBJECT,
  112. TASK_ANTLIONGUARD_SHOVE_PHYSOBJECT,
  113. TASK_ANTLIONGUARD_SUMMON,
  114. TASK_ANTLIONGUARD_SET_FLINCH_ACTIVITY,
  115. TASK_ANTLIONGUARD_GET_PATH_TO_CHARGE_POSITION,
  116. TASK_ANTLIONGUARD_GET_PATH_TO_NEAREST_NODE,
  117. TASK_ANTLIONGUARD_GET_CHASE_PATH_ENEMY_TOLERANCE,
  118. TASK_ANTLIONGUARD_OPPORTUNITY_THROW,
  119. TASK_ANTLIONGUARD_FIND_PHYSOBJECT,
  120. };
  121. //==================================================
  122. // AntlionGuardConditions
  123. //==================================================
  124. enum
  125. {
  126. COND_ANTLIONGUARD_PHYSICS_TARGET = LAST_SHARED_CONDITION,
  127. COND_ANTLIONGUARD_PHYSICS_TARGET_INVALID,
  128. COND_ANTLIONGUARD_HAS_CHARGE_TARGET,
  129. COND_ANTLIONGUARD_CAN_SUMMON,
  130. COND_ANTLIONGUARD_CAN_CHARGE
  131. };
  132. enum
  133. {
  134. SQUAD_SLOT_ANTLIONGUARD_CHARGE = LAST_SHARED_SQUADSLOT,
  135. };
  136. //==================================================
  137. // AntlionGuard Activities
  138. //==================================================
  139. Activity ACT_ANTLIONGUARD_SEARCH;
  140. Activity ACT_ANTLIONGUARD_PEEK_FLINCH;
  141. Activity ACT_ANTLIONGUARD_PEEK_ENTER;
  142. Activity ACT_ANTLIONGUARD_PEEK_EXIT;
  143. Activity ACT_ANTLIONGUARD_PEEK1;
  144. Activity ACT_ANTLIONGUARD_BARK;
  145. Activity ACT_ANTLIONGUARD_PEEK_SIGHTED;
  146. Activity ACT_ANTLIONGUARD_SHOVE_PHYSOBJECT;
  147. Activity ACT_ANTLIONGUARD_FLINCH_LIGHT;
  148. Activity ACT_ANTLIONGUARD_UNBURROW;
  149. Activity ACT_ANTLIONGUARD_ROAR;
  150. Activity ACT_ANTLIONGUARD_RUN_HURT;
  151. // Flinches
  152. Activity ACT_ANTLIONGUARD_PHYSHIT_FR;
  153. Activity ACT_ANTLIONGUARD_PHYSHIT_FL;
  154. Activity ACT_ANTLIONGUARD_PHYSHIT_RR;
  155. Activity ACT_ANTLIONGUARD_PHYSHIT_RL;
  156. // Charge
  157. Activity ACT_ANTLIONGUARD_CHARGE_START;
  158. Activity ACT_ANTLIONGUARD_CHARGE_CANCEL;
  159. Activity ACT_ANTLIONGUARD_CHARGE_RUN;
  160. Activity ACT_ANTLIONGUARD_CHARGE_CRASH;
  161. Activity ACT_ANTLIONGUARD_CHARGE_STOP;
  162. Activity ACT_ANTLIONGUARD_CHARGE_HIT;
  163. Activity ACT_ANTLIONGUARD_CHARGE_ANTICIPATION;
  164. // Anim events
  165. int AE_ANTLIONGUARD_CHARGE_HIT;
  166. int AE_ANTLIONGUARD_SHOVE_PHYSOBJECT;
  167. int AE_ANTLIONGUARD_SHOVE;
  168. int AE_ANTLIONGUARD_FOOTSTEP_LIGHT;
  169. int AE_ANTLIONGUARD_FOOTSTEP_HEAVY;
  170. int AE_ANTLIONGUARD_CHARGE_EARLYOUT;
  171. int AE_ANTLIONGUARD_VOICE_GROWL;
  172. int AE_ANTLIONGUARD_VOICE_BARK;
  173. int AE_ANTLIONGUARD_VOICE_PAIN;
  174. int AE_ANTLIONGUARD_VOICE_SQUEEZE;
  175. int AE_ANTLIONGUARD_VOICE_SCRATCH;
  176. int AE_ANTLIONGUARD_VOICE_GRUNT;
  177. int AE_ANTLIONGUARD_VOICE_ROAR;
  178. int AE_ANTLIONGUARD_BURROW_OUT;
  179. struct PhysicsObjectCriteria_t
  180. {
  181. CBaseEntity *pTarget;
  182. Vector vecCenter; // Center point to look around
  183. float flRadius; // Radius to search within
  184. float flTargetCone;
  185. bool bPreferObjectsAlongTargetVector; // Prefer objects that we can strike easily as we move towards our target
  186. float flNearRadius; // If we won't hit the player with the object, but get this close, throw anyway
  187. };
  188. #define MAX_FAILED_PHYSOBJECTS 8
  189. //-----------------------------------------------------------------------------
  190. // Purpose:
  191. //-----------------------------------------------------------------------------
  192. class CNPC_AntlionGuard : public CAI_BlendedNPC
  193. {
  194. public:
  195. DECLARE_CLASS( CNPC_AntlionGuard, CAI_BlendedNPC );
  196. DECLARE_SERVERCLASS();
  197. DECLARE_DATADESC();
  198. CNPC_AntlionGuard( void );
  199. Class_T Classify( void ) { return CLASS_ANTLION; }
  200. virtual int GetSoundInterests( void ) { return (SOUND_WORLD|SOUND_COMBAT|SOUND_PLAYER|SOUND_DANGER); }
  201. virtual bool QueryHearSound( CSound *pSound );
  202. const impactdamagetable_t &GetPhysicsImpactDamageTable( void );
  203. virtual int MeleeAttack1Conditions( float flDot, float flDist );
  204. virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
  205. virtual int TranslateSchedule( int scheduleType );
  206. virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info );
  207. virtual void DeathSound( const CTakeDamageInfo &info );
  208. virtual void Event_Killed( const CTakeDamageInfo &info );
  209. virtual int SelectSchedule( void );
  210. virtual float GetAutoAimRadius() { return 36.0f; }
  211. virtual void Precache( void );
  212. virtual void Spawn( void );
  213. virtual void Activate( void );
  214. virtual void HandleAnimEvent( animevent_t *pEvent );
  215. virtual void UpdateEfficiency( bool bInPVS ) { SetEfficiency( ( GetSleepState() != AISS_AWAKE ) ? AIE_DORMANT : AIE_NORMAL ); SetMoveEfficiency( AIME_NORMAL ); }
  216. virtual void PrescheduleThink( void );
  217. virtual void GatherConditions( void );
  218. virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
  219. virtual void StartTask( const Task_t *pTask );
  220. virtual void RunTask( const Task_t *pTask );
  221. virtual void StopLoopingSounds();
  222. virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sender );
  223. // Input handlers.
  224. void InputSetShoveTarget( inputdata_t &inputdata );
  225. void InputSetChargeTarget( inputdata_t &inputdata );
  226. void InputClearChargeTarget( inputdata_t &inputdata );
  227. void InputUnburrow( inputdata_t &inputdata );
  228. void InputRagdoll( inputdata_t &inputdata );
  229. void InputEnableBark( inputdata_t &inputdata );
  230. void InputDisableBark( inputdata_t &inputdata );
  231. void InputSummonedAntlionDied( inputdata_t &inputdata );
  232. void InputEnablePreferPhysicsAttack( inputdata_t &inputdata );
  233. void InputDisablePreferPhysicsAttack( inputdata_t &inputdata );
  234. virtual bool IsLightDamage( const CTakeDamageInfo &info );
  235. virtual bool IsHeavyDamage( const CTakeDamageInfo &info );
  236. virtual bool OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval );
  237. virtual bool BecomeRagdollOnClient( const Vector &force );
  238. virtual void UpdateOnRemove( void );
  239. virtual bool IsUnreachable( CBaseEntity* pEntity ); // Is entity is unreachable?
  240. virtual float MaxYawSpeed( void );
  241. virtual bool OverrideMove( float flInterval );
  242. virtual bool CanBecomeRagdoll( void );
  243. virtual bool ShouldProbeCollideAgainstEntity( CBaseEntity *pEntity );
  244. virtual Activity NPC_TranslateActivity( Activity baseAct );
  245. #if HL2_EPISODIC
  246. //---------------------------------
  247. // Navigation & Movement -- prevent stopping paths for the guard
  248. //---------------------------------
  249. class CNavigator : public CAI_ComponentWithOuter<CNPC_AntlionGuard, CAI_Navigator>
  250. {
  251. typedef CAI_ComponentWithOuter<CNPC_AntlionGuard, CAI_Navigator> BaseClass;
  252. public:
  253. CNavigator( CNPC_AntlionGuard *pOuter )
  254. : BaseClass( pOuter )
  255. {
  256. }
  257. bool GetStoppingPath( CAI_WaypointList *pClippedWaypoints );
  258. };
  259. CAI_Navigator * CreateNavigator() { return new CNavigator( this ); }
  260. #endif
  261. DEFINE_CUSTOM_AI;
  262. private:
  263. inline bool CanStandAtPoint( const Vector &vecPos, Vector *pOut );
  264. bool RememberFailedPhysicsTarget( CBaseEntity *pTarget );
  265. void GetPhysicsShoveDir( CBaseEntity *pObject, float flMass, Vector *pOut );
  266. void CreateGlow( CSprite **pSprite, const char *pAttachName );
  267. void DestroyGlows( void );
  268. void Footstep( bool bHeavy );
  269. int SelectCombatSchedule( void );
  270. int SelectUnreachableSchedule( void );
  271. bool CanSummon( bool bIgnoreTime );
  272. void SummonAntlions( void );
  273. void ChargeLookAhead( void );
  274. bool EnemyIsRightInFrontOfMe( CBaseEntity **pEntity );
  275. bool HandleChargeImpact( Vector vecImpact, CBaseEntity *pEntity );
  276. bool ShouldCharge( const Vector &startPos, const Vector &endPos, bool useTime, bool bCheckForCancel );
  277. bool ShouldWatchEnemy( void );
  278. void ImpactShock( const Vector &origin, float radius, float magnitude, CBaseEntity *pIgnored = NULL );
  279. void BuildScheduleTestBits( void );
  280. void Shove( void );
  281. void FoundEnemy( void );
  282. void LostEnemy( void );
  283. void UpdateHead( void );
  284. void UpdatePhysicsTarget( bool bPreferObjectsAlongTargetVector, float flRadius = ANTLIONGUARD_FARTHEST_PHYSICS_OBJECT );
  285. void MaintainPhysicsTarget( void );
  286. void ChargeDamage( CBaseEntity *pTarget );
  287. void StartSounds( void );
  288. void SetHeavyDamageAnim( const Vector &vecSource );
  289. float ChargeSteer( void );
  290. CBaseEntity *FindPhysicsObjectTarget( const PhysicsObjectCriteria_t &criteria );
  291. Vector GetPhysicsHitPosition( CBaseEntity *pObject, CBaseEntity *pTarget, Vector *vecTrajectory, float *flClearDistance );
  292. bool CanStandAtShoveTarget( CBaseEntity *pShoveObject, CBaseEntity *pTarget, Vector *pOut );
  293. CBaseEntity *GetNextShoveTarget( CBaseEntity *pLastEntity, AISightIter_t &iter );
  294. int m_nFlinchActivity;
  295. bool m_bStopped;
  296. bool m_bIsBurrowed;
  297. bool m_bBarkEnabled;
  298. float m_flNextSummonTime;
  299. int m_iNumLiveAntlions;
  300. float m_flSearchNoiseTime;
  301. float m_flAngerNoiseTime;
  302. float m_flBreathTime;
  303. float m_flChargeTime;
  304. float m_flPhysicsCheckTime;
  305. float m_flNextHeavyFlinchTime;
  306. float m_flNextRoarTime;
  307. int m_iChargeMisses;
  308. bool m_bDecidedNotToStop;
  309. bool m_bPreferPhysicsAttack;
  310. CNetworkVar( bool, m_bCavernBreed ); // If this guard is meant to be a cavern dweller (uses different assets)
  311. CNetworkVar( bool, m_bInCavern ); // Behavioral hint telling the guard to change his behavior
  312. Vector m_vecPhysicsTargetStartPos;
  313. Vector m_vecPhysicsHitPosition;
  314. EHANDLE m_hShoveTarget;
  315. EHANDLE m_hChargeTarget;
  316. EHANDLE m_hChargeTargetPosition;
  317. EHANDLE m_hOldTarget;
  318. EHANDLE m_hPhysicsTarget;
  319. CUtlVectorFixed<EHANDLE, MAX_FAILED_PHYSOBJECTS> m_FailedPhysicsTargets;
  320. COutputEvent m_OnSummon;
  321. CSoundPatch *m_pGrowlHighSound;
  322. CSoundPatch *m_pGrowlLowSound;
  323. CSoundPatch *m_pGrowlIdleSound;
  324. CSoundPatch *m_pBreathSound;
  325. CSoundPatch *m_pConfusedSound;
  326. string_t m_iszPhysicsPropClass;
  327. string_t m_strShoveTargets;
  328. CSprite *m_hCaveGlow[2];
  329. #if ANTLIONGUARD_BLOOD_EFFECTS
  330. CNetworkVar( uint8, m_iBleedingLevel );
  331. unsigned char GetBleedingLevel( void ) const;
  332. #endif
  333. protected:
  334. int m_poseThrow;
  335. int m_poseHead_Yaw, m_poseHead_Pitch;
  336. virtual void PopulatePoseParameters( void );
  337. // inline accessors
  338. public:
  339. inline bool IsCavernBreed( void ) const { return m_bCavernBreed; }
  340. inline bool IsInCavern( void ) const { return m_bInCavern; }
  341. };
  342. //==================================================
  343. // CNPC_AntlionGuard::m_DataDesc
  344. //==================================================
  345. BEGIN_DATADESC( CNPC_AntlionGuard )
  346. DEFINE_FIELD( m_nFlinchActivity, FIELD_INTEGER ),
  347. DEFINE_FIELD( m_bStopped, FIELD_BOOLEAN ),
  348. DEFINE_KEYFIELD( m_bIsBurrowed, FIELD_BOOLEAN, "startburrowed" ),
  349. DEFINE_KEYFIELD( m_bBarkEnabled, FIELD_BOOLEAN, "allowbark" ),
  350. DEFINE_FIELD( m_flNextSummonTime, FIELD_TIME ),
  351. DEFINE_FIELD( m_iNumLiveAntlions, FIELD_INTEGER ),
  352. DEFINE_FIELD( m_flSearchNoiseTime, FIELD_TIME ),
  353. DEFINE_FIELD( m_flAngerNoiseTime, FIELD_TIME ),
  354. DEFINE_FIELD( m_flBreathTime, FIELD_TIME ),
  355. DEFINE_FIELD( m_flChargeTime, FIELD_TIME ),
  356. DEFINE_FIELD( m_hShoveTarget, FIELD_EHANDLE ),
  357. DEFINE_FIELD( m_hChargeTarget, FIELD_EHANDLE ),
  358. DEFINE_FIELD( m_hChargeTargetPosition, FIELD_EHANDLE ),
  359. DEFINE_FIELD( m_hOldTarget, FIELD_EHANDLE ),
  360. // m_FailedPhysicsTargets // We do not save/load these
  361. DEFINE_FIELD( m_hPhysicsTarget, FIELD_EHANDLE ),
  362. DEFINE_FIELD( m_vecPhysicsTargetStartPos, FIELD_POSITION_VECTOR ),
  363. DEFINE_FIELD( m_vecPhysicsHitPosition, FIELD_POSITION_VECTOR ),
  364. DEFINE_FIELD( m_flPhysicsCheckTime, FIELD_TIME ),
  365. DEFINE_FIELD( m_flNextHeavyFlinchTime, FIELD_TIME ),
  366. DEFINE_FIELD( m_flNextRoarTime, FIELD_TIME ),
  367. DEFINE_FIELD( m_iChargeMisses, FIELD_INTEGER ),
  368. DEFINE_FIELD( m_bDecidedNotToStop, FIELD_BOOLEAN ),
  369. DEFINE_FIELD( m_bPreferPhysicsAttack, FIELD_BOOLEAN ),
  370. #if ANTLIONGUARD_BLOOD_EFFECTS
  371. DEFINE_FIELD( m_iBleedingLevel, FIELD_CHARACTER ),
  372. #endif
  373. DEFINE_KEYFIELD( m_bCavernBreed,FIELD_BOOLEAN, "cavernbreed" ),
  374. DEFINE_KEYFIELD( m_bInCavern, FIELD_BOOLEAN, "incavern" ),
  375. DEFINE_KEYFIELD( m_strShoveTargets, FIELD_STRING, "shovetargets" ),
  376. DEFINE_AUTO_ARRAY( m_hCaveGlow, FIELD_CLASSPTR ),
  377. DEFINE_OUTPUT( m_OnSummon, "OnSummon" ),
  378. DEFINE_SOUNDPATCH( m_pGrowlHighSound ),
  379. DEFINE_SOUNDPATCH( m_pGrowlLowSound ),
  380. DEFINE_SOUNDPATCH( m_pGrowlIdleSound ),
  381. DEFINE_SOUNDPATCH( m_pBreathSound ),
  382. DEFINE_SOUNDPATCH( m_pConfusedSound ),
  383. DEFINE_INPUTFUNC( FIELD_STRING, "SetShoveTarget", InputSetShoveTarget ),
  384. DEFINE_INPUTFUNC( FIELD_STRING, "SetChargeTarget", InputSetChargeTarget ),
  385. DEFINE_INPUTFUNC( FIELD_VOID, "ClearChargeTarget", InputClearChargeTarget ),
  386. DEFINE_INPUTFUNC( FIELD_VOID, "Unburrow", InputUnburrow ),
  387. DEFINE_INPUTFUNC( FIELD_VOID, "Ragdoll", InputRagdoll ),
  388. DEFINE_INPUTFUNC( FIELD_VOID, "EnableBark", InputEnableBark ),
  389. DEFINE_INPUTFUNC( FIELD_VOID, "DisableBark", InputDisableBark ),
  390. DEFINE_INPUTFUNC( FIELD_VOID, "SummonedAntlionDied", InputSummonedAntlionDied ),
  391. DEFINE_INPUTFUNC( FIELD_VOID, "EnablePreferPhysicsAttack", InputEnablePreferPhysicsAttack ),
  392. DEFINE_INPUTFUNC( FIELD_VOID, "DisablePreferPhysicsAttack", InputDisablePreferPhysicsAttack ),
  393. END_DATADESC()
  394. //Fast Growl (Growl High)
  395. envelopePoint_t envAntlionGuardFastGrowl[] =
  396. {
  397. { 1.0f, 1.0f,
  398. 0.2f, 0.4f,
  399. },
  400. { 0.1f, 0.1f,
  401. 0.8f, 1.0f,
  402. },
  403. { 0.0f, 0.0f,
  404. 0.4f, 0.8f,
  405. },
  406. };
  407. //Bark 1 (Growl High)
  408. envelopePoint_t envAntlionGuardBark1[] =
  409. {
  410. { 1.0f, 1.0f,
  411. 0.1f, 0.2f,
  412. },
  413. { 0.0f, 0.0f,
  414. 0.4f, 0.6f,
  415. },
  416. };
  417. //Bark 2 (Confused)
  418. envelopePoint_t envAntlionGuardBark2[] =
  419. {
  420. { 1.0f, 1.0f,
  421. 0.1f, 0.2f,
  422. },
  423. { 0.2f, 0.3f,
  424. 0.1f, 0.2f,
  425. },
  426. { 0.0f, 0.0f,
  427. 0.4f, 0.6f,
  428. },
  429. };
  430. //Pain
  431. envelopePoint_t envAntlionGuardPain1[] =
  432. {
  433. { 1.0f, 1.0f,
  434. 0.1f, 0.2f,
  435. },
  436. { -1.0f, -1.0f,
  437. 0.5f, 0.8f,
  438. },
  439. { 0.1f, 0.2f,
  440. 0.1f, 0.2f,
  441. },
  442. { 0.0f, 0.0f,
  443. 0.5f, 0.75f,
  444. },
  445. };
  446. //Squeeze (High Growl)
  447. envelopePoint_t envAntlionGuardSqueeze[] =
  448. {
  449. { 1.0f, 1.0f,
  450. 0.1f, 0.2f,
  451. },
  452. { 0.0f, 0.0f,
  453. 1.0f, 1.5f,
  454. },
  455. };
  456. //Scratch (Low Growl)
  457. envelopePoint_t envAntlionGuardScratch[] =
  458. {
  459. { 1.0f, 1.0f,
  460. 0.4f, 0.6f,
  461. },
  462. { 0.5f, 0.5f,
  463. 0.4f, 0.6f,
  464. },
  465. { 0.0f, 0.0f,
  466. 1.0f, 1.5f,
  467. },
  468. };
  469. //Grunt
  470. envelopePoint_t envAntlionGuardGrunt[] =
  471. {
  472. { 0.6f, 1.0f,
  473. 0.1f, 0.2f,
  474. },
  475. { 0.0f, 0.0f,
  476. 0.1f, 0.2f,
  477. },
  478. };
  479. envelopePoint_t envAntlionGuardGrunt2[] =
  480. {
  481. { 0.2f, 0.4f,
  482. 0.1f, 0.2f,
  483. },
  484. { 0.0f, 0.0f,
  485. 0.4f, 0.6f,
  486. },
  487. };
  488. //==============================================================================================
  489. // ANTLION GUARD PHYSICS DAMAGE TABLE
  490. //==============================================================================================
  491. static impactentry_t antlionGuardLinearTable[] =
  492. {
  493. { 100*100, 10 },
  494. { 250*250, 25 },
  495. { 350*350, 50 },
  496. { 500*500, 75 },
  497. { 1000*1000,100 },
  498. };
  499. static impactentry_t antlionGuardAngularTable[] =
  500. {
  501. { 50* 50, 10 },
  502. { 100*100, 25 },
  503. { 150*150, 50 },
  504. { 200*200, 75 },
  505. };
  506. impactdamagetable_t gAntlionGuardImpactDamageTable =
  507. {
  508. antlionGuardLinearTable,
  509. antlionGuardAngularTable,
  510. ARRAYSIZE(antlionGuardLinearTable),
  511. ARRAYSIZE(antlionGuardAngularTable),
  512. 200*200,// minimum linear speed squared
  513. 180*180,// minimum angular speed squared (360 deg/s to cause spin/slice damage)
  514. 15, // can't take damage from anything under 15kg
  515. 10, // anything less than 10kg is "small"
  516. 5, // never take more than 1 pt of damage from anything under 15kg
  517. 128*128,// <15kg objects must go faster than 36 in/s to do damage
  518. 45, // large mass in kg
  519. 2, // large mass scale (anything over 500kg does 4X as much energy to read from damage table)
  520. 1, // large mass falling scale
  521. 0, // my min velocity
  522. };
  523. //-----------------------------------------------------------------------------
  524. // Purpose:
  525. // Output : const impactdamagetable_t
  526. //-----------------------------------------------------------------------------
  527. const impactdamagetable_t &CNPC_AntlionGuard::GetPhysicsImpactDamageTable( void )
  528. {
  529. return gAntlionGuardImpactDamageTable;
  530. }
  531. //==================================================
  532. // CNPC_AntlionGuard
  533. //==================================================
  534. CNPC_AntlionGuard::CNPC_AntlionGuard( void )
  535. {
  536. m_bCavernBreed = false;
  537. m_bInCavern = false;
  538. m_iszPhysicsPropClass = AllocPooledString( "prop_physics" );
  539. }
  540. LINK_ENTITY_TO_CLASS( npc_antlionguard, CNPC_AntlionGuard );
  541. IMPLEMENT_SERVERCLASS_ST(CNPC_AntlionGuard, DT_NPC_AntlionGuard)
  542. SendPropBool( SENDINFO( m_bCavernBreed ) ),
  543. SendPropBool( SENDINFO( m_bInCavern ) ),
  544. #if ANTLIONGUARD_BLOOD_EFFECTS
  545. SendPropInt( SENDINFO( m_iBleedingLevel ), 2, SPROP_UNSIGNED ),
  546. #endif
  547. END_SEND_TABLE()
  548. //-----------------------------------------------------------------------------
  549. // Purpose:
  550. //-----------------------------------------------------------------------------
  551. void CNPC_AntlionGuard::UpdateOnRemove( void )
  552. {
  553. DestroyGlows();
  554. // Chain to the base class
  555. BaseClass::UpdateOnRemove();
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Purpose:
  559. //-----------------------------------------------------------------------------
  560. void CNPC_AntlionGuard::Precache( void )
  561. {
  562. PrecacheModel( ANTLIONGUARD_MODEL );
  563. PrecacheScriptSound( "NPC_AntlionGuard.Shove" );
  564. PrecacheScriptSound( "NPC_AntlionGuard.HitHard" );
  565. if ( HasSpawnFlags(SF_ANTLIONGUARD_INSIDE_FOOTSTEPS) )
  566. {
  567. PrecacheScriptSound( "NPC_AntlionGuard.Inside.StepLight" );
  568. PrecacheScriptSound( "NPC_AntlionGuard.Inside.StepHeavy" );
  569. }
  570. else
  571. {
  572. PrecacheScriptSound( "NPC_AntlionGuard.StepLight" );
  573. PrecacheScriptSound( "NPC_AntlionGuard.StepHeavy" );
  574. }
  575. #if HL2_EPISODIC
  576. PrecacheScriptSound( "NPC_AntlionGuard.NearStepLight" );
  577. PrecacheScriptSound( "NPC_AntlionGuard.NearStepHeavy" );
  578. PrecacheScriptSound( "NPC_AntlionGuard.FarStepLight" );
  579. PrecacheScriptSound( "NPC_AntlionGuard.FarStepHeavy" );
  580. PrecacheScriptSound( "NPC_AntlionGuard.BreatheLoop" );
  581. PrecacheScriptSound( "NPC_AntlionGuard.ShellCrack" );
  582. PrecacheScriptSound( "NPC_AntlionGuard.Pain_Roar" );
  583. PrecacheModel( "sprites/grubflare1.vmt" );
  584. #endif // HL2_EPISODIC
  585. PrecacheScriptSound( "NPC_AntlionGuard.Anger" );
  586. PrecacheScriptSound( "NPC_AntlionGuard.Roar" );
  587. PrecacheScriptSound( "NPC_AntlionGuard.Die" );
  588. PrecacheScriptSound( "NPC_AntlionGuard.GrowlHigh" );
  589. PrecacheScriptSound( "NPC_AntlionGuard.GrowlIdle" );
  590. PrecacheScriptSound( "NPC_AntlionGuard.BreathSound" );
  591. PrecacheScriptSound( "NPC_AntlionGuard.Confused" );
  592. PrecacheScriptSound( "NPC_AntlionGuard.Fallover" );
  593. PrecacheScriptSound( "NPC_AntlionGuard.FrustratedRoar" );
  594. PrecacheParticleSystem( "blood_antlionguard_injured_light" );
  595. PrecacheParticleSystem( "blood_antlionguard_injured_heavy" );
  596. BaseClass::Precache();
  597. }
  598. //-----------------------------------------------------------------------------
  599. // Purpose:
  600. //-----------------------------------------------------------------------------
  601. void CNPC_AntlionGuard::DestroyGlows( void )
  602. {
  603. if ( m_hCaveGlow[0] )
  604. {
  605. UTIL_Remove( m_hCaveGlow[0] );
  606. // reset it to NULL in case there is a double death cleanup for some reason.
  607. m_hCaveGlow[0] = NULL;
  608. }
  609. if ( m_hCaveGlow[1] )
  610. {
  611. UTIL_Remove( m_hCaveGlow[1] );
  612. // reset it to NULL in case there is a double death cleanup for some reason.
  613. m_hCaveGlow[1] = NULL;
  614. }
  615. }
  616. //-----------------------------------------------------------------------------
  617. // Purpose:
  618. //-----------------------------------------------------------------------------
  619. void CNPC_AntlionGuard::CreateGlow( CSprite **pSprite, const char *pAttachName )
  620. {
  621. if ( pSprite == NULL )
  622. return;
  623. // Create the glow sprite
  624. *pSprite = CSprite::SpriteCreate( "sprites/grubflare1.vmt", GetLocalOrigin(), false );
  625. Assert( *pSprite );
  626. if ( *pSprite == NULL )
  627. return;
  628. (*pSprite)->TurnOn();
  629. (*pSprite)->SetTransparency( kRenderWorldGlow, 156, 169, 121, 164, kRenderFxNoDissipation );
  630. (*pSprite)->SetScale( 1.0f );
  631. (*pSprite)->SetGlowProxySize( 16.0f );
  632. int nAttachment = LookupAttachment( pAttachName );
  633. (*pSprite)->SetParent( this, nAttachment );
  634. (*pSprite)->SetLocalOrigin( vec3_origin );
  635. // Don't uselessly animate, we're a static sprite!
  636. (*pSprite)->SetThink( NULL );
  637. (*pSprite)->SetNextThink( TICK_NEVER_THINK );
  638. }
  639. //-----------------------------------------------------------------------------
  640. // Purpose:
  641. //-----------------------------------------------------------------------------
  642. void CNPC_AntlionGuard::Spawn( void )
  643. {
  644. Precache();
  645. SetModel( ANTLIONGUARD_MODEL );
  646. // Switch our skin (for now), if we're the cavern guard
  647. if ( m_bCavernBreed )
  648. {
  649. m_nSkin = 1;
  650. // Add glows
  651. CreateGlow( &(m_hCaveGlow[0]), "attach_glow1" );
  652. CreateGlow( &(m_hCaveGlow[1]), "attach_glow2" );
  653. }
  654. else
  655. {
  656. m_hCaveGlow[0] = NULL;
  657. m_hCaveGlow[1] = NULL;
  658. }
  659. SetHullType( HULL_LARGE );
  660. SetHullSizeNormal();
  661. SetDefaultEyeOffset();
  662. SetSolid( SOLID_BBOX );
  663. AddSolidFlags( FSOLID_NOT_STANDABLE );
  664. SetMoveType( MOVETYPE_STEP );
  665. SetNavType( NAV_GROUND );
  666. SetBloodColor( BLOOD_COLOR_YELLOW );
  667. m_iHealth = sk_antlionguard_health.GetFloat();
  668. m_iMaxHealth = m_iHealth;
  669. m_flFieldOfView = ANTLIONGUARD_FOV_NORMAL;
  670. m_flPhysicsCheckTime = 0;
  671. m_flChargeTime = 0;
  672. m_flNextRoarTime = 0;
  673. m_flNextSummonTime = 0;
  674. m_iNumLiveAntlions = 0;
  675. m_iChargeMisses = 0;
  676. m_flNextHeavyFlinchTime = 0;
  677. m_bDecidedNotToStop = false;
  678. ClearHintGroup();
  679. m_bStopped = false;
  680. m_hShoveTarget = NULL;
  681. m_hChargeTarget = NULL;
  682. m_hChargeTargetPosition = NULL;
  683. m_hPhysicsTarget = NULL;
  684. m_HackedGunPos.x = 10;
  685. m_HackedGunPos.y = 0;
  686. m_HackedGunPos.z = 30;
  687. CapabilitiesClear();
  688. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_SQUAD );
  689. CapabilitiesAdd( bits_CAP_SKIP_NAV_GROUND_CHECK );
  690. NPCInit();
  691. BaseClass::Spawn();
  692. //See if we're supposed to start burrowed
  693. if ( m_bIsBurrowed )
  694. {
  695. AddEffects( EF_NODRAW );
  696. AddFlag( FL_NOTARGET );
  697. m_spawnflags |= SF_NPC_GAG;
  698. AddSolidFlags( FSOLID_NOT_SOLID );
  699. m_takedamage = DAMAGE_NO;
  700. if ( m_hCaveGlow[0] )
  701. m_hCaveGlow[0]->TurnOff();
  702. if ( m_hCaveGlow[1] )
  703. m_hCaveGlow[1]->TurnOff();
  704. }
  705. // Do not dissolve
  706. AddEFlags( EFL_NO_DISSOLVE );
  707. // We get a minute of free knowledge about the target
  708. GetEnemies()->SetEnemyDiscardTime( 120.0f );
  709. GetEnemies()->SetFreeKnowledgeDuration( 60.0f );
  710. // We need to bloat the absbox to encompass all the hitboxes
  711. Vector absMin = -Vector(100,100,0);
  712. Vector absMax = Vector(100,100,128);
  713. CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &absMin, &absMax );
  714. }
  715. //-----------------------------------------------------------------------------
  716. // Purpose:
  717. //-----------------------------------------------------------------------------
  718. void CNPC_AntlionGuard::Activate( void )
  719. {
  720. BaseClass::Activate();
  721. // Find all nearby physics objects and add them to the list of objects we will sense
  722. CBaseEntity *pObject = NULL;
  723. while ( ( pObject = gEntList.FindEntityInSphere( pObject, WorldSpaceCenter(), 2500 ) ) != NULL )
  724. {
  725. // Can't throw around debris
  726. if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  727. continue;
  728. // We can only throw a few types of things
  729. if ( !FClassnameIs( pObject, "prop_physics" ) && !FClassnameIs( pObject, "func_physbox" ) )
  730. continue;
  731. // Ensure it's mass is within range
  732. IPhysicsObject *pPhysObj = pObject->VPhysicsGetObject();
  733. if( ( pPhysObj == NULL ) || ( pPhysObj->GetMass() > ANTLIONGUARD_MAX_OBJECT_MASS ) || ( pPhysObj->GetMass() < ANTLIONGUARD_MIN_OBJECT_MASS ) )
  734. continue;
  735. // Tell the AI sensing list that we want to consider this
  736. g_AI_SensedObjectsManager.AddEntity( pObject );
  737. if ( g_debug_antlionguard.GetInt() == 5 )
  738. {
  739. Msg("Antlion Guard: Added prop with model '%s' to sense list.\n", STRING(pObject->GetModelName()) );
  740. pObject->m_debugOverlays |= OVERLAY_BBOX_BIT;
  741. }
  742. }
  743. }
  744. //-----------------------------------------------------------------------------
  745. // Purpose: Return true if the guard's allowed to summon antlions
  746. //-----------------------------------------------------------------------------
  747. bool CNPC_AntlionGuard::CanSummon( bool bIgnoreTime )
  748. {
  749. if ( !m_bBarkEnabled )
  750. return false;
  751. if ( !bIgnoreTime && m_flNextSummonTime > gpGlobals->curtime )
  752. return false;
  753. // Hit the max number of them allowed? Only summon when we're 2 down.
  754. if ( m_iNumLiveAntlions >= MAX(1, ANTLIONGUARD_SUMMON_COUNT-1) )
  755. return false;
  756. return true;
  757. }
  758. //-----------------------------------------------------------------------------
  759. // Purpose: Our enemy is unreachable. Select a schedule.
  760. //-----------------------------------------------------------------------------
  761. int CNPC_AntlionGuard::SelectUnreachableSchedule( void )
  762. {
  763. // If we're in the cavern setting, we opt out of this
  764. if ( m_bInCavern )
  765. return SCHED_ANTLIONGUARD_CHASE_ENEMY_TOLERANCE;
  766. // Summon antlions if we can
  767. if ( HasCondition( COND_ANTLIONGUARD_CAN_SUMMON ) )
  768. return SCHED_ANTLIONGUARD_SUMMON;
  769. // First, look for objects near ourselves
  770. PhysicsObjectCriteria_t criteria;
  771. criteria.bPreferObjectsAlongTargetVector = false;
  772. criteria.flNearRadius = (40*12);
  773. criteria.flRadius = (250*12);
  774. criteria.flTargetCone = -1.0f;
  775. criteria.pTarget = GetEnemy();
  776. criteria.vecCenter = GetAbsOrigin();
  777. CBaseEntity *pTarget = FindPhysicsObjectTarget( criteria );
  778. if ( pTarget == NULL && GetEnemy() )
  779. {
  780. // Use the same criteria, but search near the target instead of us
  781. criteria.vecCenter = GetEnemy()->GetAbsOrigin();
  782. pTarget = FindPhysicsObjectTarget( criteria );
  783. }
  784. // If we found one, we'll want to attack it
  785. if ( pTarget )
  786. {
  787. m_hPhysicsTarget = pTarget;
  788. SetCondition( COND_ANTLIONGUARD_PHYSICS_TARGET );
  789. m_vecPhysicsTargetStartPos = m_hPhysicsTarget->WorldSpaceCenter();
  790. // Tell any squadmates I'm going for this item so they don't as well
  791. if ( GetSquad() != NULL )
  792. {
  793. GetSquad()->BroadcastInteraction( g_interactionAntlionGuardFoundPhysicsObject, (void *)((CBaseEntity *)m_hPhysicsTarget), this );
  794. }
  795. return SCHED_ANTLIONGUARD_PHYSICS_ATTACK;
  796. }
  797. // Deal with a visible player
  798. if ( HasCondition( COND_SEE_ENEMY ) )
  799. {
  800. // Roar at the player as show of frustration
  801. if ( m_flNextRoarTime < gpGlobals->curtime )
  802. {
  803. m_flNextRoarTime = gpGlobals->curtime + RandomFloat( 20,40 );
  804. return SCHED_ANTLIONGUARD_ROAR;
  805. }
  806. // If we're under attack, then let's leave for a bit
  807. if ( GetEnemy() && HasCondition( COND_HEAVY_DAMAGE ) )
  808. {
  809. Vector dir = GetEnemy()->GetAbsOrigin() - GetAbsOrigin();
  810. VectorNormalize(dir);
  811. GetMotor()->SetIdealYaw( -dir );
  812. return SCHED_ANTLIONGUARD_TAKE_COVER_FROM_ENEMY;
  813. }
  814. }
  815. // If we're too far away, try to close distance to our target first
  816. float flDistToEnemySqr = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
  817. if ( flDistToEnemySqr > Square( 100*12 ) )
  818. return SCHED_ANTLIONGUARD_CHASE_ENEMY_TOLERANCE;
  819. // Fire that we're unable to reach our target!
  820. if ( GetEnemy() && GetEnemy()->IsPlayer() )
  821. {
  822. m_OnLostPlayer.FireOutput( this, this );
  823. }
  824. m_OnLostEnemy.FireOutput( this, this );
  825. GetEnemies()->MarkAsEluded( GetEnemy() );
  826. // Move randomly for the moment
  827. return SCHED_ANTLIONGUARD_CANT_ATTACK;
  828. }
  829. //-----------------------------------------------------------------------------
  830. // Purpose:
  831. //-----------------------------------------------------------------------------
  832. int CNPC_AntlionGuard::SelectCombatSchedule( void )
  833. {
  834. ClearHintGroup();
  835. /*
  836. bool bCanCharge = false;
  837. if ( HasCondition( COND_SEE_ENEMY ) )
  838. {
  839. bCanCharge = ShouldCharge( GetAbsOrigin(), GetEnemy()->GetAbsOrigin(), true, false );
  840. }
  841. */
  842. // Attack if we can
  843. if ( HasCondition(COND_CAN_MELEE_ATTACK1) )
  844. return SCHED_MELEE_ATTACK1;
  845. // Otherwise, summon antlions
  846. if ( HasCondition(COND_ANTLIONGUARD_CAN_SUMMON) )
  847. {
  848. // If I can charge, and have antlions, charge instead
  849. if ( HasCondition( COND_ANTLIONGUARD_CAN_CHARGE ) && m_iNumLiveAntlions )
  850. return SCHED_ANTLIONGUARD_CHARGE;
  851. return SCHED_ANTLIONGUARD_SUMMON;
  852. }
  853. // See if we can bark
  854. if ( HasCondition( COND_ENEMY_UNREACHABLE ) )
  855. return SelectUnreachableSchedule();
  856. //Physics target
  857. if ( HasCondition( COND_ANTLIONGUARD_PHYSICS_TARGET ) && !m_bInCavern )
  858. return SCHED_ANTLIONGUARD_PHYSICS_ATTACK;
  859. // If we've missed a couple of times, and we can summon, make it harder
  860. if ( m_iChargeMisses >= 2 && CanSummon(true) )
  861. {
  862. m_iChargeMisses--;
  863. return SCHED_ANTLIONGUARD_SUMMON;
  864. }
  865. // Charging
  866. if ( HasCondition( COND_ANTLIONGUARD_CAN_CHARGE ) )
  867. {
  868. // Don't let other squad members charge while we're doing it
  869. OccupyStrategySlot( SQUAD_SLOT_ANTLIONGUARD_CHARGE );
  870. return SCHED_ANTLIONGUARD_CHARGE;
  871. }
  872. return BaseClass::SelectSchedule();
  873. }
  874. //-----------------------------------------------------------------------------
  875. // Purpose:
  876. // Output : Returns true on success, false on failure.
  877. //-----------------------------------------------------------------------------
  878. bool CNPC_AntlionGuard::ShouldCharge( const Vector &startPos, const Vector &endPos, bool useTime, bool bCheckForCancel )
  879. {
  880. // Don't charge in tight spaces unless forced to
  881. if ( hl2_episodic.GetBool() && m_bInCavern && !(m_hChargeTarget.Get() && m_hChargeTarget->IsAlive()) )
  882. return false;
  883. // Must have a target
  884. if ( !GetEnemy() )
  885. return false;
  886. // No one else in the squad can be charging already
  887. if ( IsStrategySlotRangeOccupied( SQUAD_SLOT_ANTLIONGUARD_CHARGE, SQUAD_SLOT_ANTLIONGUARD_CHARGE ) )
  888. return false;
  889. // Don't check the distance once we start charging
  890. if ( !bCheckForCancel )
  891. {
  892. // Don't allow use to charge again if it's been too soon
  893. if ( useTime && ( m_flChargeTime > gpGlobals->curtime ) )
  894. return false;
  895. float distance = UTIL_DistApprox2D( startPos, endPos );
  896. // Must be within our tolerance range
  897. if ( ( distance < ANTLIONGUARD_CHARGE_MIN ) || ( distance > ANTLIONGUARD_CHARGE_MAX ) )
  898. return false;
  899. }
  900. if ( GetSquad() )
  901. {
  902. // If someone in our squad is closer to the enemy, then don't charge (we end up hitting them more often than not!)
  903. float flOurDistToEnemySqr = ( GetAbsOrigin() - GetEnemy()->GetAbsOrigin() ).LengthSqr();
  904. AISquadIter_t iter;
  905. for ( CAI_BaseNPC *pSquadMember = GetSquad()->GetFirstMember( &iter ); pSquadMember; pSquadMember = GetSquad()->GetNextMember( &iter ) )
  906. {
  907. if ( pSquadMember->IsAlive() == false || pSquadMember == this )
  908. continue;
  909. if ( ( pSquadMember->GetAbsOrigin() - GetEnemy()->GetAbsOrigin() ).LengthSqr() < flOurDistToEnemySqr )
  910. return false;
  911. }
  912. }
  913. //FIXME: We'd like to exclude small physics objects from this check!
  914. // We only need to hit the endpos with the edge of our bounding box
  915. Vector vecDir = endPos - startPos;
  916. VectorNormalize( vecDir );
  917. float flWidth = WorldAlignSize().x * 0.5;
  918. Vector vecTargetPos = endPos - (vecDir * flWidth);
  919. // See if we can directly move there
  920. AIMoveTrace_t moveTrace;
  921. GetMoveProbe()->MoveLimit( NAV_GROUND, startPos, vecTargetPos, MASK_NPCSOLID_BRUSHONLY, GetEnemy(), &moveTrace );
  922. // Draw the probe
  923. if ( g_debug_antlionguard.GetInt() == 1 )
  924. {
  925. Vector enemyDir = (vecTargetPos - startPos);
  926. float enemyDist = VectorNormalize( enemyDir );
  927. NDebugOverlay::BoxDirection( startPos, GetHullMins(), GetHullMaxs() + Vector(enemyDist,0,0), enemyDir, 0, 255, 0, 8, 1.0f );
  928. }
  929. // If we're not blocked, charge
  930. if ( IsMoveBlocked( moveTrace ) )
  931. {
  932. // Don't allow it if it's too close to us
  933. if ( UTIL_DistApprox( WorldSpaceCenter(), moveTrace.vEndPosition ) < ANTLIONGUARD_CHARGE_MIN )
  934. return false;
  935. // Allow some special cases to not block us
  936. if ( moveTrace.pObstruction != NULL )
  937. {
  938. // If we've hit the world, see if it's a cliff
  939. if ( moveTrace.pObstruction == GetContainingEntity( INDEXENT(0) ) )
  940. {
  941. // Can't be too far above/below the target
  942. if ( fabs( moveTrace.vEndPosition.z - vecTargetPos.z ) > StepHeight() )
  943. return false;
  944. // Allow it if we got pretty close
  945. if ( UTIL_DistApprox( moveTrace.vEndPosition, vecTargetPos ) < 64 )
  946. return true;
  947. }
  948. // Hit things that will take damage
  949. if ( moveTrace.pObstruction->m_takedamage != DAMAGE_NO )
  950. return true;
  951. // Hit things that will move
  952. if ( moveTrace.pObstruction->GetMoveType() == MOVETYPE_VPHYSICS )
  953. return true;
  954. }
  955. return false;
  956. }
  957. // Only update this if we've requested it
  958. if ( useTime )
  959. {
  960. m_flChargeTime = gpGlobals->curtime + 4.0f;
  961. }
  962. return true;
  963. }
  964. //-----------------------------------------------------------------------------
  965. // Purpose:
  966. //-----------------------------------------------------------------------------
  967. int CNPC_AntlionGuard::SelectSchedule( void )
  968. {
  969. // Don't do anything if we're burrowed
  970. if ( m_bIsBurrowed )
  971. return SCHED_IDLE_STAND;
  972. #if 0
  973. // Debug physics object finding
  974. if ( 0 ) //g_debug_antlionguard.GetInt() == 3 )
  975. {
  976. m_flPhysicsCheckTime = 0;
  977. UpdatePhysicsTarget( false );
  978. return SCHED_IDLE_STAND;
  979. }
  980. #endif
  981. // Flinch on heavy damage, but not if we've flinched too recently.
  982. // This is done to prevent stun-locks from grenades.
  983. if ( !m_bInCavern && HasCondition( COND_HEAVY_DAMAGE ) && m_flNextHeavyFlinchTime < gpGlobals->curtime )
  984. {
  985. m_flNextHeavyFlinchTime = gpGlobals->curtime + 8.0f;
  986. return SCHED_ANTLIONGUARD_PHYSICS_DAMAGE_HEAVY;
  987. }
  988. // Prefer to use physics, in this case
  989. if ( m_bPreferPhysicsAttack )
  990. {
  991. // If we have a target, try to go for it
  992. if ( HasCondition( COND_ANTLIONGUARD_PHYSICS_TARGET ) )
  993. return SCHED_ANTLIONGUARD_PHYSICS_ATTACK;
  994. }
  995. // Charge after a target if it's set
  996. if ( m_hChargeTarget && m_hChargeTargetPosition )
  997. {
  998. ClearCondition( COND_ANTLIONGUARD_HAS_CHARGE_TARGET );
  999. ClearHintGroup();
  1000. if ( m_hChargeTarget->IsAlive() == false )
  1001. {
  1002. m_hChargeTarget = NULL;
  1003. m_hChargeTargetPosition = NULL;
  1004. SetEnemy( m_hOldTarget );
  1005. if ( m_hOldTarget == NULL )
  1006. {
  1007. m_NPCState = NPC_STATE_ALERT;
  1008. }
  1009. }
  1010. else
  1011. {
  1012. m_hOldTarget = GetEnemy();
  1013. SetEnemy( m_hChargeTarget );
  1014. UpdateEnemyMemory( m_hChargeTarget, m_hChargeTarget->GetAbsOrigin() );
  1015. //If we can't see the target, run to somewhere we can
  1016. if ( ShouldCharge( GetAbsOrigin(), GetEnemy()->GetAbsOrigin(), false, false ) == false )
  1017. return SCHED_ANTLIONGUARD_FIND_CHARGE_POSITION;
  1018. return SCHED_ANTLIONGUARD_CHARGE_TARGET;
  1019. }
  1020. }
  1021. // See if we need to clear a path to our enemy
  1022. if ( HasCondition( COND_ENEMY_OCCLUDED ) || HasCondition( COND_ENEMY_UNREACHABLE ) )
  1023. {
  1024. CBaseEntity *pBlocker = GetEnemyOccluder();
  1025. if ( ( pBlocker != NULL ) && FClassnameIs( pBlocker, "prop_physics" ) && !m_bInCavern )
  1026. {
  1027. m_hPhysicsTarget = pBlocker;
  1028. return SCHED_ANTLIONGUARD_PHYSICS_ATTACK;
  1029. }
  1030. }
  1031. //Only do these in combat states
  1032. if ( m_NPCState == NPC_STATE_COMBAT && GetEnemy() )
  1033. return SelectCombatSchedule();
  1034. return BaseClass::SelectSchedule();
  1035. }
  1036. //-----------------------------------------------------------------------------
  1037. // Purpose:
  1038. //-----------------------------------------------------------------------------
  1039. int CNPC_AntlionGuard::MeleeAttack1Conditions( float flDot, float flDist )
  1040. {
  1041. // Don't attack again too soon
  1042. if ( GetNextAttack() > gpGlobals->curtime )
  1043. return 0;
  1044. // While charging, we can't melee attack
  1045. if ( IsCurSchedule( SCHED_ANTLIONGUARD_CHARGE ) )
  1046. return 0;
  1047. if ( hl2_episodic.GetBool() && m_bInCavern )
  1048. {
  1049. // Predict where they'll be and see if THAT is within range
  1050. Vector vecPredPos;
  1051. UTIL_PredictedPosition( GetEnemy(), 0.25f, &vecPredPos );
  1052. if ( ( GetAbsOrigin() - vecPredPos ).Length() > ANTLIONGUARD_MELEE1_RANGE )
  1053. return COND_TOO_FAR_TO_ATTACK;
  1054. }
  1055. else
  1056. {
  1057. // Must be close enough
  1058. if ( flDist > ANTLIONGUARD_MELEE1_RANGE )
  1059. return COND_TOO_FAR_TO_ATTACK;
  1060. }
  1061. // Must be within a viable cone
  1062. if ( flDot < ANTLIONGUARD_MELEE1_CONE )
  1063. return COND_NOT_FACING_ATTACK;
  1064. // If the enemy is on top of me, I'm allowed to hit the sucker
  1065. if ( GetEnemy()->GetGroundEntity() == this )
  1066. return COND_CAN_MELEE_ATTACK1;
  1067. trace_t tr;
  1068. TraceHull_SkipPhysics( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), Vector(-10,-10,-10), Vector(10,10,10), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, &tr, VPhysicsGetObject()->GetMass() * 0.5 );
  1069. // If we hit anything, go for it
  1070. if ( tr.fraction < 1.0f )
  1071. return COND_CAN_MELEE_ATTACK1;
  1072. return 0;
  1073. }
  1074. //-----------------------------------------------------------------------------
  1075. // Purpose:
  1076. //-----------------------------------------------------------------------------
  1077. float CNPC_AntlionGuard::MaxYawSpeed( void )
  1078. {
  1079. Activity eActivity = GetActivity();
  1080. // Stay still
  1081. if (( eActivity == ACT_ANTLIONGUARD_SEARCH ) ||
  1082. ( eActivity == ACT_ANTLIONGUARD_PEEK_ENTER ) ||
  1083. ( eActivity == ACT_ANTLIONGUARD_PEEK_EXIT ) ||
  1084. ( eActivity == ACT_ANTLIONGUARD_PEEK1 ) ||
  1085. ( eActivity == ACT_ANTLIONGUARD_BARK ) ||
  1086. ( eActivity == ACT_ANTLIONGUARD_PEEK_SIGHTED ) ||
  1087. ( eActivity == ACT_MELEE_ATTACK1 ) )
  1088. return 0.0f;
  1089. CBaseEntity *pEnemy = GetEnemy();
  1090. if ( pEnemy != NULL && pEnemy->IsPlayer() == false )
  1091. return 16.0f;
  1092. // Turn slowly when you're charging
  1093. if ( eActivity == ACT_ANTLIONGUARD_CHARGE_START )
  1094. return 4.0f;
  1095. if ( hl2_episodic.GetBool() && m_bInCavern )
  1096. {
  1097. // Allow a better turning rate when moving quickly but not charging the player
  1098. if ( ( eActivity == ACT_ANTLIONGUARD_CHARGE_RUN ) && IsCurSchedule( SCHED_ANTLIONGUARD_CHARGE ) == false )
  1099. return 16.0f;
  1100. }
  1101. // Turn more slowly as we close in on our target
  1102. if ( eActivity == ACT_ANTLIONGUARD_CHARGE_RUN )
  1103. {
  1104. if ( pEnemy == NULL )
  1105. return 2.0f;
  1106. float dist = UTIL_DistApprox2D( GetEnemy()->WorldSpaceCenter(), WorldSpaceCenter() );
  1107. if ( dist > 512 )
  1108. return 16.0f;
  1109. //FIXME: Alter by skill level
  1110. float yawSpeed = RemapVal( dist, 0, 512, 1.0f, 2.0f );
  1111. yawSpeed = clamp( yawSpeed, 1.0f, 2.0f );
  1112. return yawSpeed;
  1113. }
  1114. if ( eActivity == ACT_ANTLIONGUARD_CHARGE_STOP )
  1115. return 8.0f;
  1116. switch( eActivity )
  1117. {
  1118. case ACT_TURN_LEFT:
  1119. case ACT_TURN_RIGHT:
  1120. return 40.0f;
  1121. break;
  1122. case ACT_RUN:
  1123. default:
  1124. return 20.0f;
  1125. break;
  1126. }
  1127. }
  1128. //-----------------------------------------------------------------------------
  1129. // Purpose:
  1130. // Input : flInterval -
  1131. // Output : Returns true on success, false on failure.
  1132. //-----------------------------------------------------------------------------
  1133. bool CNPC_AntlionGuard::OverrideMove( float flInterval )
  1134. {
  1135. // If the guard's charging, we're handling the movement
  1136. if ( IsCurSchedule( SCHED_ANTLIONGUARD_CHARGE ) )
  1137. return true;
  1138. // TODO: This code increases the guard's ability to successfully hit a target, but adds a new dimension of navigation
  1139. // trouble to do with him not being able to "close the distance" between himself and the object he wants to hit.
  1140. // Fixing this will require some thought on how he picks the correct distances to his targets and when he's "close enough". -- jdw
  1141. /*
  1142. if ( m_hPhysicsTarget != NULL )
  1143. {
  1144. float flWidth = m_hPhysicsTarget->CollisionProp()->BoundingRadius2D();
  1145. GetLocalNavigator()->AddObstacle( m_hPhysicsTarget->WorldSpaceCenter(), flWidth * 0.75f, AIMST_AVOID_OBJECT );
  1146. //NDebugOverlay::Sphere( m_hPhysicsTarget->WorldSpaceCenter(), vec3_angle, flWidth, 255, 255, 255, 0, true, 0.5f );
  1147. }
  1148. */
  1149. return BaseClass::OverrideMove( flInterval );
  1150. }
  1151. //-----------------------------------------------------------------------------
  1152. // Purpose:
  1153. //-----------------------------------------------------------------------------
  1154. void CNPC_AntlionGuard::Shove( void )
  1155. {
  1156. if ( GetNextAttack() > gpGlobals->curtime )
  1157. return;
  1158. CBaseEntity *pHurt = NULL;
  1159. CBaseEntity *pTarget;
  1160. pTarget = ( m_hShoveTarget == NULL ) ? GetEnemy() : m_hShoveTarget.Get();
  1161. if ( pTarget == NULL )
  1162. {
  1163. m_hShoveTarget = NULL;
  1164. return;
  1165. }
  1166. //Always damage bullseyes if we're scripted to hit them
  1167. if ( pTarget->Classify() == CLASS_BULLSEYE )
  1168. {
  1169. pTarget->TakeDamage( CTakeDamageInfo( this, this, 1.0f, DMG_CRUSH ) );
  1170. m_hShoveTarget = NULL;
  1171. return;
  1172. }
  1173. float damage = ( pTarget->IsPlayer() ) ? sk_antlionguard_dmg_shove.GetFloat() : 250;
  1174. // If the target's still inside the shove cone, ensure we hit him
  1175. Vector vecForward, vecEnd;
  1176. AngleVectors( GetAbsAngles(), &vecForward );
  1177. float flDistSqr = ( pTarget->WorldSpaceCenter() - WorldSpaceCenter() ).LengthSqr();
  1178. Vector2D v2LOS = ( pTarget->WorldSpaceCenter() - WorldSpaceCenter() ).AsVector2D();
  1179. Vector2DNormalize(v2LOS);
  1180. float flDot = DotProduct2D (v2LOS, vecForward.AsVector2D() );
  1181. if ( flDistSqr < (ANTLIONGUARD_MELEE1_RANGE*ANTLIONGUARD_MELEE1_RANGE) && flDot >= ANTLIONGUARD_MELEE1_CONE )
  1182. {
  1183. vecEnd = pTarget->WorldSpaceCenter();
  1184. }
  1185. else
  1186. {
  1187. vecEnd = WorldSpaceCenter() + ( BodyDirection3D() * ANTLIONGUARD_MELEE1_RANGE );
  1188. }
  1189. // Use the melee trace to ensure we hit everything there
  1190. trace_t tr;
  1191. CTakeDamageInfo dmgInfo( this, this, damage, DMG_SLASH );
  1192. CTraceFilterMelee traceFilter( this, COLLISION_GROUP_NONE, &dmgInfo, 1.0, true );
  1193. Ray_t ray;
  1194. ray.Init( WorldSpaceCenter(), vecEnd, Vector(-16,-16,-16), Vector(16,16,16) );
  1195. enginetrace->TraceRay( ray, MASK_SHOT_HULL, &traceFilter, &tr );
  1196. pHurt = tr.m_pEnt;
  1197. // Knock things around
  1198. ImpactShock( tr.endpos, 100.0f, 250.0f );
  1199. if ( pHurt )
  1200. {
  1201. Vector traceDir = ( tr.endpos - tr.startpos );
  1202. VectorNormalize( traceDir );
  1203. // Generate enough force to make a 75kg guy move away at 600 in/sec
  1204. Vector vecForce = traceDir * ImpulseScale( 75, 600 );
  1205. CTakeDamageInfo info( this, this, vecForce, tr.endpos, damage, DMG_CLUB );
  1206. pHurt->TakeDamage( info );
  1207. m_hShoveTarget = NULL;
  1208. EmitSound( "NPC_AntlionGuard.Shove" );
  1209. // If the player, throw him around
  1210. if ( pHurt->IsPlayer() )
  1211. {
  1212. //Punch the view
  1213. pHurt->ViewPunch( QAngle(20,0,-20) );
  1214. //Shake the screen
  1215. UTIL_ScreenShake( pHurt->GetAbsOrigin(), 100.0, 1.5, 1.0, 2, SHAKE_START );
  1216. //Red damage indicator
  1217. color32 red = {128,0,0,128};
  1218. UTIL_ScreenFade( pHurt, red, 1.0f, 0.1f, FFADE_IN );
  1219. Vector forward, up;
  1220. AngleVectors( GetLocalAngles(), &forward, NULL, &up );
  1221. pHurt->ApplyAbsVelocityImpulse( forward * 400 + up * 150 );
  1222. // in the episodes, the cavern guard poisons the player
  1223. #if HL2_EPISODIC
  1224. // If I am a cavern guard attacking the player, and he still lives, then poison him too.
  1225. if ( m_bInCavern && pHurt->IsPlayer() && pHurt->IsAlive() && pHurt->m_iHealth > ANTLIONGUARD_POISON_TO)
  1226. {
  1227. // That didn't finish them. Take them down to one point with poison damage. It'll heal.
  1228. pHurt->TakeDamage( CTakeDamageInfo( this, this, pHurt->m_iHealth - ANTLIONGUARD_POISON_TO, DMG_POISON ) );
  1229. }
  1230. #endif
  1231. }
  1232. else
  1233. {
  1234. CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pHurt );
  1235. if ( pVictim )
  1236. {
  1237. if ( NPC_Rollermine_IsRollermine( pVictim ) )
  1238. {
  1239. Pickup_OnPhysGunDrop( pVictim, NULL, LAUNCHED_BY_CANNON );
  1240. }
  1241. // FIXME: This causes NPCs that are not physically motivated to hop into the air strangely -- jdw
  1242. // pVictim->ApplyAbsVelocityImpulse( BodyDirection2D() * 400 + Vector( 0, 0, 250 ) );
  1243. }
  1244. m_hShoveTarget = NULL;
  1245. }
  1246. }
  1247. }
  1248. //-----------------------------------------------------------------------------
  1249. // Purpose:
  1250. //-----------------------------------------------------------------------------
  1251. class CTraceFilterCharge : public CTraceFilterEntitiesOnly
  1252. {
  1253. public:
  1254. // It does have a base, but we'll never network anything below here..
  1255. DECLARE_CLASS_NOBASE( CTraceFilterCharge );
  1256. CTraceFilterCharge( const IHandleEntity *passentity, int collisionGroup, CNPC_AntlionGuard *pAttacker )
  1257. : m_pPassEnt(passentity), m_collisionGroup(collisionGroup), m_pAttacker(pAttacker)
  1258. {
  1259. }
  1260. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  1261. {
  1262. if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
  1263. return false;
  1264. if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
  1265. return false;
  1266. // Don't test if the game code tells us we should ignore this collision...
  1267. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  1268. if ( pEntity )
  1269. {
  1270. if ( !pEntity->ShouldCollide( m_collisionGroup, contentsMask ) )
  1271. return false;
  1272. if ( !g_pGameRules->ShouldCollide( m_collisionGroup, pEntity->GetCollisionGroup() ) )
  1273. return false;
  1274. if ( pEntity->m_takedamage == DAMAGE_NO )
  1275. return false;
  1276. // Translate the vehicle into its driver for damage
  1277. if ( pEntity->GetServerVehicle() != NULL )
  1278. {
  1279. CBaseEntity *pDriver = pEntity->GetServerVehicle()->GetPassenger();
  1280. if ( pDriver != NULL )
  1281. {
  1282. pEntity = pDriver;
  1283. }
  1284. }
  1285. Vector attackDir = pEntity->WorldSpaceCenter() - m_pAttacker->WorldSpaceCenter();
  1286. VectorNormalize( attackDir );
  1287. float flDamage = ( pEntity->IsPlayer() ) ? sk_antlionguard_dmg_shove.GetFloat() : 250;;
  1288. CTakeDamageInfo info( m_pAttacker, m_pAttacker, flDamage, DMG_CRUSH );
  1289. CalculateMeleeDamageForce( &info, attackDir, info.GetAttacker()->WorldSpaceCenter(), 4.0f );
  1290. CBaseCombatCharacter *pVictimBCC = pEntity->MyCombatCharacterPointer();
  1291. // Only do these comparisons between NPCs
  1292. if ( pVictimBCC )
  1293. {
  1294. // Can only damage other NPCs that we hate
  1295. if ( m_pAttacker->IRelationType( pEntity ) == D_HT )
  1296. {
  1297. pEntity->TakeDamage( info );
  1298. return true;
  1299. }
  1300. }
  1301. else
  1302. {
  1303. // Otherwise just damage passive objects in our way
  1304. pEntity->TakeDamage( info );
  1305. Pickup_ForcePlayerToDropThisObject( pEntity );
  1306. }
  1307. }
  1308. return false;
  1309. }
  1310. public:
  1311. const IHandleEntity *m_pPassEnt;
  1312. int m_collisionGroup;
  1313. CNPC_AntlionGuard *m_pAttacker;
  1314. };
  1315. #define MIN_FOOTSTEP_NEAR_DIST Square( 80*12.0f )// ft
  1316. //-----------------------------------------------------------------------------
  1317. // Purpose: Plays a footstep sound with temporary distance fades
  1318. // Input : bHeavy - Larger back hoof is considered a "heavy" step
  1319. //-----------------------------------------------------------------------------
  1320. void CNPC_AntlionGuard::Footstep( bool bHeavy )
  1321. {
  1322. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  1323. Assert( pPlayer != NULL );
  1324. if ( pPlayer == NULL )
  1325. return;
  1326. float flDistanceToPlayerSqr = ( pPlayer->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
  1327. float flNearVolume = RemapValClamped( flDistanceToPlayerSqr, Square(10*12.0f), MIN_FOOTSTEP_NEAR_DIST, VOL_NORM, 0.0f );
  1328. EmitSound_t soundParams;
  1329. CPASAttenuationFilter filter( this );
  1330. if ( bHeavy )
  1331. {
  1332. if ( flNearVolume > 0.0f )
  1333. {
  1334. soundParams.m_pSoundName = "NPC_AntlionGuard.NearStepHeavy";
  1335. soundParams.m_flVolume = flNearVolume;
  1336. soundParams.m_nFlags = SND_CHANGE_VOL;
  1337. EmitSound( filter, entindex(), soundParams );
  1338. }
  1339. EmitSound( "NPC_AntlionGuard.FarStepHeavy" );
  1340. }
  1341. else
  1342. {
  1343. if ( flNearVolume > 0.0f )
  1344. {
  1345. soundParams.m_pSoundName = "NPC_AntlionGuard.NearStepLight";
  1346. soundParams.m_flVolume = flNearVolume;
  1347. soundParams.m_nFlags = SND_CHANGE_VOL;
  1348. EmitSound( filter, entindex(), soundParams );
  1349. }
  1350. EmitSound( "NPC_AntlionGuard.FarStepLight" );
  1351. }
  1352. }
  1353. float GetCurrentGravity( void );
  1354. //-----------------------------------------------------------------------------
  1355. // Purpose:
  1356. // Input : *pObject -
  1357. // *pOut -
  1358. //-----------------------------------------------------------------------------
  1359. void CNPC_AntlionGuard::GetPhysicsShoveDir( CBaseEntity *pObject, float flMass, Vector *pOut )
  1360. {
  1361. const Vector vecStart = pObject->WorldSpaceCenter();
  1362. const Vector vecTarget = GetEnemy()->WorldSpaceCenter();
  1363. const float flBaseSpeed = 800.0f;
  1364. float flSpeed = RemapValClamped( flMass, 0.0f, 150.0f, flBaseSpeed * 2.0f, flBaseSpeed );
  1365. // Try the most direct route
  1366. Vector vecToss = VecCheckThrow( this, vecStart, vecTarget, flSpeed, 1.0f );
  1367. // If this failed then try a little faster (flattens the arc)
  1368. if ( vecToss == vec3_origin )
  1369. {
  1370. vecToss = VecCheckThrow( this, vecStart, vecTarget, flSpeed * 1.25f, 1.0f );
  1371. if ( vecToss == vec3_origin )
  1372. {
  1373. const float flGravity = GetCurrentGravity();
  1374. vecToss = (vecTarget - vecStart);
  1375. // throw at a constant time
  1376. float time = vecToss.Length( ) / flSpeed;
  1377. vecToss = vecToss * (1.0f / time);
  1378. // adjust upward toss to compensate for gravity loss
  1379. vecToss.z += flGravity * time * 0.5f;
  1380. }
  1381. }
  1382. // Save out the result
  1383. if ( pOut )
  1384. {
  1385. *pOut = vecToss;
  1386. }
  1387. }
  1388. //-----------------------------------------------------------------------------
  1389. // Purpose:
  1390. // Input : *pEvent -
  1391. //-----------------------------------------------------------------------------
  1392. void CNPC_AntlionGuard::HandleAnimEvent( animevent_t *pEvent )
  1393. {
  1394. if ( pEvent->event == AE_ANTLIONGUARD_CHARGE_EARLYOUT )
  1395. {
  1396. // Robin: Removed this because it usually made him look less intelligent, not more.
  1397. // This code left here so we don't get warnings in the console.
  1398. /*
  1399. // Cancel the charge if it's no longer valid
  1400. if ( ShouldCharge( GetAbsOrigin(), GetEnemy()->GetAbsOrigin(), false, true ) == false )
  1401. {
  1402. SetSchedule( SCHED_ANTLIONGUARD_CHARGE_CANCEL );
  1403. }
  1404. */
  1405. return;
  1406. }
  1407. // Don't handle anim events after death
  1408. if ( m_NPCState == NPC_STATE_DEAD )
  1409. {
  1410. BaseClass::HandleAnimEvent( pEvent );
  1411. return;
  1412. }
  1413. if ( pEvent->event == AE_ANTLIONGUARD_SHOVE_PHYSOBJECT )
  1414. {
  1415. if ( m_hPhysicsTarget == NULL )
  1416. {
  1417. // Disrupt other objects near us anyway
  1418. ImpactShock( WorldSpaceCenter(), 150, 250.0f );
  1419. return;
  1420. }
  1421. // If we have no enemy, we don't really have a direction to throw the object
  1422. // in. But, we still want to clobber it so the animevent doesn't happen fruitlessly,
  1423. // and we want to clear the condition flags and other state regarding the m_hPhysicsTarget.
  1424. // So, skip the code relevant to computing a launch vector specific to the object I'm
  1425. // striking, but use the ImpactShock call further below to punch everything in the neighborhood.
  1426. CBaseEntity *pEnemy = GetEnemy();
  1427. if ( pEnemy != NULL )
  1428. {
  1429. //Setup the throw velocity
  1430. IPhysicsObject *physObj = m_hPhysicsTarget->VPhysicsGetObject();
  1431. Vector vecShoveVel = ( pEnemy->GetAbsOrigin() - m_hPhysicsTarget->WorldSpaceCenter() );
  1432. float flTargetDist = VectorNormalize( vecShoveVel );
  1433. // Must still be close enough to our target
  1434. Vector shoveDir = m_hPhysicsTarget->WorldSpaceCenter() - WorldSpaceCenter();
  1435. float shoveDist = VectorNormalize( shoveDir );
  1436. if ( shoveDist > 300.0f )
  1437. {
  1438. // Pick a new target next time (this one foiled us!)
  1439. RememberFailedPhysicsTarget( m_hPhysicsTarget );
  1440. m_hPhysicsTarget = NULL;
  1441. return;
  1442. }
  1443. // Toss this if we're episodic
  1444. if ( hl2_episodic.GetBool() )
  1445. {
  1446. Vector vecTargetDir = vecShoveVel;
  1447. // Get our shove direction
  1448. GetPhysicsShoveDir( m_hPhysicsTarget, physObj->GetMass(), &vecShoveVel );
  1449. // If the player isn't looking at me, and isn't reachable, be more forgiving about hitting them
  1450. if ( HasCondition( COND_ENEMY_UNREACHABLE ) && HasCondition( COND_ENEMY_FACING_ME ) == false )
  1451. {
  1452. // Build an arc around the top of the target that we'll offset our aim by
  1453. Vector vecOffset;
  1454. float flSin, flCos;
  1455. float flRad = random->RandomFloat( 0, M_PI / 6.0f ); // +/- 30 deg
  1456. if ( random->RandomInt( 0, 1 ) )
  1457. flRad *= -1.0f;
  1458. SinCos( flRad, &flSin, &flCos );
  1459. // Rotate the 2-d circle to be "facing" along our shot direction
  1460. Vector vecArc;
  1461. QAngle vecAngles;
  1462. VectorAngles( vecTargetDir, vecAngles );
  1463. VectorRotate( Vector( 0.0f, flCos, flSin ), vecAngles, vecArc );
  1464. // Find the radius by which to avoid the player
  1465. float flOffsetRadius = ( m_hPhysicsTarget->CollisionProp()->BoundingRadius() + GetEnemy()->CollisionProp()->BoundingRadius() ) * 1.5f;
  1466. // Add this to our velocity to offset it
  1467. vecShoveVel += ( vecArc * flOffsetRadius );
  1468. }
  1469. // Factor out mass
  1470. vecShoveVel *= physObj->GetMass();
  1471. // FIXME: We need to restore this on the object at some point if we do this!
  1472. float flDragCoef = 0.0f;
  1473. physObj->SetDragCoefficient( &flDragCoef, &flDragCoef );
  1474. }
  1475. else
  1476. {
  1477. if ( flTargetDist < 512 )
  1478. flTargetDist = 512;
  1479. if ( flTargetDist > 1024 )
  1480. flTargetDist = 1024;
  1481. vecShoveVel *= flTargetDist * 3 * physObj->GetMass(); //FIXME: Scale by skill
  1482. vecShoveVel[2] += physObj->GetMass() * 350.0f;
  1483. }
  1484. if ( NPC_Rollermine_IsRollermine( m_hPhysicsTarget ) )
  1485. {
  1486. Pickup_OnPhysGunDrop( m_hPhysicsTarget, NULL, LAUNCHED_BY_CANNON );
  1487. }
  1488. //Send it flying
  1489. AngularImpulse angVel( random->RandomFloat(-180, 180), 100, random->RandomFloat(-360, 360) );
  1490. physObj->ApplyForceCenter( vecShoveVel );
  1491. physObj->AddVelocity( NULL, &angVel );
  1492. }
  1493. //Display dust
  1494. Vector vecRandom = RandomVector( -1, 1);
  1495. VectorNormalize( vecRandom );
  1496. g_pEffects->Dust( m_hPhysicsTarget->WorldSpaceCenter(), vecRandom, 64.0f, 32 );
  1497. // If it's being held by the player, break that bond
  1498. Pickup_ForcePlayerToDropThisObject( m_hPhysicsTarget );
  1499. EmitSound( "NPC_AntlionGuard.HitHard" );
  1500. //Clear the state information, we're done
  1501. ClearCondition( COND_ANTLIONGUARD_PHYSICS_TARGET );
  1502. ClearCondition( COND_ANTLIONGUARD_PHYSICS_TARGET_INVALID );
  1503. // Disrupt other objects near it, including the m_hPhysicsTarget if we had no valid enemy
  1504. ImpactShock( m_hPhysicsTarget->WorldSpaceCenter(), 150, 250.0f, pEnemy != NULL ? m_hPhysicsTarget : NULL );
  1505. // Notify any squad members that we're no longer monopolizing this object
  1506. if ( GetSquad() != NULL )
  1507. {
  1508. GetSquad()->BroadcastInteraction( g_interactionAntlionGuardShovedPhysicsObject, (void *)((CBaseEntity *)m_hPhysicsTarget), this );
  1509. }
  1510. m_hPhysicsTarget = NULL;
  1511. m_FailedPhysicsTargets.RemoveAll();
  1512. return;
  1513. }
  1514. if ( pEvent->event == AE_ANTLIONGUARD_CHARGE_HIT )
  1515. {
  1516. UTIL_ScreenShake( GetAbsOrigin(), 32.0f, 4.0f, 1.0f, 512, SHAKE_START );
  1517. EmitSound( "NPC_AntlionGuard.HitHard" );
  1518. Vector startPos = GetAbsOrigin();
  1519. float checkSize = ( CollisionProp()->BoundingRadius() + 8.0f );
  1520. Vector endPos = startPos + ( BodyDirection3D() * checkSize );
  1521. CTraceFilterCharge traceFilter( this, COLLISION_GROUP_NONE, this );
  1522. Ray_t ray;
  1523. ray.Init( startPos, endPos, GetHullMins(), GetHullMaxs() );
  1524. trace_t tr;
  1525. enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
  1526. if ( g_debug_antlionguard.GetInt() == 1 )
  1527. {
  1528. Vector hullMaxs = GetHullMaxs();
  1529. hullMaxs.x += checkSize;
  1530. NDebugOverlay::BoxDirection( startPos, GetHullMins(), hullMaxs, BodyDirection2D(), 100, 255, 255, 20, 1.0f );
  1531. }
  1532. //NDebugOverlay::Box3D( startPos, endPos, BodyDirection2D(),
  1533. if ( m_hChargeTarget && m_hChargeTarget->IsAlive() == false )
  1534. {
  1535. m_hChargeTarget = NULL;
  1536. m_hChargeTargetPosition = NULL;
  1537. }
  1538. // Cause a shock wave from this point which will distrupt nearby physics objects
  1539. ImpactShock( tr.endpos, 200, 500 );
  1540. return;
  1541. }
  1542. if ( pEvent->event == AE_ANTLIONGUARD_SHOVE )
  1543. {
  1544. EmitSound("NPC_AntlionGuard.StepLight", pEvent->eventtime );
  1545. Shove();
  1546. return;
  1547. }
  1548. if ( pEvent->event == AE_ANTLIONGUARD_FOOTSTEP_LIGHT )
  1549. {
  1550. if ( HasSpawnFlags(SF_ANTLIONGUARD_INSIDE_FOOTSTEPS) )
  1551. {
  1552. #if HL2_EPISODIC
  1553. Footstep( false );
  1554. #else
  1555. EmitSound("NPC_AntlionGuard.Inside.StepLight", pEvent->eventtime );
  1556. #endif // HL2_EPISODIC
  1557. }
  1558. else
  1559. {
  1560. #if HL2_EPISODIC
  1561. Footstep( false );
  1562. #else
  1563. EmitSound("NPC_AntlionGuard.StepLight", pEvent->eventtime );
  1564. #endif // HL2_EPISODIC
  1565. }
  1566. return;
  1567. }
  1568. if ( pEvent->event == AE_ANTLIONGUARD_FOOTSTEP_HEAVY )
  1569. {
  1570. if ( HasSpawnFlags(SF_ANTLIONGUARD_INSIDE_FOOTSTEPS) )
  1571. {
  1572. #if HL2_EPISODIC
  1573. Footstep( true );
  1574. #else
  1575. EmitSound( "NPC_AntlionGuard.Inside.StepHeavy", pEvent->eventtime );
  1576. #endif // HL2_EPISODIC
  1577. }
  1578. else
  1579. {
  1580. #if HL2_EPISODIC
  1581. Footstep( true );
  1582. #else
  1583. EmitSound( "NPC_AntlionGuard.StepHeavy", pEvent->eventtime );
  1584. #endif // HL2_EPISODIC
  1585. }
  1586. return;
  1587. }
  1588. if ( pEvent->event == AE_ANTLIONGUARD_VOICE_GROWL )
  1589. {
  1590. StartSounds();
  1591. float duration = 0.0f;
  1592. if ( random->RandomInt( 0, 10 ) < 6 )
  1593. {
  1594. duration = ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pGrowlHighSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardFastGrowl, ARRAYSIZE(envAntlionGuardFastGrowl) );
  1595. }
  1596. else
  1597. {
  1598. duration = 1.0f;
  1599. EmitSound( "NPC_AntlionGuard.FrustratedRoar" );
  1600. ENVELOPE_CONTROLLER.SoundFadeOut( m_pGrowlHighSound, 0.5f, false );
  1601. }
  1602. m_flAngerNoiseTime = gpGlobals->curtime + duration + random->RandomFloat( 2.0f, 4.0f );
  1603. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.0f, 0.1f );
  1604. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, 0.1f );
  1605. m_flBreathTime = gpGlobals->curtime + duration - 0.2f;
  1606. EmitSound( "NPC_AntlionGuard.Anger" );
  1607. return;
  1608. }
  1609. if ( pEvent->event == AE_ANTLIONGUARD_VOICE_BARK )
  1610. {
  1611. StartSounds();
  1612. float duration = ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pGrowlHighSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardBark1, ARRAYSIZE(envAntlionGuardBark1) );
  1613. ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pConfusedSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardBark2, ARRAYSIZE(envAntlionGuardBark2) );
  1614. m_flAngerNoiseTime = gpGlobals->curtime + duration + random->RandomFloat( 2.0f, 4.0f );
  1615. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.0f, 0.1f );
  1616. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, 0.1f );
  1617. m_flBreathTime = gpGlobals->curtime + duration - 0.2f;
  1618. return;
  1619. }
  1620. if ( pEvent->event == AE_ANTLIONGUARD_VOICE_ROAR )
  1621. {
  1622. StartSounds();
  1623. float duration = ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pGrowlHighSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardFastGrowl, ARRAYSIZE(envAntlionGuardFastGrowl) );
  1624. m_flAngerNoiseTime = gpGlobals->curtime + duration + random->RandomFloat( 2.0f, 4.0f );
  1625. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.0f, 0.1f );
  1626. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, 0.1f );
  1627. m_flBreathTime = gpGlobals->curtime + duration - 0.2f;
  1628. EmitSound( "NPC_AntlionGuard.Roar" );
  1629. return;
  1630. }
  1631. if ( pEvent->event == AE_ANTLIONGUARD_VOICE_PAIN )
  1632. {
  1633. StartSounds();
  1634. float duration = ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pConfusedSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardPain1, ARRAYSIZE(envAntlionGuardPain1) );
  1635. ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pGrowlHighSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardBark2, ARRAYSIZE(envAntlionGuardBark2) );
  1636. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.0f, 0.1f );
  1637. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, 0.1f );
  1638. m_flBreathTime = gpGlobals->curtime + duration - 0.2f;
  1639. return;
  1640. }
  1641. if ( pEvent->event == AE_ANTLIONGUARD_VOICE_SQUEEZE )
  1642. {
  1643. StartSounds();
  1644. float duration = ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pGrowlHighSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardSqueeze, ARRAYSIZE(envAntlionGuardSqueeze) );
  1645. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.6f, random->RandomFloat( 2.0f, 4.0f ) );
  1646. ENVELOPE_CONTROLLER.SoundChangePitch( m_pBreathSound, random->RandomInt( 60, 80 ), random->RandomFloat( 2.0f, 4.0f ) );
  1647. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, 0.1f );
  1648. m_flBreathTime = gpGlobals->curtime + ( duration * 0.5f );
  1649. EmitSound( "NPC_AntlionGuard.Anger" );
  1650. return;
  1651. }
  1652. if ( pEvent->event == AE_ANTLIONGUARD_VOICE_SCRATCH )
  1653. {
  1654. StartSounds();
  1655. float duration = random->RandomFloat( 2.0f, 4.0f );
  1656. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.6f, duration );
  1657. ENVELOPE_CONTROLLER.SoundChangePitch( m_pBreathSound, random->RandomInt( 60, 80 ), duration );
  1658. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, 0.1f );
  1659. m_flBreathTime = gpGlobals->curtime + duration;
  1660. EmitSound( "NPC_AntlionGuard.Anger" );
  1661. return;
  1662. }
  1663. if ( pEvent->event == AE_ANTLIONGUARD_VOICE_GRUNT )
  1664. {
  1665. StartSounds();
  1666. float duration = ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pConfusedSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardGrunt, ARRAYSIZE(envAntlionGuardGrunt) );
  1667. ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pGrowlHighSound, SOUNDCTRL_CHANGE_VOLUME, envAntlionGuardGrunt2, ARRAYSIZE(envAntlionGuardGrunt2) );
  1668. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.0f, 0.1f );
  1669. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, 0.1f );
  1670. m_flBreathTime = gpGlobals->curtime + duration;
  1671. return;
  1672. }
  1673. if ( pEvent->event == AE_ANTLIONGUARD_BURROW_OUT )
  1674. {
  1675. EmitSound( "NPC_Antlion.BurrowOut" );
  1676. //Shake the screen
  1677. UTIL_ScreenShake( GetAbsOrigin(), 0.5f, 80.0f, 1.0f, 256.0f, SHAKE_START );
  1678. //Throw dust up
  1679. UTIL_CreateAntlionDust( GetAbsOrigin() + Vector(0,0,24), GetLocalAngles() );
  1680. RemoveEffects( EF_NODRAW );
  1681. RemoveFlag( FL_NOTARGET );
  1682. if ( m_bCavernBreed )
  1683. {
  1684. if ( m_hCaveGlow[0] )
  1685. m_hCaveGlow[0]->TurnOn();
  1686. if ( m_hCaveGlow[1] )
  1687. m_hCaveGlow[1]->TurnOn();
  1688. }
  1689. return;
  1690. }
  1691. BaseClass::HandleAnimEvent( pEvent );
  1692. }
  1693. //-----------------------------------------------------------------------------
  1694. // Purpose:
  1695. //-----------------------------------------------------------------------------
  1696. void CNPC_AntlionGuard::SetHeavyDamageAnim( const Vector &vecSource )
  1697. {
  1698. if ( !m_bInCavern )
  1699. {
  1700. Vector vFacing = BodyDirection2D();
  1701. Vector vDamageDir = ( vecSource - WorldSpaceCenter() );
  1702. vDamageDir.z = 0.0f;
  1703. VectorNormalize( vDamageDir );
  1704. Vector vDamageRight, vDamageUp;
  1705. VectorVectors( vDamageDir, vDamageRight, vDamageUp );
  1706. float damageDot = DotProduct( vFacing, vDamageDir );
  1707. float damageRightDot = DotProduct( vFacing, vDamageRight );
  1708. // See if it's in front
  1709. if ( damageDot > 0.0f )
  1710. {
  1711. // See if it's right
  1712. if ( damageRightDot > 0.0f )
  1713. {
  1714. m_nFlinchActivity = ACT_ANTLIONGUARD_PHYSHIT_FR;
  1715. }
  1716. else
  1717. {
  1718. m_nFlinchActivity = ACT_ANTLIONGUARD_PHYSHIT_FL;
  1719. }
  1720. }
  1721. else
  1722. {
  1723. // Otherwise it's from behind
  1724. if ( damageRightDot < 0.0f )
  1725. {
  1726. m_nFlinchActivity = ACT_ANTLIONGUARD_PHYSHIT_RR;
  1727. }
  1728. else
  1729. {
  1730. m_nFlinchActivity = ACT_ANTLIONGUARD_PHYSHIT_RL;
  1731. }
  1732. }
  1733. }
  1734. }
  1735. //-----------------------------------------------------------------------------
  1736. // Purpose:
  1737. // Input : &info -
  1738. //-----------------------------------------------------------------------------
  1739. int CNPC_AntlionGuard::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  1740. {
  1741. CTakeDamageInfo dInfo = info;
  1742. // Don't take damage from another antlion guard!
  1743. if ( dInfo.GetAttacker() && dInfo.GetAttacker() != this && FClassnameIs( dInfo.GetAttacker(), "npc_antlionguard" ) )
  1744. return 0;
  1745. if ( ( dInfo.GetDamageType() & DMG_CRUSH ) && !( dInfo.GetDamageType() & DMG_VEHICLE ) )
  1746. {
  1747. // Don't take damage from physics objects that weren't thrown by the player.
  1748. CBaseEntity *pInflictor = dInfo.GetInflictor();
  1749. IPhysicsObject *pObj = pInflictor->VPhysicsGetObject();
  1750. if ( !pObj || !( pObj->GetGameFlags() & FVPHYSICS_WAS_THROWN ) )
  1751. {
  1752. return 0;
  1753. }
  1754. }
  1755. // Hack to make antlion guard harder in HARD
  1756. if ( g_pGameRules->IsSkillLevel(SKILL_HARD) && !(info.GetDamageType() & DMG_CRUSH) )
  1757. {
  1758. dInfo.SetDamage( dInfo.GetDamage() * 0.75 );
  1759. }
  1760. // Cap damage taken by crushing (otherwise we can get crushed oddly)
  1761. if ( ( dInfo.GetDamageType() & DMG_CRUSH ) && dInfo.GetDamage() > 100 )
  1762. {
  1763. dInfo.SetDamage( 100 );
  1764. }
  1765. // Only take damage from what we classify as heavy damages (explosions, refrigerators, etc)
  1766. if ( IsHeavyDamage( dInfo ) )
  1767. {
  1768. // Always take a set amount of damage from a combine ball
  1769. if ( info.GetInflictor() && UTIL_IsCombineBall( info.GetInflictor() ) )
  1770. {
  1771. dInfo.SetDamage( 50 );
  1772. }
  1773. UTIL_ScreenShake( GetAbsOrigin(), 32.0f, 8.0f, 0.5f, 512, SHAKE_START );
  1774. // Set our response animation
  1775. SetHeavyDamageAnim( dInfo.GetDamagePosition() );
  1776. // Explosive barrels don't carry through their attacker, so this
  1777. // condition isn't set, and we still want to flinch. So we set it.
  1778. SetCondition( COND_HEAVY_DAMAGE );
  1779. // TODO: Make this its own effect!
  1780. CEffectData data;
  1781. data.m_vOrigin = dInfo.GetDamagePosition();
  1782. data.m_vNormal = -dInfo.GetDamageForce();
  1783. VectorNormalize( data.m_vNormal );
  1784. DispatchEffect( "HunterDamage", data );
  1785. // Play a sound for a physics impact
  1786. if ( dInfo.GetDamageType() & DMG_CRUSH )
  1787. {
  1788. EmitSound("NPC_AntlionGuard.ShellCrack");
  1789. }
  1790. // Roar in pain
  1791. EmitSound( "NPC_AntlionGuard.Pain_Roar" );
  1792. // TODO: This will require a more complete solution; for now let's shelve it!
  1793. /*
  1794. if ( dInfo.GetDamageType() & DMG_BLAST )
  1795. {
  1796. // Create the dust effect in place
  1797. CParticleSystem *pParticle = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
  1798. if ( pParticle != NULL )
  1799. {
  1800. // Setup our basic parameters
  1801. pParticle->KeyValue( "start_active", "1" );
  1802. pParticle->KeyValue( "effect_name", "fire_medium_02_nosmoke" );
  1803. pParticle->SetParent( this );
  1804. pParticle->SetParentAttachment( "SetParentAttachment", "attach_glow2", true );
  1805. pParticle->SetLocalOrigin( Vector( -16, 24, 0 ) );
  1806. DispatchSpawn( pParticle );
  1807. if ( gpGlobals->curtime > 0.5f )
  1808. pParticle->Activate();
  1809. pParticle->SetThink( &CBaseEntity::SUB_Remove );
  1810. pParticle->SetNextThink( gpGlobals->curtime + random->RandomFloat( 2.0f, 3.0f ) );
  1811. }
  1812. }
  1813. */
  1814. }
  1815. int nPreHealth = GetHealth();
  1816. int nDamageTaken = BaseClass::OnTakeDamage_Alive( dInfo );
  1817. // See if we've crossed a measured phase in our health and flinch from that to show we do take damage
  1818. if ( !m_bInCavern && HasCondition( COND_HEAVY_DAMAGE ) == false && ( info.GetDamageType() & DMG_BULLET ) )
  1819. {
  1820. bool bTakeHeavyDamage = false;
  1821. // Do an early flinch so that players understand the guard can be hurt by
  1822. if ( ( (float) GetHealth() / (float) GetMaxHealth() ) > 0.9f )
  1823. {
  1824. float flPrePerc = (float) nPreHealth / (float) GetMaxHealth();
  1825. float flPostPerc = (float) GetHealth() / (float) GetMaxHealth();
  1826. if ( flPrePerc >= 0.95f && flPostPerc < 0.95f )
  1827. {
  1828. bTakeHeavyDamage = true;
  1829. }
  1830. }
  1831. // Otherwise see if we've passed a measured point in our health
  1832. if ( bTakeHeavyDamage == false )
  1833. {
  1834. const float flNumDamagePhases = 5.0f;
  1835. float flDenom = ( (float) GetMaxHealth() / flNumDamagePhases );
  1836. int nPreDamagePhase = ceil( (float) nPreHealth / flDenom );
  1837. int nPostDamagePhase = ceil( (float) GetHealth() / flDenom );
  1838. if ( nPreDamagePhase != nPostDamagePhase )
  1839. {
  1840. bTakeHeavyDamage = true;
  1841. }
  1842. }
  1843. // Flinch if we should
  1844. if ( bTakeHeavyDamage )
  1845. {
  1846. // Set our response animation
  1847. SetHeavyDamageAnim( dInfo.GetDamagePosition() );
  1848. // Roar in pain
  1849. EmitSound( "NPC_AntlionGuard.Pain_Roar" );
  1850. // Flinch!
  1851. SetCondition( COND_HEAVY_DAMAGE );
  1852. }
  1853. }
  1854. return nDamageTaken;
  1855. }
  1856. //-----------------------------------------------------------------------------
  1857. // Purpose:
  1858. // Input : *pAttacker -
  1859. // flDamage -
  1860. // &vecDir -
  1861. // *ptr -
  1862. // bitsDamageType -
  1863. //-----------------------------------------------------------------------------
  1864. void CNPC_AntlionGuard::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  1865. {
  1866. CTakeDamageInfo info = inputInfo;
  1867. // Bullets are weak against us, buckshot less so
  1868. if ( info.GetDamageType() & DMG_BUCKSHOT )
  1869. {
  1870. info.ScaleDamage( 0.5f );
  1871. }
  1872. else if ( info.GetDamageType() & DMG_BULLET )
  1873. {
  1874. info.ScaleDamage( 0.25f );
  1875. }
  1876. // Make sure we haven't rounded down to a minimal amount
  1877. if ( info.GetDamage() < 1.0f )
  1878. {
  1879. info.SetDamage( 1.0f );
  1880. }
  1881. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  1882. }
  1883. //-----------------------------------------------------------------------------
  1884. // Purpose:
  1885. // Input : *pTask -
  1886. //-----------------------------------------------------------------------------
  1887. void CNPC_AntlionGuard::StartTask( const Task_t *pTask )
  1888. {
  1889. switch ( pTask->iTask )
  1890. {
  1891. case TASK_ANTLIONGUARD_SET_FLINCH_ACTIVITY:
  1892. SetIdealActivity( (Activity) m_nFlinchActivity );
  1893. break;
  1894. case TASK_ANTLIONGUARD_SUMMON:
  1895. SummonAntlions();
  1896. m_OnSummon.FireOutput( this, this, 0 );
  1897. TaskComplete();
  1898. break;
  1899. case TASK_ANTLIONGUARD_SHOVE_PHYSOBJECT:
  1900. {
  1901. if ( ( m_hPhysicsTarget == NULL ) || ( GetEnemy() == NULL ) )
  1902. {
  1903. TaskFail( "Tried to shove a NULL physics target!\n" );
  1904. break;
  1905. }
  1906. //Get the direction and distance to our thrown object
  1907. Vector dirToTarget = ( m_hPhysicsTarget->WorldSpaceCenter() - WorldSpaceCenter() );
  1908. float distToTarget = VectorNormalize( dirToTarget );
  1909. dirToTarget.z = 0;
  1910. //Validate it's still close enough to shove
  1911. //FIXME: Real numbers
  1912. if ( distToTarget > 256.0f )
  1913. {
  1914. RememberFailedPhysicsTarget( m_hPhysicsTarget );
  1915. m_hPhysicsTarget = NULL;
  1916. TaskFail( "Shove target moved\n" );
  1917. break;
  1918. }
  1919. //Validate its offset from our facing
  1920. float targetYaw = UTIL_VecToYaw( dirToTarget );
  1921. float offset = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( GetLocalAngles().y ) );
  1922. if ( fabs( offset ) > 55 )
  1923. {
  1924. RememberFailedPhysicsTarget( m_hPhysicsTarget );
  1925. m_hPhysicsTarget = NULL;
  1926. TaskFail( "Shove target off-center\n" );
  1927. break;
  1928. }
  1929. //Blend properly
  1930. SetPoseParameter( m_poseThrow, offset );
  1931. //Start playing the animation
  1932. SetActivity( ACT_ANTLIONGUARD_SHOVE_PHYSOBJECT );
  1933. }
  1934. break;
  1935. case TASK_ANTLIONGUARD_FIND_PHYSOBJECT:
  1936. {
  1937. if ( m_bInCavern && !m_bPreferPhysicsAttack )
  1938. {
  1939. TaskFail( "Cavern guard is not allowed to use physics attacks." );
  1940. }
  1941. // Force the antlion guard to find a physobject
  1942. m_flPhysicsCheckTime = 0;
  1943. UpdatePhysicsTarget( false, (100*12) );
  1944. if ( m_hPhysicsTarget )
  1945. {
  1946. TaskComplete();
  1947. }
  1948. else
  1949. {
  1950. TaskFail( "Failed to find a physobject.\n" );
  1951. }
  1952. }
  1953. break;
  1954. case TASK_ANTLIONGUARD_GET_PATH_TO_PHYSOBJECT:
  1955. {
  1956. if ( ( m_hPhysicsTarget == NULL ) || ( GetEnemy() == NULL ) )
  1957. {
  1958. TaskFail( "Tried to find a path to NULL physics target!\n" );
  1959. break;
  1960. }
  1961. Vector vecGoalPos = m_vecPhysicsHitPosition;
  1962. AI_NavGoal_t goal( GOALTYPE_LOCATION, vecGoalPos, ACT_RUN );
  1963. if ( GetNavigator()->SetGoal( goal ) )
  1964. {
  1965. if ( g_debug_antlionguard.GetInt() == 1 )
  1966. {
  1967. NDebugOverlay::Cross3D( vecGoalPos, Vector(8,8,8), -Vector(8,8,8), 0, 255, 0, true, 2.0f );
  1968. NDebugOverlay::Line( vecGoalPos, m_hPhysicsTarget->WorldSpaceCenter(), 0, 255, 0, true, 2.0f );
  1969. NDebugOverlay::Line( m_hPhysicsTarget->WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), 0, 255, 0, true, 2.0f );
  1970. }
  1971. // Face the enemy
  1972. GetNavigator()->SetArrivalDirection( GetEnemy() );
  1973. TaskComplete();
  1974. }
  1975. else
  1976. {
  1977. if ( g_debug_antlionguard.GetInt() == 1 )
  1978. {
  1979. NDebugOverlay::Cross3D( vecGoalPos, Vector(8,8,8), -Vector(8,8,8), 255, 0, 0, true, 2.0f );
  1980. NDebugOverlay::Line( vecGoalPos, m_hPhysicsTarget->WorldSpaceCenter(), 255, 0, 0, true, 2.0f );
  1981. NDebugOverlay::Line( m_hPhysicsTarget->WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), 255, 0, 0, true, 2.0f );
  1982. }
  1983. RememberFailedPhysicsTarget( m_hPhysicsTarget );
  1984. m_hPhysicsTarget = NULL;
  1985. TaskFail( "Unable to navigate to physics attack target!\n" );
  1986. break;
  1987. }
  1988. //!!!HACKHACK - this is a hack that covers a bug in antlion guard physics target selection! (Tracker #77601)
  1989. // This piece of code (combined with some code in GatherConditions) COVERS THE BUG by escaping the schedule
  1990. // if 30 seconds have passed (it should never take this long for the guard to get to an object and hit it).
  1991. // It's too scary to figure out why this particular antlion guard can't get to its object, but we're shipping
  1992. // like, tomorrow. (sjb) 8/22/2007
  1993. m_flWaitFinished = gpGlobals->curtime + 30.0f;
  1994. }
  1995. break;
  1996. case TASK_ANTLIONGUARD_CHARGE:
  1997. {
  1998. // HACK: Because the guard stops running his normal blended movement
  1999. // here, he also needs to remove his blended movement layers!
  2000. GetMotor()->MoveStop();
  2001. SetActivity( ACT_ANTLIONGUARD_CHARGE_START );
  2002. m_bDecidedNotToStop = false;
  2003. }
  2004. break;
  2005. case TASK_ANTLIONGUARD_GET_PATH_TO_CHARGE_POSITION:
  2006. {
  2007. if ( !m_hChargeTargetPosition )
  2008. {
  2009. TaskFail( "Tried to find a charge position without one specified.\n" );
  2010. break;
  2011. }
  2012. // Move to the charge position
  2013. AI_NavGoal_t goal( GOALTYPE_LOCATION, m_hChargeTargetPosition->GetAbsOrigin(), ACT_RUN );
  2014. if ( GetNavigator()->SetGoal( goal ) )
  2015. {
  2016. // We want to face towards the charge target
  2017. Vector vecDir = m_hChargeTarget->GetAbsOrigin() - m_hChargeTargetPosition->GetAbsOrigin();
  2018. VectorNormalize( vecDir );
  2019. vecDir.z = 0;
  2020. GetNavigator()->SetArrivalDirection( vecDir );
  2021. TaskComplete();
  2022. }
  2023. else
  2024. {
  2025. m_hChargeTarget = NULL;
  2026. m_hChargeTargetPosition = NULL;
  2027. TaskFail( FAIL_NO_ROUTE );
  2028. }
  2029. }
  2030. break;
  2031. case TASK_ANTLIONGUARD_GET_PATH_TO_NEAREST_NODE:
  2032. {
  2033. if ( !GetEnemy() )
  2034. {
  2035. TaskFail( FAIL_NO_ENEMY );
  2036. break;
  2037. }
  2038. // Find the nearest node to the enemy
  2039. int node = GetNavigator()->GetNetwork()->NearestNodeToPoint( this, GetEnemy()->GetAbsOrigin(), false );
  2040. CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( node );
  2041. if( pNode == NULL )
  2042. {
  2043. TaskFail( FAIL_NO_ROUTE );
  2044. break;
  2045. }
  2046. Vector vecNodePos = pNode->GetPosition( GetHullType() );
  2047. AI_NavGoal_t goal( GOALTYPE_LOCATION, vecNodePos, ACT_RUN );
  2048. if ( GetNavigator()->SetGoal( goal ) )
  2049. {
  2050. GetNavigator()->SetArrivalDirection( GetEnemy() );
  2051. TaskComplete();
  2052. break;
  2053. }
  2054. TaskFail( FAIL_NO_ROUTE );
  2055. break;
  2056. }
  2057. break;
  2058. case TASK_ANTLIONGUARD_GET_CHASE_PATH_ENEMY_TOLERANCE:
  2059. {
  2060. // Chase the enemy, but allow local navigation to succeed if it gets within the goal tolerance
  2061. GetNavigator()->SetLocalSucceedOnWithinTolerance( true );
  2062. if ( GetNavigator()->SetGoal( GOALTYPE_ENEMY ) )
  2063. {
  2064. TaskComplete();
  2065. }
  2066. else
  2067. {
  2068. RememberUnreachable(GetEnemy());
  2069. TaskFail(FAIL_NO_ROUTE);
  2070. }
  2071. GetNavigator()->SetLocalSucceedOnWithinTolerance( false );
  2072. }
  2073. break;
  2074. case TASK_ANTLIONGUARD_OPPORTUNITY_THROW:
  2075. {
  2076. // If we've got some live antlions, look for a physics object to throw
  2077. if ( m_iNumLiveAntlions >= 2 && RandomFloat(0,1) > 0.5 )
  2078. {
  2079. m_FailedPhysicsTargets.RemoveAll();
  2080. UpdatePhysicsTarget( false, m_bPreferPhysicsAttack ? (100*12) : ANTLIONGUARD_FARTHEST_PHYSICS_OBJECT );
  2081. if ( HasCondition( COND_ANTLIONGUARD_PHYSICS_TARGET ) && !m_bInCavern )
  2082. {
  2083. SetSchedule( SCHED_ANTLIONGUARD_PHYSICS_ATTACK );
  2084. }
  2085. }
  2086. TaskComplete();
  2087. }
  2088. break;
  2089. default:
  2090. BaseClass::StartTask(pTask);
  2091. break;
  2092. }
  2093. }
  2094. //-----------------------------------------------------------------------------
  2095. // Purpose: Calculate & apply damage & force for a charge to a target.
  2096. // Done outside of the guard because we need to do this inside a trace filter.
  2097. //-----------------------------------------------------------------------------
  2098. void ApplyChargeDamage( CBaseEntity *pAntlionGuard, CBaseEntity *pTarget, float flDamage )
  2099. {
  2100. Vector attackDir = ( pTarget->WorldSpaceCenter() - pAntlionGuard->WorldSpaceCenter() );
  2101. VectorNormalize( attackDir );
  2102. Vector offset = RandomVector( -32, 32 ) + pTarget->WorldSpaceCenter();
  2103. // Generate enough force to make a 75kg guy move away at 700 in/sec
  2104. Vector vecForce = attackDir * ImpulseScale( 75, 700 );
  2105. // Deal the damage
  2106. CTakeDamageInfo info( pAntlionGuard, pAntlionGuard, vecForce, offset, flDamage, DMG_CLUB );
  2107. pTarget->TakeDamage( info );
  2108. #if HL2_EPISODIC
  2109. // If I am a cavern guard attacking the player, and he still lives, then poison him too.
  2110. Assert( dynamic_cast<CNPC_AntlionGuard *>(pAntlionGuard) );
  2111. if ( static_cast<CNPC_AntlionGuard *>(pAntlionGuard)->IsInCavern() && pTarget->IsPlayer() && pTarget->IsAlive() && pTarget->m_iHealth > ANTLIONGUARD_POISON_TO)
  2112. {
  2113. // That didn't finish them. Take them down to one point with poison damage. It'll heal.
  2114. pTarget->TakeDamage( CTakeDamageInfo( pAntlionGuard, pAntlionGuard, pTarget->m_iHealth - ANTLIONGUARD_POISON_TO, DMG_POISON ) );
  2115. }
  2116. #endif
  2117. }
  2118. //-----------------------------------------------------------------------------
  2119. // Purpose: A simple trace filter class to skip small moveable physics objects
  2120. //-----------------------------------------------------------------------------
  2121. class CTraceFilterSkipPhysics : public CTraceFilter
  2122. {
  2123. public:
  2124. // It does have a base, but we'll never network anything below here..
  2125. DECLARE_CLASS_NOBASE( CTraceFilterSkipPhysics );
  2126. CTraceFilterSkipPhysics( const IHandleEntity *passentity, int collisionGroup, float minMass )
  2127. : m_pPassEnt(passentity), m_collisionGroup(collisionGroup), m_minMass(minMass)
  2128. {
  2129. }
  2130. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  2131. {
  2132. if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
  2133. return false;
  2134. if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
  2135. return false;
  2136. // Don't test if the game code tells us we should ignore this collision...
  2137. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  2138. if ( pEntity )
  2139. {
  2140. if ( !pEntity->ShouldCollide( m_collisionGroup, contentsMask ) )
  2141. return false;
  2142. if ( !g_pGameRules->ShouldCollide( m_collisionGroup, pEntity->GetCollisionGroup() ) )
  2143. return false;
  2144. // don't test small moveable physics objects (unless it's an NPC)
  2145. if ( !pEntity->IsNPC() && pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
  2146. {
  2147. IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject();
  2148. Assert(pPhysics);
  2149. if ( pPhysics->IsMoveable() && pPhysics->GetMass() < m_minMass )
  2150. return false;
  2151. }
  2152. // If we hit an antlion, don't stop, but kill it
  2153. if ( pEntity->Classify() == CLASS_ANTLION )
  2154. {
  2155. CBaseEntity *pGuard = (CBaseEntity*)EntityFromEntityHandle( m_pPassEnt );
  2156. ApplyChargeDamage( pGuard, pEntity, pEntity->GetHealth() );
  2157. return false;
  2158. }
  2159. }
  2160. return true;
  2161. }
  2162. private:
  2163. const IHandleEntity *m_pPassEnt;
  2164. int m_collisionGroup;
  2165. float m_minMass;
  2166. };
  2167. inline void TraceHull_SkipPhysics( const Vector &vecAbsStart, const Vector &vecAbsEnd, const Vector &hullMin,
  2168. const Vector &hullMax, unsigned int mask, const CBaseEntity *ignore,
  2169. int collisionGroup, trace_t *ptr, float minMass )
  2170. {
  2171. Ray_t ray;
  2172. ray.Init( vecAbsStart, vecAbsEnd, hullMin, hullMax );
  2173. CTraceFilterSkipPhysics traceFilter( ignore, collisionGroup, minMass );
  2174. enginetrace->TraceRay( ray, mask, &traceFilter, ptr );
  2175. }
  2176. //-----------------------------------------------------------------------------
  2177. // Purpose: Return true if our charge target is right in front of the guard
  2178. // Output : Returns true on success, false on failure.
  2179. //-----------------------------------------------------------------------------
  2180. bool CNPC_AntlionGuard::EnemyIsRightInFrontOfMe( CBaseEntity **pEntity )
  2181. {
  2182. if ( !GetEnemy() )
  2183. return false;
  2184. if ( (GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter()).LengthSqr() < (156*156) )
  2185. {
  2186. Vector vecLOS = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() );
  2187. vecLOS.z = 0;
  2188. VectorNormalize( vecLOS );
  2189. Vector vBodyDir = BodyDirection2D();
  2190. if ( DotProduct( vecLOS, vBodyDir ) > 0.8 )
  2191. {
  2192. // He's in front of me, and close. Make sure he's not behind a wall.
  2193. trace_t tr;
  2194. UTIL_TraceLine( WorldSpaceCenter(), GetEnemy()->EyePosition(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  2195. if ( tr.m_pEnt == GetEnemy() )
  2196. {
  2197. *pEntity = tr.m_pEnt;
  2198. return true;
  2199. }
  2200. }
  2201. }
  2202. return false;
  2203. }
  2204. //-----------------------------------------------------------------------------
  2205. // Purpose: While charging, look ahead and see if we're going to run into anything.
  2206. // If we are, start the gesture so it looks like we're anticipating the hit.
  2207. //-----------------------------------------------------------------------------
  2208. void CNPC_AntlionGuard::ChargeLookAhead( void )
  2209. {
  2210. trace_t tr;
  2211. Vector vecForward;
  2212. GetVectors( &vecForward, NULL, NULL );
  2213. Vector vecTestPos = GetAbsOrigin() + ( vecForward * m_flGroundSpeed * 0.75 );
  2214. Vector testHullMins = GetHullMins();
  2215. testHullMins.z += (StepHeight() * 2);
  2216. TraceHull_SkipPhysics( GetAbsOrigin(), vecTestPos, testHullMins, GetHullMaxs(), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, &tr, VPhysicsGetObject()->GetMass() * 0.5 );
  2217. //NDebugOverlay::Box( tr.startpos, testHullMins, GetHullMaxs(), 0, 255, 0, true, 0.1f );
  2218. //NDebugOverlay::Box( vecTestPos, testHullMins, GetHullMaxs(), 255, 0, 0, true, 0.1f );
  2219. if ( tr.fraction != 1.0 )
  2220. {
  2221. // Start playing the hit animation
  2222. AddGesture( ACT_ANTLIONGUARD_CHARGE_ANTICIPATION );
  2223. }
  2224. }
  2225. //-----------------------------------------------------------------------------
  2226. // Purpose: Handles the guard charging into something. Returns true if it hit the world.
  2227. //-----------------------------------------------------------------------------
  2228. bool CNPC_AntlionGuard::HandleChargeImpact( Vector vecImpact, CBaseEntity *pEntity )
  2229. {
  2230. // Cause a shock wave from this point which will disrupt nearby physics objects
  2231. ImpactShock( vecImpact, 128, 350 );
  2232. // Did we hit anything interesting?
  2233. if ( !pEntity || pEntity->IsWorld() )
  2234. {
  2235. // Robin: Due to some of the finicky details in the motor, the guard will hit
  2236. // the world when it is blocked by our enemy when trying to step up
  2237. // during a moveprobe. To get around this, we see if the enemy's within
  2238. // a volume in front of the guard when we hit the world, and if he is,
  2239. // we hit him anyway.
  2240. EnemyIsRightInFrontOfMe( &pEntity );
  2241. // Did we manage to find him? If not, increment our charge miss count and abort.
  2242. if ( pEntity->IsWorld() )
  2243. {
  2244. m_iChargeMisses++;
  2245. return true;
  2246. }
  2247. }
  2248. // Hit anything we don't like
  2249. if ( IRelationType( pEntity ) == D_HT && ( GetNextAttack() < gpGlobals->curtime ) )
  2250. {
  2251. EmitSound( "NPC_AntlionGuard.Shove" );
  2252. if ( !IsPlayingGesture( ACT_ANTLIONGUARD_CHARGE_HIT ) )
  2253. {
  2254. RestartGesture( ACT_ANTLIONGUARD_CHARGE_HIT );
  2255. }
  2256. ChargeDamage( pEntity );
  2257. pEntity->ApplyAbsVelocityImpulse( ( BodyDirection2D() * 400 ) + Vector( 0, 0, 200 ) );
  2258. if ( !pEntity->IsAlive() && GetEnemy() == pEntity )
  2259. {
  2260. SetEnemy( NULL );
  2261. }
  2262. SetNextAttack( gpGlobals->curtime + 2.0f );
  2263. SetActivity( ACT_ANTLIONGUARD_CHARGE_STOP );
  2264. // We've hit something, so clear our miss count
  2265. m_iChargeMisses = 0;
  2266. return false;
  2267. }
  2268. // Hit something we don't hate. If it's not moveable, crash into it.
  2269. if ( pEntity->GetMoveType() == MOVETYPE_NONE || pEntity->GetMoveType() == MOVETYPE_PUSH )
  2270. return true;
  2271. // If it's a vphysics object that's too heavy, crash into it too.
  2272. if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
  2273. {
  2274. IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject();
  2275. if ( pPhysics )
  2276. {
  2277. // If the object is being held by the player, knock it out of his hands
  2278. if ( pPhysics->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  2279. {
  2280. Pickup_ForcePlayerToDropThisObject( pEntity );
  2281. return false;
  2282. }
  2283. if ( (!pPhysics->IsMoveable() || pPhysics->GetMass() > VPhysicsGetObject()->GetMass() * 0.5f ) )
  2284. return true;
  2285. }
  2286. }
  2287. return false;
  2288. /*
  2289. ROBIN: Wrote & then removed this. If we want to have large rocks that the guard
  2290. should smack around, then we should enable it.
  2291. else
  2292. {
  2293. // If we hit a physics prop, smack the crap out of it. (large rocks)
  2294. // Factor the object mass into it, because we want to move it no matter how heavy it is.
  2295. if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
  2296. {
  2297. CTakeDamageInfo info( this, this, 250, DMG_BLAST );
  2298. info.SetDamagePosition( vecImpact );
  2299. float flForce = ImpulseScale( pEntity->VPhysicsGetObject()->GetMass(), 250 );
  2300. flForce *= random->RandomFloat( 0.85, 1.15 );
  2301. // Calculate the vector and stuff it into the takedamageinfo
  2302. Vector vecForce = BodyDirection3D();
  2303. VectorNormalize( vecForce );
  2304. vecForce *= flForce;
  2305. vecForce *= phys_pushscale.GetFloat();
  2306. info.SetDamageForce( vecForce );
  2307. pEntity->VPhysicsTakeDamage( info );
  2308. }
  2309. }
  2310. */
  2311. }
  2312. //-----------------------------------------------------------------------------
  2313. // Purpose:
  2314. // Output : float
  2315. //-----------------------------------------------------------------------------
  2316. float CNPC_AntlionGuard::ChargeSteer( void )
  2317. {
  2318. trace_t tr;
  2319. Vector testPos, steer, forward, right;
  2320. QAngle angles;
  2321. const float testLength = m_flGroundSpeed * 0.15f;
  2322. //Get our facing
  2323. GetVectors( &forward, &right, NULL );
  2324. steer = forward;
  2325. const float faceYaw = UTIL_VecToYaw( forward );
  2326. //Offset right
  2327. VectorAngles( forward, angles );
  2328. angles[YAW] += 45.0f;
  2329. AngleVectors( angles, &forward );
  2330. //Probe out
  2331. testPos = GetAbsOrigin() + ( forward * testLength );
  2332. //Offset by step height
  2333. Vector testHullMins = GetHullMins();
  2334. testHullMins.z += (StepHeight() * 2);
  2335. //Probe
  2336. TraceHull_SkipPhysics( GetAbsOrigin(), testPos, testHullMins, GetHullMaxs(), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr, VPhysicsGetObject()->GetMass() * 0.5f );
  2337. //Debug info
  2338. if ( g_debug_antlionguard.GetInt() == 1 )
  2339. {
  2340. if ( tr.fraction == 1.0f )
  2341. {
  2342. NDebugOverlay::BoxDirection( GetAbsOrigin(), testHullMins, GetHullMaxs() + Vector(testLength,0,0), forward, 0, 255, 0, 8, 0.1f );
  2343. }
  2344. else
  2345. {
  2346. NDebugOverlay::BoxDirection( GetAbsOrigin(), testHullMins, GetHullMaxs() + Vector(testLength,0,0), forward, 255, 0, 0, 8, 0.1f );
  2347. }
  2348. }
  2349. //Add in this component
  2350. steer += ( right * 0.5f ) * ( 1.0f - tr.fraction );
  2351. //Offset left
  2352. angles[YAW] -= 90.0f;
  2353. AngleVectors( angles, &forward );
  2354. //Probe out
  2355. testPos = GetAbsOrigin() + ( forward * testLength );
  2356. // Probe
  2357. TraceHull_SkipPhysics( GetAbsOrigin(), testPos, testHullMins, GetHullMaxs(), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr, VPhysicsGetObject()->GetMass() * 0.5f );
  2358. //Debug
  2359. if ( g_debug_antlionguard.GetInt() == 1 )
  2360. {
  2361. if ( tr.fraction == 1.0f )
  2362. {
  2363. NDebugOverlay::BoxDirection( GetAbsOrigin(), testHullMins, GetHullMaxs() + Vector(testLength,0,0), forward, 0, 255, 0, 8, 0.1f );
  2364. }
  2365. else
  2366. {
  2367. NDebugOverlay::BoxDirection( GetAbsOrigin(), testHullMins, GetHullMaxs() + Vector(testLength,0,0), forward, 255, 0, 0, 8, 0.1f );
  2368. }
  2369. }
  2370. //Add in this component
  2371. steer -= ( right * 0.5f ) * ( 1.0f - tr.fraction );
  2372. //Debug
  2373. if ( g_debug_antlionguard.GetInt() == 1 )
  2374. {
  2375. NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + ( steer * 512.0f ), 255, 255, 0, true, 0.1f );
  2376. NDebugOverlay::Cross3D( GetAbsOrigin() + ( steer * 512.0f ), Vector(2,2,2), -Vector(2,2,2), 255, 255, 0, true, 0.1f );
  2377. NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + ( BodyDirection3D() * 256.0f ), 255, 0, 255, true, 0.1f );
  2378. NDebugOverlay::Cross3D( GetAbsOrigin() + ( BodyDirection3D() * 256.0f ), Vector(2,2,2), -Vector(2,2,2), 255, 0, 255, true, 0.1f );
  2379. }
  2380. return UTIL_AngleDiff( UTIL_VecToYaw( steer ), faceYaw );
  2381. }
  2382. //-----------------------------------------------------------------------------
  2383. // Purpose:
  2384. // Input : *pTask -
  2385. //-----------------------------------------------------------------------------
  2386. void CNPC_AntlionGuard::RunTask( const Task_t *pTask )
  2387. {
  2388. switch (pTask->iTask)
  2389. {
  2390. case TASK_ANTLIONGUARD_SET_FLINCH_ACTIVITY:
  2391. AutoMovement( );
  2392. if ( IsActivityFinished() )
  2393. {
  2394. TaskComplete();
  2395. }
  2396. break;
  2397. case TASK_ANTLIONGUARD_SHOVE_PHYSOBJECT:
  2398. if ( IsActivityFinished() )
  2399. {
  2400. TaskComplete();
  2401. }
  2402. break;
  2403. /*
  2404. case TASK_RUN_PATH:
  2405. {
  2406. }
  2407. break;
  2408. */
  2409. case TASK_ANTLIONGUARD_CHARGE:
  2410. {
  2411. Activity eActivity = GetActivity();
  2412. // See if we're trying to stop after hitting/missing our target
  2413. if ( eActivity == ACT_ANTLIONGUARD_CHARGE_STOP || eActivity == ACT_ANTLIONGUARD_CHARGE_CRASH )
  2414. {
  2415. if ( IsActivityFinished() )
  2416. {
  2417. TaskComplete();
  2418. return;
  2419. }
  2420. // Still in the process of slowing down. Run movement until it's done.
  2421. AutoMovement();
  2422. return;
  2423. }
  2424. // Check for manual transition
  2425. if ( ( eActivity == ACT_ANTLIONGUARD_CHARGE_START ) && ( IsActivityFinished() ) )
  2426. {
  2427. SetActivity( ACT_ANTLIONGUARD_CHARGE_RUN );
  2428. }
  2429. // See if we're still running
  2430. if ( eActivity == ACT_ANTLIONGUARD_CHARGE_RUN || eActivity == ACT_ANTLIONGUARD_CHARGE_START )
  2431. {
  2432. if ( HasCondition( COND_NEW_ENEMY ) || HasCondition( COND_LOST_ENEMY ) || HasCondition( COND_ENEMY_DEAD ) )
  2433. {
  2434. SetActivity( ACT_ANTLIONGUARD_CHARGE_STOP );
  2435. return;
  2436. }
  2437. else
  2438. {
  2439. if ( GetEnemy() != NULL )
  2440. {
  2441. Vector goalDir = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() );
  2442. VectorNormalize( goalDir );
  2443. if ( DotProduct( BodyDirection2D(), goalDir ) < 0.25f )
  2444. {
  2445. if ( !m_bDecidedNotToStop )
  2446. {
  2447. // We've missed the target. Randomly decide not to stop, which will cause
  2448. // the guard to just try and swing around for another pass.
  2449. m_bDecidedNotToStop = true;
  2450. if ( RandomFloat(0,1) > 0.3 )
  2451. {
  2452. m_iChargeMisses++;
  2453. SetActivity( ACT_ANTLIONGUARD_CHARGE_STOP );
  2454. }
  2455. }
  2456. }
  2457. else
  2458. {
  2459. m_bDecidedNotToStop = false;
  2460. }
  2461. }
  2462. }
  2463. }
  2464. // Steer towards our target
  2465. float idealYaw;
  2466. if ( GetEnemy() == NULL )
  2467. {
  2468. idealYaw = GetMotor()->GetIdealYaw();
  2469. }
  2470. else
  2471. {
  2472. idealYaw = CalcIdealYaw( GetEnemy()->GetAbsOrigin() );
  2473. }
  2474. // Add in our steering offset
  2475. idealYaw += ChargeSteer();
  2476. // Turn to face
  2477. GetMotor()->SetIdealYawAndUpdate( idealYaw );
  2478. // See if we're going to run into anything soon
  2479. ChargeLookAhead();
  2480. // Let our animations simply move us forward. Keep the result
  2481. // of the movement so we know whether we've hit our target.
  2482. AIMoveTrace_t moveTrace;
  2483. if ( AutoMovement( GetEnemy(), &moveTrace ) == false )
  2484. {
  2485. // Only stop if we hit the world
  2486. if ( HandleChargeImpact( moveTrace.vEndPosition, moveTrace.pObstruction ) )
  2487. {
  2488. // If we're starting up, this is an error
  2489. if ( eActivity == ACT_ANTLIONGUARD_CHARGE_START )
  2490. {
  2491. TaskFail( "Unable to make initial movement of charge\n" );
  2492. return;
  2493. }
  2494. // Crash unless we're trying to stop already
  2495. if ( eActivity != ACT_ANTLIONGUARD_CHARGE_STOP )
  2496. {
  2497. if ( moveTrace.fStatus == AIMR_BLOCKED_WORLD && moveTrace.vHitNormal == vec3_origin )
  2498. {
  2499. SetActivity( ACT_ANTLIONGUARD_CHARGE_STOP );
  2500. }
  2501. else
  2502. {
  2503. SetActivity( ACT_ANTLIONGUARD_CHARGE_CRASH );
  2504. }
  2505. }
  2506. }
  2507. else if ( moveTrace.pObstruction )
  2508. {
  2509. // If we hit an antlion, don't stop, but kill it
  2510. if ( moveTrace.pObstruction->Classify() == CLASS_ANTLION )
  2511. {
  2512. if ( FClassnameIs( moveTrace.pObstruction, "npc_antlionguard" ) )
  2513. {
  2514. // Crash unless we're trying to stop already
  2515. if ( eActivity != ACT_ANTLIONGUARD_CHARGE_STOP )
  2516. {
  2517. SetActivity( ACT_ANTLIONGUARD_CHARGE_STOP );
  2518. }
  2519. }
  2520. else
  2521. {
  2522. ApplyChargeDamage( this, moveTrace.pObstruction, moveTrace.pObstruction->GetHealth() );
  2523. }
  2524. }
  2525. }
  2526. }
  2527. }
  2528. break;
  2529. case TASK_WAIT_FOR_MOVEMENT:
  2530. {
  2531. // the cavern antlion can clothesline gordon
  2532. if ( m_bInCavern )
  2533. {
  2534. // See if we're going to run into anything soon
  2535. ChargeLookAhead();
  2536. if ( HasCondition(COND_CAN_MELEE_ATTACK1) )
  2537. {
  2538. CBaseEntity *pEntity = GetEnemy();
  2539. if (pEntity && pEntity->IsPlayer())
  2540. {
  2541. EmitSound( "NPC_AntlionGuard.Shove" );
  2542. if ( !IsPlayingGesture( ACT_ANTLIONGUARD_CHARGE_HIT ) )
  2543. {
  2544. RestartGesture( ACT_ANTLIONGUARD_CHARGE_HIT );
  2545. }
  2546. ChargeDamage( pEntity );
  2547. pEntity->ApplyAbsVelocityImpulse( ( BodyDirection2D() * 400 ) + Vector( 0, 0, 200 ) );
  2548. if ( !pEntity->IsAlive() && GetEnemy() == pEntity )
  2549. {
  2550. SetEnemy( NULL );
  2551. }
  2552. SetNextAttack( gpGlobals->curtime + 2.0f );
  2553. SetActivity( ACT_ANTLIONGUARD_CHARGE_STOP );
  2554. // We've hit something, so clear our miss count
  2555. m_iChargeMisses = 0;
  2556. AutoMovement();
  2557. TaskComplete();
  2558. return;
  2559. }
  2560. }
  2561. }
  2562. BaseClass::RunTask( pTask );
  2563. }
  2564. break;
  2565. default:
  2566. BaseClass::RunTask(pTask);
  2567. break;
  2568. }
  2569. }
  2570. //-----------------------------------------------------------------------------
  2571. // Purpose: Summon antlions around the guard
  2572. //-----------------------------------------------------------------------------
  2573. void CNPC_AntlionGuard::SummonAntlions( void )
  2574. {
  2575. // We want to spawn them around the guard
  2576. Vector vecForward, vecRight;
  2577. AngleVectors( QAngle(0,GetAbsAngles().y,0), &vecForward, &vecRight, NULL );
  2578. // Spawn positions
  2579. struct spawnpos_t
  2580. {
  2581. float flForward;
  2582. float flRight;
  2583. };
  2584. spawnpos_t sAntlionSpawnPositions[] =
  2585. {
  2586. { 0, 200 },
  2587. { 0, -200 },
  2588. { 128, 128 },
  2589. { 128, -128 },
  2590. { -128, 128 },
  2591. { -128, -128 },
  2592. { 200, 0 },
  2593. { -200, 0 },
  2594. };
  2595. // Only spawn up to our max count
  2596. int iSpawnPoint = 0;
  2597. for ( int i = 0; (m_iNumLiveAntlions < ANTLIONGUARD_SUMMON_COUNT) && (iSpawnPoint < ARRAYSIZE(sAntlionSpawnPositions)); i++ )
  2598. {
  2599. // Determine spawn position for the antlion
  2600. Vector vecSpawn = GetAbsOrigin() + ( sAntlionSpawnPositions[iSpawnPoint].flForward * vecForward ) + ( sAntlionSpawnPositions[iSpawnPoint].flRight * vecRight );
  2601. iSpawnPoint++;
  2602. // Randomise it a little
  2603. vecSpawn.x += RandomFloat( -64, 64 );
  2604. vecSpawn.y += RandomFloat( -64, 64 );
  2605. vecSpawn.z += 64;
  2606. // Make sure it's clear, and make sure we hit something
  2607. trace_t tr;
  2608. UTIL_TraceHull( vecSpawn, vecSpawn - Vector(0,0,128), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), MASK_NPCSOLID, NULL, COLLISION_GROUP_NONE, &tr );
  2609. if ( tr.startsolid || tr.allsolid || tr.fraction == 1.0 )
  2610. {
  2611. if ( g_debug_antlionguard.GetInt() == 2 )
  2612. {
  2613. NDebugOverlay::Box( tr.endpos, NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 255, 0, 0, true, 5.0f );
  2614. }
  2615. continue;
  2616. }
  2617. // Ensure it's dirt or sand
  2618. const surfacedata_t *pdata = physprops->GetSurfaceData( tr.surface.surfaceProps );
  2619. if ( ( pdata->game.material != CHAR_TEX_DIRT ) && ( pdata->game.material != CHAR_TEX_SAND ) )
  2620. {
  2621. if ( g_debug_antlionguard.GetInt() == 2 )
  2622. {
  2623. NDebugOverlay::Box( tr.endpos, NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 255, 128, 128, true, 5.0f );
  2624. }
  2625. continue;
  2626. }
  2627. // Make sure the guard can see it
  2628. trace_t tr_vis;
  2629. UTIL_TraceLine( WorldSpaceCenter(), tr.endpos, MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr_vis );
  2630. if ( tr_vis.fraction != 1.0 )
  2631. {
  2632. if ( g_debug_antlionguard.GetInt() == 2 )
  2633. {
  2634. NDebugOverlay::Line( WorldSpaceCenter(), tr.endpos, 255, 0, 0, true, 5.0f );
  2635. }
  2636. continue;
  2637. }
  2638. CAI_BaseNPC *pent = (CAI_BaseNPC*)CreateEntityByName( "npc_antlion" );
  2639. if ( !pent )
  2640. break;
  2641. CNPC_Antlion *pAntlion = assert_cast<CNPC_Antlion*>(pent);
  2642. if ( g_debug_antlionguard.GetInt() == 2 )
  2643. {
  2644. NDebugOverlay::Box( tr.endpos, NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 0, 255, 0, true, 5.0f );
  2645. NDebugOverlay::Line( WorldSpaceCenter(), tr.endpos, 0, 255, 0, true, 5.0f );
  2646. }
  2647. vecSpawn = tr.endpos;
  2648. pAntlion->SetAbsOrigin( vecSpawn );
  2649. // Start facing our enemy if we have one, otherwise just match the guard.
  2650. Vector vecFacing = vecForward;
  2651. if ( GetEnemy() )
  2652. {
  2653. vecFacing = GetEnemy()->GetAbsOrigin() - GetAbsOrigin();
  2654. VectorNormalize( vecFacing );
  2655. }
  2656. QAngle vecAngles;
  2657. VectorAngles( vecFacing, vecAngles );
  2658. pAntlion->SetAbsAngles( vecAngles );
  2659. pAntlion->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
  2660. pAntlion->AddSpawnFlags( SF_NPC_FADE_CORPSE );
  2661. // Make the antlion fire my input when he dies
  2662. pAntlion->KeyValue( "OnDeath", UTIL_VarArgs("%s,SummonedAntlionDied,,0,-1", STRING(GetEntityName())) );
  2663. // Start the antlion burrowed, and tell him to come up
  2664. pAntlion->m_bStartBurrowed = true;
  2665. DispatchSpawn( pAntlion );
  2666. pAntlion->Activate();
  2667. g_EventQueue.AddEvent( pAntlion, "Unburrow", RandomFloat(0.1, 1.0), this, this );
  2668. // Add it to our squad
  2669. if ( GetSquad() != NULL )
  2670. {
  2671. GetSquad()->AddToSquad( pAntlion );
  2672. }
  2673. // Set the antlion's enemy to our enemy
  2674. if ( GetEnemy() )
  2675. {
  2676. pAntlion->SetEnemy( GetEnemy() );
  2677. pAntlion->SetState( NPC_STATE_COMBAT );
  2678. pAntlion->UpdateEnemyMemory( GetEnemy(), GetEnemy()->GetAbsOrigin() );
  2679. }
  2680. m_iNumLiveAntlions++;
  2681. }
  2682. if ( g_debug_antlionguard.GetInt() == 2 )
  2683. {
  2684. Msg("Guard summoned antlion count: %d\n", m_iNumLiveAntlions );
  2685. }
  2686. if ( m_iNumLiveAntlions > 2 )
  2687. {
  2688. m_flNextSummonTime = gpGlobals->curtime + RandomFloat( 15,20 );
  2689. }
  2690. else
  2691. {
  2692. m_flNextSummonTime = gpGlobals->curtime + RandomFloat( 10,15 );
  2693. }
  2694. }
  2695. //-----------------------------------------------------------------------------
  2696. // Purpose:
  2697. //-----------------------------------------------------------------------------
  2698. void CNPC_AntlionGuard::FoundEnemy( void )
  2699. {
  2700. m_flAngerNoiseTime = gpGlobals->curtime + 2.0f;
  2701. SetState( NPC_STATE_COMBAT );
  2702. }
  2703. //-----------------------------------------------------------------------------
  2704. // Purpose:
  2705. //-----------------------------------------------------------------------------
  2706. void CNPC_AntlionGuard::LostEnemy( void )
  2707. {
  2708. m_flSearchNoiseTime = gpGlobals->curtime + 2.0f;
  2709. SetState( NPC_STATE_ALERT );
  2710. m_OnLostPlayer.FireOutput( this, this );
  2711. }
  2712. //-----------------------------------------------------------------------------
  2713. // Purpose:
  2714. //-----------------------------------------------------------------------------
  2715. void CNPC_AntlionGuard::InputSetShoveTarget( inputdata_t &inputdata )
  2716. {
  2717. if ( IsAlive() == false )
  2718. return;
  2719. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller );
  2720. if ( pTarget == NULL )
  2721. {
  2722. Warning( "**Guard %s cannot find shove target %s\n", GetClassname(), inputdata.value.String() );
  2723. m_hShoveTarget = NULL;
  2724. return;
  2725. }
  2726. m_hShoveTarget = pTarget;
  2727. }
  2728. //-----------------------------------------------------------------------------
  2729. // Purpose:
  2730. //-----------------------------------------------------------------------------
  2731. void CNPC_AntlionGuard::InputSetChargeTarget( inputdata_t &inputdata )
  2732. {
  2733. if ( !IsAlive() )
  2734. return;
  2735. // Pull the target & position out of the string
  2736. char parseString[255];
  2737. Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));
  2738. // Get charge target name
  2739. char *pszParam = strtok(parseString," ");
  2740. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, pszParam, NULL, inputdata.pActivator, inputdata.pCaller );
  2741. if ( !pTarget )
  2742. {
  2743. Warning( "ERROR: Guard %s cannot find charge target '%s'\n", STRING(GetEntityName()), pszParam );
  2744. return;
  2745. }
  2746. // Get the charge position name
  2747. pszParam = strtok(NULL," ");
  2748. CBaseEntity *pPosition = gEntList.FindEntityByName( NULL, pszParam, NULL, inputdata.pActivator, inputdata.pCaller );
  2749. if ( !pPosition )
  2750. {
  2751. Warning( "ERROR: Guard %s cannot find charge position '%s'\nMake sure you've specified the parameters as [target start]!\n", STRING(GetEntityName()), pszParam );
  2752. return;
  2753. }
  2754. // Make sure we don't stack charge targets
  2755. if ( m_hChargeTarget )
  2756. {
  2757. if ( GetEnemy() == m_hChargeTarget )
  2758. {
  2759. SetEnemy( NULL );
  2760. }
  2761. }
  2762. SetCondition( COND_ANTLIONGUARD_HAS_CHARGE_TARGET );
  2763. m_hChargeTarget = pTarget;
  2764. m_hChargeTargetPosition = pPosition;
  2765. }
  2766. //-----------------------------------------------------------------------------
  2767. // Purpose:
  2768. // Input : &inputdata -
  2769. //-----------------------------------------------------------------------------
  2770. void CNPC_AntlionGuard::InputClearChargeTarget( inputdata_t &inputdata )
  2771. {
  2772. m_hChargeTarget = NULL;
  2773. }
  2774. //-----------------------------------------------------------------------------
  2775. // Purpose:
  2776. // Input : baseAct -
  2777. // Output : Activity
  2778. //-----------------------------------------------------------------------------
  2779. Activity CNPC_AntlionGuard::NPC_TranslateActivity( Activity baseAct )
  2780. {
  2781. //See which run to use
  2782. if ( ( baseAct == ACT_RUN ) && IsCurSchedule( SCHED_ANTLIONGUARD_CHARGE ) )
  2783. return (Activity) ACT_ANTLIONGUARD_CHARGE_RUN;
  2784. // Do extra code if we're trying to close on an enemy in a confined space (unless scripted)
  2785. if ( hl2_episodic.GetBool() && m_bInCavern && baseAct == ACT_RUN && IsInAScript() == false )
  2786. return (Activity) ACT_ANTLIONGUARD_CHARGE_RUN;
  2787. if ( ( baseAct == ACT_RUN ) && ( m_iHealth <= (m_iMaxHealth/4) ) )
  2788. return (Activity) ACT_ANTLIONGUARD_RUN_HURT;
  2789. return baseAct;
  2790. }
  2791. //-----------------------------------------------------------------------------
  2792. // Purpose:
  2793. // Output : Returns true on success, false on failure.
  2794. //-----------------------------------------------------------------------------
  2795. bool CNPC_AntlionGuard::ShouldWatchEnemy( void )
  2796. {
  2797. Activity nActivity = GetActivity();
  2798. if ( ( nActivity == ACT_ANTLIONGUARD_SEARCH ) ||
  2799. ( nActivity == ACT_ANTLIONGUARD_PEEK_ENTER ) ||
  2800. ( nActivity == ACT_ANTLIONGUARD_PEEK_EXIT ) ||
  2801. ( nActivity == ACT_ANTLIONGUARD_PEEK1 ) ||
  2802. ( nActivity == ACT_ANTLIONGUARD_PEEK_SIGHTED ) ||
  2803. ( nActivity == ACT_ANTLIONGUARD_SHOVE_PHYSOBJECT ) ||
  2804. ( nActivity == ACT_ANTLIONGUARD_PHYSHIT_FR ) ||
  2805. ( nActivity == ACT_ANTLIONGUARD_PHYSHIT_FL ) ||
  2806. ( nActivity == ACT_ANTLIONGUARD_PHYSHIT_RR ) ||
  2807. ( nActivity == ACT_ANTLIONGUARD_PHYSHIT_RL ) ||
  2808. ( nActivity == ACT_ANTLIONGUARD_CHARGE_CRASH ) ||
  2809. ( nActivity == ACT_ANTLIONGUARD_CHARGE_HIT ) ||
  2810. ( nActivity == ACT_ANTLIONGUARD_CHARGE_ANTICIPATION ) )
  2811. {
  2812. return false;
  2813. }
  2814. return true;
  2815. }
  2816. //-----------------------------------------------------------------------------
  2817. // Purpose:
  2818. //-----------------------------------------------------------------------------
  2819. void CNPC_AntlionGuard::UpdateHead( void )
  2820. {
  2821. float yaw = GetPoseParameter( m_poseHead_Yaw );
  2822. float pitch = GetPoseParameter( m_poseHead_Pitch );
  2823. // If we should be watching our enemy, turn our head
  2824. if ( ShouldWatchEnemy() && ( GetEnemy() != NULL ) )
  2825. {
  2826. Vector enemyDir = GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter();
  2827. VectorNormalize( enemyDir );
  2828. float angle = VecToYaw( BodyDirection3D() );
  2829. float angleDiff = VecToYaw( enemyDir );
  2830. angleDiff = UTIL_AngleDiff( angleDiff, angle + yaw );
  2831. SetPoseParameter( m_poseHead_Yaw, UTIL_Approach( yaw + angleDiff, yaw, 50 ) );
  2832. angle = UTIL_VecToPitch( BodyDirection3D() );
  2833. angleDiff = UTIL_VecToPitch( enemyDir );
  2834. angleDiff = UTIL_AngleDiff( angleDiff, angle + pitch );
  2835. SetPoseParameter( m_poseHead_Pitch, UTIL_Approach( pitch + angleDiff, pitch, 50 ) );
  2836. }
  2837. else
  2838. {
  2839. // Otherwise turn the head back to its normal position
  2840. SetPoseParameter( m_poseHead_Yaw, UTIL_Approach( 0, yaw, 10 ) );
  2841. SetPoseParameter( m_poseHead_Pitch, UTIL_Approach( 0, pitch, 10 ) );
  2842. }
  2843. }
  2844. //-----------------------------------------------------------------------------
  2845. // Purpose:
  2846. //-----------------------------------------------------------------------------
  2847. void CNPC_AntlionGuard::MaintainPhysicsTarget( void )
  2848. {
  2849. if ( m_hPhysicsTarget == NULL || GetEnemy() == NULL )
  2850. return;
  2851. // Update our current target to make sure it's still valid
  2852. float flTargetDistSqr = ( m_hPhysicsTarget->WorldSpaceCenter() - m_vecPhysicsTargetStartPos ).LengthSqr();
  2853. bool bTargetMoved = ( flTargetDistSqr > Square(30*12.0f) );
  2854. bool bEnemyCloser = ( ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr() <= flTargetDistSqr );
  2855. // Make sure this hasn't moved too far or that the player is now closer
  2856. if ( bTargetMoved || bEnemyCloser )
  2857. {
  2858. ClearCondition( COND_ANTLIONGUARD_PHYSICS_TARGET );
  2859. SetCondition( COND_ANTLIONGUARD_PHYSICS_TARGET_INVALID );
  2860. m_hPhysicsTarget = NULL;
  2861. return;
  2862. }
  2863. else
  2864. {
  2865. SetCondition( COND_ANTLIONGUARD_PHYSICS_TARGET );
  2866. ClearCondition( COND_ANTLIONGUARD_PHYSICS_TARGET_INVALID );
  2867. }
  2868. if ( g_debug_antlionguard.GetInt() == 3 )
  2869. {
  2870. NDebugOverlay::Cross3D( m_hPhysicsTarget->WorldSpaceCenter(), -Vector(32,32,32), Vector(32,32,32), 255, 255, 255, true, 1.0f );
  2871. }
  2872. }
  2873. //-----------------------------------------------------------------------------
  2874. // Purpose:
  2875. //-----------------------------------------------------------------------------
  2876. void CNPC_AntlionGuard::UpdatePhysicsTarget( bool bPreferObjectsAlongTargetVector, float flRadius )
  2877. {
  2878. if ( GetEnemy() == NULL )
  2879. return;
  2880. // Already have a target, don't bother looking
  2881. if ( m_hPhysicsTarget != NULL )
  2882. return;
  2883. // Too soon to check again
  2884. if ( m_flPhysicsCheckTime > gpGlobals->curtime )
  2885. return;
  2886. // Attempt to find a valid shove target
  2887. PhysicsObjectCriteria_t criteria;
  2888. criteria.pTarget = GetEnemy();
  2889. criteria.vecCenter = GetEnemy()->GetAbsOrigin();
  2890. criteria.flRadius = flRadius;
  2891. criteria.flTargetCone = ANTLIONGUARD_OBJECTFINDING_FOV;
  2892. criteria.bPreferObjectsAlongTargetVector = bPreferObjectsAlongTargetVector;
  2893. criteria.flNearRadius = (20*12); // TODO: It may preferable to disable this for legacy products as well -- jdw
  2894. m_hPhysicsTarget = FindPhysicsObjectTarget( criteria );
  2895. // Found one, so interrupt us if we care
  2896. if ( m_hPhysicsTarget != NULL )
  2897. {
  2898. SetCondition( COND_ANTLIONGUARD_PHYSICS_TARGET );
  2899. m_vecPhysicsTargetStartPos = m_hPhysicsTarget->WorldSpaceCenter();
  2900. }
  2901. // Don't search again for another second
  2902. m_flPhysicsCheckTime = gpGlobals->curtime + 1.0f;
  2903. }
  2904. //-----------------------------------------------------------------------------
  2905. // Purpose: Let the probe know I can run through small debris
  2906. //-----------------------------------------------------------------------------
  2907. bool CNPC_AntlionGuard::ShouldProbeCollideAgainstEntity( CBaseEntity *pEntity )
  2908. {
  2909. if ( m_iszPhysicsPropClass != pEntity->m_iClassname )
  2910. return BaseClass::ShouldProbeCollideAgainstEntity( pEntity );
  2911. if ( m_hPhysicsTarget == pEntity )
  2912. return false;
  2913. if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
  2914. {
  2915. IPhysicsObject *pPhysObj = pEntity->VPhysicsGetObject();
  2916. if( pPhysObj && pPhysObj->GetMass() <= ANTLIONGUARD_MAX_OBJECT_MASS )
  2917. {
  2918. return false;
  2919. }
  2920. }
  2921. return BaseClass::ShouldProbeCollideAgainstEntity( pEntity );
  2922. }
  2923. //-----------------------------------------------------------------------------
  2924. // Purpose:
  2925. //-----------------------------------------------------------------------------
  2926. void CNPC_AntlionGuard::PrescheduleThink( void )
  2927. {
  2928. BaseClass::PrescheduleThink();
  2929. // Don't do anything after death
  2930. if ( m_NPCState == NPC_STATE_DEAD )
  2931. return;
  2932. // If we're burrowed, then don't do any of this
  2933. if ( m_bIsBurrowed )
  2934. return;
  2935. // Automatically update our physics target when chasing enemies
  2936. if ( IsCurSchedule( SCHED_ANTLIONGUARD_CHASE_ENEMY ) ||
  2937. IsCurSchedule( SCHED_ANTLIONGUARD_PATROL_RUN ) ||
  2938. IsCurSchedule( SCHED_ANTLIONGUARD_CANT_ATTACK ) ||
  2939. IsCurSchedule( SCHED_ANTLIONGUARD_CHASE_ENEMY_TOLERANCE ) )
  2940. {
  2941. bool bCheckAlongLine = ( IsCurSchedule( SCHED_ANTLIONGUARD_CHASE_ENEMY ) || IsCurSchedule( SCHED_ANTLIONGUARD_CHASE_ENEMY_TOLERANCE ) );
  2942. UpdatePhysicsTarget( bCheckAlongLine );
  2943. }
  2944. else if ( !IsCurSchedule( SCHED_ANTLIONGUARD_PHYSICS_ATTACK ) )
  2945. {
  2946. ClearCondition( COND_ANTLIONGUARD_PHYSICS_TARGET );
  2947. m_hPhysicsTarget = NULL;
  2948. }
  2949. UpdateHead();
  2950. if ( ( m_flGroundSpeed <= 0.0f ) )
  2951. {
  2952. if ( m_bStopped == false )
  2953. {
  2954. StartSounds();
  2955. float duration = random->RandomFloat( 2.0f, 8.0f );
  2956. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.0f, duration );
  2957. ENVELOPE_CONTROLLER.SoundChangePitch( m_pBreathSound, random->RandomInt( 40, 60 ), duration );
  2958. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, duration );
  2959. ENVELOPE_CONTROLLER.SoundChangePitch( m_pGrowlIdleSound, random->RandomInt( 120, 140 ), duration );
  2960. m_flBreathTime = gpGlobals->curtime + duration - (duration*0.75f);
  2961. }
  2962. m_bStopped = true;
  2963. if ( m_flBreathTime < gpGlobals->curtime )
  2964. {
  2965. StartSounds();
  2966. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, random->RandomFloat( 0.2f, 0.3f ), random->RandomFloat( 0.5f, 1.0f ) );
  2967. ENVELOPE_CONTROLLER.SoundChangePitch( m_pGrowlIdleSound, random->RandomInt( 80, 120 ), random->RandomFloat( 0.5f, 1.0f ) );
  2968. m_flBreathTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 8.0f );
  2969. }
  2970. }
  2971. else
  2972. {
  2973. if ( m_bStopped )
  2974. {
  2975. StartSounds();
  2976. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pBreathSound, 0.6f, random->RandomFloat( 2.0f, 4.0f ) );
  2977. ENVELOPE_CONTROLLER.SoundChangePitch( m_pBreathSound, random->RandomInt( 140, 160 ), random->RandomFloat( 2.0f, 4.0f ) );
  2978. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pGrowlIdleSound, 0.0f, 1.0f );
  2979. ENVELOPE_CONTROLLER.SoundChangePitch( m_pGrowlIdleSound, random->RandomInt( 90, 110 ), 0.2f );
  2980. }
  2981. m_bStopped = false;
  2982. }
  2983. // Put danger sounds out in front of us
  2984. for ( int i = 0; i < 3; i++ )
  2985. {
  2986. CSoundEnt::InsertSound( SOUND_DANGER, WorldSpaceCenter() + ( BodyDirection3D() * 128 * (i+1) ), 128, 0.1f, this );
  2987. }
  2988. #if ANTLIONGUARD_BLOOD_EFFECTS
  2989. // compute and if necessary transmit the bleeding level for the particle effect
  2990. m_iBleedingLevel = GetBleedingLevel();
  2991. #endif
  2992. }
  2993. //-----------------------------------------------------------------------------
  2994. // Purpose:
  2995. //-----------------------------------------------------------------------------
  2996. void CNPC_AntlionGuard::GatherConditions( void )
  2997. {
  2998. BaseClass::GatherConditions();
  2999. if ( CanSummon(false) )
  3000. {
  3001. SetCondition( COND_ANTLIONGUARD_CAN_SUMMON );
  3002. }
  3003. else
  3004. {
  3005. ClearCondition( COND_ANTLIONGUARD_CAN_SUMMON );
  3006. }
  3007. // Make sure our physics target is still valid
  3008. MaintainPhysicsTarget();
  3009. if( IsCurSchedule(SCHED_ANTLIONGUARD_PHYSICS_ATTACK) )
  3010. {
  3011. if( gpGlobals->curtime > m_flWaitFinished )
  3012. {
  3013. ClearCondition( COND_ANTLIONGUARD_PHYSICS_TARGET );
  3014. SetCondition( COND_ANTLIONGUARD_PHYSICS_TARGET_INVALID );
  3015. m_hPhysicsTarget = NULL;
  3016. }
  3017. }
  3018. // See if we can charge the target
  3019. if ( GetEnemy() )
  3020. {
  3021. if ( ShouldCharge( GetAbsOrigin(), GetEnemy()->GetAbsOrigin(), true, false ) )
  3022. {
  3023. SetCondition( COND_ANTLIONGUARD_CAN_CHARGE );
  3024. }
  3025. else
  3026. {
  3027. ClearCondition( COND_ANTLIONGUARD_CAN_CHARGE );
  3028. }
  3029. }
  3030. }
  3031. //-----------------------------------------------------------------------------
  3032. // Purpose:
  3033. //-----------------------------------------------------------------------------
  3034. void CNPC_AntlionGuard::StopLoopingSounds()
  3035. {
  3036. //Stop all sounds
  3037. ENVELOPE_CONTROLLER.SoundDestroy( m_pGrowlHighSound );
  3038. ENVELOPE_CONTROLLER.SoundDestroy( m_pGrowlIdleSound );
  3039. ENVELOPE_CONTROLLER.SoundDestroy( m_pBreathSound );
  3040. ENVELOPE_CONTROLLER.SoundDestroy( m_pConfusedSound );
  3041. m_pGrowlHighSound = NULL;
  3042. m_pGrowlIdleSound = NULL;
  3043. m_pBreathSound = NULL;
  3044. m_pConfusedSound = NULL;
  3045. BaseClass::StopLoopingSounds();
  3046. }
  3047. //-----------------------------------------------------------------------------
  3048. // Purpose:
  3049. // Input : &inputdata -
  3050. //-----------------------------------------------------------------------------
  3051. void CNPC_AntlionGuard::InputUnburrow( inputdata_t &inputdata )
  3052. {
  3053. if ( IsAlive() == false )
  3054. return;
  3055. if ( m_bIsBurrowed == false )
  3056. return;
  3057. m_spawnflags &= ~SF_NPC_GAG;
  3058. RemoveSolidFlags( FSOLID_NOT_SOLID );
  3059. AddSolidFlags( FSOLID_NOT_STANDABLE );
  3060. m_takedamage = DAMAGE_YES;
  3061. SetSchedule( SCHED_ANTLIONGUARD_UNBURROW );
  3062. m_bIsBurrowed = false;
  3063. }
  3064. //------------------------------------------------------------------------------
  3065. // Purpose : Returns true is entity was remembered as unreachable.
  3066. // After a time delay reachability is checked
  3067. // Input :
  3068. // Output :
  3069. //------------------------------------------------------------------------------
  3070. bool CNPC_AntlionGuard::IsUnreachable(CBaseEntity *pEntity)
  3071. {
  3072. float UNREACHABLE_DIST_TOLERANCE_SQ = (240 * 240);
  3073. // Note that it's ok to remove elements while I'm iterating
  3074. // as long as I iterate backwards and remove them using FastRemove
  3075. for (int i=m_UnreachableEnts.Size()-1;i>=0;i--)
  3076. {
  3077. // Remove any dead elements
  3078. if (m_UnreachableEnts[i].hUnreachableEnt == NULL)
  3079. {
  3080. m_UnreachableEnts.FastRemove(i);
  3081. }
  3082. else if (pEntity == m_UnreachableEnts[i].hUnreachableEnt)
  3083. {
  3084. // Test for reachability on any elements that have timed out
  3085. if ( gpGlobals->curtime > m_UnreachableEnts[i].fExpireTime ||
  3086. pEntity->GetAbsOrigin().DistToSqr(m_UnreachableEnts[i].vLocationWhenUnreachable) > UNREACHABLE_DIST_TOLERANCE_SQ)
  3087. {
  3088. m_UnreachableEnts.FastRemove(i);
  3089. return false;
  3090. }
  3091. return true;
  3092. }
  3093. }
  3094. return false;
  3095. }
  3096. //-----------------------------------------------------------------------------
  3097. // Purpose: Return the point at which the guard wants to stand on to knock the physics object at the target entity
  3098. // Input : *pObject - Object to be shoved.
  3099. // *pTarget - Target to be shoved at.
  3100. // *vecTrajectory - Trajectory to our target
  3101. // *flClearDistance - Distance behind the entity we're clear to use
  3102. // Output : Position at which to attempt to strike the object
  3103. //-----------------------------------------------------------------------------
  3104. Vector CNPC_AntlionGuard::GetPhysicsHitPosition( CBaseEntity *pObject, CBaseEntity *pTarget, Vector *vecTrajectory, float *flClearDistance )
  3105. {
  3106. // Get the trajectory we want to knock the object along
  3107. Vector vecToTarget = pTarget->WorldSpaceCenter() - pObject->WorldSpaceCenter();
  3108. VectorNormalize( vecToTarget );
  3109. vecToTarget.z = 0;
  3110. // Get the distance we want to be from the object when we hit it
  3111. IPhysicsObject *pPhys = pObject->VPhysicsGetObject();
  3112. Vector extent = physcollision->CollideGetExtent( pPhys->GetCollide(), pObject->GetAbsOrigin(), pObject->GetAbsAngles(), -vecToTarget );
  3113. float flDist = ( extent - pObject->WorldSpaceCenter() ).Length() + CollisionProp()->BoundingRadius() + 32.0f;
  3114. if ( vecTrajectory != NULL )
  3115. {
  3116. *vecTrajectory = vecToTarget;
  3117. }
  3118. if ( flClearDistance != NULL )
  3119. {
  3120. *flClearDistance = flDist;
  3121. }
  3122. // Position at which we'd like to be
  3123. return (pObject->WorldSpaceCenter() + ( vecToTarget * -flDist ));
  3124. }
  3125. //-----------------------------------------------------------------------------
  3126. // Purpose: See if we're able to stand on the ground at this point
  3127. // Input : &vecPos - Position to try
  3128. // *pOut - Result position (only valid if we return true)
  3129. // Output : Returns true on success, false on failure.
  3130. //-----------------------------------------------------------------------------
  3131. inline bool CNPC_AntlionGuard::CanStandAtPoint( const Vector &vecPos, Vector *pOut )
  3132. {
  3133. Vector vecStart = vecPos + Vector( 0, 0, (4*12) );
  3134. Vector vecEnd = vecPos - Vector( 0, 0, (4*12) );
  3135. trace_t tr;
  3136. bool bTraceCleared = false;
  3137. // Start high and try to go lower, looking for the ground between here and there
  3138. // We do this first because it's more likely to succeed in the typical guard arenas (with open terrain)
  3139. UTIL_TraceHull( vecStart, vecEnd, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  3140. if ( tr.startsolid && !tr.allsolid )
  3141. {
  3142. // We started in solid but didn't end up there, see if we can stand where we ended up
  3143. UTIL_TraceHull( tr.endpos, tr.endpos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  3144. // Must not be in solid
  3145. bTraceCleared = ( !tr.allsolid && !tr.startsolid );
  3146. }
  3147. else
  3148. {
  3149. // Must not be in solid and must have found a floor (otherwise we're potentially hanging over a ledge)
  3150. bTraceCleared = ( tr.allsolid == false && tr.fraction < 1.0f );
  3151. }
  3152. // Either we're clear or we're still unlucky
  3153. if ( bTraceCleared == false )
  3154. {
  3155. if ( g_debug_antlionguard.GetInt() == 3 )
  3156. {
  3157. NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 15.0f );
  3158. }
  3159. return false;
  3160. }
  3161. if ( pOut )
  3162. {
  3163. *pOut = tr.endpos;
  3164. }
  3165. if ( g_debug_antlionguard.GetInt() == 3 )
  3166. {
  3167. NDebugOverlay::Box( (*pOut), GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 15.0f );
  3168. }
  3169. return true;
  3170. }
  3171. //-----------------------------------------------------------------------------
  3172. // Purpose: Determines whether or not the guard can stand in a position to strike a specified object
  3173. // Input : *pShoveObject - Object being shoved
  3174. // *pTarget - Target we're shoving the object at
  3175. // *pOut - The position we decide to stand at
  3176. // Output : Returns true if the guard can stand and deliver.
  3177. //-----------------------------------------------------------------------------
  3178. bool CNPC_AntlionGuard::CanStandAtShoveTarget( CBaseEntity *pShoveObject, CBaseEntity *pTarget, Vector *pOut )
  3179. {
  3180. // Get the position we want to be at to swing at the object
  3181. float flClearDistance;
  3182. Vector vecTrajectory;
  3183. Vector vecHitPosition = GetPhysicsHitPosition( pShoveObject, pTarget, &vecTrajectory, &flClearDistance );
  3184. Vector vecStandPosition;
  3185. if ( g_debug_antlionguard.GetInt() == 3 )
  3186. {
  3187. NDebugOverlay::HorzArrow( pShoveObject->WorldSpaceCenter(), pShoveObject->WorldSpaceCenter() + vecTrajectory * 64.0f, 16.0f, 255, 255, 0, 16, true, 15.0f );
  3188. }
  3189. // If we failed, try around the sides
  3190. if ( CanStandAtPoint( vecHitPosition, &vecStandPosition ) == false )
  3191. {
  3192. // Get the angle (in reverse) to the target
  3193. float flRad = atan2( -vecTrajectory.y, -vecTrajectory.x );
  3194. float flRadOffset = DEG2RAD( 45.0f );
  3195. // Build an offset vector, rotating around the base
  3196. Vector vecSkewTrajectory;
  3197. SinCos( flRad + flRadOffset, &vecSkewTrajectory.y, &vecSkewTrajectory.x );
  3198. vecSkewTrajectory.z = 0.0f;
  3199. // Try to the side
  3200. if ( CanStandAtPoint( ( pShoveObject->WorldSpaceCenter() + ( vecSkewTrajectory * flClearDistance ) ), &vecStandPosition ) == false )
  3201. {
  3202. // Try the other side
  3203. SinCos( flRad - flRadOffset, &vecSkewTrajectory.y, &vecSkewTrajectory.x );
  3204. vecSkewTrajectory.z = 0.0f;
  3205. if ( CanStandAtPoint( ( pShoveObject->WorldSpaceCenter() + ( vecSkewTrajectory * flClearDistance ) ), &vecStandPosition ) == false )
  3206. return false;
  3207. }
  3208. }
  3209. // Found it, report it
  3210. if ( pOut != NULL )
  3211. {
  3212. *pOut = vecStandPosition;
  3213. }
  3214. return true;
  3215. }
  3216. //-----------------------------------------------------------------------------
  3217. // Purpose: Iterate through a number of lists depending on our criteria
  3218. //-----------------------------------------------------------------------------
  3219. CBaseEntity *CNPC_AntlionGuard::GetNextShoveTarget( CBaseEntity *pLastEntity, AISightIter_t &iter )
  3220. {
  3221. // Try to find scripted items first
  3222. if ( m_strShoveTargets != NULL_STRING )
  3223. {
  3224. CBaseEntity *pFound = gEntList.FindEntityByName( pLastEntity, m_strShoveTargets );
  3225. if ( pFound )
  3226. return pFound;
  3227. }
  3228. // Failing that, use our senses
  3229. if ( iter != (AISightIter_t)(-1) )
  3230. return GetSenses()->GetNextSeenEntity( &iter );
  3231. return GetSenses()->GetFirstSeenEntity( &iter, SEEN_MISC );
  3232. }
  3233. //-----------------------------------------------------------------------------
  3234. // Purpose: Search for a physics item to swat at the player
  3235. // Output : Returns the object we're going to swat.
  3236. //-----------------------------------------------------------------------------
  3237. CBaseEntity *CNPC_AntlionGuard::FindPhysicsObjectTarget( const PhysicsObjectCriteria_t &criteria )
  3238. {
  3239. // Must have a valid target entity
  3240. if ( criteria.pTarget == NULL )
  3241. return NULL;
  3242. if ( g_debug_antlionguard.GetInt() == 3 )
  3243. {
  3244. NDebugOverlay::Circle( GetAbsOrigin(), QAngle( -90, 0, 0 ), criteria.flRadius, 255, 0, 0, 8, true, 2.0f );
  3245. }
  3246. // Get the vector to our target, from ourself
  3247. Vector vecDirToTarget = criteria.pTarget->GetAbsOrigin() - GetAbsOrigin();
  3248. VectorNormalize( vecDirToTarget );
  3249. vecDirToTarget.z = 0;
  3250. // Cost is determined by distance to the object, modified by how "in line" it is with our target direction of travel
  3251. // Use the distance to the player as the base cost for throwing an object (avoids pushing things too close to the player)
  3252. float flLeastCost = ( criteria.bPreferObjectsAlongTargetVector ) ? ( criteria.pTarget->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr() : Square( criteria.flRadius );
  3253. float flCost;
  3254. AISightIter_t iter = (AISightIter_t)(-1);
  3255. CBaseEntity *pObject = NULL;
  3256. CBaseEntity *pNearest = NULL;
  3257. Vector vecBestHitPosition = vec3_origin;
  3258. // Look through the list of sensed objects for possible targets
  3259. while( ( pObject = GetNextShoveTarget( pObject, iter ) ) != NULL )
  3260. {
  3261. // If we couldn't shove this object last time, don't try again
  3262. if ( m_FailedPhysicsTargets.Find( pObject ) != m_FailedPhysicsTargets.InvalidIndex() )
  3263. continue;
  3264. // Ignore things less than half a foot in diameter
  3265. if ( pObject->CollisionProp()->BoundingRadius() < 6.0f )
  3266. continue;
  3267. IPhysicsObject *pPhysObj = pObject->VPhysicsGetObject();
  3268. if ( pPhysObj == NULL )
  3269. continue;
  3270. // Ignore motion disabled props
  3271. if ( pPhysObj->IsMoveable() == false )
  3272. continue;
  3273. // Ignore things lighter than 5kg
  3274. if ( pPhysObj->GetMass() < 5.0f )
  3275. continue;
  3276. // Ignore objects moving too quickly (they'll be too hard to catch otherwise)
  3277. Vector velocity;
  3278. pPhysObj->GetVelocity( &velocity, NULL );
  3279. if ( velocity.LengthSqr() > (16*16) )
  3280. continue;
  3281. // Get the direction from us to the physics object
  3282. Vector vecDirToObject = pObject->WorldSpaceCenter() - GetAbsOrigin();
  3283. VectorNormalize( vecDirToObject );
  3284. vecDirToObject.z = 0;
  3285. Vector vecObjCenter = pObject->WorldSpaceCenter();
  3286. float flDistSqr = 0.0f;
  3287. float flDot = 0.0f;
  3288. // If we want to find things along the vector to the target, do so
  3289. if ( criteria.bPreferObjectsAlongTargetVector )
  3290. {
  3291. // Object must be closer than our target
  3292. if ( ( GetAbsOrigin() - vecObjCenter ).LengthSqr() > ( GetAbsOrigin() - criteria.pTarget->GetAbsOrigin() ).LengthSqr() )
  3293. continue;
  3294. // Calculate a "cost" to this object
  3295. flDistSqr = ( GetAbsOrigin() - vecObjCenter ).LengthSqr();
  3296. flDot = DotProduct( vecDirToTarget, vecDirToObject );
  3297. // Ignore things outside our allowed cone
  3298. if ( flDot < criteria.flTargetCone )
  3299. continue;
  3300. // The more perpendicular we are, the higher the cost
  3301. float flCostScale = RemapValClamped( flDot, 1.0f, criteria.flTargetCone, 1.0f, 4.0f );
  3302. flCost = flDistSqr * flCostScale;
  3303. }
  3304. else
  3305. {
  3306. // Straight distance cost
  3307. flCost = ( criteria.vecCenter - vecObjCenter ).LengthSqr();
  3308. }
  3309. // This must be a less costly object to use
  3310. if ( flCost >= flLeastCost )
  3311. {
  3312. if ( g_debug_antlionguard.GetInt() == 3 )
  3313. {
  3314. NDebugOverlay::Box( vecObjCenter, -Vector(16,16,16), Vector(16,16,16), 255, 0, 0, 0, 2.0f );
  3315. }
  3316. continue;
  3317. }
  3318. // Check for a (roughly) clear trajectory path from the object to target
  3319. trace_t tr;
  3320. UTIL_TraceLine( vecObjCenter, criteria.pTarget->BodyTarget( vecObjCenter ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  3321. // See how close to our target we got (we still look good hurling things that won't necessarily hit)
  3322. if ( ( tr.endpos - criteria.pTarget->WorldSpaceCenter() ).LengthSqr() > Square(criteria.flNearRadius) )
  3323. continue;
  3324. // Must be able to stand at a position to hit the object
  3325. Vector vecHitPosition;
  3326. if ( CanStandAtShoveTarget( pObject, criteria.pTarget, &vecHitPosition ) == false )
  3327. {
  3328. if ( g_debug_antlionguard.GetInt() == 3 )
  3329. {
  3330. NDebugOverlay::HorzArrow( GetAbsOrigin(), pObject->WorldSpaceCenter(), 32.0f, 255, 0, 0, 64, true, 2.0f );
  3331. }
  3332. continue;
  3333. }
  3334. // Take this as the best object so far
  3335. pNearest = pObject;
  3336. flLeastCost = flCost;
  3337. vecBestHitPosition = vecHitPosition;
  3338. if ( g_debug_antlionguard.GetInt() == 3 )
  3339. {
  3340. NDebugOverlay::HorzArrow( GetAbsOrigin(), pObject->WorldSpaceCenter(), 16.0f, 255, 255, 0, 0, true, 2.0f );
  3341. }
  3342. }
  3343. // Set extra info if we've succeeded
  3344. if ( pNearest != NULL )
  3345. {
  3346. m_vecPhysicsHitPosition = vecBestHitPosition;
  3347. if ( g_debug_antlionguard.GetInt() == 3 )
  3348. {
  3349. NDebugOverlay::HorzArrow( GetAbsOrigin(), pNearest->WorldSpaceCenter(), 32.0f, 0, 255, 0, 128, true, 2.0f );
  3350. }
  3351. }
  3352. return pNearest;
  3353. }
  3354. //-----------------------------------------------------------------------------
  3355. // Purpose: Allows for modification of the interrupt mask for the current schedule.
  3356. // In the most cases the base implementation should be called first.
  3357. //-----------------------------------------------------------------------------
  3358. void CNPC_AntlionGuard::BuildScheduleTestBits( void )
  3359. {
  3360. BaseClass::BuildScheduleTestBits();
  3361. // Interrupt if we can shove something
  3362. if ( IsCurSchedule( SCHED_ANTLIONGUARD_CHASE_ENEMY ) )
  3363. {
  3364. SetCustomInterruptCondition( COND_ANTLIONGUARD_PHYSICS_TARGET );
  3365. SetCustomInterruptCondition( COND_ANTLIONGUARD_CAN_SUMMON );
  3366. }
  3367. // Interrupt if we've been given a charge target
  3368. if ( IsCurSchedule( SCHED_ANTLIONGUARD_CHARGE ) == false )
  3369. {
  3370. SetCustomInterruptCondition( COND_ANTLIONGUARD_HAS_CHARGE_TARGET );
  3371. }
  3372. // Once we commit to doing this, just do it!
  3373. if ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) )
  3374. {
  3375. ClearCustomInterruptCondition( COND_ENEMY_OCCLUDED );
  3376. }
  3377. // Always take heavy damage
  3378. SetCustomInterruptCondition( COND_HEAVY_DAMAGE );
  3379. }
  3380. //-----------------------------------------------------------------------------
  3381. // Purpose:
  3382. // Input : &origin -
  3383. // radius -
  3384. // magnitude -
  3385. //-----------------------------------------------------------------------------
  3386. void CNPC_AntlionGuard::ImpactShock( const Vector &origin, float radius, float magnitude, CBaseEntity *pIgnored )
  3387. {
  3388. // Also do a local physics explosion to push objects away
  3389. float adjustedDamage, flDist;
  3390. Vector vecSpot;
  3391. float falloff = 1.0f / 2.5f;
  3392. CBaseEntity *pEntity = NULL;
  3393. // Find anything within our radius
  3394. while ( ( pEntity = gEntList.FindEntityInSphere( pEntity, origin, radius ) ) != NULL )
  3395. {
  3396. // Don't affect the ignored target
  3397. if ( pEntity == pIgnored )
  3398. continue;
  3399. if ( pEntity == this )
  3400. continue;
  3401. // UNDONE: Ask the object if it should get force if it's not MOVETYPE_VPHYSICS?
  3402. if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS || ( pEntity->VPhysicsGetObject() && pEntity->IsPlayer() == false ) )
  3403. {
  3404. vecSpot = pEntity->BodyTarget( GetAbsOrigin() );
  3405. // decrease damage for an ent that's farther from the bomb.
  3406. flDist = ( GetAbsOrigin() - vecSpot ).Length();
  3407. if ( radius == 0 || flDist <= radius )
  3408. {
  3409. adjustedDamage = flDist * falloff;
  3410. adjustedDamage = magnitude - adjustedDamage;
  3411. if ( adjustedDamage < 1 )
  3412. {
  3413. adjustedDamage = 1;
  3414. }
  3415. CTakeDamageInfo info( this, this, adjustedDamage, DMG_BLAST );
  3416. CalculateExplosiveDamageForce( &info, (vecSpot - GetAbsOrigin()), GetAbsOrigin() );
  3417. pEntity->VPhysicsTakeDamage( info );
  3418. }
  3419. }
  3420. }
  3421. }
  3422. //-----------------------------------------------------------------------------
  3423. // Purpose:
  3424. // Input : *pTarget -
  3425. //-----------------------------------------------------------------------------
  3426. void CNPC_AntlionGuard::ChargeDamage( CBaseEntity *pTarget )
  3427. {
  3428. if ( pTarget == NULL )
  3429. return;
  3430. CBasePlayer *pPlayer = ToBasePlayer( pTarget );
  3431. if ( pPlayer != NULL )
  3432. {
  3433. //Kick the player angles
  3434. pPlayer->ViewPunch( QAngle( 20, 20, -30 ) );
  3435. Vector dir = pPlayer->WorldSpaceCenter() - WorldSpaceCenter();
  3436. VectorNormalize( dir );
  3437. dir.z = 0.0f;
  3438. Vector vecNewVelocity = dir * 250.0f;
  3439. vecNewVelocity[2] += 128.0f;
  3440. pPlayer->SetAbsVelocity( vecNewVelocity );
  3441. color32 red = {128,0,0,128};
  3442. UTIL_ScreenFade( pPlayer, red, 1.0f, 0.1f, FFADE_IN );
  3443. }
  3444. // Player takes less damage
  3445. float flDamage = ( pPlayer == NULL ) ? 250 : sk_antlionguard_dmg_charge.GetFloat();
  3446. // If it's being held by the player, break that bond
  3447. Pickup_ForcePlayerToDropThisObject( pTarget );
  3448. // Calculate the physics force
  3449. ApplyChargeDamage( this, pTarget, flDamage );
  3450. }
  3451. //-----------------------------------------------------------------------------
  3452. // Purpose:
  3453. // Input : &inputdata -
  3454. //-----------------------------------------------------------------------------
  3455. void CNPC_AntlionGuard::InputRagdoll( inputdata_t &inputdata )
  3456. {
  3457. if ( IsAlive() == false )
  3458. return;
  3459. //Set us to nearly dead so the velocity from death is minimal
  3460. SetHealth( 1 );
  3461. CTakeDamageInfo info( this, this, GetHealth(), DMG_CRUSH );
  3462. BaseClass::TakeDamage( info );
  3463. }
  3464. //-----------------------------------------------------------------------------
  3465. // Purpose: make m_bPreferPhysicsAttack true
  3466. //-----------------------------------------------------------------------------
  3467. void CNPC_AntlionGuard::InputEnablePreferPhysicsAttack( inputdata_t &inputdata )
  3468. {
  3469. m_bPreferPhysicsAttack = true;
  3470. }
  3471. //-----------------------------------------------------------------------------
  3472. // Purpose: make m_bPreferPhysicsAttack false
  3473. //-----------------------------------------------------------------------------
  3474. void CNPC_AntlionGuard::InputDisablePreferPhysicsAttack( inputdata_t &inputdata )
  3475. {
  3476. m_bPreferPhysicsAttack = false;
  3477. }
  3478. //-----------------------------------------------------------------------------
  3479. // Purpose:
  3480. //-----------------------------------------------------------------------------
  3481. int CNPC_AntlionGuard::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  3482. {
  3483. // Figure out what to do next
  3484. if ( failedSchedule == SCHED_ANTLIONGUARD_CHASE_ENEMY && HasCondition( COND_ENEMY_UNREACHABLE ) )
  3485. return SelectUnreachableSchedule();
  3486. return BaseClass::SelectFailSchedule( failedSchedule,failedTask, taskFailCode );
  3487. }
  3488. //-----------------------------------------------------------------------------
  3489. // Purpose:
  3490. // Input : scheduleType -
  3491. // Output : int
  3492. //-----------------------------------------------------------------------------
  3493. int CNPC_AntlionGuard::TranslateSchedule( int scheduleType )
  3494. {
  3495. switch( scheduleType )
  3496. {
  3497. case SCHED_CHASE_ENEMY:
  3498. return SCHED_ANTLIONGUARD_CHASE_ENEMY;
  3499. break;
  3500. }
  3501. return BaseClass::TranslateSchedule( scheduleType );
  3502. }
  3503. //-----------------------------------------------------------------------------
  3504. // Purpose:
  3505. //-----------------------------------------------------------------------------
  3506. void CNPC_AntlionGuard::StartSounds( void )
  3507. {
  3508. //Initialize the additive sound channels
  3509. CPASAttenuationFilter filter( this );
  3510. if ( m_pGrowlHighSound == NULL )
  3511. {
  3512. m_pGrowlHighSound = ENVELOPE_CONTROLLER.SoundCreate( filter, entindex(), CHAN_VOICE, "NPC_AntlionGuard.GrowlHigh", ATTN_NORM );
  3513. if ( m_pGrowlHighSound )
  3514. {
  3515. ENVELOPE_CONTROLLER.Play( m_pGrowlHighSound,0.0f, 100 );
  3516. }
  3517. }
  3518. if ( m_pGrowlIdleSound == NULL )
  3519. {
  3520. m_pGrowlIdleSound = ENVELOPE_CONTROLLER.SoundCreate( filter, entindex(), CHAN_STATIC, "NPC_AntlionGuard.GrowlIdle", ATTN_NORM );
  3521. if ( m_pGrowlIdleSound )
  3522. {
  3523. ENVELOPE_CONTROLLER.Play( m_pGrowlIdleSound,0.0f, 100 );
  3524. }
  3525. }
  3526. if ( m_pBreathSound == NULL )
  3527. {
  3528. m_pBreathSound = ENVELOPE_CONTROLLER.SoundCreate( filter, entindex(), CHAN_ITEM, "NPC_AntlionGuard.BreathSound", ATTN_NORM );
  3529. if ( m_pBreathSound )
  3530. {
  3531. ENVELOPE_CONTROLLER.Play( m_pBreathSound, 0.0f, 100 );
  3532. }
  3533. }
  3534. if ( m_pConfusedSound == NULL )
  3535. {
  3536. m_pConfusedSound = ENVELOPE_CONTROLLER.SoundCreate( filter, entindex(), CHAN_WEAPON,"NPC_AntlionGuard.Confused", ATTN_NORM );
  3537. if ( m_pConfusedSound )
  3538. {
  3539. ENVELOPE_CONTROLLER.Play( m_pConfusedSound, 0.0f, 100 );
  3540. }
  3541. }
  3542. }
  3543. //-----------------------------------------------------------------------------
  3544. // Purpose:
  3545. //-----------------------------------------------------------------------------
  3546. void CNPC_AntlionGuard::InputEnableBark( inputdata_t &inputdata )
  3547. {
  3548. m_bBarkEnabled = true;
  3549. }
  3550. //-----------------------------------------------------------------------------
  3551. // Purpose:
  3552. //-----------------------------------------------------------------------------
  3553. void CNPC_AntlionGuard::InputDisableBark( inputdata_t &inputdata )
  3554. {
  3555. m_bBarkEnabled = false;
  3556. }
  3557. //-----------------------------------------------------------------------------
  3558. // Purpose:
  3559. //-----------------------------------------------------------------------------
  3560. void CNPC_AntlionGuard::DeathSound( const CTakeDamageInfo &info )
  3561. {
  3562. EmitSound( "NPC_AntlionGuard.Die" );
  3563. }
  3564. //-----------------------------------------------------------------------------
  3565. // Purpose:
  3566. // Input : &info -
  3567. //-----------------------------------------------------------------------------
  3568. void CNPC_AntlionGuard::Event_Killed( const CTakeDamageInfo &info )
  3569. {
  3570. BaseClass::Event_Killed( info );
  3571. // Tell all of my antlions to burrow away, 'cos they fear the Freeman
  3572. if ( m_iNumLiveAntlions )
  3573. {
  3574. CBaseEntity *pSearch = NULL;
  3575. // Iterate through all antlions and see if there are any orphans
  3576. while ( ( pSearch = gEntList.FindEntityByClassname( pSearch, "npc_antlion" ) ) != NULL )
  3577. {
  3578. CNPC_Antlion *pAntlion = assert_cast<CNPC_Antlion *>(pSearch);
  3579. // See if it's a live orphan
  3580. if ( pAntlion && pAntlion->GetOwnerEntity() == NULL && pAntlion->IsAlive() )
  3581. {
  3582. g_EventQueue.AddEvent( pAntlion, "BurrowAway", RandomFloat(0.1, 2.0), this, this );
  3583. }
  3584. }
  3585. }
  3586. DestroyGlows();
  3587. // If I'm bleeding, stop due to decreased pressure of hemolymph after
  3588. // cessation of aortic contraction
  3589. #if ANTLIONGUARD_BLOOD_EFFECTS
  3590. m_iBleedingLevel = 0;
  3591. #endif
  3592. }
  3593. //-----------------------------------------------------------------------------
  3594. // Purpose: Don't become a ragdoll until we've finished our death anim
  3595. // Output : Returns true on success, false on failure.
  3596. //-----------------------------------------------------------------------------
  3597. bool CNPC_AntlionGuard::CanBecomeRagdoll( void )
  3598. {
  3599. if ( IsCurSchedule( SCHED_DIE ) )
  3600. return true;
  3601. return hl2_episodic.GetBool();
  3602. }
  3603. //-----------------------------------------------------------------------------
  3604. // Purpose:
  3605. // Input : &force -
  3606. // Output : Returns true on success, false on failure.
  3607. //-----------------------------------------------------------------------------
  3608. bool CNPC_AntlionGuard::BecomeRagdollOnClient( const Vector &force )
  3609. {
  3610. if ( !CanBecomeRagdoll() )
  3611. return false;
  3612. EmitSound( "NPC_AntlionGuard.Fallover" );
  3613. // Become server-side ragdoll if we're flagged to do it
  3614. if ( m_spawnflags & SF_ANTLIONGUARD_SERVERSIDE_RAGDOLL )
  3615. {
  3616. CTakeDamageInfo info;
  3617. // Fake the info
  3618. info.SetDamageType( DMG_GENERIC );
  3619. info.SetDamageForce( force );
  3620. info.SetDamagePosition( WorldSpaceCenter() );
  3621. CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_NONE );
  3622. // Transfer our name to the new ragdoll
  3623. pRagdoll->SetName( GetEntityName() );
  3624. pRagdoll->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  3625. // Get rid of our old body
  3626. UTIL_Remove(this);
  3627. return true;
  3628. }
  3629. return BaseClass::BecomeRagdollOnClient( force );
  3630. }
  3631. //-----------------------------------------------------------------------------
  3632. // Purpose: Override how we face our target as we move
  3633. // Output :
  3634. //-----------------------------------------------------------------------------
  3635. bool CNPC_AntlionGuard::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval )
  3636. {
  3637. Vector vecFacePosition = vec3_origin;
  3638. CBaseEntity *pFaceTarget = NULL;
  3639. bool bFaceTarget = false;
  3640. // FIXME: this will break scripted sequences that walk when they have an enemy
  3641. if ( m_hChargeTarget )
  3642. {
  3643. vecFacePosition = m_hChargeTarget->GetAbsOrigin();
  3644. pFaceTarget = m_hChargeTarget;
  3645. bFaceTarget = true;
  3646. }
  3647. #ifdef HL2_EPISODIC
  3648. else if ( GetEnemy() && IsCurSchedule( SCHED_ANTLIONGUARD_CANT_ATTACK ) )
  3649. {
  3650. // Always face our enemy when randomly patrolling around
  3651. vecFacePosition = GetEnemy()->EyePosition();
  3652. pFaceTarget = GetEnemy();
  3653. bFaceTarget = true;
  3654. }
  3655. #endif // HL2_EPISODIC
  3656. else if ( GetEnemy() && GetNavigator()->GetMovementActivity() == ACT_RUN )
  3657. {
  3658. Vector vecEnemyLKP = GetEnemyLKP();
  3659. // Only start facing when we're close enough
  3660. if ( ( UTIL_DistApprox( vecEnemyLKP, GetAbsOrigin() ) < 512 ) || IsCurSchedule( SCHED_ANTLIONGUARD_PATROL_RUN ) )
  3661. {
  3662. vecFacePosition = vecEnemyLKP;
  3663. pFaceTarget = GetEnemy();
  3664. bFaceTarget = true;
  3665. }
  3666. }
  3667. // Face
  3668. if ( bFaceTarget )
  3669. {
  3670. AddFacingTarget( pFaceTarget, vecFacePosition, 1.0, 0.2 );
  3671. }
  3672. return BaseClass::OverrideMoveFacing( move, flInterval );
  3673. }
  3674. //-----------------------------------------------------------------------------
  3675. // Purpose:
  3676. // Input : &info -
  3677. // Output : Returns true on success, false on failure.
  3678. //-----------------------------------------------------------------------------
  3679. bool CNPC_AntlionGuard::IsHeavyDamage( const CTakeDamageInfo &info )
  3680. {
  3681. // Struck by blast
  3682. if ( info.GetDamageType() & DMG_BLAST )
  3683. {
  3684. if ( info.GetDamage() > MIN_BLAST_DAMAGE )
  3685. return true;
  3686. }
  3687. // Struck by large object
  3688. if ( info.GetDamageType() & DMG_CRUSH )
  3689. {
  3690. IPhysicsObject *pPhysObject = info.GetInflictor()->VPhysicsGetObject();
  3691. if ( ( pPhysObject != NULL ) && ( pPhysObject->GetGameFlags() & FVPHYSICS_WAS_THROWN ) )
  3692. {
  3693. // Always take hits from a combine ball
  3694. if ( UTIL_IsAR2CombineBall( info.GetInflictor() ) )
  3695. return true;
  3696. // If we're under half health, stop being interrupted by heavy damage
  3697. if ( GetHealth() < (GetMaxHealth() * 0.25) )
  3698. return false;
  3699. // Ignore physics damages that don't do much damage
  3700. if ( info.GetDamage() < MIN_CRUSH_DAMAGE )
  3701. return false;
  3702. return true;
  3703. }
  3704. return false;
  3705. }
  3706. return false;
  3707. }
  3708. //-----------------------------------------------------------------------------
  3709. // Purpose:
  3710. // Input : &info -
  3711. // Output : Returns true on success, false on failure.
  3712. //-----------------------------------------------------------------------------
  3713. bool CNPC_AntlionGuard::IsLightDamage( const CTakeDamageInfo &info )
  3714. {
  3715. return false;
  3716. }
  3717. //-----------------------------------------------------------------------------
  3718. // Purpose:
  3719. // Input : *pChild -
  3720. //-----------------------------------------------------------------------------
  3721. void CNPC_AntlionGuard::InputSummonedAntlionDied( inputdata_t &inputdata )
  3722. {
  3723. m_iNumLiveAntlions--;
  3724. Assert( m_iNumLiveAntlions >= 0 );
  3725. if ( g_debug_antlionguard.GetInt() == 2 )
  3726. {
  3727. Msg("Guard summoned antlion count: %d\n", m_iNumLiveAntlions );
  3728. }
  3729. }
  3730. //-----------------------------------------------------------------------------
  3731. // Purpose: Filter out sounds we don't care about
  3732. // Input : *pSound - sound to test against
  3733. // Output : Returns true on success, false on failure.
  3734. //-----------------------------------------------------------------------------
  3735. bool CNPC_AntlionGuard::QueryHearSound( CSound *pSound )
  3736. {
  3737. // Don't bother with danger sounds from antlions or other guards
  3738. if ( pSound->SoundType() == SOUND_DANGER && ( pSound->m_hOwner != NULL && pSound->m_hOwner->Classify() == CLASS_ANTLION ) )
  3739. return false;
  3740. return BaseClass::QueryHearSound( pSound );
  3741. }
  3742. #if HL2_EPISODIC
  3743. //---------------------------------------------------------
  3744. // Prevent the cavern guard from using stopping paths, as it occasionally forces him off the navmesh.
  3745. //---------------------------------------------------------
  3746. bool CNPC_AntlionGuard::CNavigator::GetStoppingPath( CAI_WaypointList *pClippedWaypoints )
  3747. {
  3748. if (GetOuter()->m_bInCavern)
  3749. {
  3750. return false;
  3751. }
  3752. else
  3753. {
  3754. return BaseClass::GetStoppingPath( pClippedWaypoints );
  3755. }
  3756. }
  3757. #endif
  3758. //-----------------------------------------------------------------------------
  3759. // Purpose:
  3760. // Input : *pTarget -
  3761. // Output : Returns true on success, false on failure.
  3762. //-----------------------------------------------------------------------------
  3763. bool CNPC_AntlionGuard::RememberFailedPhysicsTarget( CBaseEntity *pTarget )
  3764. {
  3765. // Already in the list?
  3766. if ( m_FailedPhysicsTargets.Find( pTarget ) != m_FailedPhysicsTargets.InvalidIndex() )
  3767. return true;
  3768. // We're not holding on to any more
  3769. if ( ( m_FailedPhysicsTargets.Count() + 1 ) > MAX_FAILED_PHYSOBJECTS )
  3770. return false;
  3771. m_FailedPhysicsTargets.AddToTail( pTarget );
  3772. return true;
  3773. }
  3774. //-----------------------------------------------------------------------------
  3775. // Purpose: Handle squad or NPC interactions
  3776. // Output : Returns true on success, false on failure.
  3777. //-----------------------------------------------------------------------------
  3778. bool CNPC_AntlionGuard::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sender )
  3779. {
  3780. // Don't chase targets that other guards in our squad may be going after!
  3781. if ( interactionType == g_interactionAntlionGuardFoundPhysicsObject )
  3782. {
  3783. RememberFailedPhysicsTarget( (CBaseEntity *) data );
  3784. return true;
  3785. }
  3786. // Mark a shoved object as being free to pursue again
  3787. if ( interactionType == g_interactionAntlionGuardShovedPhysicsObject )
  3788. {
  3789. CBaseEntity *pObject = (CBaseEntity *) data;
  3790. m_FailedPhysicsTargets.FindAndRemove( pObject );
  3791. return true;
  3792. }
  3793. return BaseClass::HandleInteraction( interactionType, data, sender );
  3794. }
  3795. //-----------------------------------------------------------------------------
  3796. // Purpose: Cache whatever pose parameters we intend to use
  3797. //-----------------------------------------------------------------------------
  3798. void CNPC_AntlionGuard::PopulatePoseParameters( void )
  3799. {
  3800. m_poseThrow = LookupPoseParameter("throw");
  3801. m_poseHead_Pitch = LookupPoseParameter("head_pitch");
  3802. m_poseHead_Yaw = LookupPoseParameter("head_yaw" );
  3803. BaseClass::PopulatePoseParameters();
  3804. }
  3805. #if ANTLIONGUARD_BLOOD_EFFECTS
  3806. //-----------------------------------------------------------------------------
  3807. // Purpose: Return desired level for the continuous bleeding effect (not the
  3808. // individual blood spurts you see per bullet hit)
  3809. // Return 0 for don't bleed,
  3810. // 1 for mild bleeding
  3811. // 2 for severe bleeding
  3812. //-----------------------------------------------------------------------------
  3813. unsigned char CNPC_AntlionGuard::GetBleedingLevel( void ) const
  3814. {
  3815. if ( m_iHealth > ( m_iMaxHealth >> 1 ) )
  3816. { // greater than 50%
  3817. return 0;
  3818. }
  3819. else if ( m_iHealth > ( m_iMaxHealth >> 2 ) )
  3820. { // less than 50% but greater than 25%
  3821. return 1;
  3822. }
  3823. else
  3824. {
  3825. return 2;
  3826. }
  3827. }
  3828. #endif
  3829. //-----------------------------------------------------------------------------
  3830. //
  3831. // Schedules
  3832. //
  3833. //-----------------------------------------------------------------------------
  3834. AI_BEGIN_CUSTOM_NPC( npc_antlionguard, CNPC_AntlionGuard )
  3835. // Interactions
  3836. DECLARE_INTERACTION( g_interactionAntlionGuardFoundPhysicsObject )
  3837. DECLARE_INTERACTION( g_interactionAntlionGuardShovedPhysicsObject )
  3838. // Squadslots
  3839. DECLARE_SQUADSLOT( SQUAD_SLOT_ANTLIONGUARD_CHARGE )
  3840. //Tasks
  3841. DECLARE_TASK( TASK_ANTLIONGUARD_CHARGE )
  3842. DECLARE_TASK( TASK_ANTLIONGUARD_GET_PATH_TO_PHYSOBJECT )
  3843. DECLARE_TASK( TASK_ANTLIONGUARD_SHOVE_PHYSOBJECT )
  3844. DECLARE_TASK( TASK_ANTLIONGUARD_SUMMON )
  3845. DECLARE_TASK( TASK_ANTLIONGUARD_SET_FLINCH_ACTIVITY )
  3846. DECLARE_TASK( TASK_ANTLIONGUARD_GET_PATH_TO_CHARGE_POSITION )
  3847. DECLARE_TASK( TASK_ANTLIONGUARD_GET_PATH_TO_NEAREST_NODE )
  3848. DECLARE_TASK( TASK_ANTLIONGUARD_GET_CHASE_PATH_ENEMY_TOLERANCE )
  3849. DECLARE_TASK( TASK_ANTLIONGUARD_OPPORTUNITY_THROW )
  3850. DECLARE_TASK( TASK_ANTLIONGUARD_FIND_PHYSOBJECT )
  3851. //Activities
  3852. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_SEARCH )
  3853. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PEEK_FLINCH )
  3854. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PEEK_ENTER )
  3855. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PEEK_EXIT )
  3856. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PEEK1 )
  3857. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_BARK )
  3858. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PEEK_SIGHTED )
  3859. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_CHARGE_START )
  3860. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_CHARGE_CANCEL )
  3861. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_CHARGE_RUN )
  3862. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_CHARGE_CRASH )
  3863. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_CHARGE_STOP )
  3864. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_CHARGE_HIT )
  3865. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_CHARGE_ANTICIPATION )
  3866. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_SHOVE_PHYSOBJECT )
  3867. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_FLINCH_LIGHT )
  3868. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_UNBURROW )
  3869. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_ROAR )
  3870. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_RUN_HURT )
  3871. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PHYSHIT_FR )
  3872. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PHYSHIT_FL )
  3873. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PHYSHIT_RR )
  3874. DECLARE_ACTIVITY( ACT_ANTLIONGUARD_PHYSHIT_RL )
  3875. //Adrian: events go here
  3876. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_CHARGE_HIT )
  3877. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_SHOVE_PHYSOBJECT )
  3878. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_SHOVE )
  3879. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_FOOTSTEP_LIGHT )
  3880. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_FOOTSTEP_HEAVY )
  3881. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_CHARGE_EARLYOUT )
  3882. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_VOICE_GROWL )
  3883. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_VOICE_BARK )
  3884. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_VOICE_PAIN )
  3885. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_VOICE_SQUEEZE )
  3886. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_VOICE_SCRATCH )
  3887. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_VOICE_GRUNT )
  3888. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_BURROW_OUT )
  3889. DECLARE_ANIMEVENT( AE_ANTLIONGUARD_VOICE_ROAR )
  3890. DECLARE_CONDITION( COND_ANTLIONGUARD_PHYSICS_TARGET )
  3891. DECLARE_CONDITION( COND_ANTLIONGUARD_PHYSICS_TARGET_INVALID )
  3892. DECLARE_CONDITION( COND_ANTLIONGUARD_HAS_CHARGE_TARGET )
  3893. DECLARE_CONDITION( COND_ANTLIONGUARD_CAN_SUMMON )
  3894. DECLARE_CONDITION( COND_ANTLIONGUARD_CAN_CHARGE )
  3895. //==================================================
  3896. // SCHED_ANTLIONGUARD_SUMMON
  3897. //==================================================
  3898. DEFINE_SCHEDULE
  3899. (
  3900. SCHED_ANTLIONGUARD_SUMMON,
  3901. " Tasks"
  3902. " TASK_STOP_MOVING 0"
  3903. " TASK_FACE_ENEMY 0"
  3904. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLIONGUARD_BARK"
  3905. " TASK_ANTLIONGUARD_SUMMON 0"
  3906. " TASK_ANTLIONGUARD_OPPORTUNITY_THROW 0"
  3907. " "
  3908. " Interrupts"
  3909. " COND_HEAVY_DAMAGE"
  3910. )
  3911. //==================================================
  3912. // SCHED_ANTLIONGUARD_CHARGE
  3913. //==================================================
  3914. DEFINE_SCHEDULE
  3915. (
  3916. SCHED_ANTLIONGUARD_CHARGE,
  3917. " Tasks"
  3918. " TASK_STOP_MOVING 0"
  3919. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLIONGUARD_CHASE_ENEMY"
  3920. " TASK_FACE_ENEMY 0"
  3921. " TASK_ANTLIONGUARD_CHARGE 0"
  3922. ""
  3923. " Interrupts"
  3924. " COND_TASK_FAILED"
  3925. " COND_HEAVY_DAMAGE"
  3926. // These are deliberately left out so they can be detected during the
  3927. // charge Task and correctly play the charge stop animation.
  3928. //" COND_NEW_ENEMY"
  3929. //" COND_ENEMY_DEAD"
  3930. //" COND_LOST_ENEMY"
  3931. )
  3932. //==================================================
  3933. // SCHED_ANTLIONGUARD_CHARGE_TARGET
  3934. //==================================================
  3935. DEFINE_SCHEDULE
  3936. (
  3937. SCHED_ANTLIONGUARD_CHARGE_TARGET,
  3938. " Tasks"
  3939. " TASK_STOP_MOVING 0"
  3940. " TASK_FACE_ENEMY 0"
  3941. " TASK_ANTLIONGUARD_CHARGE 0"
  3942. ""
  3943. " Interrupts"
  3944. " COND_TASK_FAILED"
  3945. " COND_ENEMY_DEAD"
  3946. " COND_HEAVY_DAMAGE"
  3947. )
  3948. //==================================================
  3949. // SCHED_ANTLIONGUARD_CHARGE_SMASH
  3950. //==================================================
  3951. DEFINE_SCHEDULE
  3952. (
  3953. SCHED_ANTLIONGUARD_CHARGE_CRASH,
  3954. " Tasks"
  3955. " TASK_STOP_MOVING 0"
  3956. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLIONGUARD_CHARGE_CRASH"
  3957. ""
  3958. " Interrupts"
  3959. " COND_TASK_FAILED"
  3960. " COND_HEAVY_DAMAGE"
  3961. )
  3962. //==================================================
  3963. // SCHED_ANTLIONGUARD_PHYSICS_ATTACK
  3964. //==================================================
  3965. DEFINE_SCHEDULE
  3966. (
  3967. SCHED_ANTLIONGUARD_PHYSICS_ATTACK,
  3968. " Tasks"
  3969. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLIONGUARD_CHASE_ENEMY"
  3970. " TASK_ANTLIONGUARD_GET_PATH_TO_PHYSOBJECT 0"
  3971. " TASK_RUN_PATH 0"
  3972. " TASK_WAIT_FOR_MOVEMENT 0"
  3973. " TASK_FACE_ENEMY 0"
  3974. " TASK_ANTLIONGUARD_SHOVE_PHYSOBJECT 0"
  3975. ""
  3976. " Interrupts"
  3977. " COND_TASK_FAILED"
  3978. " COND_ENEMY_DEAD"
  3979. " COND_LOST_ENEMY"
  3980. " COND_NEW_ENEMY"
  3981. " COND_ANTLIONGUARD_PHYSICS_TARGET_INVALID"
  3982. " COND_HEAVY_DAMAGE"
  3983. )
  3984. //==================================================
  3985. // SCHED_FORCE_ANTLIONGUARD_PHYSICS_ATTACK
  3986. //==================================================
  3987. DEFINE_SCHEDULE
  3988. (
  3989. SCHED_FORCE_ANTLIONGUARD_PHYSICS_ATTACK,
  3990. " Tasks"
  3991. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLIONGUARD_CANT_ATTACK"
  3992. " TASK_ANTLIONGUARD_FIND_PHYSOBJECT 0"
  3993. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ANTLIONGUARD_PHYSICS_ATTACK"
  3994. ""
  3995. " Interrupts"
  3996. " COND_ANTLIONGUARD_PHYSICS_TARGET"
  3997. " COND_HEAVY_DAMAGE"
  3998. )
  3999. //==================================================
  4000. // SCHED_ANTLIONGUARD_CANT_ATTACK
  4001. // If we're here, the guard can't chase enemy, can't find a physobject to attack with, and can't summon
  4002. //==================================================
  4003. #ifdef HL2_EPISODIC
  4004. DEFINE_SCHEDULE
  4005. (
  4006. SCHED_ANTLIONGUARD_CANT_ATTACK,
  4007. " Tasks"
  4008. " TASK_SET_ROUTE_SEARCH_TIME 2" // Spend 5 seconds trying to build a path if stuck
  4009. " TASK_GET_PATH_TO_RANDOM_NODE 1024"
  4010. " TASK_WALK_PATH 0"
  4011. " TASK_WAIT_FOR_MOVEMENT 0"
  4012. " TASK_WAIT_PVS 0"
  4013. ""
  4014. " Interrupts"
  4015. " COND_GIVE_WAY"
  4016. " COND_NEW_ENEMY"
  4017. " COND_ANTLIONGUARD_PHYSICS_TARGET"
  4018. " COND_HEAVY_DAMAGE"
  4019. )
  4020. #else
  4021. DEFINE_SCHEDULE
  4022. (
  4023. SCHED_ANTLIONGUARD_CANT_ATTACK,
  4024. " Tasks"
  4025. " TASK_WAIT 5"
  4026. ""
  4027. " Interrupts"
  4028. )
  4029. #endif
  4030. //==================================================
  4031. // SCHED_ANTLIONGUARD_PHYSICS_DAMAGE_HEAVY
  4032. //==================================================
  4033. DEFINE_SCHEDULE
  4034. (
  4035. SCHED_ANTLIONGUARD_PHYSICS_DAMAGE_HEAVY,
  4036. " Tasks"
  4037. " TASK_STOP_MOVING 0"
  4038. " TASK_RESET_ACTIVITY 0"
  4039. " TASK_ANTLIONGUARD_SET_FLINCH_ACTIVITY 0"
  4040. ""
  4041. " Interrupts"
  4042. )
  4043. //==================================================
  4044. // SCHED_ANTLIONGUARD_UNBURROW
  4045. //==================================================
  4046. DEFINE_SCHEDULE
  4047. (
  4048. SCHED_ANTLIONGUARD_UNBURROW,
  4049. " Tasks"
  4050. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLIONGUARD_UNBURROW"
  4051. ""
  4052. " Interrupts"
  4053. )
  4054. //==================================================
  4055. // SCHED_ANTLIONGUARD_CHARGE_CANCEL
  4056. //==================================================
  4057. DEFINE_SCHEDULE
  4058. (
  4059. SCHED_ANTLIONGUARD_CHARGE_CANCEL,
  4060. " Tasks"
  4061. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLIONGUARD_CHARGE_CANCEL"
  4062. ""
  4063. " Interrupts"
  4064. )
  4065. //==================================================
  4066. // SCHED_ANTLIONGUARD_FIND_CHARGE_POSITION
  4067. //==================================================
  4068. DEFINE_SCHEDULE
  4069. (
  4070. SCHED_ANTLIONGUARD_FIND_CHARGE_POSITION,
  4071. " Tasks"
  4072. " TASK_ANTLIONGUARD_GET_PATH_TO_CHARGE_POSITION 0"
  4073. " TASK_RUN_PATH 0"
  4074. " TASK_WAIT_FOR_MOVEMENT 0"
  4075. " "
  4076. " Interrupts"
  4077. " COND_ENEMY_DEAD"
  4078. " COND_GIVE_WAY"
  4079. " COND_TASK_FAILED"
  4080. " COND_HEAVY_DAMAGE"
  4081. )
  4082. //=========================================================
  4083. // > SCHED_ANTLIONGUARD_CHASE_ENEMY_TOLERANCE
  4084. //=========================================================
  4085. DEFINE_SCHEDULE
  4086. (
  4087. SCHED_ANTLIONGUARD_CHASE_ENEMY_TOLERANCE,
  4088. " Tasks"
  4089. " TASK_STOP_MOVING 0"
  4090. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLIONGUARD_PATROL_RUN"
  4091. " TASK_ANTLIONGUARD_GET_PATH_TO_NEAREST_NODE 500"
  4092. " TASK_RUN_PATH 0"
  4093. " TASK_WAIT_FOR_MOVEMENT 0"
  4094. " TASK_FACE_ENEMY 0"
  4095. ""
  4096. " Interrupts"
  4097. " COND_TASK_FAILED"
  4098. " COND_CAN_MELEE_ATTACK1"
  4099. " COND_GIVE_WAY"
  4100. " COND_NEW_ENEMY"
  4101. " COND_ANTLIONGUARD_CAN_SUMMON"
  4102. " COND_ANTLIONGUARD_PHYSICS_TARGET"
  4103. " COND_HEAVY_DAMAGE"
  4104. " COND_ANTLIONGUARD_CAN_CHARGE"
  4105. );
  4106. //=========================================================
  4107. // > PATROL_RUN
  4108. //=========================================================
  4109. DEFINE_SCHEDULE
  4110. (
  4111. SCHED_ANTLIONGUARD_PATROL_RUN,
  4112. " Tasks"
  4113. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLIONGUARD_CANT_ATTACK"
  4114. " TASK_SET_ROUTE_SEARCH_TIME 3" // Spend 3 seconds trying to build a path if stuck
  4115. " TASK_ANTLIONGUARD_GET_PATH_TO_NEAREST_NODE 500"
  4116. " TASK_RUN_PATH 0"
  4117. " TASK_WAIT_FOR_MOVEMENT 0"
  4118. ""
  4119. " Interrupts"
  4120. " COND_TASK_FAILED"
  4121. " COND_CAN_MELEE_ATTACK1"
  4122. " COND_GIVE_WAY"
  4123. " COND_NEW_ENEMY"
  4124. " COND_ANTLIONGUARD_PHYSICS_TARGET"
  4125. " COND_ANTLIONGUARD_CAN_SUMMON"
  4126. " COND_HEAVY_DAMAGE"
  4127. " COND_ANTLIONGUARD_CAN_CHARGE"
  4128. );
  4129. //==================================================
  4130. // SCHED_ANTLIONGUARD_ROAR
  4131. //==================================================
  4132. DEFINE_SCHEDULE
  4133. (
  4134. SCHED_ANTLIONGUARD_ROAR,
  4135. " Tasks"
  4136. " TASK_STOP_MOVING 0"
  4137. " TASK_FACE_ENEMY 0"
  4138. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLIONGUARD_ROAR"
  4139. " "
  4140. " Interrupts"
  4141. " COND_HEAVY_DAMAGE"
  4142. )
  4143. //==================================================
  4144. // SCHED_ANTLIONGUARD_TAKE_COVER_FROM_ENEMY
  4145. //==================================================
  4146. DEFINE_SCHEDULE
  4147. (
  4148. SCHED_ANTLIONGUARD_TAKE_COVER_FROM_ENEMY,
  4149. " Tasks"
  4150. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLIONGUARD_CANT_ATTACK"
  4151. " TASK_FIND_COVER_FROM_ENEMY 0"
  4152. " TASK_RUN_PATH 0"
  4153. " TASK_WAIT_FOR_MOVEMENT 0"
  4154. " TASK_STOP_MOVING 0"
  4155. ""
  4156. " Interrupts"
  4157. " COND_TASK_FAILED"
  4158. " COND_NEW_ENEMY"
  4159. " COND_ENEMY_DEAD"
  4160. " COND_ANTLIONGUARD_PHYSICS_TARGET"
  4161. " COND_ANTLIONGUARD_CAN_SUMMON"
  4162. " COND_HEAVY_DAMAGE"
  4163. )
  4164. //=========================================================
  4165. // SCHED_ANTLIONGUARD_CHASE_ENEMY
  4166. //=========================================================
  4167. DEFINE_SCHEDULE
  4168. (
  4169. SCHED_ANTLIONGUARD_CHASE_ENEMY,
  4170. " Tasks"
  4171. " TASK_STOP_MOVING 0"
  4172. " TASK_GET_CHASE_PATH_TO_ENEMY 300"
  4173. " TASK_RUN_PATH 0"
  4174. " TASK_WAIT_FOR_MOVEMENT 0"
  4175. " TASK_FACE_ENEMY 0"
  4176. ""
  4177. " Interrupts"
  4178. " COND_NEW_ENEMY"
  4179. " COND_ENEMY_DEAD"
  4180. " COND_ENEMY_UNREACHABLE"
  4181. " COND_CAN_RANGE_ATTACK1"
  4182. // " COND_CAN_MELEE_ATTACK1"
  4183. " COND_CAN_RANGE_ATTACK2"
  4184. " COND_CAN_MELEE_ATTACK2"
  4185. " COND_TOO_CLOSE_TO_ATTACK"
  4186. " COND_TASK_FAILED"
  4187. " COND_LOST_ENEMY"
  4188. " COND_HEAVY_DAMAGE"
  4189. " COND_ANTLIONGUARD_CAN_CHARGE"
  4190. )
  4191. AI_END_CUSTOM_NPC()