Guest User

npc_fire_antlion.cpp

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