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.

5103 lines
141 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Antlion - nasty bug
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_hint.h"
  8. #include "ai_squad.h"
  9. #include "ai_moveprobe.h"
  10. #include "ai_route.h"
  11. #include "npcevent.h"
  12. #include "gib.h"
  13. #include "entitylist.h"
  14. #include "ndebugoverlay.h"
  15. #include "antlion_dust.h"
  16. #include "engine/IEngineSound.h"
  17. #include "globalstate.h"
  18. #include "movevars_shared.h"
  19. #include "te_effect_dispatch.h"
  20. #include "vehicle_base.h"
  21. #include "mapentities.h"
  22. #include "antlion_maker.h"
  23. #include "npc_antlion.h"
  24. #include "decals.h"
  25. #include "hl2_shareddefs.h"
  26. #include "explode.h"
  27. #include "weapon_physcannon.h"
  28. #include "baseparticleentity.h"
  29. #include "props.h"
  30. #include "particle_parse.h"
  31. #include "ai_tacticalservices.h"
  32. #ifdef HL2_EPISODIC
  33. #include "grenade_spit.h"
  34. #endif
  35. // memdbgon must be the last include file in a .cpp file!!!
  36. #include "tier0/memdbgon.h"
  37. //Debug visualization
  38. ConVar g_debug_antlion( "g_debug_antlion", "0" );
  39. // base antlion stuff
  40. ConVar sk_antlion_health( "sk_antlion_health", "0" );
  41. ConVar sk_antlion_swipe_damage( "sk_antlion_swipe_damage", "0" );
  42. ConVar sk_antlion_jump_damage( "sk_antlion_jump_damage", "0" );
  43. ConVar sk_antlion_air_attack_dmg( "sk_antlion_air_attack_dmg", "0" );
  44. #ifdef HL2_EPISODIC
  45. // workers
  46. #define ANTLION_WORKERS_BURST() (true)
  47. #define ANTLION_WORKER_BURST_IS_POISONOUS() (true)
  48. ConVar sk_antlion_worker_burst_damage( "sk_antlion_worker_burst_damage", "50", FCVAR_NONE, "How much damage is inflicted by an antlion worker's death explosion." );
  49. ConVar sk_antlion_worker_health( "sk_antlion_worker_health", "0", FCVAR_NONE, "Hitpoints of an antlion worker. If 0, will use base antlion hitpoints." );
  50. ConVar sk_antlion_worker_spit_speed( "sk_antlion_worker_spit_speed", "0", FCVAR_NONE, "Speed at which an antlion spit grenade travels." );
  51. // This must agree with the AntlionWorkerBurstRadius() function!
  52. ConVar sk_antlion_worker_burst_radius( "sk_antlion_worker_burst_radius", "160", FCVAR_NONE, "Effect radius of an antlion worker's death explosion." );
  53. #endif
  54. ConVar g_test_new_antlion_jump( "g_test_new_antlion_jump", "1", FCVAR_ARCHIVE );
  55. ConVar antlion_easycrush( "antlion_easycrush", "1" );
  56. ConVar g_antlion_cascade_push( "g_antlion_cascade_push", "1", FCVAR_ARCHIVE );
  57. ConVar g_debug_antlion_worker( "g_debug_antlion_worker", "0" );
  58. extern ConVar bugbait_radius;
  59. int AE_ANTLION_WALK_FOOTSTEP;
  60. int AE_ANTLION_MELEE_HIT1;
  61. int AE_ANTLION_MELEE_HIT2;
  62. int AE_ANTLION_MELEE_POUNCE;
  63. int AE_ANTLION_FOOTSTEP_SOFT;
  64. int AE_ANTLION_FOOTSTEP_HEAVY;
  65. int AE_ANTLION_START_JUMP;
  66. int AE_ANTLION_BURROW_IN;
  67. int AE_ANTLION_BURROW_OUT;
  68. int AE_ANTLION_VANISH;
  69. int AE_ANTLION_OPEN_WINGS;
  70. int AE_ANTLION_CLOSE_WINGS;
  71. int AE_ANTLION_MELEE1_SOUND;
  72. int AE_ANTLION_MELEE2_SOUND;
  73. int AE_ANTLION_WORKER_EXPLODE_SCREAM;
  74. int AE_ANTLION_WORKER_EXPLODE_WARN;
  75. int AE_ANTLION_WORKER_EXPLODE;
  76. int AE_ANTLION_WORKER_SPIT;
  77. int AE_ANTLION_WORKER_DONT_EXPLODE;
  78. //Attack range definitions
  79. #define ANTLION_MELEE1_RANGE 100.0f
  80. #define ANTLION_MELEE2_RANGE 64.0f
  81. #define ANTLION_MELEE2_RANGE_MAX 175.0f
  82. #define ANTLION_MELEE2_RANGE_MIN 64.0f
  83. #define ANTLION_JUMP_MIN 128.0f
  84. #define ANTLION_JUMP_MAX_RISE 512.0f
  85. #define ANTLION_JUMP_MAX 1024.0f
  86. #define ANTLION_MIN_BUGBAIT_GOAL_TARGET_RADIUS 512
  87. //Interaction IDs
  88. int g_interactionAntlionFoundTarget = 0;
  89. int g_interactionAntlionFiredAtTarget = 0;
  90. #define ANTLION_MODEL "models/antlion.mdl"
  91. #define ANTLION_WORKER_MODEL "models/antlion_worker.mdl"
  92. #define ANTLION_BURROW_IN 0
  93. #define ANTLION_BURROW_OUT 1
  94. #define ANTLION_BUGBAIT_NAV_TOLERANCE 200
  95. #define ANTLION_OBEY_FOLLOW_TIME 5.0f
  96. //==================================================
  97. // AntlionSquadSlots
  98. //==================================================
  99. enum
  100. {
  101. SQUAD_SLOT_ANTLION_JUMP = LAST_SHARED_SQUADSLOT,
  102. SQUAD_SLOT_ANTLION_WORKER_FIRE,
  103. };
  104. //==================================================
  105. // Antlion Activities
  106. //==================================================
  107. int ACT_ANTLION_JUMP_START;
  108. int ACT_ANTLION_DISTRACT;
  109. int ACT_ANTLION_DISTRACT_ARRIVED;
  110. int ACT_ANTLION_BURROW_IN;
  111. int ACT_ANTLION_BURROW_OUT;
  112. int ACT_ANTLION_BURROW_IDLE;
  113. int ACT_ANTLION_RUN_AGITATED;
  114. int ACT_ANTLION_FLIP;
  115. int ACT_ANTLION_ZAP_FLIP;
  116. int ACT_ANTLION_POUNCE;
  117. int ACT_ANTLION_POUNCE_MOVING;
  118. int ACT_ANTLION_DROWN;
  119. int ACT_ANTLION_LAND;
  120. int ACT_ANTLION_WORKER_EXPLODE;
  121. //==================================================
  122. // CNPC_Antlion
  123. //==================================================
  124. CNPC_Antlion::CNPC_Antlion( void )
  125. {
  126. m_flIdleDelay = 0.0f;
  127. m_flBurrowTime = 0.0f;
  128. m_flJumpTime = 0.0f;
  129. m_flPounceTime = 0.0f;
  130. m_flObeyFollowTime = 0.0f;
  131. m_iUnBurrowAttempts = 0;
  132. m_flAlertRadius = 256.0f;
  133. m_flFieldOfView = -0.5f;
  134. m_bStartBurrowed = false;
  135. m_bAgitatedSound = false;
  136. m_bWingsOpen = false;
  137. m_flIgnoreSoundTime = 0.0f;
  138. m_bHasHeardSound = false;
  139. m_flNextAcknowledgeTime = 0.0f;
  140. m_flNextJumpPushTime = 0.0f;
  141. m_vecLastJumpAttempt.Init();
  142. m_vecSavedJump.Init();
  143. m_hFightGoalTarget = NULL;
  144. m_hFollowTarget = NULL;
  145. m_bLoopingStarted = false;
  146. m_bForcedStuckJump = false;
  147. m_nBodyBone = -1;
  148. m_bSuppressUnburrowEffects = false;
  149. }
  150. LINK_ENTITY_TO_CLASS( npc_antlion, CNPC_Antlion );
  151. //==================================================
  152. // CNPC_Antlion::m_DataDesc
  153. //==================================================
  154. BEGIN_DATADESC( CNPC_Antlion )
  155. DEFINE_KEYFIELD( m_bStartBurrowed, FIELD_BOOLEAN, "startburrowed" ),
  156. DEFINE_KEYFIELD( m_bIgnoreBugbait, FIELD_BOOLEAN, "ignorebugbait" ),
  157. DEFINE_KEYFIELD( m_flAlertRadius, FIELD_FLOAT, "radius" ),
  158. DEFINE_KEYFIELD( m_flEludeDistance, FIELD_FLOAT, "eludedist" ),
  159. DEFINE_KEYFIELD( m_bSuppressUnburrowEffects, FIELD_BOOLEAN, "unburroweffects" ),
  160. DEFINE_FIELD( m_vecSaveSpitVelocity, FIELD_VECTOR ),
  161. DEFINE_FIELD( m_flIdleDelay, FIELD_TIME ),
  162. DEFINE_FIELD( m_flBurrowTime, FIELD_TIME ),
  163. DEFINE_FIELD( m_flJumpTime, FIELD_TIME ),
  164. DEFINE_FIELD( m_flPounceTime, FIELD_TIME ),
  165. DEFINE_FIELD( m_iUnBurrowAttempts, FIELD_INTEGER ),
  166. DEFINE_FIELD( m_iContext, FIELD_INTEGER ),
  167. DEFINE_FIELD( m_vecSavedJump, FIELD_VECTOR ),
  168. DEFINE_FIELD( m_vecLastJumpAttempt, FIELD_VECTOR ),
  169. DEFINE_FIELD( m_flIgnoreSoundTime, FIELD_TIME ),
  170. DEFINE_FIELD( m_vecHeardSound, FIELD_POSITION_VECTOR ),
  171. DEFINE_FIELD( m_bHasHeardSound, FIELD_BOOLEAN ),
  172. DEFINE_FIELD( m_bAgitatedSound, FIELD_BOOLEAN ),
  173. DEFINE_FIELD( m_bWingsOpen, FIELD_BOOLEAN ),
  174. DEFINE_FIELD( m_flNextAcknowledgeTime, FIELD_TIME ),
  175. DEFINE_FIELD( m_hFollowTarget, FIELD_EHANDLE ),
  176. DEFINE_FIELD( m_hFightGoalTarget, FIELD_EHANDLE ),
  177. DEFINE_FIELD( m_strParentSpawner, FIELD_STRING ),
  178. DEFINE_FIELD( m_flSuppressFollowTime, FIELD_FLOAT ),
  179. DEFINE_FIELD( m_MoveState, FIELD_INTEGER ),
  180. DEFINE_FIELD( m_flObeyFollowTime, FIELD_TIME ),
  181. DEFINE_FIELD( m_bLeapAttack, FIELD_BOOLEAN ),
  182. DEFINE_FIELD( m_bDisableJump, FIELD_BOOLEAN ),
  183. DEFINE_FIELD( m_flTimeDrown, FIELD_TIME ),
  184. DEFINE_FIELD( m_flTimeDrownSplash, FIELD_TIME ),
  185. DEFINE_FIELD( m_bDontExplode, FIELD_BOOLEAN ),
  186. DEFINE_FIELD( m_flNextJumpPushTime, FIELD_TIME ),
  187. DEFINE_FIELD( m_bForcedStuckJump, FIELD_BOOLEAN ),
  188. DEFINE_FIELD( m_flZapDuration, FIELD_TIME ),
  189. #if HL2_EPISODIC
  190. DEFINE_FIELD( m_bHasDoneAirAttack, FIELD_BOOLEAN ),
  191. #endif
  192. // DEFINE_FIELD( m_bLoopingStarted, FIELD_BOOLEAN ),
  193. // m_FollowBehavior
  194. // m_AssaultBehavior
  195. DEFINE_INPUTFUNC( FIELD_VOID, "Unburrow", InputUnburrow ),
  196. DEFINE_INPUTFUNC( FIELD_VOID, "Burrow", InputBurrow ),
  197. DEFINE_INPUTFUNC( FIELD_VOID, "BurrowAway", InputBurrowAway ),
  198. DEFINE_INPUTFUNC( FIELD_STRING, "FightToPosition", InputFightToPosition ),
  199. DEFINE_INPUTFUNC( FIELD_STRING, "StopFightToPosition", InputStopFightToPosition ),
  200. DEFINE_INPUTFUNC( FIELD_VOID, "EnableJump", InputEnableJump ),
  201. DEFINE_INPUTFUNC( FIELD_VOID, "DisableJump", InputDisableJump ),
  202. DEFINE_INPUTFUNC( FIELD_VOID, "IgnoreBugbait", InputIgnoreBugbait ),
  203. DEFINE_INPUTFUNC( FIELD_VOID, "HearBugbait", InputHearBugbait ),
  204. DEFINE_INPUTFUNC( FIELD_STRING, "JumpAtTarget", InputJumpAtTarget ),
  205. DEFINE_OUTPUT( m_OnReachFightGoal, "OnReachedFightGoal" ),
  206. DEFINE_OUTPUT( m_OnUnBurrowed, "OnUnBurrowed" ),
  207. // Function Pointers
  208. DEFINE_ENTITYFUNC( Touch ),
  209. DEFINE_USEFUNC( BurrowUse ),
  210. DEFINE_THINKFUNC( ZapThink ),
  211. // DEFINE_FIELD( FIELD_SHORT, m_hFootstep ),
  212. END_DATADESC()
  213. //-----------------------------------------------------------------------------
  214. // Purpose:
  215. //-----------------------------------------------------------------------------
  216. void CNPC_Antlion::Spawn( void )
  217. {
  218. Precache();
  219. #ifdef _XBOX
  220. // Always fade the corpse
  221. AddSpawnFlags( SF_NPC_FADE_CORPSE );
  222. #endif // _XBOX
  223. #ifdef HL2_EPISODIC
  224. if ( IsWorker() )
  225. {
  226. SetModel( ANTLION_WORKER_MODEL );
  227. AddSpawnFlags( SF_NPC_LONG_RANGE );
  228. SetBloodColor( BLOOD_COLOR_ANTLION_WORKER );
  229. }
  230. else
  231. {
  232. SetModel( ANTLION_MODEL );
  233. SetBloodColor( BLOOD_COLOR_ANTLION );
  234. }
  235. #else
  236. SetModel( ANTLION_MODEL );
  237. SetBloodColor( BLOOD_COLOR_YELLOW );
  238. #endif // HL2_EPISODIC
  239. SetHullType(HULL_MEDIUM);
  240. SetHullSizeNormal();
  241. SetDefaultEyeOffset();
  242. SetNavType( NAV_GROUND );
  243. m_NPCState = NPC_STATE_NONE;
  244. #if HL2_EPISODIC
  245. m_iHealth = ( IsWorker() ) ? sk_antlion_worker_health.GetFloat() : sk_antlion_health.GetFloat();
  246. #else
  247. m_iHealth = sk_antlion_health.GetFloat();
  248. #endif // _DEBUG
  249. SetSolid( SOLID_BBOX );
  250. AddSolidFlags( FSOLID_NOT_STANDABLE );
  251. SetMoveType( MOVETYPE_STEP );
  252. //Only do this if a squadname appears in the entity
  253. if ( m_SquadName != NULL_STRING )
  254. {
  255. CapabilitiesAdd( bits_CAP_SQUAD );
  256. }
  257. SetCollisionGroup( HL2COLLISION_GROUP_ANTLION );
  258. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_MOVE_JUMP | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK2 );
  259. // Workers shoot projectiles
  260. if ( IsWorker() )
  261. {
  262. CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 );
  263. // CapabilitiesRemove( bits_CAP_INNATE_MELEE_ATTACK2 );
  264. }
  265. // JAY: Optimize these out for now
  266. if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false )
  267. CapabilitiesAdd( bits_CAP_SKIP_NAV_GROUND_CHECK );
  268. NPCInit();
  269. if ( IsWorker() )
  270. {
  271. // Bump up the worker's eye position a bit
  272. SetViewOffset( Vector( 0, 0, 32 ) );
  273. }
  274. // Antlions will always pursue
  275. m_flDistTooFar = FLT_MAX;
  276. m_bDisableJump = false;
  277. //See if we're supposed to start burrowed
  278. if ( m_bStartBurrowed )
  279. {
  280. AddEffects( EF_NODRAW );
  281. AddFlag( FL_NOTARGET );
  282. m_spawnflags |= SF_NPC_GAG;
  283. AddSolidFlags( FSOLID_NOT_SOLID );
  284. m_takedamage = DAMAGE_NO;
  285. SetState( NPC_STATE_IDLE );
  286. SetActivity( (Activity) ACT_ANTLION_BURROW_IDLE );
  287. SetSchedule( SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER );
  288. SetUse( &CNPC_Antlion::BurrowUse );
  289. }
  290. BaseClass::Spawn();
  291. m_nSkin = random->RandomInt( 0, ANTLION_SKIN_COUNT-1 );
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose:
  295. //-----------------------------------------------------------------------------
  296. void CNPC_Antlion::Activate( void )
  297. {
  298. // If we're friendly to the player, setup a relationship to reflect it
  299. if ( IsAllied() )
  300. {
  301. // Handle all clients
  302. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  303. {
  304. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  305. if ( pPlayer != NULL )
  306. {
  307. AddEntityRelationship( pPlayer, D_LI, 99 );
  308. }
  309. }
  310. }
  311. BaseClass::Activate();
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose: override this to simplify the physics shadow of the antlions
  315. //-----------------------------------------------------------------------------
  316. bool CNPC_Antlion::CreateVPhysics()
  317. {
  318. bool bRet = BaseClass::CreateVPhysics();
  319. return bRet;
  320. }
  321. // Use all the gibs
  322. #define NUM_ANTLION_GIBS_UNIQUE 3
  323. const char *pszAntlionGibs_Unique[NUM_ANTLION_GIBS_UNIQUE] = {
  324. "models/gibs/antlion_gib_large_1.mdl",
  325. "models/gibs/antlion_gib_large_2.mdl",
  326. "models/gibs/antlion_gib_large_3.mdl"
  327. };
  328. #define NUM_ANTLION_GIBS_MEDIUM 3
  329. const char *pszAntlionGibs_Medium[NUM_ANTLION_GIBS_MEDIUM] = {
  330. "models/gibs/antlion_gib_medium_1.mdl",
  331. "models/gibs/antlion_gib_medium_2.mdl",
  332. "models/gibs/antlion_gib_medium_3.mdl"
  333. };
  334. // XBox doesn't use the smaller gibs, so don't cache them
  335. #define NUM_ANTLION_GIBS_SMALL 3
  336. const char *pszAntlionGibs_Small[NUM_ANTLION_GIBS_SMALL] = {
  337. "models/gibs/antlion_gib_small_1.mdl",
  338. "models/gibs/antlion_gib_small_2.mdl",
  339. "models/gibs/antlion_gib_small_3.mdl"
  340. };
  341. //-----------------------------------------------------------------------------
  342. // Purpose:
  343. //-----------------------------------------------------------------------------
  344. void CNPC_Antlion::Precache( void )
  345. {
  346. #ifdef HL2_EPISODIC
  347. if ( IsWorker() )
  348. {
  349. PrecacheModel( ANTLION_WORKER_MODEL );
  350. PropBreakablePrecacheAll( MAKE_STRING( ANTLION_WORKER_MODEL ) );
  351. UTIL_PrecacheOther( "grenade_spit" );
  352. PrecacheParticleSystem( "blood_impact_antlion_worker_01" );
  353. PrecacheParticleSystem( "antlion_gib_02" );
  354. PrecacheParticleSystem( "blood_impact_yellow_01" );
  355. }
  356. else
  357. #endif // HL2_EPISODIC
  358. {
  359. PrecacheModel( ANTLION_MODEL );
  360. PropBreakablePrecacheAll( MAKE_STRING( ANTLION_MODEL ) );
  361. PrecacheParticleSystem( "blood_impact_antlion_01" );
  362. PrecacheParticleSystem( "AntlionGib" );
  363. }
  364. for ( int i = 0; i < NUM_ANTLION_GIBS_UNIQUE; ++i )
  365. {
  366. PrecacheModel( pszAntlionGibs_Unique[ i ] );
  367. }
  368. for ( int i = 0; i < NUM_ANTLION_GIBS_MEDIUM; ++i )
  369. {
  370. PrecacheModel( pszAntlionGibs_Medium[ i ] );
  371. }
  372. for ( int i = 0; i < NUM_ANTLION_GIBS_SMALL; ++i )
  373. {
  374. PrecacheModel( pszAntlionGibs_Small[ i ] );
  375. }
  376. PrecacheScriptSound( "NPC_Antlion.RunOverByVehicle" );
  377. PrecacheScriptSound( "NPC_Antlion.MeleeAttack" );
  378. m_hFootstep = PrecacheScriptSound( "NPC_Antlion.Footstep" );
  379. PrecacheScriptSound( "NPC_Antlion.BurrowIn" );
  380. PrecacheScriptSound( "NPC_Antlion.BurrowOut" );
  381. PrecacheScriptSound( "NPC_Antlion.FootstepSoft" );
  382. PrecacheScriptSound( "NPC_Antlion.FootstepHeavy" );
  383. PrecacheScriptSound( "NPC_Antlion.MeleeAttackSingle" );
  384. PrecacheScriptSound( "NPC_Antlion.MeleeAttackDouble" );
  385. PrecacheScriptSound( "NPC_Antlion.Distracted" );
  386. PrecacheScriptSound( "NPC_Antlion.Idle" );
  387. PrecacheScriptSound( "NPC_Antlion.Pain" );
  388. PrecacheScriptSound( "NPC_Antlion.Land" );
  389. PrecacheScriptSound( "NPC_Antlion.WingsOpen" );
  390. PrecacheScriptSound( "NPC_Antlion.LoopingAgitated" );
  391. PrecacheScriptSound( "NPC_Antlion.Distracted" );
  392. #ifdef HL2_EPISODIC
  393. PrecacheScriptSound( "NPC_Antlion.PoisonBurstScream" );
  394. PrecacheScriptSound( "NPC_Antlion.PoisonBurstScreamSubmerged" );
  395. PrecacheScriptSound( "NPC_Antlion.PoisonBurstExplode" );
  396. PrecacheScriptSound( "NPC_Antlion.MeleeAttack_Muffled" );
  397. PrecacheScriptSound( "NPC_Antlion.TrappedMetal" );
  398. PrecacheScriptSound( "NPC_Antlion.ZappedFlip" );
  399. PrecacheScriptSound( "NPC_Antlion.PoisonShoot" );
  400. PrecacheScriptSound( "NPC_Antlion.PoisonBall" );
  401. #endif
  402. BaseClass::Precache();
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose:
  406. // Output : Returns true on success, false on failure.
  407. //-----------------------------------------------------------------------------
  408. inline CBaseEntity *CNPC_Antlion::EntityToWatch( void )
  409. {
  410. return ( m_hFollowTarget != NULL ) ? m_hFollowTarget.Get() : GetEnemy();
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Purpose: Cache whatever pose parameters we intend to use
  414. //-----------------------------------------------------------------------------
  415. void CNPC_Antlion::PopulatePoseParameters( void )
  416. {
  417. m_poseHead_Pitch = LookupPoseParameter("head_pitch");
  418. m_poseHead_Yaw = LookupPoseParameter("head_yaw" );
  419. BaseClass::PopulatePoseParameters();
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose:
  423. //-----------------------------------------------------------------------------
  424. void CNPC_Antlion::UpdateHead( void )
  425. {
  426. float yaw = GetPoseParameter( m_poseHead_Yaw );
  427. float pitch = GetPoseParameter( m_poseHead_Pitch );
  428. CBaseEntity *pTarget = EntityToWatch();
  429. if ( pTarget != NULL )
  430. {
  431. Vector enemyDir = pTarget->WorldSpaceCenter() - WorldSpaceCenter();
  432. VectorNormalize( enemyDir );
  433. if ( DotProduct( enemyDir, BodyDirection3D() ) < 0.0f )
  434. {
  435. SetPoseParameter( m_poseHead_Yaw, UTIL_Approach( 0, yaw, 10 ) );
  436. SetPoseParameter( m_poseHead_Pitch, UTIL_Approach( 0, pitch, 10 ) );
  437. return;
  438. }
  439. float facingYaw = VecToYaw( BodyDirection3D() );
  440. float yawDiff = VecToYaw( enemyDir );
  441. yawDiff = UTIL_AngleDiff( yawDiff, facingYaw + yaw );
  442. float facingPitch = UTIL_VecToPitch( BodyDirection3D() );
  443. float pitchDiff = UTIL_VecToPitch( enemyDir );
  444. pitchDiff = UTIL_AngleDiff( pitchDiff, facingPitch + pitch );
  445. SetPoseParameter( m_poseHead_Yaw, UTIL_Approach( yaw + yawDiff, yaw, 50 ) );
  446. SetPoseParameter( m_poseHead_Pitch, UTIL_Approach( pitch + pitchDiff, pitch, 50 ) );
  447. }
  448. else
  449. {
  450. SetPoseParameter( m_poseHead_Yaw, UTIL_Approach( 0, yaw, 10 ) );
  451. SetPoseParameter( m_poseHead_Pitch, UTIL_Approach( 0, pitch, 10 ) );
  452. }
  453. }
  454. #define ANTLION_VIEW_FIELD_NARROW 0.85f
  455. //-----------------------------------------------------------------------------
  456. // Purpose:
  457. // Input : *pEntity -
  458. // Output : Returns true on success, false on failure.
  459. //-----------------------------------------------------------------------------
  460. bool CNPC_Antlion::FInViewCone( CBaseEntity *pEntity )
  461. {
  462. m_flFieldOfView = ( GetEnemy() != NULL ) ? ANTLION_VIEW_FIELD_NARROW : VIEW_FIELD_WIDE;
  463. return BaseClass::FInViewCone( pEntity );
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose:
  467. // Input : &vecSpot -
  468. // Output : Returns true on success, false on failure.
  469. //-----------------------------------------------------------------------------
  470. bool CNPC_Antlion::FInViewCone( const Vector &vecSpot )
  471. {
  472. m_flFieldOfView = ( GetEnemy() != NULL ) ? ANTLION_VIEW_FIELD_NARROW : VIEW_FIELD_WIDE;
  473. return BaseClass::FInViewCone( vecSpot );
  474. }
  475. //-----------------------------------------------------------------------------
  476. //-----------------------------------------------------------------------------
  477. bool CNPC_Antlion::CanBecomeRagdoll()
  478. {
  479. // This prevents us from dying in the regular way. It forces a schedule selection
  480. // that will select SCHED_DIE, where we can do our poison burst thing.
  481. #ifdef HL2_EPISODIC
  482. if ( IsWorker() && ANTLION_WORKERS_BURST() )
  483. {
  484. // If we're in a script, we're allowed to ragdoll. This lets the vort's dynamic
  485. // interaction ragdoll us.
  486. return ( m_NPCState == NPC_STATE_SCRIPT || m_bDontExplode );
  487. }
  488. #endif
  489. return BaseClass::CanBecomeRagdoll();
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose:
  493. // Input : *pVictim -
  494. //-----------------------------------------------------------------------------
  495. void CNPC_Antlion::Event_Killed( const CTakeDamageInfo &info )
  496. {
  497. //Turn off wings
  498. SetWings( false );
  499. VacateStrategySlot();
  500. if ( IsCurSchedule(SCHED_ANTLION_BURROW_IN) || IsCurSchedule(SCHED_ANTLION_BURROW_OUT) )
  501. {
  502. AddEFlags( EF_NOSHADOW );
  503. }
  504. if ( info.GetDamageType() & DMG_CRUSH )
  505. {
  506. CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, GetAbsOrigin(), 256, 0.5f, this );
  507. }
  508. BaseClass::Event_Killed( info );
  509. CBaseEntity *pAttacker = info.GetInflictor();
  510. if ( pAttacker && pAttacker->GetServerVehicle() && ShouldGib( info ) == true )
  511. {
  512. trace_t tr;
  513. UTIL_TraceLine( GetAbsOrigin() + Vector( 0, 0, 64 ), pAttacker->GetAbsOrigin(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  514. UTIL_DecalTrace( &tr, "Antlion.Splat" );
  515. SpawnBlood( GetAbsOrigin(), g_vecAttackDir, BloodColor(), info.GetDamage() );
  516. CPASAttenuationFilter filter( this );
  517. EmitSound( filter, entindex(), "NPC_Antlion.RunOverByVehicle" );
  518. }
  519. // Stop our zap effect!
  520. SetContextThink( NULL, gpGlobals->curtime, "ZapThink" );
  521. }
  522. //-----------------------------------------------------------------------------
  523. //-----------------------------------------------------------------------------
  524. void CNPC_Antlion::MeleeAttack( float distance, float damage, QAngle &viewPunch, Vector &shove )
  525. {
  526. Vector vecForceDir;
  527. // Always hurt bullseyes for now
  528. if ( ( GetEnemy() != NULL ) && ( GetEnemy()->Classify() == CLASS_BULLSEYE ) )
  529. {
  530. vecForceDir = (GetEnemy()->GetAbsOrigin() - GetAbsOrigin());
  531. CTakeDamageInfo info( this, this, damage, DMG_SLASH );
  532. CalculateMeleeDamageForce( &info, vecForceDir, GetEnemy()->GetAbsOrigin() );
  533. GetEnemy()->TakeDamage( info );
  534. return;
  535. }
  536. CBaseEntity *pHurt = CheckTraceHullAttack( distance, -Vector(16,16,32), Vector(16,16,32), damage, DMG_SLASH, 5.0f );
  537. if ( pHurt )
  538. {
  539. vecForceDir = ( pHurt->WorldSpaceCenter() - WorldSpaceCenter() );
  540. //FIXME: Until the interaction is setup, kill combine soldiers in one hit -- jdw
  541. if ( FClassnameIs( pHurt, "npc_combine_s" ) )
  542. {
  543. CTakeDamageInfo dmgInfo( this, this, pHurt->m_iHealth+25, DMG_SLASH );
  544. CalculateMeleeDamageForce( &dmgInfo, vecForceDir, pHurt->GetAbsOrigin() );
  545. pHurt->TakeDamage( dmgInfo );
  546. return;
  547. }
  548. CBasePlayer *pPlayer = ToBasePlayer( pHurt );
  549. if ( pPlayer != NULL )
  550. {
  551. //Kick the player angles
  552. if ( !(pPlayer->GetFlags() & FL_GODMODE ) && pPlayer->GetMoveType() != MOVETYPE_NOCLIP )
  553. {
  554. pPlayer->ViewPunch( viewPunch );
  555. Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
  556. VectorNormalize(dir);
  557. QAngle angles;
  558. VectorAngles( dir, angles );
  559. Vector forward, right;
  560. AngleVectors( angles, &forward, &right, NULL );
  561. //Push the target back
  562. pHurt->ApplyAbsVelocityImpulse( - right * shove[1] - forward * shove[0] );
  563. }
  564. }
  565. // Play a random attack hit sound
  566. EmitSound( "NPC_Antlion.MeleeAttack" );
  567. }
  568. }
  569. // Number of times the antlions will attempt to generate a random chase position
  570. #define NUM_CHASE_POSITION_ATTEMPTS 3
  571. //-----------------------------------------------------------------------------
  572. // Purpose:
  573. // Input : &targetPos -
  574. // &result -
  575. // Output : Returns true on success, false on failure.
  576. //-----------------------------------------------------------------------------
  577. bool CNPC_Antlion::FindChasePosition( const Vector &targetPos, Vector &result )
  578. {
  579. if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == true )
  580. {
  581. result = targetPos;
  582. return true;
  583. }
  584. Vector runDir = ( targetPos - GetAbsOrigin() );
  585. VectorNormalize( runDir );
  586. Vector vRight, vUp;
  587. VectorVectors( runDir, vRight, vUp );
  588. for ( int i = 0; i < NUM_CHASE_POSITION_ATTEMPTS; i++ )
  589. {
  590. result = targetPos;
  591. result += -runDir * random->RandomInt( 64, 128 );
  592. result += vRight * random->RandomInt( -128, 128 );
  593. //FIXME: We need to do a more robust search here
  594. // Find a ground position and try to get there
  595. if ( GetGroundPosition( result, result ) )
  596. return true;
  597. }
  598. //TODO: If we're making multiple inquiries to this, make sure it's evenly spread
  599. if ( g_debug_antlion.GetInt() == 1 )
  600. {
  601. NDebugOverlay::Cross3D( result, -Vector(32,32,32), Vector(32,32,32), 255, 255, 0, true, 2.0f );
  602. }
  603. return false;
  604. }
  605. //-----------------------------------------------------------------------------
  606. // Purpose:
  607. // Input : &testPos -
  608. //-----------------------------------------------------------------------------
  609. bool CNPC_Antlion::GetGroundPosition( const Vector &testPos, Vector &result )
  610. {
  611. // Trace up to clear the ground
  612. trace_t tr;
  613. AI_TraceHull( testPos, testPos + Vector( 0, 0, 64 ), NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  614. // If we're stuck in solid, this can't be valid
  615. if ( tr.allsolid )
  616. {
  617. if ( g_debug_antlion.GetInt() == 3 )
  618. {
  619. NDebugOverlay::BoxDirection( testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ) + Vector( 0, 0, 128 ), Vector( 0, 0, 1 ), 255, 0, 0, true, 2.0f );
  620. }
  621. return false;
  622. }
  623. if ( g_debug_antlion.GetInt() == 3 )
  624. {
  625. NDebugOverlay::BoxDirection( testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ) + Vector( 0, 0, 128 ), Vector( 0, 0, 1 ), 0, 255, 0, true, 2.0f );
  626. }
  627. // Trace down to find the ground
  628. AI_TraceHull( tr.endpos, tr.endpos - Vector( 0, 0, 128 ), NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  629. if ( g_debug_antlion.GetInt() == 3 )
  630. {
  631. NDebugOverlay::BoxDirection( tr.endpos, NAI_Hull::Mins( GetHullType() ) - Vector( 0, 0, 256 ), NAI_Hull::Maxs( GetHullType() ), Vector( 0, 0, 1 ), 255, 255, 0, true, 2.0f );
  632. }
  633. // We must end up on the floor with this trace
  634. if ( tr.fraction < 1.0f )
  635. {
  636. if ( g_debug_antlion.GetInt() == 3 )
  637. {
  638. NDebugOverlay::Cross3D( tr.endpos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), 255, 0, 0, true, 2.0f );
  639. }
  640. result = tr.endpos;
  641. return true;
  642. }
  643. // Ended up in open space
  644. return false;
  645. }
  646. void CNPC_Antlion::ManageFleeCapabilities( bool bEnable )
  647. {
  648. if ( bEnable == false )
  649. {
  650. //Remove the jump capabilty when we build our route.
  651. //We'll enable it back again after the route has been built.
  652. CapabilitiesRemove( bits_CAP_MOVE_JUMP );
  653. if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false )
  654. CapabilitiesRemove( bits_CAP_SKIP_NAV_GROUND_CHECK );
  655. }
  656. else
  657. {
  658. if ( m_bDisableJump == false )
  659. CapabilitiesAdd( bits_CAP_MOVE_JUMP );
  660. if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false )
  661. CapabilitiesAdd( bits_CAP_SKIP_NAV_GROUND_CHECK );
  662. }
  663. }
  664. //-----------------------------------------------------------------------------
  665. // Purpose:
  666. // Input : soundType -
  667. // Output : Returns true on success, false on failure.
  668. //-----------------------------------------------------------------------------
  669. bool CNPC_Antlion::GetPathToSoundFleePoint( int soundType )
  670. {
  671. CSound *pSound = GetLoudestSoundOfType( soundType );
  672. if ( pSound == NULL )
  673. {
  674. //NOTENOTE: If you're here, there's a disparity between Listen() and GetLoudestSoundOfType() - jdw
  675. TaskFail( "Unable to find thumper sound!" );
  676. return false;
  677. }
  678. ManageFleeCapabilities( false );
  679. //Try and find a hint-node first
  680. CHintCriteria hintCriteria;
  681. hintCriteria.SetHintType( HINT_ANTLION_THUMPER_FLEE_POINT );
  682. hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );
  683. hintCriteria.AddIncludePosition( WorldSpaceCenter(), 2500 );
  684. CAI_Hint *pHint = CAI_HintManager::FindHint( WorldSpaceCenter(), hintCriteria );
  685. Vector vecFleeGoal;
  686. Vector vecSoundPos = pSound->GetSoundOrigin();
  687. // Put the sound location on the same plane as the antlion.
  688. vecSoundPos.z = GetAbsOrigin().z;
  689. Vector vecFleeDir = GetAbsOrigin() - vecSoundPos;
  690. VectorNormalize( vecFleeDir );
  691. if ( pHint != NULL )
  692. {
  693. // Get our goal position
  694. pHint->GetPosition( this, &vecFleeGoal );
  695. // Find a route to that position
  696. AI_NavGoal_t goal( vecFleeGoal, (Activity) ACT_ANTLION_RUN_AGITATED, 128, AIN_DEF_FLAGS );
  697. if ( GetNavigator()->SetGoal( goal ) )
  698. {
  699. pHint->Lock( this );
  700. pHint->Unlock( 2.0f );
  701. GetNavigator()->SetArrivalActivity( (Activity) ACT_ANTLION_DISTRACT_ARRIVED );
  702. GetNavigator()->SetArrivalDirection( -vecFleeDir );
  703. ManageFleeCapabilities( true );
  704. return true;
  705. }
  706. }
  707. //Make us offset this a little at least
  708. float flFleeYaw = VecToYaw( vecFleeDir ) + random->RandomInt( -20, 20 );
  709. vecFleeDir = UTIL_YawToVector( flFleeYaw );
  710. // Move us to the outer radius of the noise (with some randomness)
  711. vecFleeGoal = vecSoundPos + vecFleeDir * ( pSound->Volume() + random->RandomInt( 32, 64 ) );
  712. // Find a route to that position
  713. AI_NavGoal_t goal( vecFleeGoal + Vector( 0, 0, 8 ), (Activity) ACT_ANTLION_RUN_AGITATED, 512, AIN_DEF_FLAGS );
  714. if ( GetNavigator()->SetGoal( goal ) )
  715. {
  716. GetNavigator()->SetArrivalActivity( (Activity) ACT_ANTLION_DISTRACT_ARRIVED );
  717. GetNavigator()->SetArrivalDirection( -vecFleeDir );
  718. ManageFleeCapabilities( true );
  719. return true;
  720. }
  721. ManageFleeCapabilities( true );
  722. return false;
  723. }
  724. //-----------------------------------------------------------------------------
  725. // Purpose: Returns whether the enemy has been seen within the time period supplied
  726. // Input : flTime - Timespan we consider
  727. // Output : Returns true on success, false on failure.
  728. //-----------------------------------------------------------------------------
  729. bool CNPC_Antlion::SeenEnemyWithinTime( float flTime )
  730. {
  731. float flLastSeenTime = GetEnemies()->LastTimeSeen( GetEnemy() );
  732. return ( flLastSeenTime != 0.0f && ( gpGlobals->curtime - flLastSeenTime ) < flTime );
  733. }
  734. //-----------------------------------------------------------------------------
  735. // Purpose: Test whether this antlion can hit the target
  736. //-----------------------------------------------------------------------------
  737. bool CNPC_Antlion::InnateWeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions )
  738. {
  739. if ( GetNextAttack() > gpGlobals->curtime )
  740. return false;
  741. // If we can see the enemy, or we've seen them in the last few seconds just try to lob in there
  742. if ( SeenEnemyWithinTime( 3.0f ) )
  743. {
  744. Vector vSpitPos;
  745. GetAttachment( "mouth", vSpitPos );
  746. return GetSpitVector( vSpitPos, targetPos, &m_vecSaveSpitVelocity );
  747. }
  748. return BaseClass::InnateWeaponLOSCondition( ownerPos, targetPos, bSetConditions );
  749. }
  750. //
  751. // FIXME: Create this in a better fashion!
  752. //
  753. Vector VecCheckThrowTolerance( CBaseEntity *pEdict, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flTolerance )
  754. {
  755. flSpeed = MAX( 1.0f, flSpeed );
  756. float flGravity = GetCurrentGravity();
  757. Vector vecGrenadeVel = (vecSpot2 - vecSpot1);
  758. // throw at a constant time
  759. float time = vecGrenadeVel.Length( ) / flSpeed;
  760. vecGrenadeVel = vecGrenadeVel * (1.0 / time);
  761. // adjust upward toss to compensate for gravity loss
  762. vecGrenadeVel.z += flGravity * time * 0.5;
  763. Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
  764. vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);
  765. trace_t tr;
  766. UTIL_TraceLine( vecSpot1, vecApex, MASK_SOLID, pEdict, COLLISION_GROUP_NONE, &tr );
  767. if (tr.fraction != 1.0)
  768. {
  769. // fail!
  770. if ( g_debug_antlion_worker.GetBool() )
  771. {
  772. NDebugOverlay::Line( vecSpot1, vecApex, 255, 0, 0, true, 5.0 );
  773. }
  774. return vec3_origin;
  775. }
  776. if ( g_debug_antlion_worker.GetBool() )
  777. {
  778. NDebugOverlay::Line( vecSpot1, vecApex, 0, 255, 0, true, 5.0 );
  779. }
  780. UTIL_TraceLine( vecApex, vecSpot2, MASK_SOLID_BRUSHONLY, pEdict, COLLISION_GROUP_NONE, &tr );
  781. if ( tr.fraction != 1.0 )
  782. {
  783. bool bFail = true;
  784. // Didn't make it all the way there, but check if we're within our tolerance range
  785. if ( flTolerance > 0.0f )
  786. {
  787. float flNearness = ( tr.endpos - vecSpot2 ).LengthSqr();
  788. if ( flNearness < Square( flTolerance ) )
  789. {
  790. if ( g_debug_antlion_worker.GetBool() )
  791. {
  792. NDebugOverlay::Sphere( tr.endpos, vec3_angle, flTolerance, 0, 255, 0, 0, true, 5.0 );
  793. }
  794. bFail = false;
  795. }
  796. }
  797. if ( bFail )
  798. {
  799. if ( g_debug_antlion_worker.GetBool() )
  800. {
  801. NDebugOverlay::Line( vecApex, vecSpot2, 255, 0, 0, true, 5.0 );
  802. NDebugOverlay::Sphere( tr.endpos, vec3_angle, flTolerance, 255, 0, 0, 0, true, 5.0 );
  803. }
  804. return vec3_origin;
  805. }
  806. }
  807. if ( g_debug_antlion_worker.GetBool() )
  808. {
  809. NDebugOverlay::Line( vecApex, vecSpot2, 0, 255, 0, true, 5.0 );
  810. }
  811. return vecGrenadeVel;
  812. }
  813. //-----------------------------------------------------------------------------
  814. // Purpose: Get a toss direction that will properly lob spit to hit a target
  815. // Input : &vecStartPos - Where the spit will start from
  816. // &vecTarget - Where the spit is meant to land
  817. // *vecOut - The resulting vector to lob the spit
  818. // Output : Returns true on success, false on failure.
  819. //-----------------------------------------------------------------------------
  820. bool CNPC_Antlion::GetSpitVector( const Vector &vecStartPos, const Vector &vecTarget, Vector *vecOut )
  821. {
  822. // antlion workers exist only in episodic.
  823. #if HL2_EPISODIC
  824. // Try the most direct route
  825. Vector vecToss = VecCheckThrowTolerance( this, vecStartPos, vecTarget, sk_antlion_worker_spit_speed.GetFloat(), (10.0f*12.0f) );
  826. // If this failed then try a little faster (flattens the arc)
  827. if ( vecToss == vec3_origin )
  828. {
  829. vecToss = VecCheckThrowTolerance( this, vecStartPos, vecTarget, sk_antlion_worker_spit_speed.GetFloat() * 1.5f, (10.0f*12.0f) );
  830. if ( vecToss == vec3_origin )
  831. return false;
  832. }
  833. // Save out the result
  834. if ( vecOut )
  835. {
  836. *vecOut = vecToss;
  837. }
  838. return true;
  839. #else
  840. return false;
  841. #endif
  842. }
  843. //-----------------------------------------------------------------------------
  844. // Purpose:
  845. // Input : flDuration -
  846. //-----------------------------------------------------------------------------
  847. void CNPC_Antlion::DelaySquadAttack( float flDuration )
  848. {
  849. if ( GetSquad() )
  850. {
  851. // Reduce the duration by as much as 50% of the total time to make this less robotic
  852. float flAdjDuration = flDuration - random->RandomFloat( 0.0f, (flDuration*0.5f) );
  853. GetSquad()->BroadcastInteraction( g_interactionAntlionFiredAtTarget, (void *)&flAdjDuration, this );
  854. }
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose:
  858. // Input : *pEvent -
  859. //-----------------------------------------------------------------------------
  860. void CNPC_Antlion::HandleAnimEvent( animevent_t *pEvent )
  861. {
  862. #ifdef HL2_EPISODIC
  863. // Handle the spit event
  864. if ( pEvent->event == AE_ANTLION_WORKER_SPIT )
  865. {
  866. if ( GetEnemy() )
  867. {
  868. Vector vSpitPos;
  869. GetAttachment( "mouth", vSpitPos );
  870. Vector vTarget;
  871. // If our enemy is looking at us and far enough away, lead him
  872. if ( HasCondition( COND_ENEMY_FACING_ME ) && UTIL_DistApprox( GetAbsOrigin(), GetEnemy()->GetAbsOrigin() ) > (40*12) )
  873. {
  874. UTIL_PredictedPosition( GetEnemy(), 0.5f, &vTarget );
  875. vTarget.z = GetEnemy()->GetAbsOrigin().z;
  876. }
  877. else
  878. {
  879. // Otherwise he can't see us and he won't be able to dodge
  880. vTarget = GetEnemy()->BodyTarget( vSpitPos, true );
  881. }
  882. vTarget[2] += random->RandomFloat( 0.0f, 32.0f );
  883. // Try and spit at our target
  884. Vector vecToss;
  885. if ( GetSpitVector( vSpitPos, vTarget, &vecToss ) == false )
  886. {
  887. // Now try where they were
  888. if ( GetSpitVector( vSpitPos, m_vSavePosition, &vecToss ) == false )
  889. {
  890. // Failing that, just shoot with the old velocity we calculated initially!
  891. vecToss = m_vecSaveSpitVelocity;
  892. }
  893. }
  894. // Find what our vertical theta is to estimate the time we'll impact the ground
  895. Vector vecToTarget = ( vTarget - vSpitPos );
  896. VectorNormalize( vecToTarget );
  897. float flVelocity = VectorNormalize( vecToss );
  898. float flCosTheta = DotProduct( vecToTarget, vecToss );
  899. float flTime = (vSpitPos-vTarget).Length2D() / ( flVelocity * flCosTheta );
  900. // Emit a sound where this is going to hit so that targets get a chance to act correctly
  901. CSoundEnt::InsertSound( SOUND_DANGER, vTarget, (15*12), flTime, this );
  902. // Don't fire again until this volley would have hit the ground (with some lag behind it)
  903. SetNextAttack( gpGlobals->curtime + flTime + random->RandomFloat( 0.5f, 2.0f ) );
  904. // Tell any squadmates not to fire for some portion of the time this volley will be in the air (except on hard)
  905. if ( g_pGameRules->IsSkillLevel( SKILL_HARD ) == false )
  906. DelaySquadAttack( flTime );
  907. for ( int i = 0; i < 6; i++ )
  908. {
  909. CGrenadeSpit *pGrenade = (CGrenadeSpit*) CreateEntityByName( "grenade_spit" );
  910. pGrenade->SetAbsOrigin( vSpitPos );
  911. pGrenade->SetAbsAngles( vec3_angle );
  912. DispatchSpawn( pGrenade );
  913. pGrenade->SetThrower( this );
  914. pGrenade->SetOwnerEntity( this );
  915. if ( i == 0 )
  916. {
  917. pGrenade->SetSpitSize( SPIT_LARGE );
  918. pGrenade->SetAbsVelocity( vecToss * flVelocity );
  919. }
  920. else
  921. {
  922. pGrenade->SetAbsVelocity( ( vecToss + RandomVector( -0.035f, 0.035f ) ) * flVelocity );
  923. pGrenade->SetSpitSize( random->RandomInt( SPIT_SMALL, SPIT_MEDIUM ) );
  924. }
  925. // Tumble through the air
  926. pGrenade->SetLocalAngularVelocity(
  927. QAngle( random->RandomFloat( -250, -500 ),
  928. random->RandomFloat( -250, -500 ),
  929. random->RandomFloat( -250, -500 ) ) );
  930. }
  931. for ( int i = 0; i < 8; i++ )
  932. {
  933. DispatchParticleEffect( "blood_impact_yellow_01", vSpitPos + RandomVector( -12.0f, 12.0f ), RandomAngle( 0, 360 ) );
  934. }
  935. EmitSound( "NPC_Antlion.PoisonShoot" );
  936. }
  937. return;
  938. }
  939. if ( pEvent->event == AE_ANTLION_WORKER_DONT_EXPLODE )
  940. {
  941. m_bDontExplode = true;
  942. return;
  943. }
  944. #endif // HL2_EPISODIC
  945. if ( pEvent->event == AE_ANTLION_WALK_FOOTSTEP )
  946. {
  947. MakeAIFootstepSound( 240.0f );
  948. EmitSound( "NPC_Antlion.Footstep", m_hFootstep, pEvent->eventtime );
  949. return;
  950. }
  951. if ( pEvent->event == AE_ANTLION_MELEE_HIT1 )
  952. {
  953. QAngle qa( 20.0f, 0.0f, -12.0f );
  954. Vector vec( -250.0f, 1.0f, 1.0f );
  955. MeleeAttack( ANTLION_MELEE1_RANGE, sk_antlion_swipe_damage.GetFloat(), qa, vec );
  956. return;
  957. }
  958. if ( pEvent->event == AE_ANTLION_MELEE_HIT2 )
  959. {
  960. QAngle qa( 20.0f, 0.0f, 0.0f );
  961. Vector vec( -350.0f, 1.0f, 1.0f );
  962. MeleeAttack( ANTLION_MELEE1_RANGE, sk_antlion_swipe_damage.GetFloat(), qa, vec );
  963. return;
  964. }
  965. if ( pEvent->event == AE_ANTLION_MELEE_POUNCE )
  966. {
  967. QAngle qa( 4.0f, 0.0f, 0.0f );
  968. Vector vec( -250.0f, 1.0f, 1.0f );
  969. MeleeAttack( ANTLION_MELEE2_RANGE, sk_antlion_swipe_damage.GetFloat(), qa, vec );
  970. return;
  971. }
  972. if ( pEvent->event == AE_ANTLION_OPEN_WINGS )
  973. {
  974. SetWings( true );
  975. return;
  976. }
  977. if ( pEvent->event == AE_ANTLION_CLOSE_WINGS )
  978. {
  979. SetWings( false );
  980. return;
  981. }
  982. if ( pEvent->event == AE_ANTLION_VANISH )
  983. {
  984. AddSolidFlags( FSOLID_NOT_SOLID );
  985. m_takedamage = DAMAGE_NO;
  986. AddEffects( EF_NODRAW );
  987. SetWings( false );
  988. return;
  989. }
  990. if ( pEvent->event == AE_ANTLION_BURROW_IN )
  991. {
  992. //Burrowing sound
  993. EmitSound( "NPC_Antlion.BurrowIn" );
  994. //Shake the screen
  995. UTIL_ScreenShake( GetAbsOrigin(), 0.5f, 80.0f, 1.0f, 256.0f, SHAKE_START );
  996. //Throw dust up
  997. CreateDust();
  998. if ( GetHintNode() )
  999. {
  1000. GetHintNode()->Unlock( 2.0f );
  1001. }
  1002. return;
  1003. }
  1004. if ( pEvent->event == AE_ANTLION_BURROW_OUT )
  1005. {
  1006. EmitSound( "NPC_Antlion.BurrowOut" );
  1007. //Shake the screen
  1008. UTIL_ScreenShake( GetAbsOrigin(), 0.5f, 80.0f, 1.0f, 256.0f, SHAKE_START );
  1009. //Throw dust up
  1010. CreateDust();
  1011. RemoveEffects( EF_NODRAW );
  1012. RemoveFlag( FL_NOTARGET );
  1013. return;
  1014. }
  1015. if ( pEvent->event == AE_ANTLION_FOOTSTEP_SOFT )
  1016. {
  1017. EmitSound( "NPC_Antlion.FootstepSoft", pEvent->eventtime );
  1018. return;
  1019. }
  1020. if ( pEvent->event == AE_ANTLION_FOOTSTEP_HEAVY )
  1021. {
  1022. EmitSound( "NPC_Antlion.FootstepHeavy", pEvent->eventtime );
  1023. return;
  1024. }
  1025. if ( pEvent->event == AE_ANTLION_MELEE1_SOUND )
  1026. {
  1027. EmitSound( "NPC_Antlion.MeleeAttackSingle" );
  1028. return;
  1029. }
  1030. if ( pEvent->event == AE_ANTLION_MELEE2_SOUND )
  1031. {
  1032. EmitSound( "NPC_Antlion.MeleeAttackDouble" );
  1033. return;
  1034. }
  1035. if ( pEvent->event == AE_ANTLION_START_JUMP )
  1036. {
  1037. StartJump();
  1038. return;
  1039. }
  1040. // antlion worker events
  1041. #if HL2_EPISODIC
  1042. if ( pEvent->event == AE_ANTLION_WORKER_EXPLODE_SCREAM )
  1043. {
  1044. if ( GetWaterLevel() < 2 )
  1045. {
  1046. EmitSound( "NPC_Antlion.PoisonBurstScream" );
  1047. }
  1048. else
  1049. {
  1050. EmitSound( "NPC_Antlion.PoisonBurstScreamSubmerged" );
  1051. }
  1052. return;
  1053. }
  1054. if ( pEvent->event == AE_ANTLION_WORKER_EXPLODE_WARN )
  1055. {
  1056. CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, GetAbsOrigin(), sk_antlion_worker_burst_radius.GetFloat(), 0.5f, this );
  1057. return;
  1058. }
  1059. if ( pEvent->event == AE_ANTLION_WORKER_EXPLODE )
  1060. {
  1061. CTakeDamageInfo info( this, this, sk_antlion_worker_burst_damage.GetFloat(), DMG_BLAST_SURFACE | ( ANTLION_WORKER_BURST_IS_POISONOUS() ? DMG_POISON : DMG_ACID ) );
  1062. Event_Gibbed( info );
  1063. return;
  1064. }
  1065. #endif
  1066. BaseClass::HandleAnimEvent( pEvent );
  1067. }
  1068. bool CNPC_Antlion::IsUnusableNode(int iNodeID, CAI_Hint *pHint)
  1069. {
  1070. bool iBaseReturn = BaseClass::IsUnusableNode( iNodeID, pHint );
  1071. if ( g_test_new_antlion_jump.GetBool() == 0 )
  1072. return iBaseReturn;
  1073. CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( iNodeID );
  1074. if ( pNode )
  1075. {
  1076. if ( pNode->IsLocked() )
  1077. return true;
  1078. }
  1079. return iBaseReturn;
  1080. }
  1081. void CNPC_Antlion::LockJumpNode( void )
  1082. {
  1083. if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false )
  1084. return;
  1085. if ( GetNavigator()->GetPath() == NULL )
  1086. return;
  1087. if ( g_test_new_antlion_jump.GetBool() == false )
  1088. return;
  1089. AI_Waypoint_t *pWaypoint = GetNavigator()->GetPath()->GetCurWaypoint();
  1090. while ( pWaypoint )
  1091. {
  1092. AI_Waypoint_t *pNextWaypoint = pWaypoint->GetNext();
  1093. if ( pNextWaypoint && pNextWaypoint->NavType() == NAV_JUMP && pWaypoint->iNodeID != NO_NODE )
  1094. {
  1095. CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( pWaypoint->iNodeID );
  1096. if ( pNode )
  1097. {
  1098. //NDebugOverlay::Box( pNode->GetOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 255, 0, 0, 0, 2 );
  1099. pNode->Lock( 0.5f );
  1100. break;
  1101. }
  1102. }
  1103. else
  1104. {
  1105. pWaypoint = pWaypoint->GetNext();
  1106. }
  1107. }
  1108. }
  1109. //-----------------------------------------------------------------------------
  1110. //-----------------------------------------------------------------------------
  1111. bool CNPC_Antlion::OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  1112. {
  1113. bool iBaseReturn = BaseClass::OnObstructionPreSteer( pMoveGoal, distClear, pResult );
  1114. if ( g_test_new_antlion_jump.GetBool() == false )
  1115. return iBaseReturn;
  1116. if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false )
  1117. return iBaseReturn;
  1118. CAI_BaseNPC *pBlocker = pMoveGoal->directTrace.pObstruction->MyNPCPointer();
  1119. if ( pBlocker && pBlocker->Classify() == CLASS_ANTLION )
  1120. {
  1121. // HACKHACK
  1122. CNPC_Antlion *pAntlion = dynamic_cast< CNPC_Antlion * > ( pBlocker );
  1123. if ( pAntlion )
  1124. {
  1125. if ( pAntlion->AllowedToBePushed() == true && GetEnemy() == NULL )
  1126. {
  1127. //NDebugOverlay::Box( pAntlion->GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 2 );
  1128. pAntlion->GetMotor()->SetIdealYawToTarget( WorldSpaceCenter() );
  1129. pAntlion->SetSchedule( SCHED_MOVE_AWAY );
  1130. pAntlion->m_flNextJumpPushTime = gpGlobals->curtime + 2.0f;
  1131. }
  1132. }
  1133. }
  1134. return iBaseReturn;
  1135. }
  1136. bool NPC_Antlion_IsAntlion( CBaseEntity *pEntity )
  1137. {
  1138. CNPC_Antlion *pAntlion = dynamic_cast<CNPC_Antlion *>(pEntity);
  1139. return pAntlion ? true : false;
  1140. }
  1141. class CTraceFilterAntlion : public CTraceFilterEntitiesOnly
  1142. {
  1143. public:
  1144. CTraceFilterAntlion( const CBaseEntity *pEntity ) { m_pIgnore = pEntity; }
  1145. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  1146. {
  1147. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  1148. if ( m_pIgnore == pEntity )
  1149. return false;
  1150. if ( pEntity->IsNPC() == false )
  1151. return false;
  1152. if ( NPC_Antlion_IsAntlion( pEntity ) )
  1153. return true;
  1154. return false;
  1155. }
  1156. private:
  1157. const CBaseEntity *m_pIgnore;
  1158. };
  1159. //-----------------------------------------------------------------------------
  1160. // Purpose:
  1161. //-----------------------------------------------------------------------------
  1162. void CNPC_Antlion::StartTask( const Task_t *pTask )
  1163. {
  1164. switch ( pTask->iTask )
  1165. {
  1166. case TASK_ANTLION_FIND_COVER_FROM_SAVEPOSITION:
  1167. {
  1168. Vector coverPos;
  1169. if ( GetTacticalServices()->FindCoverPos( m_vSavePosition, EyePosition(), 0, CoverRadius(), &coverPos ) )
  1170. {
  1171. AI_NavGoal_t goal(coverPos, ACT_RUN, AIN_HULL_TOLERANCE);
  1172. GetNavigator()->SetGoal( goal );
  1173. m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  1174. }
  1175. else
  1176. {
  1177. // no coverwhatsoever.
  1178. TaskFail(FAIL_NO_COVER);
  1179. }
  1180. }
  1181. break;
  1182. case TASK_ANNOUNCE_ATTACK:
  1183. {
  1184. EmitSound( "NPC_Antlion.MeleeAttackSingle" );
  1185. TaskComplete();
  1186. break;
  1187. }
  1188. case TASK_ANTLION_FACE_JUMP:
  1189. break;
  1190. case TASK_ANTLION_DROWN:
  1191. {
  1192. // Set the gravity really low here! Sink slowly
  1193. SetGravity( 0 );
  1194. SetAbsVelocity( vec3_origin );
  1195. m_flTimeDrownSplash = gpGlobals->curtime + random->RandomFloat( 0, 0.5 );
  1196. m_flTimeDrown = gpGlobals->curtime + 4;
  1197. break;
  1198. }
  1199. case TASK_ANTLION_REACH_FIGHT_GOAL:
  1200. m_OnReachFightGoal.FireOutput( this, this );
  1201. TaskComplete();
  1202. break;
  1203. case TASK_ANTLION_DISMOUNT_NPC:
  1204. {
  1205. CBaseEntity *pGroundEnt = GetGroundEntity();
  1206. if( pGroundEnt != NULL )
  1207. {
  1208. trace_t trace;
  1209. CTraceFilterAntlion traceFilter( this );
  1210. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin(), WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, &traceFilter, &trace );
  1211. if ( trace.m_pEnt )
  1212. {
  1213. m_bDontExplode = true;
  1214. OnTakeDamage( CTakeDamageInfo( this, this, m_iHealth+1, DMG_GENERIC ) );
  1215. return;
  1216. }
  1217. // Jump behind the other NPC so I don't block their path.
  1218. Vector vecJumpDir;
  1219. pGroundEnt->GetVectors( &vecJumpDir, NULL, NULL );
  1220. SetGroundEntity( NULL );
  1221. // Bump up
  1222. UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0, 0 , 1 ) );
  1223. SetAbsVelocity( vecJumpDir * -200 + Vector( 0, 0, 100 ) );
  1224. // Doing ACT_RESET first assures they play the animation, even when in transition
  1225. ResetActivity();
  1226. SetActivity( (Activity) ACT_ANTLION_FLIP );
  1227. }
  1228. else
  1229. {
  1230. // Dead or gone now
  1231. TaskComplete();
  1232. }
  1233. }
  1234. break;
  1235. case TASK_ANTLION_FACE_BUGBAIT:
  1236. //Must have a saved sound
  1237. //FIXME: This isn't assured to be still pointing to the right place, need to protect this
  1238. if ( !m_bHasHeardSound )
  1239. {
  1240. TaskFail( "No remembered bug bait sound to run to!" );
  1241. return;
  1242. }
  1243. GetMotor()->SetIdealYawToTargetAndUpdate( m_vecHeardSound );
  1244. SetTurnActivity();
  1245. break;
  1246. case TASK_ANTLION_GET_PATH_TO_BUGBAIT:
  1247. {
  1248. //Must have a saved sound
  1249. //FIXME: This isn't assured to be still pointing to the right place, need to protect this
  1250. if ( !m_bHasHeardSound )
  1251. {
  1252. TaskFail( "No remembered bug bait sound to run to!" );
  1253. return;
  1254. }
  1255. Vector goalPos;
  1256. // Find the position to chase to
  1257. if ( FindChasePosition( m_vecHeardSound, goalPos ) )
  1258. {
  1259. AI_NavGoal_t goal( goalPos, (Activity) ACT_ANTLION_RUN_AGITATED, ANTLION_BUGBAIT_NAV_TOLERANCE );
  1260. //Try to run directly there
  1261. if ( GetNavigator()->SetGoal( goal, AIN_DISCARD_IF_FAIL ) == false )
  1262. {
  1263. //Try and get as close as possible otherwise
  1264. AI_NavGoal_t nearGoal( GOALTYPE_LOCATION_NEAREST_NODE, goalPos, (Activity) ACT_ANTLION_RUN_AGITATED, ANTLION_BUGBAIT_NAV_TOLERANCE );
  1265. if ( GetNavigator()->SetGoal( nearGoal, AIN_CLEAR_PREVIOUS_STATE ) )
  1266. {
  1267. //FIXME: HACK! The internal pathfinding is setting this without our consent, so override it!
  1268. ClearCondition( COND_TASK_FAILED );
  1269. LockJumpNode();
  1270. TaskComplete();
  1271. return;
  1272. }
  1273. else
  1274. {
  1275. TaskFail( "Antlion failed to find path to bugbait position\n" );
  1276. return;
  1277. }
  1278. }
  1279. else
  1280. {
  1281. LockJumpNode();
  1282. TaskComplete();
  1283. return;
  1284. }
  1285. }
  1286. TaskFail( "Antlion failed to find path to bugbait position\n" );
  1287. break;
  1288. }
  1289. case TASK_ANTLION_WAIT_FOR_TRIGGER:
  1290. m_flIdleDelay = gpGlobals->curtime + 1.0f;
  1291. break;
  1292. case TASK_ANTLION_JUMP:
  1293. if ( CheckLanding() )
  1294. {
  1295. TaskComplete();
  1296. }
  1297. break;
  1298. case TASK_ANTLION_CHECK_FOR_UNBORROW:
  1299. m_iUnBurrowAttempts = 0;
  1300. if ( ValidBurrowPoint( GetAbsOrigin() ) )
  1301. {
  1302. m_spawnflags &= ~SF_NPC_GAG;
  1303. RemoveSolidFlags( FSOLID_NOT_SOLID );
  1304. TaskComplete();
  1305. }
  1306. break;
  1307. case TASK_ANTLION_BURROW_WAIT:
  1308. if ( pTask->flTaskData == 1.0f )
  1309. {
  1310. //Set our next burrow time
  1311. m_flBurrowTime = gpGlobals->curtime + random->RandomFloat( 1, 6 );
  1312. }
  1313. break;
  1314. case TASK_ANTLION_FIND_BURROW_IN_POINT:
  1315. if ( FindBurrow( GetAbsOrigin(), pTask->flTaskData, ANTLION_BURROW_IN ) == false )
  1316. {
  1317. TaskFail( "TASK_ANTLION_FIND_BURROW_IN_POINT: Unable to find burrow in position\n" );
  1318. }
  1319. else
  1320. {
  1321. TaskComplete();
  1322. }
  1323. break;
  1324. case TASK_ANTLION_FIND_BURROW_OUT_POINT:
  1325. if ( FindBurrow( GetAbsOrigin(), pTask->flTaskData, ANTLION_BURROW_OUT ) == false )
  1326. {
  1327. TaskFail( "TASK_ANTLION_FIND_BURROW_OUT_POINT: Unable to find burrow out position\n" );
  1328. }
  1329. else
  1330. {
  1331. TaskComplete();
  1332. }
  1333. break;
  1334. case TASK_ANTLION_BURROW:
  1335. Burrow();
  1336. TaskComplete();
  1337. break;
  1338. case TASK_ANTLION_UNBURROW:
  1339. Unburrow();
  1340. TaskComplete();
  1341. break;
  1342. case TASK_ANTLION_VANISH:
  1343. AddEffects( EF_NODRAW );
  1344. AddFlag( FL_NOTARGET );
  1345. m_spawnflags |= SF_NPC_GAG;
  1346. // If the task parameter is non-zero, remove us when we vanish
  1347. if ( pTask->flTaskData )
  1348. {
  1349. CBaseEntity *pOwner = GetOwnerEntity();
  1350. if( pOwner != NULL )
  1351. {
  1352. pOwner->DeathNotice( this );
  1353. SetOwnerEntity( NULL );
  1354. }
  1355. // NOTE: We can't UTIL_Remove here, because we're in the middle of running our AI, and
  1356. // we'll crash later in the bowels of the AI. Remove ourselves next frame.
  1357. SetThink( &CNPC_Antlion::SUB_Remove );
  1358. SetNextThink( gpGlobals->curtime + 0.1 );
  1359. }
  1360. TaskComplete();
  1361. break;
  1362. case TASK_ANTLION_GET_THUMPER_ESCAPE_PATH:
  1363. {
  1364. if ( GetPathToSoundFleePoint( SOUND_THUMPER ) )
  1365. {
  1366. TaskComplete();
  1367. }
  1368. else
  1369. {
  1370. TaskFail( FAIL_NO_REACHABLE_NODE );
  1371. }
  1372. }
  1373. break;
  1374. case TASK_ANTLION_GET_PHYSICS_DANGER_ESCAPE_PATH:
  1375. {
  1376. if ( GetPathToSoundFleePoint( SOUND_PHYSICS_DANGER ) )
  1377. {
  1378. TaskComplete();
  1379. }
  1380. else
  1381. {
  1382. TaskFail( FAIL_NO_REACHABLE_NODE );
  1383. }
  1384. }
  1385. break;
  1386. default:
  1387. BaseClass::StartTask( pTask );
  1388. break;
  1389. }
  1390. }
  1391. //-----------------------------------------------------------------------------
  1392. // Purpose:
  1393. // Input : *pTask -
  1394. //-----------------------------------------------------------------------------
  1395. void CNPC_Antlion::RunTask( const Task_t *pTask )
  1396. {
  1397. // some state that needs be set each frame
  1398. #if HL2_EPISODIC
  1399. if ( GetFlags() & FL_ONGROUND )
  1400. {
  1401. m_bHasDoneAirAttack = false;
  1402. }
  1403. #endif
  1404. switch ( pTask->iTask )
  1405. {
  1406. case TASK_ANTLION_FACE_JUMP:
  1407. {
  1408. Vector jumpDir = m_vecSavedJump;
  1409. VectorNormalize( jumpDir );
  1410. QAngle jumpAngles;
  1411. VectorAngles( jumpDir, jumpAngles );
  1412. GetMotor()->SetIdealYawAndUpdate( jumpAngles[YAW], AI_KEEP_YAW_SPEED );
  1413. SetTurnActivity();
  1414. if ( GetMotor()->DeltaIdealYaw() < 2 )
  1415. {
  1416. TaskComplete();
  1417. }
  1418. }
  1419. break;
  1420. case TASK_ANTLION_DROWN:
  1421. {
  1422. if ( gpGlobals->curtime > m_flTimeDrownSplash )
  1423. {
  1424. float flWaterZ = UTIL_FindWaterSurface( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z + NAI_Hull::Maxs( GetHullType() ).z );
  1425. CEffectData data;
  1426. data.m_fFlags = 0;
  1427. data.m_vOrigin = GetAbsOrigin();
  1428. data.m_vOrigin.z = flWaterZ;
  1429. data.m_vNormal = Vector( 0, 0, 1 );
  1430. data.m_flScale = random->RandomFloat( 12.0, 16.0 );
  1431. DispatchEffect( "watersplash", data );
  1432. m_flTimeDrownSplash = gpGlobals->curtime + random->RandomFloat( 0.5, 2.5 );
  1433. }
  1434. if ( gpGlobals->curtime > m_flTimeDrown )
  1435. {
  1436. m_bDontExplode = true;
  1437. OnTakeDamage( CTakeDamageInfo( this, this, m_iHealth+1, DMG_DROWN ) );
  1438. TaskComplete();
  1439. }
  1440. break;
  1441. }
  1442. case TASK_ANTLION_REACH_FIGHT_GOAL:
  1443. break;
  1444. case TASK_ANTLION_DISMOUNT_NPC:
  1445. if ( GetFlags() & FL_ONGROUND )
  1446. {
  1447. CBaseEntity *pGroundEnt = GetGroundEntity();
  1448. if ( ( pGroundEnt != NULL ) && ( ( pGroundEnt->MyNPCPointer() != NULL ) || pGroundEnt->GetSolidFlags() & FSOLID_NOT_STANDABLE ) )
  1449. {
  1450. // Jump behind the other NPC so I don't block their path.
  1451. Vector vecJumpDir;
  1452. pGroundEnt->GetVectors( &vecJumpDir, NULL, NULL );
  1453. SetGroundEntity( NULL );
  1454. // Bump up
  1455. UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0, 0 , 1 ) );
  1456. Vector vecRandom = RandomVector( -250.0f, 250.0f );
  1457. vecRandom[2] = random->RandomFloat( 100.0f, 200.0f );
  1458. SetAbsVelocity( vecRandom );
  1459. // Doing ACT_RESET first assures they play the animation, even when in transition
  1460. ResetActivity();
  1461. SetActivity( (Activity) ACT_ANTLION_FLIP );
  1462. }
  1463. else if ( IsActivityFinished() )
  1464. {
  1465. TaskComplete();
  1466. }
  1467. }
  1468. break;
  1469. case TASK_ANTLION_FACE_BUGBAIT:
  1470. //Must have a saved sound
  1471. //FIXME: This isn't assured to be still pointing to the right place, need to protect this
  1472. if ( !m_bHasHeardSound )
  1473. {
  1474. TaskFail( "No remembered bug bait sound to run to!" );
  1475. return;
  1476. }
  1477. GetMotor()->SetIdealYawToTargetAndUpdate( m_vecHeardSound );
  1478. if ( FacingIdeal() )
  1479. {
  1480. TaskComplete();
  1481. }
  1482. break;
  1483. case TASK_ANTLION_WAIT_FOR_TRIGGER:
  1484. if ( ( m_flIdleDelay > gpGlobals->curtime ) || GetEntityName() != NULL_STRING )
  1485. return;
  1486. TaskComplete();
  1487. break;
  1488. case TASK_ANTLION_JUMP:
  1489. if ( CheckLanding() )
  1490. {
  1491. TaskComplete();
  1492. }
  1493. break;
  1494. case TASK_ANTLION_CHECK_FOR_UNBORROW:
  1495. //Must wait for our next check time
  1496. if ( m_flBurrowTime > gpGlobals->curtime )
  1497. return;
  1498. //See if we can pop up
  1499. if ( ValidBurrowPoint( GetAbsOrigin() ) )
  1500. {
  1501. m_spawnflags &= ~SF_NPC_GAG;
  1502. RemoveSolidFlags( FSOLID_NOT_SOLID );
  1503. TaskComplete();
  1504. return;
  1505. }
  1506. //Try again in a couple of seconds
  1507. m_flBurrowTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 1.0f );
  1508. m_iUnBurrowAttempts++;
  1509. // Robin: If we fail 10 times, kill ourself.
  1510. // This deals with issues where the game relies out antlion spawners
  1511. // firing their OnBlocked output, but the spawner isn't attempting to
  1512. // spawn because it has multiple live children lying around stuck under
  1513. // physics props unable to unburrow.
  1514. if ( m_iUnBurrowAttempts >= 10 )
  1515. {
  1516. m_bDontExplode = true;
  1517. m_takedamage = DAMAGE_YES;
  1518. OnTakeDamage( CTakeDamageInfo( this, this, m_iHealth+1, DMG_GENERIC ) );
  1519. }
  1520. break;
  1521. case TASK_ANTLION_BURROW_WAIT:
  1522. //See if enough time has passed
  1523. if ( m_flBurrowTime < gpGlobals->curtime )
  1524. {
  1525. TaskComplete();
  1526. }
  1527. break;
  1528. default:
  1529. BaseClass::RunTask( pTask );
  1530. break;
  1531. }
  1532. }
  1533. bool CNPC_Antlion::AllowedToBePushed( void )
  1534. {
  1535. if ( IsCurSchedule( SCHED_ANTLION_BURROW_WAIT ) ||
  1536. IsCurSchedule(SCHED_ANTLION_BURROW_IN) ||
  1537. IsCurSchedule(SCHED_ANTLION_BURROW_OUT) ||
  1538. IsCurSchedule(SCHED_ANTLION_BURROW_AWAY ) ||
  1539. IsCurSchedule( SCHED_ANTLION_RUN_TO_FIGHT_GOAL ) )
  1540. return false;
  1541. if ( IsRunningDynamicInteraction() )
  1542. return false;
  1543. if ( IsMoving() == false && IsCurSchedule( SCHED_ANTLION_FLIP ) == false
  1544. && GetNavType() != NAV_JUMP && m_flNextJumpPushTime <= gpGlobals->curtime )
  1545. {
  1546. return true;
  1547. }
  1548. return false;
  1549. }
  1550. //-----------------------------------------------------------------------------
  1551. // Purpose: Returns true if a reasonable jumping distance
  1552. // Input :
  1553. // Output :
  1554. //-----------------------------------------------------------------------------
  1555. bool CNPC_Antlion::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos ) const
  1556. {
  1557. const float MAX_JUMP_RISE = 512;
  1558. const float MAX_JUMP_DROP = 512;
  1559. const float MAX_JUMP_DISTANCE = 1024;
  1560. const float MIN_JUMP_DISTANCE = 128;
  1561. if ( CAntlionRepellant::IsPositionRepellantFree( endPos ) == false )
  1562. return false;
  1563. //Adrian: Don't try to jump if my destination is right next to me.
  1564. if ( ( endPos - GetAbsOrigin()).Length() < MIN_JUMP_DISTANCE )
  1565. return false;
  1566. if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) && g_test_new_antlion_jump.GetBool() == true )
  1567. {
  1568. trace_t tr;
  1569. AI_TraceHull( endPos, endPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  1570. if ( tr.m_pEnt )
  1571. {
  1572. CAI_BaseNPC *pBlocker = tr.m_pEnt->MyNPCPointer();
  1573. if ( pBlocker && pBlocker->Classify() == CLASS_ANTLION )
  1574. {
  1575. // HACKHACK
  1576. CNPC_Antlion *pAntlion = dynamic_cast< CNPC_Antlion * > ( pBlocker );
  1577. if ( pAntlion )
  1578. {
  1579. if ( pAntlion->AllowedToBePushed() == true )
  1580. {
  1581. // NDebugOverlay::Line( GetAbsOrigin(), endPos, 255, 0, 0, 0, 2 );
  1582. // NDebugOverlay::Box( pAntlion->GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 0, 255, 0, 2 );
  1583. pAntlion->GetMotor()->SetIdealYawToTarget( endPos );
  1584. pAntlion->SetSchedule( SCHED_MOVE_AWAY );
  1585. pAntlion->m_flNextJumpPushTime = gpGlobals->curtime + 2.0f;
  1586. }
  1587. }
  1588. }
  1589. }
  1590. }
  1591. return BaseClass::IsJumpLegal( startPos, apex, endPos, MAX_JUMP_RISE, MAX_JUMP_DROP, MAX_JUMP_DISTANCE );
  1592. }
  1593. bool CNPC_Antlion::IsFirmlyOnGround( void )
  1594. {
  1595. if( !( GetFlags()&FL_ONGROUND ) )
  1596. return false;
  1597. trace_t tr;
  1598. float flHeight = fabs( GetHullMaxs().z - GetHullMins().z );
  1599. Vector vOrigin = GetAbsOrigin() + Vector( GetHullMins().x, GetHullMins().y, 0 );
  1600. // NDebugOverlay::Line( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5 ), 255, 0, 0, true, 5 );
  1601. UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5 ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  1602. if ( tr.fraction != 1.0f )
  1603. return true;
  1604. vOrigin = GetAbsOrigin() - Vector( GetHullMins().x, GetHullMins().y, 0 );
  1605. // NDebugOverlay::Line( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5 ), 255, 0, 0, true, 5 );
  1606. UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5 ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  1607. if ( tr.fraction != 1.0f )
  1608. return true;
  1609. vOrigin = GetAbsOrigin() + Vector( GetHullMins().x, -GetHullMins().y, 0 );
  1610. // NDebugOverlay::Line( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5 ), 255, 0, 0, true, 5 );
  1611. UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5 ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  1612. if ( tr.fraction != 1.0f )
  1613. return true;
  1614. vOrigin = GetAbsOrigin() + Vector( -GetHullMins().x, GetHullMins().y, 0 );
  1615. // NDebugOverlay::Line( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5 ), 255, 0, 0, true, 5 );
  1616. UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, flHeight * 0.5 ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  1617. if ( tr.fraction != 1.0f )
  1618. return true;
  1619. return false;
  1620. }
  1621. //-----------------------------------------------------------------------------
  1622. // Purpose:
  1623. //-----------------------------------------------------------------------------
  1624. int CNPC_Antlion::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  1625. {
  1626. if ( m_FollowBehavior.GetNumFailedFollowAttempts() >= 2 )
  1627. {
  1628. if( IsFirmlyOnGround() == false )
  1629. {
  1630. Vector vecJumpDir;
  1631. vecJumpDir.z = 0;
  1632. vecJumpDir.x = 0;
  1633. vecJumpDir.y = 0;
  1634. while( vecJumpDir.x == 0 && vecJumpDir.y == 0 )
  1635. {
  1636. vecJumpDir.x = random->RandomInt( -1, 1 );
  1637. vecJumpDir.y = random->RandomInt( -1, 1 );
  1638. }
  1639. vecJumpDir.NormalizeInPlace();
  1640. SetGroundEntity( NULL );
  1641. m_vecSavedJump = vecJumpDir * 512 + Vector( 0, 0, 256 );
  1642. m_bForcedStuckJump = true;
  1643. return SCHED_ANTLION_JUMP;
  1644. }
  1645. }
  1646. // Catch the LOF failure and choose another route to take
  1647. if ( failedSchedule == SCHED_ESTABLISH_LINE_OF_FIRE )
  1648. return SCHED_ANTLION_WORKER_FLANK_RANDOM;
  1649. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  1650. }
  1651. //-----------------------------------------------------------------------------
  1652. // Purpose:
  1653. // Output : Returns true on success, false on failure.
  1654. //-----------------------------------------------------------------------------
  1655. bool CNPC_Antlion::ShouldJump( void )
  1656. {
  1657. if ( GetEnemy() == NULL )
  1658. return false;
  1659. //Too soon to try to jump
  1660. if ( m_flJumpTime > gpGlobals->curtime )
  1661. return false;
  1662. // only jump if you're on the ground
  1663. if (!(GetFlags() & FL_ONGROUND) || GetNavType() == NAV_JUMP )
  1664. return false;
  1665. // Don't jump if I'm not allowed
  1666. if ( ( CapabilitiesGet() & bits_CAP_MOVE_JUMP ) == false )
  1667. return false;
  1668. Vector vEnemyForward, vForward;
  1669. GetEnemy()->GetVectors( &vEnemyForward, NULL, NULL );
  1670. GetVectors( &vForward, NULL, NULL );
  1671. float flDot = DotProduct( vForward, vEnemyForward );
  1672. if ( flDot < 0.5f )
  1673. flDot = 0.5f;
  1674. Vector vecPredictedPos;
  1675. //Get our likely position in two seconds
  1676. UTIL_PredictedPosition( GetEnemy(), flDot * 2.5f, &vecPredictedPos );
  1677. // Don't jump if we're already near the target
  1678. if ( ( GetAbsOrigin() - vecPredictedPos ).LengthSqr() < (512*512) )
  1679. return false;
  1680. //Don't retest if the target hasn't moved enough
  1681. //FIXME: Check your own distance from last attempt as well
  1682. if ( ( ( m_vecLastJumpAttempt - vecPredictedPos ).LengthSqr() ) < (128*128) )
  1683. {
  1684. m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );
  1685. return false;
  1686. }
  1687. Vector targetDir = ( vecPredictedPos - GetAbsOrigin() );
  1688. float flDist = VectorNormalize( targetDir );
  1689. // don't jump at target it it's very close
  1690. if (flDist < ANTLION_JUMP_MIN)
  1691. return false;
  1692. Vector targetPos = vecPredictedPos + ( targetDir * (GetHullWidth()*4.0f) );
  1693. if ( CAntlionRepellant::IsPositionRepellantFree( targetPos ) == false )
  1694. return false;
  1695. // Try the jump
  1696. AIMoveTrace_t moveTrace;
  1697. GetMoveProbe()->MoveLimit( NAV_JUMP, GetAbsOrigin(), targetPos, MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace );
  1698. //See if it succeeded
  1699. if ( IsMoveBlocked( moveTrace.fStatus ) )
  1700. {
  1701. if ( g_debug_antlion.GetInt() == 2 )
  1702. {
  1703. NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 5 );
  1704. NDebugOverlay::Line( GetAbsOrigin(), targetPos, 255, 0, 0, 0, 5 );
  1705. }
  1706. m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );
  1707. return false;
  1708. }
  1709. if ( g_debug_antlion.GetInt() == 2 )
  1710. {
  1711. NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 5 );
  1712. NDebugOverlay::Line( GetAbsOrigin(), targetPos, 0, 255, 0, 0, 5 );
  1713. }
  1714. //Save this jump in case the next time fails
  1715. m_vecSavedJump = moveTrace.vJumpVelocity;
  1716. m_vecLastJumpAttempt = targetPos;
  1717. return true;
  1718. }
  1719. //-----------------------------------------------------------------------------
  1720. //-----------------------------------------------------------------------------
  1721. int CNPC_Antlion::TranslateSchedule( int scheduleType )
  1722. {
  1723. if ( ( m_hFollowTarget != NULL ) || IsAllied() )
  1724. {
  1725. if ( ( scheduleType == SCHED_IDLE_STAND ) || ( scheduleType == SCHED_ALERT_STAND ) )
  1726. return SCHED_ANTLION_BUGBAIT_IDLE_STAND;
  1727. }
  1728. return BaseClass::TranslateSchedule( scheduleType );
  1729. }
  1730. //-----------------------------------------------------------------------------
  1731. //-----------------------------------------------------------------------------
  1732. Activity CNPC_Antlion::NPC_TranslateActivity( Activity baseAct )
  1733. {
  1734. // Workers explode as long as they didn't drown.
  1735. if ( IsWorker() && ( baseAct == ACT_DIESIMPLE ) && !m_bDontExplode )
  1736. {
  1737. return ( Activity )ACT_ANTLION_WORKER_EXPLODE;
  1738. }
  1739. return BaseClass::NPC_TranslateActivity( baseAct );
  1740. }
  1741. //-----------------------------------------------------------------------------
  1742. // Purpose:
  1743. // Output : int
  1744. //-----------------------------------------------------------------------------
  1745. int CNPC_Antlion::ChooseMoveSchedule( void )
  1746. {
  1747. // See if we need to invalidate our fight goal
  1748. if ( ShouldResumeFollow() )
  1749. {
  1750. // Set us back to following
  1751. SetMoveState( ANTLION_MOVE_FOLLOW );
  1752. // Tell our parent that we've swapped modes
  1753. CAntlionTemplateMaker *pMaker = dynamic_cast<CAntlionTemplateMaker *>(GetOwnerEntity());
  1754. if ( pMaker != NULL )
  1755. {
  1756. pMaker->SetChildMoveState( ANTLION_MOVE_FOLLOW );
  1757. }
  1758. }
  1759. // Figure out our move state
  1760. switch( m_MoveState )
  1761. {
  1762. case ANTLION_MOVE_FREE:
  1763. return SCHED_NONE; // Let the base class handle us
  1764. break;
  1765. // Fighting to a position
  1766. case ANTLION_MOVE_FIGHT_TO_GOAL:
  1767. {
  1768. if ( m_hFightGoalTarget )
  1769. {
  1770. float targetDist = UTIL_DistApprox( WorldSpaceCenter(), m_hFightGoalTarget->GetAbsOrigin() );
  1771. if ( targetDist > 256 )
  1772. {
  1773. Vector testPos;
  1774. Vector targetPos = ( m_hFightGoalTarget ) ? m_hFightGoalTarget->GetAbsOrigin() : m_vSavePosition;
  1775. // Find a suitable chase position
  1776. if ( FindChasePosition( targetPos, testPos ) )
  1777. {
  1778. m_vSavePosition = testPos;
  1779. return SCHED_ANTLION_RUN_TO_FIGHT_GOAL;
  1780. }
  1781. }
  1782. }
  1783. }
  1784. break;
  1785. // Following a goal
  1786. case ANTLION_MOVE_FOLLOW:
  1787. {
  1788. if ( m_FollowBehavior.CanSelectSchedule() )
  1789. {
  1790. // See if we should burrow away if our target it too far off
  1791. if ( ShouldAbandonFollow() )
  1792. return SCHED_ANTLION_BURROW_AWAY;
  1793. DeferSchedulingToBehavior( &m_FollowBehavior );
  1794. return BaseClass::SelectSchedule();
  1795. }
  1796. }
  1797. break;
  1798. }
  1799. return SCHED_NONE;
  1800. }
  1801. //-----------------------------------------------------------------------------
  1802. // Purpose:
  1803. //-----------------------------------------------------------------------------
  1804. void CNPC_Antlion::ZapThink( void )
  1805. {
  1806. CEffectData data;
  1807. data.m_nEntIndex = entindex();
  1808. data.m_flMagnitude = 4;
  1809. data.m_flScale = random->RandomFloat( 0.25f, 1.0f );
  1810. DispatchEffect( "TeslaHitboxes", data );
  1811. if ( m_flZapDuration > gpGlobals->curtime )
  1812. {
  1813. SetContextThink( &CNPC_Antlion::ZapThink, gpGlobals->curtime + random->RandomFloat( 0.05f, 0.25f ), "ZapThink" );
  1814. }
  1815. else
  1816. {
  1817. SetContextThink( NULL, gpGlobals->curtime, "ZapThink" );
  1818. }
  1819. }
  1820. //-----------------------------------------------------------------------------
  1821. // Purpose:
  1822. // Output : int
  1823. //-----------------------------------------------------------------------------
  1824. int CNPC_Antlion::SelectSchedule( void )
  1825. {
  1826. // Workers explode when killed unless told otherwise by anim events etc.
  1827. m_bDontExplode = false;
  1828. // Clear out this condition
  1829. ClearCondition( COND_ANTLION_RECEIVED_ORDERS );
  1830. // If we're supposed to be burrowed, stay there
  1831. if ( m_bStartBurrowed )
  1832. return SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER;
  1833. // See if a friendly player is pushing us away
  1834. if ( HasCondition( COND_PLAYER_PUSHING ) )
  1835. return SCHED_MOVE_AWAY;
  1836. //Flipped?
  1837. if ( HasCondition( COND_ANTLION_FLIPPED ) )
  1838. {
  1839. ClearCondition( COND_ANTLION_FLIPPED );
  1840. // See if it's a forced, electrical flip
  1841. if ( m_flZapDuration > gpGlobals->curtime )
  1842. {
  1843. SetContextThink( &CNPC_Antlion::ZapThink, gpGlobals->curtime, "ZapThink" );
  1844. return SCHED_ANTLION_ZAP_FLIP;
  1845. }
  1846. // Regular flip
  1847. return SCHED_ANTLION_FLIP;
  1848. }
  1849. if( HasCondition( COND_ANTLION_IN_WATER ) )
  1850. {
  1851. // No matter what, drown in water
  1852. return SCHED_ANTLION_DROWN;
  1853. }
  1854. // If we're flagged to burrow away when eluded, do so
  1855. if ( ( m_spawnflags & SF_ANTLION_BURROW_ON_ELUDED ) && ( HasCondition( COND_ENEMY_UNREACHABLE ) || HasCondition( COND_ENEMY_TOO_FAR ) ) )
  1856. return SCHED_ANTLION_BURROW_AWAY;
  1857. //Hear a thumper?
  1858. if ( HasCondition( COND_HEAR_THUMPER ) )
  1859. {
  1860. // Ignore thumpers that aren't visible
  1861. CSound *pSound = GetLoudestSoundOfType( SOUND_THUMPER );
  1862. if ( pSound )
  1863. {
  1864. CTakeDamageInfo info;
  1865. PainSound( info );
  1866. ClearCondition( COND_HEAR_THUMPER );
  1867. return SCHED_ANTLION_FLEE_THUMPER;
  1868. }
  1869. }
  1870. //Hear a physics danger sound?
  1871. if( HasCondition( COND_HEAR_PHYSICS_DANGER ) )
  1872. {
  1873. CTakeDamageInfo info;
  1874. PainSound( info );
  1875. return SCHED_ANTLION_FLEE_PHYSICS_DANGER;
  1876. }
  1877. //On another NPC's head?
  1878. if( HasCondition( COND_ANTLION_ON_NPC ) )
  1879. {
  1880. // You're on an NPC's head. Get off.
  1881. return SCHED_ANTLION_DISMOUNT_NPC;
  1882. }
  1883. // If we're scripted to jump at a target, do so
  1884. if ( HasCondition( COND_ANTLION_CAN_JUMP_AT_TARGET ) )
  1885. {
  1886. // NDebugOverlay::Cross3D( m_vecSavedJump, 32.0f, 255, 0, 0, true, 2.0f );
  1887. ClearCondition( COND_ANTLION_CAN_JUMP_AT_TARGET );
  1888. return SCHED_ANTLION_JUMP;
  1889. }
  1890. //Hear bug bait splattered?
  1891. if ( HasCondition( COND_HEAR_BUGBAIT ) && ( m_bIgnoreBugbait == false ) )
  1892. {
  1893. //Play a special sound
  1894. if ( m_flNextAcknowledgeTime < gpGlobals->curtime )
  1895. {
  1896. EmitSound( "NPC_Antlion.Distracted" );
  1897. m_flNextAcknowledgeTime = gpGlobals->curtime + 1.0f;
  1898. }
  1899. m_flIdleDelay = gpGlobals->curtime + 4.0f;
  1900. //If the sound is valid, act upon it
  1901. if ( m_bHasHeardSound )
  1902. {
  1903. //Mark anything in the area as more interesting
  1904. CBaseEntity *pTarget = NULL;
  1905. CBaseEntity *pNewEnemy = NULL;
  1906. Vector soundOrg = m_vecHeardSound;
  1907. //Find all entities within that sphere
  1908. while ( ( pTarget = gEntList.FindEntityInSphere( pTarget, soundOrg, bugbait_radius.GetInt() ) ) != NULL )
  1909. {
  1910. CAI_BaseNPC *pNPC = pTarget->MyNPCPointer();
  1911. if ( pNPC == NULL )
  1912. continue;
  1913. if ( pNPC->CanBeAnEnemyOf( this ) == false )
  1914. continue;
  1915. //Check to see if the default relationship is hatred, and if so intensify that
  1916. if ( ( IRelationType( pNPC ) == D_HT ) && ( pNPC->IsPlayer() == false ) )
  1917. {
  1918. AddEntityRelationship( pNPC, D_HT, 99 );
  1919. //Try to spread out the enemy distribution
  1920. if ( ( pNewEnemy == NULL ) || ( random->RandomInt( 0, 1 ) ) )
  1921. {
  1922. pNewEnemy = pNPC;
  1923. continue;
  1924. }
  1925. }
  1926. }
  1927. // If we have a new enemy, take it
  1928. if ( pNewEnemy != NULL )
  1929. {
  1930. //Setup our ignore info
  1931. SetEnemy( pNewEnemy );
  1932. }
  1933. ClearCondition( COND_HEAR_BUGBAIT );
  1934. return SCHED_ANTLION_CHASE_BUGBAIT;
  1935. }
  1936. }
  1937. if( m_AssaultBehavior.CanSelectSchedule() )
  1938. {
  1939. DeferSchedulingToBehavior( &m_AssaultBehavior );
  1940. return BaseClass::SelectSchedule();
  1941. }
  1942. //Otherwise do basic state schedule selection
  1943. switch ( m_NPCState )
  1944. {
  1945. case NPC_STATE_COMBAT:
  1946. {
  1947. // Worker-only AI
  1948. if ( hl2_episodic.GetBool() && IsWorker() )
  1949. {
  1950. // Melee attack if we can
  1951. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  1952. return SCHED_MELEE_ATTACK1;
  1953. // Pounce if they're too near us
  1954. if ( HasCondition( COND_CAN_MELEE_ATTACK2 ) )
  1955. {
  1956. m_flPounceTime = gpGlobals->curtime + 1.5f;
  1957. if ( m_bLeapAttack == true )
  1958. return SCHED_ANTLION_POUNCE_MOVING;
  1959. return SCHED_ANTLION_POUNCE;
  1960. }
  1961. // A squadmate died, so run away!
  1962. if ( HasCondition( COND_ANTLION_SQUADMATE_KILLED ) )
  1963. {
  1964. SetNextAttack( gpGlobals->curtime + random->RandomFloat( 2.0f, 4.0f ) );
  1965. ClearCondition( COND_ANTLION_SQUADMATE_KILLED );
  1966. return SCHED_ANTLION_TAKE_COVER_FROM_ENEMY;
  1967. }
  1968. // Flee on heavy damage
  1969. if ( HasCondition( COND_HEAVY_DAMAGE ) )
  1970. {
  1971. SetNextAttack( gpGlobals->curtime + random->RandomFloat( 2.0f, 4.0f ) );
  1972. return SCHED_ANTLION_TAKE_COVER_FROM_ENEMY;
  1973. }
  1974. // Range attack if we're able
  1975. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  1976. {
  1977. if ( OccupyStrategySlot( SQUAD_SLOT_ANTLION_WORKER_FIRE ) )
  1978. {
  1979. EmitSound( "NPC_Antlion.PoisonBurstScream" );
  1980. SetNextAttack( gpGlobals->curtime + random->RandomFloat( 0.5f, 2.5f ) );
  1981. if ( GetEnemy() )
  1982. {
  1983. m_vSavePosition = GetEnemy()->BodyTarget( GetAbsOrigin() );
  1984. }
  1985. return SCHED_ANTLION_WORKER_RANGE_ATTACK1;
  1986. }
  1987. }
  1988. // Back up, we're too near an enemy or can't see them
  1989. if ( HasCondition( COND_TOO_CLOSE_TO_ATTACK ) || HasCondition( COND_ENEMY_OCCLUDED ) )
  1990. return SCHED_ESTABLISH_LINE_OF_FIRE;
  1991. // See if we need to destroy breakable cover
  1992. if ( HasCondition( COND_WEAPON_SIGHT_OCCLUDED ) )
  1993. return SCHED_SHOOT_ENEMY_COVER;
  1994. // Run around randomly if our target is looking in our direction
  1995. if ( HasCondition( COND_BEHIND_ENEMY ) == false )
  1996. return SCHED_ANTLION_WORKER_FLANK_RANDOM;
  1997. // Face our target and continue to fire
  1998. return SCHED_COMBAT_FACE;
  1999. }
  2000. else
  2001. {
  2002. // Lunge at the enemy
  2003. if ( HasCondition( COND_CAN_MELEE_ATTACK2 ) )
  2004. {
  2005. m_flPounceTime = gpGlobals->curtime + 1.5f;
  2006. if ( m_bLeapAttack == true )
  2007. return SCHED_ANTLION_POUNCE_MOVING;
  2008. else
  2009. return SCHED_ANTLION_POUNCE;
  2010. }
  2011. // Try to jump
  2012. if ( HasCondition( COND_ANTLION_CAN_JUMP ) )
  2013. return SCHED_ANTLION_JUMP;
  2014. }
  2015. }
  2016. break;
  2017. default:
  2018. {
  2019. int moveSched = ChooseMoveSchedule();
  2020. if ( moveSched != SCHED_NONE )
  2021. return moveSched;
  2022. if ( GetEnemy() == NULL && ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) ) )
  2023. {
  2024. Vector vecEnemyLKP;
  2025. // Retrieve a memory for the damage taken
  2026. // Fill in where we're trying to look
  2027. if ( GetEnemies()->Find( AI_UNKNOWN_ENEMY ) )
  2028. {
  2029. vecEnemyLKP = GetEnemies()->LastKnownPosition( AI_UNKNOWN_ENEMY );
  2030. }
  2031. else
  2032. {
  2033. // Don't have an enemy, so face the direction the last attack came from (don't face north)
  2034. vecEnemyLKP = WorldSpaceCenter() + ( g_vecAttackDir * 128 );
  2035. }
  2036. // If we're already facing the attack direction, then take cover from it
  2037. if ( FInViewCone( vecEnemyLKP ) )
  2038. {
  2039. // Save this position for our cover search
  2040. m_vSavePosition = vecEnemyLKP;
  2041. return SCHED_ANTLION_TAKE_COVER_FROM_SAVEPOSITION;
  2042. }
  2043. // By default, we'll turn to face the attack
  2044. }
  2045. }
  2046. break;
  2047. }
  2048. return BaseClass::SelectSchedule();
  2049. }
  2050. void CNPC_Antlion::Ignite ( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner )
  2051. {
  2052. #ifdef HL2_EPISODIC
  2053. float flDamage = m_iHealth + 1;
  2054. CTakeDamageInfo dmgInfo( this, this, flDamage, DMG_GENERIC );
  2055. GuessDamageForce( &dmgInfo, Vector( 0, 0, 8 ), GetAbsOrigin() );
  2056. TakeDamage( dmgInfo );
  2057. #else
  2058. BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
  2059. #endif
  2060. }
  2061. //-----------------------------------------------------------------------------
  2062. //-----------------------------------------------------------------------------
  2063. int CNPC_Antlion::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  2064. {
  2065. CTakeDamageInfo newInfo = info;
  2066. if( hl2_episodic.GetBool() && antlion_easycrush.GetBool() )
  2067. {
  2068. if( newInfo.GetDamageType() & DMG_CRUSH )
  2069. {
  2070. if( newInfo.GetInflictor() && newInfo.GetInflictor()->VPhysicsGetObject() )
  2071. {
  2072. float flMass = newInfo.GetInflictor()->VPhysicsGetObject()->GetMass();
  2073. if( flMass > 250.0f && newInfo.GetDamage() < GetHealth() )
  2074. {
  2075. newInfo.SetDamage( GetHealth() );
  2076. }
  2077. }
  2078. }
  2079. }
  2080. // If we're being hoisted by a barnacle, we only take damage from that barnacle (otherwise we can die too early!)
  2081. if ( IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
  2082. {
  2083. if ( info.GetAttacker() && info.GetAttacker()->Classify() != CLASS_BARNACLE )
  2084. return 0;
  2085. }
  2086. // Find out how much damage we're about to take
  2087. int nDamageTaken = BaseClass::OnTakeDamage_Alive( newInfo );
  2088. if ( gpGlobals->curtime - m_flLastDamageTime < 0.5f )
  2089. {
  2090. // Accumulate it
  2091. m_nSustainedDamage += nDamageTaken;
  2092. }
  2093. else
  2094. {
  2095. // Reset, it's been too long
  2096. m_nSustainedDamage = nDamageTaken;
  2097. }
  2098. m_flLastDamageTime = gpGlobals->curtime;
  2099. return nDamageTaken;
  2100. }
  2101. //-----------------------------------------------------------------------------
  2102. // Purpose: Antlion who are flipped will knock over other antlions behind them!
  2103. //-----------------------------------------------------------------------------
  2104. void CNPC_Antlion::CascadePush( const Vector &vecForce )
  2105. {
  2106. // Controlled via this convar until this is proven worthwhile
  2107. if ( hl2_episodic.GetBool() == false /*|| g_antlion_cascade_push.GetBool() == false*/ )
  2108. return;
  2109. Vector vecForceDir = vecForce;
  2110. float flMagnitude = VectorNormalize( vecForceDir );
  2111. Vector vecPushBack = GetAbsOrigin() + ( vecForceDir * (flMagnitude*0.1f) );
  2112. // Make antlions flip all around us!
  2113. CBaseEntity *pEnemySearch[32];
  2114. int nNumEnemies = UTIL_EntitiesInBox( pEnemySearch, ARRAYSIZE(pEnemySearch), vecPushBack-Vector(48,48,0), vecPushBack+Vector(48,48,64), FL_NPC );
  2115. for ( int i = 0; i < nNumEnemies; i++ )
  2116. {
  2117. // We only care about antlions
  2118. if ( pEnemySearch[i] == NULL || pEnemySearch[i]->Classify() != CLASS_ANTLION || pEnemySearch[i] == this )
  2119. continue;
  2120. CNPC_Antlion *pAntlion = dynamic_cast<CNPC_Antlion *>(pEnemySearch[i]);
  2121. if ( pAntlion != NULL )
  2122. {
  2123. Vector vecDir = ( pAntlion->GetAbsOrigin() - GetAbsOrigin() );
  2124. vecDir[2] = 0.0f;
  2125. float flDist = VectorNormalize( vecDir );
  2126. float flFalloff = RemapValClamped( flDist, 0, 256, 1.0f, 0.1f );
  2127. vecDir *= ( flMagnitude * flFalloff );
  2128. vecDir[2] += ( (flMagnitude*0.25f) * flFalloff );
  2129. pAntlion->ApplyAbsVelocityImpulse( vecDir );
  2130. // Turn them over
  2131. pAntlion->Flip();
  2132. }
  2133. }
  2134. }
  2135. //-----------------------------------------------------------------------------
  2136. // Purpose:
  2137. // Output : Returns true on success, false on failure.
  2138. //-----------------------------------------------------------------------------
  2139. inline bool CNPC_Antlion::IsFlipped( void )
  2140. {
  2141. return ( GetActivity() == ACT_ANTLION_FLIP || GetActivity() == ACT_ANTLION_ZAP_FLIP );
  2142. }
  2143. //-----------------------------------------------------------------------------
  2144. // Purpose:
  2145. //-----------------------------------------------------------------------------
  2146. void CNPC_Antlion::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  2147. {
  2148. CTakeDamageInfo newInfo = info;
  2149. Vector vecShoveDir = vecDir;
  2150. vecShoveDir.z = 0.0f;
  2151. //Are we already flipped?
  2152. if ( IsFlipped() )
  2153. {
  2154. //If we were hit by physics damage, move with it
  2155. if ( newInfo.GetDamageType() & (DMG_CRUSH|DMG_PHYSGUN) )
  2156. {
  2157. PainSound( newInfo );
  2158. Vector vecForce = ( vecShoveDir * random->RandomInt( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f);
  2159. CascadePush( vecForce );
  2160. ApplyAbsVelocityImpulse( vecForce );
  2161. SetGroundEntity( NULL );
  2162. }
  2163. //More vulnerable when flipped
  2164. newInfo.ScaleDamage( 4.0f );
  2165. }
  2166. else if ( newInfo.GetDamageType() & (DMG_PHYSGUN) ||
  2167. ( newInfo.GetDamageType() & (DMG_BLAST|DMG_CRUSH) && newInfo.GetDamage() >= 25.0f ) )
  2168. {
  2169. // Don't do this if we're in an interaction
  2170. if ( !IsRunningDynamicInteraction() )
  2171. {
  2172. //Grenades, physcannons, and physics impacts make us fuh-lip!
  2173. if( hl2_episodic.GetBool() )
  2174. {
  2175. PainSound( newInfo );
  2176. if( GetFlags() & FL_ONGROUND )
  2177. {
  2178. // Only flip if on the ground.
  2179. SetCondition( COND_ANTLION_FLIPPED );
  2180. }
  2181. Vector vecForce = ( vecShoveDir * random->RandomInt( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f);
  2182. CascadePush( vecForce );
  2183. ApplyAbsVelocityImpulse( vecForce );
  2184. SetGroundEntity( NULL );
  2185. }
  2186. else
  2187. {
  2188. //Don't flip off the deck
  2189. if ( GetFlags() & FL_ONGROUND )
  2190. {
  2191. PainSound( newInfo );
  2192. SetCondition( COND_ANTLION_FLIPPED );
  2193. //Get tossed!
  2194. ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomInt( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) );
  2195. SetGroundEntity( NULL );
  2196. }
  2197. }
  2198. }
  2199. }
  2200. BaseClass::TraceAttack( newInfo, vecDir, ptr, pAccumulator );
  2201. }
  2202. void CNPC_Antlion::StopLoopingSounds( void )
  2203. {
  2204. if ( m_bLoopingStarted )
  2205. {
  2206. StopSound( "NPC_Antlion.WingsOpen" );
  2207. m_bLoopingStarted = false;
  2208. }
  2209. if ( m_bAgitatedSound )
  2210. {
  2211. StopSound( "NPC_Antlion.LoopingAgitated" );
  2212. m_bAgitatedSound = false;
  2213. }
  2214. }
  2215. //-----------------------------------------------------------------------------
  2216. // Purpose:
  2217. //-----------------------------------------------------------------------------
  2218. void CNPC_Antlion::IdleSound( void )
  2219. {
  2220. EmitSound( "NPC_Antlion.Idle" );
  2221. m_flIdleDelay = gpGlobals->curtime + 4.0f;
  2222. }
  2223. //-----------------------------------------------------------------------------
  2224. // Purpose:
  2225. //-----------------------------------------------------------------------------
  2226. void CNPC_Antlion::PainSound( const CTakeDamageInfo &info )
  2227. {
  2228. EmitSound( "NPC_Antlion.Pain" );
  2229. }
  2230. //-----------------------------------------------------------------------------
  2231. // Purpose:
  2232. // Output :
  2233. //-----------------------------------------------------------------------------
  2234. float CNPC_Antlion::GetIdealAccel( void ) const
  2235. {
  2236. return GetIdealSpeed() * 2.0;
  2237. }
  2238. //-----------------------------------------------------------------------------
  2239. // Purpose:
  2240. // Output : float
  2241. //-----------------------------------------------------------------------------
  2242. float CNPC_Antlion::MaxYawSpeed( void )
  2243. {
  2244. switch ( GetActivity() )
  2245. {
  2246. case ACT_IDLE:
  2247. return 32.0f;
  2248. break;
  2249. case ACT_WALK:
  2250. return 16.0f;
  2251. break;
  2252. default:
  2253. case ACT_RUN:
  2254. return 32.0f;
  2255. break;
  2256. }
  2257. return 32.0f;
  2258. }
  2259. //-----------------------------------------------------------------------------
  2260. // Purpose:
  2261. // Output : Returns true on success, false on failure.
  2262. //-----------------------------------------------------------------------------
  2263. bool CNPC_Antlion::ShouldPlayIdleSound( void )
  2264. {
  2265. //Only do idles in the right states
  2266. if ( ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT ) )
  2267. return false;
  2268. //Gagged monsters don't talk
  2269. if ( m_spawnflags & SF_NPC_GAG )
  2270. return false;
  2271. //Don't cut off another sound or play again too soon
  2272. if ( m_flIdleDelay > gpGlobals->curtime )
  2273. return false;
  2274. //Randomize it a bit
  2275. if ( random->RandomInt( 0, 20 ) )
  2276. return false;
  2277. return true;
  2278. }
  2279. //-----------------------------------------------------------------------------
  2280. // Purpose:
  2281. // Input : *pFriend -
  2282. //-----------------------------------------------------------------------------
  2283. void CNPC_Antlion::NotifyDeadFriend( CBaseEntity *pFriend )
  2284. {
  2285. SetCondition( COND_ANTLION_SQUADMATE_KILLED );
  2286. BaseClass::NotifyDeadFriend( pFriend );
  2287. }
  2288. //-----------------------------------------------------------------------------
  2289. // Purpose: Determine whether or not to check our attack conditions
  2290. //-----------------------------------------------------------------------------
  2291. bool CNPC_Antlion::FCanCheckAttacks( void )
  2292. {
  2293. if ( IsWorker() )
  2294. {
  2295. // Only do this if we've seen our target recently and our schedule can be interrupted
  2296. if ( SeenEnemyWithinTime( 3.0f ) && ConditionInterruptsCurSchedule( COND_CAN_RANGE_ATTACK1 ) )
  2297. return FInViewCone( GetEnemy() );
  2298. }
  2299. return BaseClass::FCanCheckAttacks();
  2300. }
  2301. //-----------------------------------------------------------------------------
  2302. // Purpose:
  2303. //-----------------------------------------------------------------------------
  2304. int CNPC_Antlion::RangeAttack1Conditions( float flDot, float flDist )
  2305. {
  2306. if ( GetNextAttack() > gpGlobals->curtime )
  2307. return COND_NOT_FACING_ATTACK;
  2308. if ( flDot < DOT_10DEGREE )
  2309. return COND_NOT_FACING_ATTACK;
  2310. if ( flDist > (150*12) )
  2311. return COND_TOO_FAR_TO_ATTACK;
  2312. if ( flDist < (20*12) )
  2313. return COND_TOO_CLOSE_TO_ATTACK;
  2314. return COND_CAN_RANGE_ATTACK1;
  2315. }
  2316. //-----------------------------------------------------------------------------
  2317. // Purpose:
  2318. //-----------------------------------------------------------------------------
  2319. int CNPC_Antlion::MeleeAttack1Conditions( float flDot, float flDist )
  2320. {
  2321. #if 1 //NOTENOTE: Use predicted position melee attacks
  2322. //Get our likely position in one half second
  2323. Vector vecPrPos;
  2324. UTIL_PredictedPosition( GetEnemy(), 0.5f, &vecPrPos );
  2325. //Get the predicted distance and direction
  2326. float flPrDist = ( vecPrPos - GetAbsOrigin() ).LengthSqr();
  2327. if ( flPrDist > Square( ANTLION_MELEE1_RANGE ) )
  2328. return COND_TOO_FAR_TO_ATTACK;
  2329. // Compare our target direction to our body facing
  2330. Vector2D vec2DPrDir = ( vecPrPos - GetAbsOrigin() ).AsVector2D();
  2331. Vector2D vec2DBodyDir = BodyDirection2D().AsVector2D();
  2332. float flPrDot = DotProduct2D ( vec2DPrDir, vec2DBodyDir );
  2333. if ( flPrDot < 0.5f )
  2334. return COND_NOT_FACING_ATTACK;
  2335. trace_t tr;
  2336. AI_TraceHull( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8,8,8), Vector(8,8,8), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  2337. // If the hit entity isn't our target and we don't hate it, don't hit it
  2338. if ( tr.m_pEnt != GetEnemy() && tr.fraction < 1.0f && IRelationType( tr.m_pEnt ) != D_HT )
  2339. return 0;
  2340. #else
  2341. if ( flDot < 0.5f )
  2342. return COND_NOT_FACING_ATTACK;
  2343. float flAdjustedDist = ANTLION_MELEE1_RANGE;
  2344. if ( GetEnemy() )
  2345. {
  2346. // Give us extra space if our enemy is in a vehicle
  2347. CBaseCombatCharacter *pCCEnemy = GetEnemy()->MyCombatCharacterPointer();
  2348. if ( pCCEnemy != NULL && pCCEnemy->IsInAVehicle() )
  2349. {
  2350. flAdjustedDist *= 2.0f;
  2351. }
  2352. }
  2353. if ( flDist > flAdjustedDist )
  2354. return COND_TOO_FAR_TO_ATTACK;
  2355. trace_t tr;
  2356. AI_TraceHull( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  2357. if ( tr.fraction < 1.0f )
  2358. return 0;
  2359. #endif
  2360. return COND_CAN_MELEE_ATTACK1;
  2361. }
  2362. //-----------------------------------------------------------------------------
  2363. // Purpose:
  2364. // Input : flDot -
  2365. // flDist -
  2366. // Output : int
  2367. //-----------------------------------------------------------------------------
  2368. int CNPC_Antlion::MeleeAttack2Conditions( float flDot, float flDist )
  2369. {
  2370. // See if it's too soon to pounce again
  2371. if ( m_flPounceTime > gpGlobals->curtime )
  2372. return 0;
  2373. float flPrDist, flPrDot;
  2374. Vector vecPrPos;
  2375. Vector2D vec2DPrDir;
  2376. //Get our likely position in one half second
  2377. UTIL_PredictedPosition( GetEnemy(), 0.25f, &vecPrPos );
  2378. //Get the predicted distance and direction
  2379. flPrDist = ( vecPrPos - GetAbsOrigin() ).Length();
  2380. vec2DPrDir = ( vecPrPos - GetAbsOrigin() ).AsVector2D();
  2381. Vector vecBodyDir = BodyDirection2D();
  2382. Vector2D vec2DBodyDir = vecBodyDir.AsVector2D();
  2383. flPrDot = DotProduct2D ( vec2DPrDir, vec2DBodyDir );
  2384. if ( ( flPrDist > ANTLION_MELEE2_RANGE_MAX ) )
  2385. {
  2386. m_flPounceTime = gpGlobals->curtime + 0.2f;
  2387. return COND_TOO_FAR_TO_ATTACK;
  2388. }
  2389. else if ( ( flPrDist < ANTLION_MELEE2_RANGE_MIN ) )
  2390. {
  2391. m_flPounceTime = gpGlobals->curtime + 0.2f;
  2392. return COND_TOO_CLOSE_TO_ATTACK;
  2393. }
  2394. trace_t tr;
  2395. AI_TraceHull( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  2396. if ( tr.fraction < 1.0f )
  2397. return 0;
  2398. if ( IsMoving() )
  2399. m_bLeapAttack = true;
  2400. else
  2401. m_bLeapAttack = false;
  2402. return COND_CAN_MELEE_ATTACK2;
  2403. }
  2404. //-----------------------------------------------------------------------------
  2405. // Purpose:
  2406. // Input : interactionType -
  2407. // *data -
  2408. // *sender -
  2409. // Output : Returns true on success, false on failure.
  2410. //-----------------------------------------------------------------------------
  2411. bool CNPC_Antlion::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sender )
  2412. {
  2413. //Check for a target found while burrowed
  2414. if ( interactionType == g_interactionAntlionFoundTarget )
  2415. {
  2416. CBaseEntity *pOther = (CBaseEntity *) data;
  2417. //Randomly delay
  2418. m_flBurrowTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 1.0f );
  2419. BurrowUse( pOther, pOther, USE_ON, 0.0f );
  2420. return true;
  2421. }
  2422. // fixed for episodic: allow interactions to fall through in the base class. ifdefed away
  2423. // for mainline in case anything depends on this bug.
  2424. #ifdef HL2_EPISODIC
  2425. if ( interactionType == g_interactionAntlionFiredAtTarget )
  2426. {
  2427. // Bump out our attack time
  2428. if ( IsWorker() )
  2429. {
  2430. float flDuration = *((float *)data);
  2431. SetNextAttack( gpGlobals->curtime + flDuration );
  2432. }
  2433. }
  2434. return BaseClass::HandleInteraction( interactionType, data, sender );
  2435. #else
  2436. return false;
  2437. #endif
  2438. }
  2439. //-----------------------------------------------------------------------------
  2440. // Purpose:
  2441. // Output : Returns true on success, false on failure.
  2442. //-----------------------------------------------------------------------------
  2443. bool CNPC_Antlion::Alone( void )
  2444. {
  2445. if ( m_pSquad == NULL )
  2446. return true;
  2447. if ( m_pSquad->NumMembers() <= 1 )
  2448. return true;
  2449. return false;
  2450. }
  2451. //-----------------------------------------------------------------------------
  2452. // Purpose:
  2453. //-----------------------------------------------------------------------------
  2454. void CNPC_Antlion::StartJump( void )
  2455. {
  2456. if ( m_bForcedStuckJump == false )
  2457. {
  2458. // FIXME: Why must this be true?
  2459. // Must be jumping at an enemy
  2460. // if ( GetEnemy() == NULL )
  2461. // return;
  2462. //Don't jump if we're not on the ground
  2463. if ( ( GetFlags() & FL_ONGROUND ) == false )
  2464. return;
  2465. }
  2466. //Take us off the ground
  2467. SetGroundEntity( NULL );
  2468. SetAbsVelocity( m_vecSavedJump );
  2469. m_bForcedStuckJump = false;
  2470. #if HL2_EPISODIC
  2471. m_bHasDoneAirAttack = false;
  2472. #endif
  2473. //Setup our jump time so that we don't try it again too soon
  2474. m_flJumpTime = gpGlobals->curtime + random->RandomInt( 2, 6 );
  2475. }
  2476. //-----------------------------------------------------------------------------
  2477. // Purpose:
  2478. // Input : sHint -
  2479. // nNodeNum -
  2480. // Output : bool CAI_BaseNPC::FValidateHintType
  2481. //-----------------------------------------------------------------------------
  2482. bool CNPC_Antlion::FValidateHintType( CAI_Hint *pHint )
  2483. {
  2484. switch ( m_iContext )
  2485. {
  2486. case ANTLION_BURROW_OUT:
  2487. {
  2488. //See if this is a valid point
  2489. Vector vHintPos;
  2490. pHint->GetPosition(this,&vHintPos);
  2491. if ( ValidBurrowPoint( vHintPos ) == false )
  2492. return false;
  2493. }
  2494. break;
  2495. }
  2496. return true;
  2497. }
  2498. //-----------------------------------------------------------------------------
  2499. // Purpose:
  2500. // Input : &origin -
  2501. //-----------------------------------------------------------------------------
  2502. void CNPC_Antlion::ClearBurrowPoint( const Vector &origin )
  2503. {
  2504. CBaseEntity *pEntity = NULL;
  2505. float flDist;
  2506. Vector vecSpot, vecCenter, vecForce;
  2507. bool bPlayerInSphere = false;
  2508. //Iterate on all entities in the vicinity.
  2509. for ( CEntitySphereQuery sphere( origin, 128 ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  2510. {
  2511. if ( pEntity->Classify() == CLASS_PLAYER )
  2512. {
  2513. bPlayerInSphere = true;
  2514. continue;
  2515. }
  2516. if ( pEntity->m_takedamage != DAMAGE_NO && pEntity->Classify() != CLASS_PLAYER && pEntity->VPhysicsGetObject() )
  2517. {
  2518. vecSpot = pEntity->BodyTarget( origin );
  2519. vecForce = ( vecSpot - origin ) + Vector( 0, 0, 16 );
  2520. // decrease damage for an ent that's farther from the bomb.
  2521. flDist = VectorNormalize( vecForce );
  2522. //float mass = pEntity->VPhysicsGetObject()->GetMass();
  2523. CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1.0f, 1.0f, 1.0f ), &vecCenter );
  2524. if ( flDist <= 128.0f )
  2525. {
  2526. pEntity->VPhysicsGetObject()->Wake();
  2527. pEntity->VPhysicsGetObject()->ApplyForceOffset( vecForce * 250.0f, vecCenter );
  2528. }
  2529. }
  2530. }
  2531. if ( bPlayerInSphere == false )
  2532. {
  2533. //Cause a ruckus
  2534. UTIL_ScreenShake( origin, 1.0f, 80.0f, 1.0f, 256.0f, SHAKE_START );
  2535. }
  2536. }
  2537. bool NPC_CheckBrushExclude( CBaseEntity *pEntity, CBaseEntity *pBrush );
  2538. //-----------------------------------------------------------------------------
  2539. // traceline methods
  2540. //-----------------------------------------------------------------------------
  2541. class CTraceFilterSimpleNPCExclude : public CTraceFilterSimple
  2542. {
  2543. public:
  2544. DECLARE_CLASS( CTraceFilterSimpleNPCExclude, CTraceFilterSimple );
  2545. CTraceFilterSimpleNPCExclude( const IHandleEntity *passentity, int collisionGroup )
  2546. : CTraceFilterSimple( passentity, collisionGroup )
  2547. {
  2548. }
  2549. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  2550. {
  2551. Assert( dynamic_cast<CBaseEntity*>(pHandleEntity) );
  2552. CBaseEntity *pTestEntity = static_cast<CBaseEntity*>(pHandleEntity);
  2553. if ( GetPassEntity() )
  2554. {
  2555. CBaseEntity *pEnt = gEntList.GetBaseEntity( GetPassEntity()->GetRefEHandle() );
  2556. if ( pEnt->IsNPC() )
  2557. {
  2558. if ( NPC_CheckBrushExclude( pEnt, pTestEntity ) == true )
  2559. return false;
  2560. }
  2561. }
  2562. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  2563. }
  2564. };
  2565. //-----------------------------------------------------------------------------
  2566. // Purpose: Determine whether a point is valid or not for burrowing up into
  2567. // Input : &point - point to test for validity
  2568. // Output : Returns true on success, false on failure.
  2569. //-----------------------------------------------------------------------------
  2570. bool CNPC_Antlion::ValidBurrowPoint( const Vector &point )
  2571. {
  2572. trace_t tr;
  2573. CTraceFilterSimpleNPCExclude filter( this, COLLISION_GROUP_NONE );
  2574. AI_TraceHull( point, point+Vector(0,0,1), GetHullMins(), GetHullMaxs(),
  2575. MASK_NPCSOLID, &filter, &tr );
  2576. //See if we were able to get there
  2577. if ( ( tr.startsolid ) || ( tr.allsolid ) || ( tr.fraction < 1.0f ) )
  2578. {
  2579. CBaseEntity *pEntity = tr.m_pEnt;
  2580. //If it's a physics object, attempt to knock is away, unless it's a car
  2581. if ( ( pEntity ) && ( pEntity->VPhysicsGetObject() ) && ( pEntity->GetServerVehicle() == NULL ) )
  2582. {
  2583. ClearBurrowPoint( point );
  2584. }
  2585. return false;
  2586. }
  2587. return true;
  2588. }
  2589. //-----------------------------------------------------------------------------
  2590. // Purpose: Finds a burrow point for the antlion
  2591. // Input : distance - radius to search for burrow spot in
  2592. // Output : Returns true on success, false on failure.
  2593. //-----------------------------------------------------------------------------
  2594. bool CNPC_Antlion::FindBurrow( const Vector &origin, float distance, int type, bool excludeNear )
  2595. {
  2596. //Burrowing in?
  2597. if ( type == ANTLION_BURROW_IN )
  2598. {
  2599. //Attempt to find a burrowing point
  2600. CHintCriteria hintCriteria;
  2601. hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
  2602. hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );
  2603. hintCriteria.AddIncludePosition( origin, distance );
  2604. if ( excludeNear )
  2605. {
  2606. hintCriteria.AddExcludePosition( origin, 128 );
  2607. }
  2608. CAI_Hint *pHint = CAI_HintManager::FindHint( this, hintCriteria );
  2609. if ( pHint == NULL )
  2610. return false;
  2611. //Free up the node for use
  2612. if ( GetHintNode() )
  2613. {
  2614. GetHintNode()->Unlock(0);
  2615. }
  2616. SetHintNode( pHint );
  2617. //Lock the node
  2618. pHint->Lock(this);
  2619. //Setup our path and attempt to run there
  2620. Vector vHintPos;
  2621. GetHintNode()->GetPosition( this, &vHintPos );
  2622. AI_NavGoal_t goal( vHintPos, ACT_RUN );
  2623. return GetNavigator()->SetGoal( goal );
  2624. }
  2625. //Burrow out
  2626. m_iContext = ANTLION_BURROW_OUT;
  2627. CHintCriteria hintCriteria;
  2628. hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
  2629. hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );
  2630. if ( GetEnemy() != NULL )
  2631. {
  2632. hintCriteria.AddIncludePosition( GetEnemy()->GetAbsOrigin(), distance );
  2633. }
  2634. //Attempt to find an open burrow point
  2635. CAI_Hint *pHint = CAI_HintManager::FindHint( this, hintCriteria );
  2636. m_iContext = -1;
  2637. if ( pHint == NULL )
  2638. return false;
  2639. //Free up the node for use
  2640. if (GetHintNode())
  2641. {
  2642. GetHintNode()->Unlock(0);
  2643. }
  2644. SetHintNode( pHint );
  2645. pHint->Lock(this);
  2646. Vector burrowPoint;
  2647. pHint->GetPosition(this,&burrowPoint);
  2648. UTIL_SetOrigin( this, burrowPoint );
  2649. //Burrowing out
  2650. return true;
  2651. }
  2652. //-----------------------------------------------------------------------------
  2653. // Purpose: Cause the antlion to unborrow
  2654. // Input : *pActivator -
  2655. // *pCaller -
  2656. // useType -
  2657. // value -
  2658. //-----------------------------------------------------------------------------
  2659. void CNPC_Antlion::BurrowUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  2660. {
  2661. //Don't allow us to do this again
  2662. SetUse( NULL );
  2663. //Allow idle sounds again
  2664. m_spawnflags &= ~SF_NPC_GAG;
  2665. //If the player activated this, then take them as an enemy
  2666. if ( ( pCaller != NULL ) && ( pCaller->IsPlayer() ) )
  2667. {
  2668. SetEnemy( pActivator );
  2669. }
  2670. //Start trying to surface
  2671. SetSchedule( SCHED_ANTLION_WAIT_UNBORROW );
  2672. }
  2673. //-----------------------------------------------------------------------------
  2674. // Purpose: Monitor the antlion's jump to play the proper landing sequence
  2675. //-----------------------------------------------------------------------------
  2676. bool CNPC_Antlion::CheckLanding( void )
  2677. {
  2678. trace_t tr;
  2679. Vector testPos;
  2680. //Amount of time to predict forward
  2681. const float timeStep = 0.1f;
  2682. //Roughly looks one second into the future
  2683. testPos = GetAbsOrigin() + ( GetAbsVelocity() * timeStep );
  2684. testPos[2] -= ( 0.5 * GetCurrentGravity() * GetGravity() * timeStep * timeStep);
  2685. if ( g_debug_antlion.GetInt() == 2 )
  2686. {
  2687. NDebugOverlay::Line( GetAbsOrigin(), testPos, 255, 0, 0, 0, 0.5f );
  2688. NDebugOverlay::Cross3D( m_vecSavedJump, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, true, 0.5f );
  2689. }
  2690. // Look below
  2691. AI_TraceHull( GetAbsOrigin(), testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  2692. //See if we're about to contact, or have already contacted the ground
  2693. if ( ( tr.fraction != 1.0f ) || ( GetFlags() & FL_ONGROUND ) )
  2694. {
  2695. int sequence = SelectWeightedSequence( (Activity)ACT_ANTLION_LAND );
  2696. if ( GetSequence() != sequence )
  2697. {
  2698. SetWings( false );
  2699. VacateStrategySlot();
  2700. SetIdealActivity( (Activity) ACT_ANTLION_LAND );
  2701. CreateDust( false );
  2702. EmitSound( "NPC_Antlion.Land" );
  2703. if ( GetEnemy() && GetEnemy()->IsPlayer() )
  2704. {
  2705. CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() );
  2706. if ( pPlayer && pPlayer->IsInAVehicle() == false )
  2707. {
  2708. QAngle qa( 4.0f, 0.0f, 0.0f );
  2709. Vector vec( -250.0f, 1.0f, 1.0f );
  2710. MeleeAttack( ANTLION_MELEE1_RANGE, sk_antlion_swipe_damage.GetFloat(), qa, vec );
  2711. }
  2712. }
  2713. SetAbsVelocity( GetAbsVelocity() * 0.33f );
  2714. return false;
  2715. }
  2716. return IsActivityFinished();
  2717. }
  2718. return false;
  2719. }
  2720. //-----------------------------------------------------------------------------
  2721. // Purpose:
  2722. // Input : *pEntity -
  2723. // Output : Returns true on success, false on failure.
  2724. //-----------------------------------------------------------------------------
  2725. bool CNPC_Antlion::QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC )
  2726. {
  2727. //If we're under the ground, don't look at enemies
  2728. if ( IsEffectActive( EF_NODRAW ) )
  2729. return false;
  2730. return BaseClass::QuerySeeEntity(pEntity, bOnlyHateOrFearIfNPC);
  2731. }
  2732. //-----------------------------------------------------------------------------
  2733. // Purpose: Turns the antlion's wings on or off
  2734. // Input : state - on or off
  2735. //-----------------------------------------------------------------------------
  2736. void CNPC_Antlion::SetWings( bool state )
  2737. {
  2738. if ( m_bWingsOpen == state )
  2739. return;
  2740. m_bWingsOpen = state;
  2741. if ( m_bWingsOpen )
  2742. {
  2743. CPASAttenuationFilter filter( this, "NPC_Antlion.WingsOpen" );
  2744. filter.MakeReliable();
  2745. EmitSound( filter, entindex(), "NPC_Antlion.WingsOpen" );
  2746. SetBodygroup( 1, 1 );
  2747. m_bLoopingStarted = true;
  2748. }
  2749. else
  2750. {
  2751. StopSound( "NPC_Antlion.WingsOpen" );
  2752. SetBodygroup( 1, 0 );
  2753. }
  2754. }
  2755. //-----------------------------------------------------------------------------
  2756. // Purpose:
  2757. //-----------------------------------------------------------------------------
  2758. void CNPC_Antlion::Burrow( void )
  2759. {
  2760. SetWings( false );
  2761. //Stop us from taking damage and being solid
  2762. m_spawnflags |= SF_NPC_GAG;
  2763. }
  2764. //-----------------------------------------------------------------------------
  2765. // Purpose:
  2766. //-----------------------------------------------------------------------------
  2767. void CNPC_Antlion::Unburrow( void )
  2768. {
  2769. m_bStartBurrowed = false;
  2770. SetWings( false );
  2771. //Become solid again and visible
  2772. m_spawnflags &= ~SF_NPC_GAG;
  2773. RemoveSolidFlags( FSOLID_NOT_SOLID );
  2774. m_takedamage = DAMAGE_YES;
  2775. SetGroundEntity( NULL );
  2776. //If we have an enemy, come out facing them
  2777. if ( GetEnemy() )
  2778. {
  2779. Vector dir = GetEnemy()->GetAbsOrigin() - GetAbsOrigin();
  2780. VectorNormalize(dir);
  2781. QAngle angles = GetAbsAngles();
  2782. angles[ YAW ] = UTIL_VecToYaw( dir );
  2783. SetLocalAngles( angles );
  2784. }
  2785. //fire output upon unburrowing
  2786. m_OnUnBurrowed.FireOutput( this, this );
  2787. }
  2788. //-----------------------------------------------------------------------------
  2789. // Purpose:
  2790. // Input : &inputdata -
  2791. //-----------------------------------------------------------------------------
  2792. void CNPC_Antlion::InputUnburrow( inputdata_t &inputdata )
  2793. {
  2794. if ( IsAlive() == false )
  2795. return;
  2796. SetSchedule( SCHED_ANTLION_WAIT_UNBORROW );
  2797. }
  2798. //-----------------------------------------------------------------------------
  2799. // Purpose:
  2800. // Input : &inputdata -
  2801. //-----------------------------------------------------------------------------
  2802. void CNPC_Antlion::InputBurrow( inputdata_t &inputdata )
  2803. {
  2804. if ( IsAlive() == false )
  2805. return;
  2806. SetSchedule( SCHED_ANTLION_BURROW_IN );
  2807. }
  2808. //-----------------------------------------------------------------------------
  2809. // Purpose:
  2810. // Input : &inputdata -
  2811. //-----------------------------------------------------------------------------
  2812. void CNPC_Antlion::InputBurrowAway( inputdata_t &inputdata )
  2813. {
  2814. if ( IsAlive() == false )
  2815. return;
  2816. SetSchedule( SCHED_ANTLION_BURROW_AWAY );
  2817. }
  2818. //-----------------------------------------------------------------------------
  2819. // Purpose:
  2820. //-----------------------------------------------------------------------------
  2821. void CNPC_Antlion::CreateDust( bool placeDecal )
  2822. {
  2823. trace_t tr;
  2824. AI_TraceLine( GetAbsOrigin()+Vector(0,0,1), GetAbsOrigin()-Vector(0,0,64), MASK_SOLID_BRUSHONLY | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP, this, COLLISION_GROUP_NONE, &tr );
  2825. if ( tr.fraction < 1.0f )
  2826. {
  2827. const surfacedata_t *pdata = physprops->GetSurfaceData( tr.surface.surfaceProps );
  2828. if ( hl2_episodic.GetBool() == true || ( pdata->game.material == CHAR_TEX_CONCRETE ) ||
  2829. ( pdata->game.material == CHAR_TEX_DIRT ) ||
  2830. ( pdata->game.material == CHAR_TEX_SAND ) )
  2831. {
  2832. if ( !m_bSuppressUnburrowEffects )
  2833. {
  2834. UTIL_CreateAntlionDust( tr.endpos + Vector(0,0,24), GetAbsAngles() );
  2835. if ( placeDecal )
  2836. {
  2837. UTIL_DecalTrace( &tr, "Antlion.Unburrow" );
  2838. }
  2839. }
  2840. }
  2841. }
  2842. }
  2843. //-----------------------------------------------------------------------------
  2844. // Purpose:
  2845. // Input : *pSound -
  2846. //-----------------------------------------------------------------------------
  2847. bool CNPC_Antlion::QueryHearSound( CSound *pSound )
  2848. {
  2849. if ( !BaseClass::QueryHearSound( pSound ) )
  2850. return false;
  2851. if ( pSound->m_iType == SOUND_BUGBAIT )
  2852. {
  2853. //Must be more recent than the current
  2854. if ( pSound->SoundExpirationTime() <= m_flIgnoreSoundTime )
  2855. return false;
  2856. //If we can hear it, store it
  2857. m_bHasHeardSound = (pSound != NULL);
  2858. if ( m_bHasHeardSound )
  2859. {
  2860. m_vecHeardSound = pSound->GetSoundOrigin();
  2861. m_flIgnoreSoundTime = pSound->SoundExpirationTime();
  2862. }
  2863. }
  2864. //Do the normal behavior at this point
  2865. return true;
  2866. }
  2867. //-----------------------------------------------------------------------------
  2868. // Purpose: Allows for modification of the interrupt mask for the current schedule.
  2869. // In the most cases the base implementation should be called first.
  2870. //-----------------------------------------------------------------------------
  2871. void CNPC_Antlion::BuildScheduleTestBits( void )
  2872. {
  2873. //Don't allow any modifications when scripted
  2874. if ( m_NPCState == NPC_STATE_SCRIPT )
  2875. return;
  2876. // If we're allied with the player, don't be startled by him
  2877. if ( IsAllied() )
  2878. {
  2879. ClearCustomInterruptCondition( COND_HEAR_PLAYER );
  2880. SetCustomInterruptCondition( COND_PLAYER_PUSHING );
  2881. }
  2882. //Make sure we interrupt a run schedule if we can jump
  2883. if ( IsCurSchedule(SCHED_CHASE_ENEMY) )
  2884. {
  2885. SetCustomInterruptCondition( COND_ANTLION_CAN_JUMP );
  2886. SetCustomInterruptCondition( COND_ENEMY_UNREACHABLE );
  2887. }
  2888. if ( !IsCurSchedule( SCHED_ANTLION_DROWN ) )
  2889. {
  2890. // Interrupt any schedule unless already drowning.
  2891. SetCustomInterruptCondition( COND_ANTLION_IN_WATER );
  2892. }
  2893. else
  2894. {
  2895. // Don't stop drowning just because you're in water!
  2896. ClearCustomInterruptCondition( COND_ANTLION_IN_WATER );
  2897. }
  2898. // Make sure we don't stop in midair
  2899. /*
  2900. if ( GetActivity() == ACT_JUMP || GetActivity() == ACT_GLIDE || GetActivity() == ACT_LAND )
  2901. {
  2902. ClearCustomInterruptCondition( COND_NEW_ENEMY );
  2903. }
  2904. */
  2905. //Interrupt any schedule unless already fleeing, burrowing, burrowed, or unburrowing.
  2906. if( !IsCurSchedule(SCHED_ANTLION_FLEE_THUMPER) &&
  2907. !IsCurSchedule(SCHED_ANTLION_FLEE_PHYSICS_DANGER) &&
  2908. !IsCurSchedule(SCHED_ANTLION_BURROW_IN) &&
  2909. !IsCurSchedule(SCHED_ANTLION_WAIT_UNBORROW) &&
  2910. !IsCurSchedule(SCHED_ANTLION_BURROW_OUT) &&
  2911. !IsCurSchedule(SCHED_ANTLION_BURROW_WAIT) &&
  2912. !IsCurSchedule(SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER)&&
  2913. !IsCurSchedule(SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW)&&
  2914. !IsCurSchedule(SCHED_ANTLION_WAIT_UNBORROW) &&
  2915. !IsCurSchedule(SCHED_ANTLION_JUMP) &&
  2916. !IsCurSchedule(SCHED_ANTLION_FLIP) &&
  2917. !IsCurSchedule(SCHED_ANTLION_DISMOUNT_NPC) &&
  2918. ( GetFlags() & FL_ONGROUND ) )
  2919. {
  2920. // Only do these if not jumping as well
  2921. if (!IsCurSchedule(SCHED_ANTLION_JUMP))
  2922. {
  2923. if ( GetEnemy() == NULL )
  2924. {
  2925. SetCustomInterruptCondition( COND_HEAR_PHYSICS_DANGER );
  2926. }
  2927. SetCustomInterruptCondition( COND_HEAR_THUMPER );
  2928. SetCustomInterruptCondition( COND_HEAR_BUGBAIT );
  2929. SetCustomInterruptCondition( COND_ANTLION_FLIPPED );
  2930. SetCustomInterruptCondition( COND_ANTLION_CAN_JUMP_AT_TARGET );
  2931. if ( GetNavType() != NAV_JUMP )
  2932. SetCustomInterruptCondition( COND_ANTLION_RECEIVED_ORDERS );
  2933. }
  2934. SetCustomInterruptCondition( COND_ANTLION_ON_NPC );
  2935. }
  2936. }
  2937. //-----------------------------------------------------------------------------
  2938. // Purpose:
  2939. // Input : *pEnemy -
  2940. // Output : Returns true on success, false on failure.
  2941. //-----------------------------------------------------------------------------
  2942. bool CNPC_Antlion::IsValidEnemy( CBaseEntity *pEnemy )
  2943. {
  2944. //See if antlions are friendly to the player in this map
  2945. if ( IsAllied() && pEnemy->IsPlayer() )
  2946. return false;
  2947. if ( pEnemy->IsWorld() )
  2948. return false;
  2949. //If we're chasing bugbait, close to within a certain radius before picking up enemies
  2950. if ( IsCurSchedule( GetGlobalScheduleId( SCHED_ANTLION_CHASE_BUGBAIT ) ) && ( GetNavigator() != NULL ) )
  2951. {
  2952. //If the enemy is without the target radius, then don't allow them
  2953. if ( ( GetNavigator()->IsGoalActive() ) && ( GetNavigator()->GetGoalPos() - pEnemy->GetAbsOrigin() ).Length() > bugbait_radius.GetFloat() )
  2954. return false;
  2955. }
  2956. // If we're following an entity we limit our attack distances
  2957. if ( m_FollowBehavior.GetFollowTarget() != NULL )
  2958. {
  2959. float enemyDist = ( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
  2960. if ( m_flObeyFollowTime > gpGlobals->curtime )
  2961. {
  2962. // Unless we're right next to the enemy, follow our target
  2963. if ( enemyDist > (128*128) )
  2964. return false;
  2965. }
  2966. else
  2967. {
  2968. // Otherwise don't follow if the target is far
  2969. if ( enemyDist > (2000*2000) )
  2970. return false;
  2971. }
  2972. }
  2973. return BaseClass::IsValidEnemy( pEnemy );
  2974. }
  2975. //-----------------------------------------------------------------------------
  2976. // Purpose:
  2977. //-----------------------------------------------------------------------------
  2978. void CNPC_Antlion::GatherConditions( void )
  2979. {
  2980. BaseClass::GatherConditions();
  2981. // See if I've landed on an NPC!
  2982. CBaseEntity *pGroundEnt = GetGroundEntity();
  2983. if ( ( ( pGroundEnt != NULL ) && ( pGroundEnt->GetSolidFlags() & FSOLID_NOT_STANDABLE ) ) && ( GetFlags() & FL_ONGROUND ) && ( !IsEffectActive( EF_NODRAW ) && !pGroundEnt->IsEffectActive( EF_NODRAW ) ) )
  2984. {
  2985. SetCondition( COND_ANTLION_ON_NPC );
  2986. }
  2987. else
  2988. {
  2989. ClearCondition( COND_ANTLION_ON_NPC );
  2990. }
  2991. // See if our follow target is too far off
  2992. /* if ( m_hFollowTarget != NULL )
  2993. {
  2994. float targetDist = UTIL_DistApprox( WorldSpaceCenter(), m_hFollowTarget->GetAbsOrigin() );
  2995. if ( targetDist > 400 )
  2996. {
  2997. SetCondition( COND_ANTLION_FOLLOW_TARGET_TOO_FAR );
  2998. }
  2999. else
  3000. {
  3001. ClearCondition( COND_ANTLION_FOLLOW_TARGET_TOO_FAR );
  3002. }
  3003. }*/
  3004. if ( IsCurSchedule( SCHED_ANTLION_BURROW_WAIT ) == false &&
  3005. IsCurSchedule(SCHED_ANTLION_BURROW_IN) == false &&
  3006. IsCurSchedule(SCHED_ANTLION_BURROW_OUT) == false &&
  3007. IsCurSchedule(SCHED_FALL_TO_GROUND ) == false &&
  3008. IsEffectActive( EF_NODRAW ) == false )
  3009. {
  3010. if( m_lifeState == LIFE_ALIVE && GetWaterLevel() > 1 )
  3011. {
  3012. // Start Drowning!
  3013. SetCondition( COND_ANTLION_IN_WATER );
  3014. }
  3015. }
  3016. //Ignore the player pushing me if I'm flipped over!
  3017. if ( IsCurSchedule( SCHED_ANTLION_FLIP ) )
  3018. ClearCondition( COND_PLAYER_PUSHING );
  3019. }
  3020. //-----------------------------------------------------------------------------
  3021. // Purpose:
  3022. //-----------------------------------------------------------------------------
  3023. void CNPC_Antlion::PrescheduleThink( void )
  3024. {
  3025. UpdateHead();
  3026. Activity eActivity = GetActivity();
  3027. //See if we need to play their agitated sound
  3028. if ( ( eActivity == ACT_ANTLION_RUN_AGITATED ) && ( m_bAgitatedSound == false ) )
  3029. {
  3030. //Start sound
  3031. CPASAttenuationFilter filter( this, "NPC_Antlion.LoopingAgitated" );
  3032. filter.MakeReliable();
  3033. EmitSound( filter, entindex(), "NPC_Antlion.LoopingAgitated" );
  3034. m_bAgitatedSound = true;
  3035. }
  3036. else if ( ( eActivity != ACT_ANTLION_RUN_AGITATED ) && ( m_bAgitatedSound == true ) )
  3037. {
  3038. //Stop sound
  3039. StopSound( "NPC_Antlion.LoopingAgitated" );
  3040. m_bAgitatedSound = false;
  3041. }
  3042. //See if our wings got interrupted from being turned off
  3043. if ( ( m_bWingsOpen ) &&
  3044. ( eActivity != ACT_ANTLION_JUMP_START ) &&
  3045. ( eActivity != ACT_JUMP ) &&
  3046. ( eActivity != ACT_GLIDE ) &&
  3047. ( eActivity != ACT_ANTLION_LAND ) &&
  3048. ( eActivity != ACT_ANTLION_DISTRACT ))
  3049. {
  3050. SetWings( false );
  3051. }
  3052. // Make sure we've turned off our burrow state if we're not in it
  3053. if ( IsEffectActive( EF_NODRAW ) &&
  3054. ( eActivity != ACT_ANTLION_BURROW_IDLE ) &&
  3055. ( eActivity != ACT_ANTLION_BURROW_OUT ) &&
  3056. ( eActivity != ACT_ANTLION_BURROW_IN) )
  3057. {
  3058. DevMsg( "Antlion failed to unburrow properly!\n" );
  3059. Assert( 0 );
  3060. RemoveEffects( EF_NODRAW );
  3061. RemoveSolidFlags( FSOLID_NOT_SOLID );
  3062. m_takedamage = DAMAGE_YES;
  3063. RemoveFlag( FL_NOTARGET );
  3064. m_spawnflags &= ~SF_NPC_GAG;
  3065. }
  3066. //New Enemy? Try to jump at him.
  3067. if ( HasCondition( COND_NEW_ENEMY ) )
  3068. {
  3069. m_flJumpTime = 0.0f;
  3070. }
  3071. // See if we should jump because of desirables conditions, or a scripted request
  3072. if ( ShouldJump() )
  3073. {
  3074. SetCondition( COND_ANTLION_CAN_JUMP );
  3075. }
  3076. else
  3077. {
  3078. ClearCondition( COND_ANTLION_CAN_JUMP );
  3079. }
  3080. BaseClass::PrescheduleThink();
  3081. }
  3082. //-----------------------------------------------------------------------------
  3083. // Purpose:
  3084. // Input : flDamage -
  3085. // bitsDamageType -
  3086. // Output : Returns true on success, false on failure.
  3087. //-----------------------------------------------------------------------------
  3088. bool CNPC_Antlion::IsLightDamage( const CTakeDamageInfo &info )
  3089. {
  3090. if ( ( random->RandomInt( 0, 1 ) ) && ( info.GetDamage() > 3 ) )
  3091. return true;
  3092. return false;
  3093. }
  3094. //-----------------------------------------------------------------------------
  3095. // Purpose:
  3096. // Output : Returns true on success, false on failure.
  3097. //-----------------------------------------------------------------------------
  3098. bool CNPC_Antlion::IsAllied( void )
  3099. {
  3100. return ( GlobalEntity_GetState( "antlion_allied" ) == GLOBAL_ON );
  3101. }
  3102. //-----------------------------------------------------------------------------
  3103. // Purpose:
  3104. // Output : Returns true on success, false on failure.
  3105. //-----------------------------------------------------------------------------
  3106. bool CNPC_Antlion::ShouldResumeFollow( void )
  3107. {
  3108. if ( IsAllied() == false )
  3109. return false;
  3110. if ( m_MoveState == ANTLION_MOVE_FOLLOW || m_hFollowTarget == NULL )
  3111. return false;
  3112. if ( m_flSuppressFollowTime > gpGlobals->curtime )
  3113. return false;
  3114. if ( GetEnemy() != NULL )
  3115. {
  3116. m_flSuppressFollowTime = gpGlobals->curtime + random->RandomInt( 5, 10 );
  3117. return false;
  3118. }
  3119. //TODO: See if the follow target has wandered off too far from where we last followed them to
  3120. return true;
  3121. }
  3122. //-----------------------------------------------------------------------------
  3123. // Purpose:
  3124. // Output : Returns true on success, false on failure.
  3125. //-----------------------------------------------------------------------------
  3126. bool CNPC_Antlion::ShouldAbandonFollow( void )
  3127. {
  3128. // Never give up if we can see the goal
  3129. if ( m_FollowBehavior.FollowTargetVisible() )
  3130. return false;
  3131. // Never give up if we're too close
  3132. float flDistance = UTIL_DistApprox2D( m_FollowBehavior.GetFollowTarget()->WorldSpaceCenter(), WorldSpaceCenter() );
  3133. if ( flDistance < 1500 )
  3134. return false;
  3135. if ( flDistance > 1500 * 2.0f )
  3136. return true;
  3137. // If we've failed too many times, give up
  3138. if ( m_FollowBehavior.GetNumFailedFollowAttempts() )
  3139. return true;
  3140. // If the target simply isn't reachable to us, give up
  3141. if ( m_FollowBehavior.TargetIsUnreachable() )
  3142. return true;
  3143. return false;
  3144. }
  3145. //-----------------------------------------------------------------------------
  3146. // Purpose:
  3147. // Input : *pTarget -
  3148. //-----------------------------------------------------------------------------
  3149. void CNPC_Antlion::SetFightTarget( CBaseEntity *pTarget )
  3150. {
  3151. m_hFightGoalTarget = pTarget;
  3152. SetCondition( COND_ANTLION_RECEIVED_ORDERS );
  3153. }
  3154. //-----------------------------------------------------------------------------
  3155. // Purpose:
  3156. // Input : &inputdata -
  3157. //-----------------------------------------------------------------------------
  3158. void CNPC_Antlion::InputFightToPosition( inputdata_t &inputdata )
  3159. {
  3160. if ( IsAlive() == false )
  3161. return;
  3162. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller );
  3163. if ( pEntity != NULL )
  3164. {
  3165. SetFightTarget( pEntity );
  3166. SetFollowTarget( NULL );
  3167. }
  3168. }
  3169. //-----------------------------------------------------------------------------
  3170. // Purpose:
  3171. // Input : &inputdata -
  3172. //-----------------------------------------------------------------------------
  3173. void CNPC_Antlion::InputStopFightToPosition( inputdata_t &inputdata )
  3174. {
  3175. SetFightTarget( NULL );
  3176. }
  3177. //-----------------------------------------------------------------------------
  3178. // Purpose:
  3179. // Input : *pEnemy -
  3180. //-----------------------------------------------------------------------------
  3181. void CNPC_Antlion::GatherEnemyConditions( CBaseEntity *pEnemy )
  3182. {
  3183. // Do the base class
  3184. BaseClass::GatherEnemyConditions( pEnemy );
  3185. // Only continue if we burrow when eluded
  3186. if ( ( m_spawnflags & SF_ANTLION_BURROW_ON_ELUDED ) == false )
  3187. return;
  3188. // If we're not already too far away, check again
  3189. //TODO: Check to make sure we don't already have a condition set that removes the need for this
  3190. if ( HasCondition( COND_ENEMY_UNREACHABLE ) == false )
  3191. {
  3192. Vector predPosition;
  3193. UTIL_PredictedPosition( GetEnemy(), 1.0f, &predPosition );
  3194. Vector predDir = ( predPosition - GetAbsOrigin() );
  3195. float predLength = VectorNormalize( predDir );
  3196. // See if we'll be outside our effective target range
  3197. if ( predLength > m_flEludeDistance )
  3198. {
  3199. Vector predVelDir = ( predPosition - GetEnemy()->GetAbsOrigin() );
  3200. float predSpeed = VectorNormalize( predVelDir );
  3201. // See if the enemy is moving mostly away from us
  3202. if ( ( predSpeed > 512.0f ) && ( DotProduct( predVelDir, predDir ) > 0.0f ) )
  3203. {
  3204. // Mark the enemy as eluded and burrow away
  3205. ClearEnemyMemory();
  3206. SetEnemy( NULL );
  3207. SetIdealState( NPC_STATE_ALERT );
  3208. SetCondition( COND_ENEMY_UNREACHABLE );
  3209. }
  3210. }
  3211. }
  3212. }
  3213. //-----------------------------------------------------------------------------
  3214. // Purpose:
  3215. // Input : &info -
  3216. // Output : Returns true on success, false on failure.
  3217. //-----------------------------------------------------------------------------
  3218. bool CNPC_Antlion::ShouldGib( const CTakeDamageInfo &info )
  3219. {
  3220. // If we're being hoisted, we only want to gib when the barnacle hurts us with his bite!
  3221. if ( IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
  3222. {
  3223. if ( info.GetAttacker() && info.GetAttacker()->Classify() != CLASS_BARNACLE )
  3224. return false;
  3225. return true;
  3226. }
  3227. if ( info.GetDamageType() & (DMG_NEVERGIB|DMG_DISSOLVE) )
  3228. return false;
  3229. #ifdef HL2_EPISODIC
  3230. if ( IsWorker() && ANTLION_WORKERS_BURST() )
  3231. return !m_bDontExplode;
  3232. #endif
  3233. if ( info.GetDamageType() & (DMG_ALWAYSGIB|DMG_BLAST) )
  3234. return true;
  3235. if ( m_iHealth < -20 )
  3236. return true;
  3237. return false;
  3238. }
  3239. //-----------------------------------------------------------------------------
  3240. // Purpose:
  3241. // Output : Returns true on success, false on failure.
  3242. //-----------------------------------------------------------------------------
  3243. bool CNPC_Antlion::CorpseGib( const CTakeDamageInfo &info )
  3244. {
  3245. #ifdef HL2_EPISODIC
  3246. if ( IsWorker() )
  3247. {
  3248. DoPoisonBurst();
  3249. }
  3250. else
  3251. #endif // HL2_EPISODIC
  3252. {
  3253. // Use the bone position to handle being moved by an animation (like a dynamic scripted sequence)
  3254. static int s_nBodyBone = -1;
  3255. if ( s_nBodyBone == -1 )
  3256. {
  3257. s_nBodyBone = LookupBone( "Antlion.Body_Bone" );
  3258. }
  3259. Vector vecOrigin;
  3260. QAngle angBone;
  3261. GetBonePosition( s_nBodyBone, vecOrigin, angBone );
  3262. DispatchParticleEffect( "AntlionGib", vecOrigin, QAngle( 0, 0, 0 ) );
  3263. }
  3264. Vector velocity = vec3_origin;
  3265. AngularImpulse angVelocity = RandomAngularImpulse( -150, 150 );
  3266. breakablepropparams_t params( EyePosition(), GetAbsAngles(), velocity, angVelocity );
  3267. params.impactEnergyScale = 1.0f;
  3268. params.defBurstScale = 150.0f;
  3269. params.defCollisionGroup = COLLISION_GROUP_DEBRIS;
  3270. PropBreakableCreateAll( GetModelIndex(), NULL, params, this, -1, true, true );
  3271. return true;
  3272. }
  3273. //-----------------------------------------------------------------------------
  3274. // Purpose:
  3275. // Input : *pOther -
  3276. //-----------------------------------------------------------------------------
  3277. void CNPC_Antlion::Touch( CBaseEntity *pOther )
  3278. {
  3279. //See if the touching entity is a vehicle
  3280. CBasePlayer *pPlayer = ToBasePlayer( AI_GetSinglePlayer() );
  3281. // FIXME: Technically we'll want to check to see if a vehicle has touched us with the player OR NPC driver
  3282. if ( pPlayer && pPlayer->IsInAVehicle() )
  3283. {
  3284. IServerVehicle *pVehicle = pPlayer->GetVehicle();
  3285. CBaseEntity *pVehicleEnt = pVehicle->GetVehicleEnt();
  3286. if ( pVehicleEnt == pOther )
  3287. {
  3288. CPropVehicleDriveable *pDrivableVehicle = dynamic_cast<CPropVehicleDriveable *>( pVehicleEnt );
  3289. if ( pDrivableVehicle != NULL )
  3290. {
  3291. //Get tossed!
  3292. Vector vecShoveDir = pOther->GetAbsVelocity();
  3293. Vector vecTargetDir = GetAbsOrigin() - pOther->GetAbsOrigin();
  3294. VectorNormalize( vecShoveDir );
  3295. VectorNormalize( vecTargetDir );
  3296. bool bBurrowingOut = IsCurSchedule( SCHED_ANTLION_BURROW_OUT );
  3297. if ( ( ( pDrivableVehicle->m_nRPM > 75 ) && DotProduct( vecShoveDir, vecTargetDir ) <= 0 ) || bBurrowingOut == true )
  3298. {
  3299. if ( IsFlipped() || bBurrowingOut == true )
  3300. {
  3301. float flDamage = m_iHealth;
  3302. if ( random->RandomInt( 0, 10 ) > 4 )
  3303. flDamage += 25;
  3304. CTakeDamageInfo dmgInfo( pVehicleEnt, pPlayer, flDamage, DMG_VEHICLE );
  3305. CalculateMeleeDamageForce( &dmgInfo, vecShoveDir, pOther->GetAbsOrigin() );
  3306. TakeDamage( dmgInfo );
  3307. }
  3308. else
  3309. {
  3310. // We're being shoved
  3311. CTakeDamageInfo dmgInfo( pVehicleEnt, pPlayer, 0, DMG_VEHICLE );
  3312. PainSound( dmgInfo );
  3313. SetCondition( COND_ANTLION_FLIPPED );
  3314. vecTargetDir[2] = 0.0f;
  3315. ApplyAbsVelocityImpulse( ( vecTargetDir * 250.0f ) + Vector(0,0,64.0f) );
  3316. SetGroundEntity( NULL );
  3317. CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, GetAbsOrigin(), 256, 0.5f, this );
  3318. }
  3319. }
  3320. }
  3321. }
  3322. }
  3323. BaseClass::Touch( pOther );
  3324. // in episodic, an antlion colliding with the player in midair does him damage.
  3325. // pursuant bugs 58590, 56960, this happens only once per glide.
  3326. #ifdef HL2_EPISODIC
  3327. if ( GetActivity() == ACT_GLIDE && IsValidEnemy( pOther ) && !m_bHasDoneAirAttack )
  3328. {
  3329. CTakeDamageInfo dmgInfo( this, this, sk_antlion_air_attack_dmg.GetInt(), DMG_SLASH );
  3330. CalculateMeleeDamageForce( &dmgInfo, Vector( 0, 0, 1 ), GetAbsOrigin() );
  3331. pOther->TakeDamage( dmgInfo );
  3332. //Kick the player angles
  3333. bool bIsPlayer = pOther->IsPlayer();
  3334. if ( bIsPlayer && !(pOther->GetFlags() & FL_GODMODE ) && pOther->GetMoveType() != MOVETYPE_NOCLIP )
  3335. {
  3336. pOther->ViewPunch( QAngle( 4.0f, 0.0f, 0.0f ) );
  3337. }
  3338. // set my "I have already attacked someone" flag
  3339. if ( bIsPlayer || pOther->IsNPC())
  3340. {
  3341. m_bHasDoneAirAttack = true;
  3342. }
  3343. }
  3344. #endif
  3345. // Did the player touch me?
  3346. if ( pOther->IsPlayer() )
  3347. {
  3348. // Don't test for this if the pusher isn't friendly
  3349. if ( IsValidEnemy( pOther ) )
  3350. return;
  3351. // Ignore if pissed at player
  3352. if ( m_afMemory & bits_MEMORY_PROVOKED )
  3353. return;
  3354. if ( !IsCurSchedule( SCHED_MOVE_AWAY ) && !IsCurSchedule( SCHED_ANTLION_BURROW_OUT ) )
  3355. TestPlayerPushing( pOther );
  3356. }
  3357. //Adrian: Explode if hit by gunship!
  3358. //Maybe only do this if hit by the propellers?
  3359. if ( pOther->IsNPC() )
  3360. {
  3361. if ( pOther->Classify() == CLASS_COMBINE_GUNSHIP )
  3362. {
  3363. float flDamage = m_iHealth + 25;
  3364. CTakeDamageInfo dmgInfo( pOther, pOther, flDamage, DMG_GENERIC );
  3365. GuessDamageForce( &dmgInfo, (pOther->GetAbsOrigin() - GetAbsOrigin()), pOther->GetAbsOrigin() );
  3366. TakeDamage( dmgInfo );
  3367. }
  3368. }
  3369. }
  3370. //-----------------------------------------------------------------------------
  3371. // Purpose: turn in the direction of movement
  3372. // Output :
  3373. //-----------------------------------------------------------------------------
  3374. bool CNPC_Antlion::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval )
  3375. {
  3376. if ( hl2_episodic.GetBool() )
  3377. {
  3378. if ( IsWorker() && GetEnemy() )
  3379. {
  3380. AddFacingTarget( GetEnemy(), GetEnemy()->WorldSpaceCenter(), 1.0f, 0.2f );
  3381. return BaseClass::OverrideMoveFacing( move, flInterval );
  3382. }
  3383. }
  3384. //Adrian: Make antlions face the thumper while they flee away.
  3385. if ( IsCurSchedule( SCHED_ANTLION_FLEE_THUMPER ) )
  3386. {
  3387. CSound *pSound = GetLoudestSoundOfType( SOUND_THUMPER );
  3388. if ( pSound )
  3389. {
  3390. AddFacingTarget( pSound->GetSoundOrigin(), 1.0, 0.5f );
  3391. }
  3392. }
  3393. else if ( GetEnemy() && GetNavigator()->GetMovementActivity() == ACT_RUN )
  3394. {
  3395. // FIXME: this will break scripted sequences that walk when they have an enemy
  3396. Vector vecEnemyLKP = GetEnemyLKP();
  3397. if ( UTIL_DistApprox( vecEnemyLKP, GetAbsOrigin() ) < 512 )
  3398. {
  3399. // Only start facing when we're close enough
  3400. AddFacingTarget( GetEnemy(), vecEnemyLKP, 1.0, 0.2 );
  3401. }
  3402. }
  3403. return BaseClass::OverrideMoveFacing( move, flInterval );
  3404. }
  3405. //-----------------------------------------------------------------------------
  3406. // Purpose:
  3407. //-----------------------------------------------------------------------------
  3408. void CNPC_Antlion::InputDisableJump( inputdata_t &inputdata )
  3409. {
  3410. m_bDisableJump = true;
  3411. CapabilitiesRemove( bits_CAP_MOVE_JUMP );
  3412. }
  3413. //-----------------------------------------------------------------------------
  3414. // Purpose:
  3415. //-----------------------------------------------------------------------------
  3416. void CNPC_Antlion::InputEnableJump( inputdata_t &inputdata )
  3417. {
  3418. m_bDisableJump = false;
  3419. CapabilitiesAdd( bits_CAP_MOVE_JUMP );
  3420. }
  3421. //-----------------------------------------------------------------------------
  3422. // Purpose:
  3423. // Input : *pTarget -
  3424. //-----------------------------------------------------------------------------
  3425. void CNPC_Antlion::SetFollowTarget( CBaseEntity *pTarget )
  3426. {
  3427. m_FollowBehavior.SetFollowTarget( pTarget );
  3428. m_hFollowTarget = pTarget;
  3429. m_flObeyFollowTime = gpGlobals->curtime + ANTLION_OBEY_FOLLOW_TIME;
  3430. SetCondition( COND_ANTLION_RECEIVED_ORDERS );
  3431. // Play an acknowledgement noise
  3432. if ( m_flNextAcknowledgeTime < gpGlobals->curtime )
  3433. {
  3434. EmitSound( "NPC_Antlion.Distracted" );
  3435. m_flNextAcknowledgeTime = gpGlobals->curtime + 1.0f;
  3436. }
  3437. }
  3438. //-----------------------------------------------------------------------------
  3439. // Purpose:
  3440. // Output : Returns true on success, false on failure.
  3441. //-----------------------------------------------------------------------------
  3442. bool CNPC_Antlion::CreateBehaviors( void )
  3443. {
  3444. AddBehavior( &m_FollowBehavior );
  3445. AddBehavior( &m_AssaultBehavior );
  3446. return BaseClass::CreateBehaviors();
  3447. }
  3448. //-----------------------------------------------------------------------------
  3449. // Purpose:
  3450. // Input : &inputdata -
  3451. //-----------------------------------------------------------------------------
  3452. void CNPC_Antlion::InputIgnoreBugbait( inputdata_t &inputdata )
  3453. {
  3454. m_bIgnoreBugbait = true;
  3455. }
  3456. //-----------------------------------------------------------------------------
  3457. // Purpose:
  3458. // Input : &inputdata -
  3459. //-----------------------------------------------------------------------------
  3460. void CNPC_Antlion::InputHearBugbait( inputdata_t &inputdata )
  3461. {
  3462. m_bIgnoreBugbait = false;
  3463. }
  3464. //-----------------------------------------------------------------------------
  3465. // Purpose:
  3466. // Input : state -
  3467. //-----------------------------------------------------------------------------
  3468. void CNPC_Antlion::SetMoveState( AntlionMoveState_e state )
  3469. {
  3470. m_MoveState = state;
  3471. switch( m_MoveState )
  3472. {
  3473. case ANTLION_MOVE_FOLLOW:
  3474. m_FollowBehavior.SetFollowTarget( m_hFollowTarget );
  3475. // Clear any previous state
  3476. m_flSuppressFollowTime = 0;
  3477. break;
  3478. case ANTLION_MOVE_FIGHT_TO_GOAL:
  3479. m_FollowBehavior.SetFollowTarget( NULL );
  3480. // Keep the time we started this
  3481. m_flSuppressFollowTime = gpGlobals->curtime + random->RandomInt( 10, 15 );
  3482. break;
  3483. default:
  3484. break;
  3485. }
  3486. }
  3487. //-----------------------------------------------------------------------------
  3488. // Purpose: Special version helps other NPCs hit overturned antlion
  3489. //-----------------------------------------------------------------------------
  3490. Vector CNPC_Antlion::BodyTarget( const Vector &posSrc, bool bNoisy /*= true*/ )
  3491. {
  3492. // Cache the bone away to avoid future lookups
  3493. if ( m_nBodyBone == -1 )
  3494. {
  3495. CBaseAnimating *pAnimating = GetBaseAnimating();
  3496. m_nBodyBone = pAnimating->LookupBone( "Antlion.Body_Bone" );
  3497. }
  3498. // Get the exact position in our center of mass (thorax)
  3499. Vector vecResult;
  3500. QAngle vecAngle;
  3501. GetBonePosition( m_nBodyBone, vecResult, vecAngle );
  3502. if ( bNoisy )
  3503. return vecResult + RandomVector( -8, 8 );
  3504. return vecResult;
  3505. }
  3506. //-----------------------------------------------------------------------------
  3507. // Purpose: Flip the antlion over
  3508. //-----------------------------------------------------------------------------
  3509. void CNPC_Antlion::Flip( bool bZapped /*= false*/ )
  3510. {
  3511. // We can't flip an already flipped antlion
  3512. if ( IsFlipped() )
  3513. return;
  3514. // Must be on the ground
  3515. if ( ( GetFlags() & FL_ONGROUND ) == false )
  3516. return;
  3517. // Can't be in a dynamic interation
  3518. if ( IsRunningDynamicInteraction() )
  3519. return;
  3520. SetCondition( COND_ANTLION_FLIPPED );
  3521. if ( bZapped )
  3522. {
  3523. m_flZapDuration = gpGlobals->curtime + SequenceDuration( SelectWeightedSequence( (Activity) ACT_ANTLION_ZAP_FLIP) ) + 0.1f;
  3524. EmitSound( "NPC_Antlion.ZappedFlip" );
  3525. }
  3526. }
  3527. //-----------------------------------------------------------------------------
  3528. // Purpose:
  3529. // Input : &inputdata -
  3530. //-----------------------------------------------------------------------------
  3531. void CNPC_Antlion::InputJumpAtTarget( inputdata_t &inputdata )
  3532. {
  3533. CBaseEntity *pJumpTarget = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller );
  3534. if ( pJumpTarget == NULL )
  3535. {
  3536. Msg("Unable to find jump target named (%s)\n", inputdata.value.String() );
  3537. return;
  3538. }
  3539. #if HL2_EPISODIC
  3540. // Try the jump
  3541. AIMoveTrace_t moveTrace;
  3542. Vector targetPos = pJumpTarget->GetAbsOrigin();
  3543. // initialize jump state
  3544. float minJumpHeight = 0.0;
  3545. float maxHorzVel = 800.0f;
  3546. // initial jump, sets baseline for minJumpHeight
  3547. Vector vecApex;
  3548. Vector rawJumpVel = GetMoveProbe()->CalcJumpLaunchVelocity(GetAbsOrigin(), targetPos, GetCurrentGravity() * GetJumpGravity(), &minJumpHeight, maxHorzVel, &vecApex );
  3549. if ( g_debug_antlion.GetInt() == 2 )
  3550. {
  3551. NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 5 );
  3552. NDebugOverlay::Line( GetAbsOrigin(), targetPos, 0, 255, 0, 0, 5 );
  3553. NDebugOverlay::Line( GetAbsOrigin(), rawJumpVel, 255, 255, 0, 0, 5 );
  3554. }
  3555. m_vecSavedJump = rawJumpVel;
  3556. #else
  3557. // Get the direction and speed to our target
  3558. Vector vecJumpDir = ( pJumpTarget->GetAbsOrigin() - GetAbsOrigin() );
  3559. VectorNormalize( vecJumpDir );
  3560. vecJumpDir *= 800.0f; // FIXME: We'd like to pass this in as a parameter, but comma delimited lists are bad
  3561. m_vecSavedJump = vecJumpDir;
  3562. #endif
  3563. SetCondition( COND_ANTLION_CAN_JUMP_AT_TARGET );
  3564. }
  3565. #if HL2_EPISODIC
  3566. //-----------------------------------------------------------------------------
  3567. // workers can explode.
  3568. //-----------------------------------------------------------------------------
  3569. void CNPC_Antlion::DoPoisonBurst()
  3570. {
  3571. if ( GetWaterLevel() < 2 )
  3572. {
  3573. CTakeDamageInfo info( this, this, sk_antlion_worker_burst_damage.GetFloat(), DMG_BLAST_SURFACE | ( ANTLION_WORKER_BURST_IS_POISONOUS() ? DMG_POISON : DMG_ACID ) );
  3574. RadiusDamage( info, GetAbsOrigin(), sk_antlion_worker_burst_radius.GetFloat(), CLASS_NONE, this );
  3575. DispatchParticleEffect( "antlion_gib_02", WorldSpaceCenter(), GetAbsAngles() );
  3576. }
  3577. else
  3578. {
  3579. CEffectData data;
  3580. data.m_vOrigin = WorldSpaceCenter();
  3581. data.m_flMagnitude = 100;
  3582. data.m_flScale = 128;
  3583. data.m_fFlags = ( SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE );
  3584. DispatchEffect( "WaterSurfaceExplosion", data );
  3585. }
  3586. EmitSound( "NPC_Antlion.PoisonBurstExplode" );
  3587. }
  3588. #endif
  3589. //-----------------------------------------------------------------------------
  3590. // Purpose:
  3591. //-----------------------------------------------------------------------------
  3592. bool CNPC_Antlion::IsHeavyDamage( const CTakeDamageInfo &info )
  3593. {
  3594. if ( hl2_episodic.GetBool() && IsWorker() )
  3595. {
  3596. if ( m_nSustainedDamage + info.GetDamage() > 6 )
  3597. return true;
  3598. }
  3599. return BaseClass::IsHeavyDamage( info );
  3600. }
  3601. //-----------------------------------------------------------------------------
  3602. // Purpose:
  3603. // Input : bForced -
  3604. // Output : Returns true on success, false on failure.
  3605. //-----------------------------------------------------------------------------
  3606. bool CNPC_Antlion::CanRunAScriptedNPCInteraction( bool bForced /*= false*/ )
  3607. {
  3608. // Workers shouldn't do DSS's because they explode
  3609. if ( IsWorker() )
  3610. return false;
  3611. return BaseClass::CanRunAScriptedNPCInteraction( bForced );
  3612. }
  3613. //---------------------------------------------------------
  3614. // Save/Restore
  3615. //---------------------------------------------------------
  3616. BEGIN_DATADESC( CAntlionRepellant )
  3617. DEFINE_KEYFIELD( m_flRepelRadius, FIELD_FLOAT, "repelradius" ),
  3618. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  3619. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  3620. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  3621. END_DATADESC()
  3622. static CUtlVector< CHandle< CAntlionRepellant > >m_hRepellantList;
  3623. CAntlionRepellant::~CAntlionRepellant()
  3624. {
  3625. m_hRepellantList.FindAndRemove( this );
  3626. }
  3627. void CAntlionRepellant::Spawn( void )
  3628. {
  3629. BaseClass::Spawn();
  3630. m_bEnabled = true;
  3631. m_hRepellantList.AddToTail( this );
  3632. }
  3633. void CAntlionRepellant::InputEnable( inputdata_t &inputdata )
  3634. {
  3635. m_bEnabled = true;
  3636. if ( m_hRepellantList.HasElement( this ) == false )
  3637. m_hRepellantList.AddToTail( this );
  3638. }
  3639. void CAntlionRepellant::InputDisable( inputdata_t &inputdata )
  3640. {
  3641. m_bEnabled = false;
  3642. m_hRepellantList.FindAndRemove( this );
  3643. }
  3644. float CAntlionRepellant::GetRadius( void )
  3645. {
  3646. if ( m_bEnabled == false )
  3647. return 0.0f;
  3648. return m_flRepelRadius;
  3649. }
  3650. void CAntlionRepellant::OnRestore( void )
  3651. {
  3652. BaseClass::OnRestore();
  3653. if ( m_bEnabled == true )
  3654. {
  3655. if ( m_hRepellantList.HasElement( this ) == false )
  3656. m_hRepellantList.AddToTail( this );
  3657. }
  3658. }
  3659. bool CAntlionRepellant::IsPositionRepellantFree( Vector vDesiredPos )
  3660. {
  3661. for ( int i = 0; i < m_hRepellantList.Count(); i++ )
  3662. {
  3663. if ( m_hRepellantList[i] )
  3664. {
  3665. CAntlionRepellant *pRep = m_hRepellantList[i].Get();
  3666. if ( pRep )
  3667. {
  3668. float flDist = (vDesiredPos - pRep->GetAbsOrigin()).Length();
  3669. if ( flDist <= pRep->GetRadius() )
  3670. return false;
  3671. }
  3672. }
  3673. }
  3674. return true;
  3675. }
  3676. LINK_ENTITY_TO_CLASS( point_antlion_repellant, CAntlionRepellant);
  3677. //-----------------------------------------------------------------------------
  3678. //
  3679. // Schedules
  3680. //
  3681. //-----------------------------------------------------------------------------
  3682. AI_BEGIN_CUSTOM_NPC( npc_antlion, CNPC_Antlion )
  3683. //Register our interactions
  3684. DECLARE_INTERACTION( g_interactionAntlionFoundTarget )
  3685. DECLARE_INTERACTION( g_interactionAntlionFiredAtTarget )
  3686. //Conditions
  3687. DECLARE_CONDITION( COND_ANTLION_FLIPPED )
  3688. DECLARE_CONDITION( COND_ANTLION_ON_NPC )
  3689. DECLARE_CONDITION( COND_ANTLION_CAN_JUMP )
  3690. DECLARE_CONDITION( COND_ANTLION_FOLLOW_TARGET_TOO_FAR )
  3691. DECLARE_CONDITION( COND_ANTLION_RECEIVED_ORDERS )
  3692. DECLARE_CONDITION( COND_ANTLION_IN_WATER )
  3693. DECLARE_CONDITION( COND_ANTLION_CAN_JUMP_AT_TARGET )
  3694. DECLARE_CONDITION( COND_ANTLION_SQUADMATE_KILLED )
  3695. //Squad slots
  3696. DECLARE_SQUADSLOT( SQUAD_SLOT_ANTLION_JUMP )
  3697. DECLARE_SQUADSLOT( SQUAD_SLOT_ANTLION_WORKER_FIRE )
  3698. //Tasks
  3699. DECLARE_TASK( TASK_ANTLION_SET_CHARGE_GOAL )
  3700. DECLARE_TASK( TASK_ANTLION_BURROW )
  3701. DECLARE_TASK( TASK_ANTLION_UNBURROW )
  3702. DECLARE_TASK( TASK_ANTLION_VANISH )
  3703. DECLARE_TASK( TASK_ANTLION_FIND_BURROW_IN_POINT )
  3704. DECLARE_TASK( TASK_ANTLION_FIND_BURROW_OUT_POINT )
  3705. DECLARE_TASK( TASK_ANTLION_BURROW_WAIT )
  3706. DECLARE_TASK( TASK_ANTLION_CHECK_FOR_UNBORROW )
  3707. DECLARE_TASK( TASK_ANTLION_JUMP )
  3708. DECLARE_TASK( TASK_ANTLION_WAIT_FOR_TRIGGER )
  3709. DECLARE_TASK( TASK_ANTLION_GET_THUMPER_ESCAPE_PATH )
  3710. DECLARE_TASK( TASK_ANTLION_GET_PATH_TO_BUGBAIT )
  3711. DECLARE_TASK( TASK_ANTLION_FACE_BUGBAIT )
  3712. DECLARE_TASK( TASK_ANTLION_DISMOUNT_NPC )
  3713. DECLARE_TASK( TASK_ANTLION_REACH_FIGHT_GOAL )
  3714. DECLARE_TASK( TASK_ANTLION_GET_PHYSICS_DANGER_ESCAPE_PATH )
  3715. DECLARE_TASK( TASK_ANTLION_FACE_JUMP )
  3716. DECLARE_TASK( TASK_ANTLION_DROWN )
  3717. DECLARE_TASK( TASK_ANTLION_GET_PATH_TO_RANDOM_NODE )
  3718. DECLARE_TASK( TASK_ANTLION_FIND_COVER_FROM_SAVEPOSITION )
  3719. //Activities
  3720. DECLARE_ACTIVITY( ACT_ANTLION_DISTRACT )
  3721. DECLARE_ACTIVITY( ACT_ANTLION_DISTRACT_ARRIVED )
  3722. DECLARE_ACTIVITY( ACT_ANTLION_JUMP_START )
  3723. DECLARE_ACTIVITY( ACT_ANTLION_BURROW_IN )
  3724. DECLARE_ACTIVITY( ACT_ANTLION_BURROW_OUT )
  3725. DECLARE_ACTIVITY( ACT_ANTLION_BURROW_IDLE )
  3726. DECLARE_ACTIVITY( ACT_ANTLION_RUN_AGITATED )
  3727. DECLARE_ACTIVITY( ACT_ANTLION_FLIP )
  3728. DECLARE_ACTIVITY( ACT_ANTLION_POUNCE )
  3729. DECLARE_ACTIVITY( ACT_ANTLION_POUNCE_MOVING )
  3730. DECLARE_ACTIVITY( ACT_ANTLION_DROWN )
  3731. DECLARE_ACTIVITY( ACT_ANTLION_LAND )
  3732. DECLARE_ACTIVITY( ACT_ANTLION_WORKER_EXPLODE )
  3733. DECLARE_ACTIVITY( ACT_ANTLION_ZAP_FLIP )
  3734. //Events
  3735. DECLARE_ANIMEVENT( AE_ANTLION_WALK_FOOTSTEP )
  3736. DECLARE_ANIMEVENT( AE_ANTLION_MELEE_HIT1 )
  3737. DECLARE_ANIMEVENT( AE_ANTLION_MELEE_HIT2 )
  3738. DECLARE_ANIMEVENT( AE_ANTLION_MELEE_POUNCE )
  3739. DECLARE_ANIMEVENT( AE_ANTLION_FOOTSTEP_SOFT )
  3740. DECLARE_ANIMEVENT( AE_ANTLION_FOOTSTEP_HEAVY )
  3741. DECLARE_ANIMEVENT( AE_ANTLION_START_JUMP )
  3742. DECLARE_ANIMEVENT( AE_ANTLION_BURROW_IN )
  3743. DECLARE_ANIMEVENT( AE_ANTLION_BURROW_OUT )
  3744. DECLARE_ANIMEVENT( AE_ANTLION_VANISH )
  3745. DECLARE_ANIMEVENT( AE_ANTLION_OPEN_WINGS )
  3746. DECLARE_ANIMEVENT( AE_ANTLION_CLOSE_WINGS )
  3747. DECLARE_ANIMEVENT( AE_ANTLION_MELEE1_SOUND )
  3748. DECLARE_ANIMEVENT( AE_ANTLION_MELEE2_SOUND )
  3749. DECLARE_ANIMEVENT( AE_ANTLION_WORKER_EXPLODE_SCREAM )
  3750. DECLARE_ANIMEVENT( AE_ANTLION_WORKER_EXPLODE_WARN )
  3751. DECLARE_ANIMEVENT( AE_ANTLION_WORKER_EXPLODE )
  3752. DECLARE_ANIMEVENT( AE_ANTLION_WORKER_SPIT )
  3753. DECLARE_ANIMEVENT( AE_ANTLION_WORKER_DONT_EXPLODE )
  3754. //Schedules
  3755. //==================================================
  3756. // Jump
  3757. //==================================================
  3758. DEFINE_SCHEDULE
  3759. (
  3760. SCHED_ANTLION_JUMP,
  3761. " Tasks"
  3762. " TASK_STOP_MOVING 0"
  3763. " TASK_ANTLION_FACE_JUMP 0"
  3764. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_JUMP_START"
  3765. " TASK_ANTLION_JUMP 0"
  3766. ""
  3767. " Interrupts"
  3768. " COND_TASK_FAILED"
  3769. )
  3770. //==================================================
  3771. // Wait for unborrow (once burrow has been triggered)
  3772. //==================================================
  3773. DEFINE_SCHEDULE
  3774. (
  3775. SCHED_ANTLION_WAIT_UNBORROW,
  3776. " Tasks"
  3777. " TASK_ANTLION_BURROW_WAIT 0"
  3778. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW"
  3779. ""
  3780. " Interrupts"
  3781. " COND_TASK_FAILED"
  3782. )
  3783. //==================================================
  3784. // Burrow Wait
  3785. //==================================================
  3786. DEFINE_SCHEDULE
  3787. (
  3788. SCHED_ANTLION_BURROW_WAIT,
  3789. " Tasks"
  3790. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLION_BURROW_WAIT"
  3791. " TASK_ANTLION_BURROW_WAIT 1"
  3792. " TASK_ANTLION_FIND_BURROW_OUT_POINT 1024"
  3793. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW"
  3794. ""
  3795. " Interrupts"
  3796. " COND_TASK_FAILED"
  3797. )
  3798. //==================================================
  3799. // Burrow In
  3800. //==================================================
  3801. DEFINE_SCHEDULE
  3802. (
  3803. SCHED_ANTLION_BURROW_IN,
  3804. " Tasks"
  3805. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  3806. " TASK_ANTLION_BURROW 0"
  3807. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_BURROW_IN"
  3808. " TASK_ANTLION_VANISH 0"
  3809. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ANTLION_BURROW_WAIT"
  3810. ""
  3811. " Interrupts"
  3812. " COND_TASK_FAILED"
  3813. )
  3814. //==================================================
  3815. // Run to burrow in
  3816. //==================================================
  3817. DEFINE_SCHEDULE
  3818. (
  3819. SCHED_ANTLION_RUN_TO_BURROW_IN,
  3820. " Tasks"
  3821. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  3822. " TASK_SET_TOLERANCE_DISTANCE 8"
  3823. " TASK_ANTLION_FIND_BURROW_IN_POINT 512"
  3824. " TASK_RUN_PATH 0"
  3825. " TASK_WAIT_FOR_MOVEMENT 0"
  3826. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ANTLION_BURROW_IN"
  3827. ""
  3828. " Interrupts"
  3829. " COND_TASK_FAILED"
  3830. " COND_GIVE_WAY"
  3831. " COND_CAN_MELEE_ATTACK1"
  3832. " COND_CAN_MELEE_ATTACK2"
  3833. )
  3834. //==================================================
  3835. // Burrow Out
  3836. //==================================================
  3837. DEFINE_SCHEDULE
  3838. (
  3839. SCHED_ANTLION_BURROW_OUT,
  3840. " Tasks"
  3841. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLION_BURROW_WAIT"
  3842. " TASK_ANTLION_UNBURROW 0"
  3843. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_BURROW_OUT"
  3844. ""
  3845. " Interrupts"
  3846. " COND_TASK_FAILED"
  3847. )
  3848. //==================================================
  3849. // Wait for unborrow (triggered)
  3850. //==================================================
  3851. DEFINE_SCHEDULE
  3852. (
  3853. SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER,
  3854. " Tasks"
  3855. " TASK_ANTLION_WAIT_FOR_TRIGGER 0"
  3856. ""
  3857. " Interrupts"
  3858. " COND_TASK_FAILED"
  3859. )
  3860. //==================================================
  3861. // Wait for clear burrow spot (triggered)
  3862. //==================================================
  3863. DEFINE_SCHEDULE
  3864. (
  3865. SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW,
  3866. " Tasks"
  3867. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLION_BURROW_WAIT"
  3868. " TASK_ANTLION_CHECK_FOR_UNBORROW 1"
  3869. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ANTLION_BURROW_OUT"
  3870. ""
  3871. " Interrupts"
  3872. " COND_TASK_FAILED"
  3873. )
  3874. //==================================================
  3875. // Run from the sound of a thumper!
  3876. //==================================================
  3877. DEFINE_SCHEDULE
  3878. (
  3879. SCHED_ANTLION_FLEE_THUMPER,
  3880. " Tasks"
  3881. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_IDLE_STAND"
  3882. " TASK_ANTLION_GET_THUMPER_ESCAPE_PATH 0"
  3883. " TASK_RUN_PATH 0"
  3884. " TASK_WAIT_FOR_MOVEMENT 0"
  3885. " TASK_STOP_MOVING 0"
  3886. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_DISTRACT_ARRIVED"
  3887. ""
  3888. " Interrupts"
  3889. " COND_TASK_FAILED"
  3890. " COND_ANTLION_FLIPPED"
  3891. )
  3892. //==================================================
  3893. // SCHED_ANTLION_CHASE_BUGBAIT
  3894. //==================================================
  3895. DEFINE_SCHEDULE
  3896. (
  3897. SCHED_ANTLION_CHASE_BUGBAIT,
  3898. " Tasks"
  3899. " TASK_STOP_MOVING 0"
  3900. " TASK_ANTLION_GET_PATH_TO_BUGBAIT 0"
  3901. " TASK_RUN_PATH 0"
  3902. " TASK_WAIT_FOR_MOVEMENT 0"
  3903. " TASK_STOP_MOVING 0"
  3904. " TASK_ANTLION_FACE_BUGBAIT 0"
  3905. ""
  3906. " Interrupts"
  3907. " COND_CAN_MELEE_ATTACK1"
  3908. " COND_SEE_ENEMY"
  3909. " COND_LIGHT_DAMAGE"
  3910. " COND_HEAVY_DAMAGE"
  3911. )
  3912. //==================================================
  3913. // SCHED_ANTLION_ZAP_FLIP
  3914. //==================================================
  3915. DEFINE_SCHEDULE
  3916. (
  3917. SCHED_ANTLION_ZAP_FLIP,
  3918. " Tasks"
  3919. " TASK_STOP_MOVING 0"
  3920. " TASK_RESET_ACTIVITY 0"
  3921. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_ZAP_FLIP"
  3922. " Interrupts"
  3923. " COND_TASK_FAILED"
  3924. )
  3925. //==================================================
  3926. // SCHED_ANTLION_FLIP
  3927. //==================================================
  3928. DEFINE_SCHEDULE
  3929. (
  3930. SCHED_ANTLION_FLIP,
  3931. " Tasks"
  3932. " TASK_STOP_MOVING 0"
  3933. " TASK_RESET_ACTIVITY 0"
  3934. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_FLIP"
  3935. " Interrupts"
  3936. " COND_TASK_FAILED"
  3937. )
  3938. //=========================================================
  3939. // Headcrab has landed atop another NPC. Get down!
  3940. //=========================================================
  3941. DEFINE_SCHEDULE
  3942. (
  3943. SCHED_ANTLION_DISMOUNT_NPC,
  3944. " Tasks"
  3945. " TASK_STOP_MOVING 0"
  3946. " TASK_ANTLION_DISMOUNT_NPC 0"
  3947. " Interrupts"
  3948. )
  3949. DEFINE_SCHEDULE
  3950. (
  3951. SCHED_ANTLION_RUN_TO_FIGHT_GOAL,
  3952. " Tasks"
  3953. " TASK_SET_TOLERANCE_DISTANCE 128"
  3954. " TASK_GET_PATH_TO_SAVEPOSITION 0"
  3955. " TASK_RUN_PATH 0"
  3956. " TASK_WAIT_FOR_MOVEMENT 0"
  3957. " TASK_ANTLION_REACH_FIGHT_GOAL 0"
  3958. " Interrupts"
  3959. " COND_NEW_ENEMY"
  3960. " COND_HEAVY_DAMAGE"
  3961. " COND_LIGHT_DAMAGE"
  3962. " COND_HEAVY_DAMAGE"
  3963. " COND_ANTLION_CAN_JUMP"
  3964. )
  3965. DEFINE_SCHEDULE
  3966. (
  3967. SCHED_ANTLION_RUN_TO_FOLLOW_GOAL,
  3968. " Tasks"
  3969. " TASK_SET_TOLERANCE_DISTANCE 128"
  3970. " TASK_GET_PATH_TO_SAVEPOSITION 0"
  3971. " TASK_RUN_PATH 0"
  3972. " TASK_WAIT_FOR_MOVEMENT 0"
  3973. " Interrupts"
  3974. " COND_NEW_ENEMY"
  3975. " COND_HEAVY_DAMAGE"
  3976. " COND_ANTLION_CAN_JUMP"
  3977. " COND_ANTLION_FOLLOW_TARGET_TOO_FAR"
  3978. )
  3979. DEFINE_SCHEDULE
  3980. (
  3981. SCHED_ANTLION_BUGBAIT_IDLE_STAND,
  3982. " Tasks"
  3983. " TASK_STOP_MOVING 0"
  3984. " TASK_FACE_PLAYER 0"
  3985. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  3986. " TASK_WAIT 2"
  3987. " Interrupts"
  3988. " COND_NEW_ENEMY"
  3989. " COND_HEAVY_DAMAGE"
  3990. " COND_LIGHT_DAMAGE"
  3991. " COND_HEAVY_DAMAGE"
  3992. " COND_HEAR_DANGER"
  3993. " COND_HEAR_COMBAT"
  3994. " COND_ANTLION_CAN_JUMP"
  3995. " COND_ANTLION_FOLLOW_TARGET_TOO_FAR"
  3996. " COND_GIVE_WAY"
  3997. )
  3998. DEFINE_SCHEDULE
  3999. (
  4000. SCHED_ANTLION_BURROW_AWAY,
  4001. " Tasks"
  4002. " TASK_STOP_MOVING 0"
  4003. " TASK_ANTLION_BURROW 0"
  4004. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_BURROW_IN"
  4005. " TASK_ANTLION_VANISH 1"
  4006. " Interrupts"
  4007. )
  4008. //==================================================
  4009. // Run from the sound of a physics crash
  4010. //==================================================
  4011. DEFINE_SCHEDULE
  4012. (
  4013. SCHED_ANTLION_FLEE_PHYSICS_DANGER,
  4014. " Tasks"
  4015. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
  4016. " TASK_ANTLION_GET_PHYSICS_DANGER_ESCAPE_PATH 1024"
  4017. " TASK_RUN_PATH 0"
  4018. " TASK_WAIT_FOR_MOVEMENT 0"
  4019. " TASK_STOP_MOVING 0"
  4020. ""
  4021. " Interrupts"
  4022. " COND_TASK_FAILED"
  4023. )
  4024. // Pounce forward at our enemy
  4025. DEFINE_SCHEDULE
  4026. (
  4027. SCHED_ANTLION_POUNCE,
  4028. " Tasks"
  4029. " TASK_STOP_MOVING 0"
  4030. " TASK_FACE_ENEMY 0"
  4031. " TASK_ANNOUNCE_ATTACK 1"
  4032. " TASK_RESET_ACTIVITY 0"
  4033. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_POUNCE"
  4034. " Interrupts"
  4035. " COND_TASK_FAILED"
  4036. )
  4037. // Pounce forward at our enemy
  4038. DEFINE_SCHEDULE
  4039. (
  4040. SCHED_ANTLION_POUNCE_MOVING,
  4041. " Tasks"
  4042. " TASK_STOP_MOVING 0"
  4043. " TASK_FACE_ENEMY 0"
  4044. " TASK_ANNOUNCE_ATTACK 1"
  4045. " TASK_RESET_ACTIVITY 0"
  4046. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ANTLION_POUNCE_MOVING"
  4047. " Interrupts"
  4048. " COND_TASK_FAILED"
  4049. )
  4050. //=========================================================
  4051. // The irreversible process of drowning
  4052. //=========================================================
  4053. DEFINE_SCHEDULE
  4054. (
  4055. SCHED_ANTLION_DROWN,
  4056. " Tasks"
  4057. " TASK_SET_ACTIVITY ACTIVITY:ACT_ANTLION_DROWN"
  4058. " TASK_ANTLION_DROWN 0"
  4059. ""
  4060. " Interrupts"
  4061. )
  4062. DEFINE_SCHEDULE
  4063. (
  4064. SCHED_ANTLION_WORKER_RANGE_ATTACK1,
  4065. " Tasks"
  4066. " TASK_STOP_MOVING 0"
  4067. " TASK_FACE_ENEMY 0"
  4068. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4069. " TASK_RANGE_ATTACK1 0"
  4070. ""
  4071. " Interrupts"
  4072. " COND_TASK_FAILED"
  4073. " COND_NEW_ENEMY"
  4074. " COND_ENEMY_DEAD"
  4075. )
  4076. DEFINE_SCHEDULE
  4077. (
  4078. SCHED_ANTLION_WORKER_FLANK_RANDOM,
  4079. " Tasks"
  4080. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLION_WORKER_RUN_RANDOM"
  4081. " TASK_SET_TOLERANCE_DISTANCE 48"
  4082. " TASK_SET_ROUTE_SEARCH_TIME 1" // Spend 1 second trying to build a path if stuck
  4083. " TASK_GET_FLANK_ARC_PATH_TO_ENEMY_LOS 30"
  4084. " TASK_RUN_PATH 0"
  4085. " TASK_WAIT_FOR_MOVEMENT 0"
  4086. ""
  4087. " Interrupts"
  4088. " COND_TASK_FAILED"
  4089. " COND_HEAVY_DAMAGE"
  4090. " COND_ANTLION_SQUADMATE_KILLED"
  4091. " COND_CAN_RANGE_ATTACK1"
  4092. " COND_CAN_MELEE_ATTACK1"
  4093. )
  4094. DEFINE_SCHEDULE
  4095. (
  4096. SCHED_ANTLION_WORKER_RUN_RANDOM,
  4097. " Tasks"
  4098. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ANTLION_TAKE_COVER_FROM_ENEMY"
  4099. " TASK_SET_TOLERANCE_DISTANCE 48"
  4100. " TASK_SET_ROUTE_SEARCH_TIME 1" // Spend 1 second trying to build a path if stuck
  4101. " TASK_GET_PATH_TO_RANDOM_NODE 128"
  4102. " TASK_RUN_PATH 0"
  4103. " TASK_WAIT_FOR_MOVEMENT 0"
  4104. ""
  4105. " Interrupts"
  4106. " COND_TASK_FAILED"
  4107. " COND_CAN_RANGE_ATTACK1"
  4108. )
  4109. DEFINE_SCHEDULE
  4110. (
  4111. SCHED_ANTLION_TAKE_COVER_FROM_ENEMY,
  4112. " Tasks"
  4113. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_FAIL_TAKE_COVER"
  4114. " TASK_FIND_COVER_FROM_ENEMY 0"
  4115. " TASK_RUN_PATH 0"
  4116. " TASK_WAIT_FOR_MOVEMENT 0"
  4117. " TASK_STOP_MOVING 0"
  4118. ""
  4119. " Interrupts"
  4120. " COND_TASK_FAILED"
  4121. " COND_NEW_ENEMY"
  4122. )
  4123. DEFINE_SCHEDULE
  4124. (
  4125. SCHED_ANTLION_TAKE_COVER_FROM_SAVEPOSITION,
  4126. " Tasks"
  4127. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_FAIL_TAKE_COVER"
  4128. " TASK_ANTLION_FIND_COVER_FROM_SAVEPOSITION 0"
  4129. " TASK_RUN_PATH 0"
  4130. " TASK_WAIT_FOR_MOVEMENT 0"
  4131. " TASK_STOP_MOVING 0"
  4132. ""
  4133. " Interrupts"
  4134. " COND_TASK_FAILED"
  4135. " COND_NEW_ENEMY"
  4136. )
  4137. AI_END_CUSTOM_NPC()
  4138. //-----------------------------------------------------------------------------
  4139. // Purpose: Whether or not the target is a worker class of antlion
  4140. // Output : Returns true on success, false on failure.
  4141. //-----------------------------------------------------------------------------
  4142. bool IsAntlionWorker( CBaseEntity *pEntity )
  4143. {
  4144. // Must at least be valid and an antlion
  4145. return ( pEntity != NULL &&
  4146. pEntity->Classify() == CLASS_ANTLION &&
  4147. pEntity->HasSpawnFlags( SF_ANTLION_WORKER ) &&
  4148. dynamic_cast<CNPC_Antlion *>(pEntity) != NULL ); // Save this as the last step
  4149. }
  4150. //-----------------------------------------------------------------------------
  4151. // Purpose: Whether or not the entity is a common antlion
  4152. // Output : Returns true on success, false on failure.
  4153. //-----------------------------------------------------------------------------
  4154. bool IsAntlion( CBaseEntity *pEntity )
  4155. {
  4156. // Must at least be valid and an antlion
  4157. return ( pEntity != NULL &&
  4158. pEntity->Classify() == CLASS_ANTLION &&
  4159. dynamic_cast<CNPC_Antlion *>(pEntity) != NULL ); // Save this as the last step
  4160. }
  4161. #ifdef HL2_EPISODIC
  4162. //-----------------------------------------------------------------------------
  4163. // Purpose: Used by other entities to judge the antlion worker's radius of damage
  4164. //-----------------------------------------------------------------------------
  4165. float AntlionWorkerBurstRadius( void )
  4166. {
  4167. return sk_antlion_worker_burst_radius.GetFloat();
  4168. }
  4169. #endif // HL2_EPISODIC