Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1395 lines
36 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Houndeye - a spooky sonic dog.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npc_houndeye.h"
  9. #include "ai_default.h"
  10. #include "ai_node.h"
  11. #include "ai_route.h"
  12. #include "AI_Navigator.h"
  13. #include "AI_Motor.h"
  14. #include "ai_squad.h"
  15. #include "AI_TacticalServices.h"
  16. #include "soundent.h"
  17. #include "EntityList.h"
  18. #include "game.h"
  19. #include "activitylist.h"
  20. #include "hl2_shareddefs.h"
  21. #include "grenade_energy.h"
  22. #include "energy_wave.h"
  23. #include "ai_interactions.h"
  24. #include "ndebugoverlay.h"
  25. #include "npcevent.h"
  26. #include "player.h"
  27. #include "vstdlib/random.h"
  28. #include "engine/IEngineSound.h"
  29. #include "movevars_shared.h"
  30. // memdbgon must be the last include file in a .cpp file!!!
  31. #include "tier0/memdbgon.h"
  32. #define HOUNDEYE_MAX_ATTACK_RADIUS 500
  33. #define HOUNDEYE_MIN_ATTACK_RADIUS 100
  34. #define HOUNDEYE_EYE_FRAMES 4 // how many different switchable maps for the eye
  35. ConVar sk_Houndeye_health( "sk_Houndeye_health","0");
  36. ConVar sk_Houndeye_dmg_blast( "sk_Houndeye_dmg_blast","0");
  37. //=========================================================
  38. // Interactions
  39. //=========================================================
  40. int g_interactionHoundeyeGroupAttack = 0;
  41. int g_interactionHoundeyeGroupRetreat = 0;
  42. int g_interactionHoundeyeGroupRalley = 0;
  43. //=========================================================
  44. // Specialized Tasks
  45. //=========================================================
  46. enum
  47. {
  48. TASK_HOUND_CLOSE_EYE = LAST_SHARED_TASK,
  49. TASK_HOUND_OPEN_EYE,
  50. TASK_HOUND_THREAT_DISPLAY,
  51. TASK_HOUND_FALL_ASLEEP,
  52. TASK_HOUND_WAKE_UP,
  53. TASK_HOUND_HOP_BACK,
  54. TASK_HOUND_GET_PATH_TO_CIRCLE,
  55. TASK_HOUND_REVERSE_STRAFE_DIR,
  56. };
  57. //-----------------------------------------------------------------------------
  58. // Custom Conditions
  59. //-----------------------------------------------------------------------------
  60. enum Houndeye_Conds
  61. {
  62. COND_HOUND_GROUP_ATTACK = LAST_SHARED_CONDITION,
  63. COND_HOUND_GROUP_RETREAT,
  64. COND_HOUND_GROUP_RALLEY,
  65. };
  66. //=========================================================
  67. // Specialized Shedules
  68. //=========================================================
  69. enum
  70. {
  71. SCHED_HOUND_AGITATED = LAST_SHARED_SCHEDULE,
  72. SCHED_HOUND_HOP_RETREAT,
  73. SCHED_HOUND_RANGE_ATTACK1,
  74. SCHED_HOUND_ATTACK_STRAFE,
  75. SCHED_HOUND_ATTACK_STRAFE_REVERSE,
  76. SCHED_HOUND_GROUP_ATTACK,
  77. SCHED_HOUND_GROUP_RETREAT,
  78. SCHED_HOUND_CHASE_ENEMY,
  79. SCHED_HOUND_COVER_WAIT,
  80. SCHED_HOUND_GROUP_RALLEY,
  81. };
  82. //=========================================================
  83. // Specialized activities
  84. //=========================================================
  85. int ACT_HOUND_GUARD;
  86. //=========================================================
  87. // Monster's Anim Events Go Here
  88. //=========================================================
  89. #define HOUND_AE_WARN 1
  90. #define HOUND_AE_STARTATTACK 2
  91. #define HOUND_AE_THUMP 3
  92. #define HOUND_AE_ANGERSOUND1 4
  93. #define HOUND_AE_ANGERSOUND2 5
  94. #define HOUND_AE_HOPBACK 6
  95. #define HOUND_AE_CLOSE_EYE 7
  96. #define HOUND_AE_LEAP_HIT 8
  97. //-----------------------------------------------------------------------------
  98. // Purpose: Initialize the custom schedules
  99. // Input :
  100. // Output :
  101. //-----------------------------------------------------------------------------
  102. void CNPC_Houndeye::InitCustomSchedules(void)
  103. {
  104. INIT_CUSTOM_AI(CNPC_Houndeye);
  105. ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_CLOSE_EYE);
  106. ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_OPEN_EYE);
  107. ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_THREAT_DISPLAY);
  108. ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_FALL_ASLEEP);
  109. ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_WAKE_UP);
  110. ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_HOP_BACK);
  111. ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_GET_PATH_TO_CIRCLE);
  112. ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_REVERSE_STRAFE_DIR);
  113. ADD_CUSTOM_CONDITION(CNPC_Houndeye, COND_HOUND_GROUP_ATTACK);
  114. ADD_CUSTOM_CONDITION(CNPC_Houndeye, COND_HOUND_GROUP_RETREAT);
  115. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_HOP_RETREAT);
  116. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_RANGE_ATTACK1);
  117. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE);
  118. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE_REVERSE);
  119. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_ATTACK);
  120. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RETREAT);
  121. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_CHASE_ENEMY);
  122. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_COVER_WAIT);
  123. ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RALLEY);
  124. ADD_CUSTOM_ACTIVITY(CNPC_Houndeye, ACT_HOUND_GUARD);
  125. g_interactionHoundeyeGroupAttack = CBaseCombatCharacter::GetInteractionID();
  126. g_interactionHoundeyeGroupRetreat = CBaseCombatCharacter::GetInteractionID();
  127. g_interactionHoundeyeGroupRalley = CBaseCombatCharacter::GetInteractionID();
  128. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_HOP_RETREAT);
  129. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_RANGE_ATTACK1);
  130. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE);
  131. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE_REVERSE);
  132. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_ATTACK);
  133. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RETREAT);
  134. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_CHASE_ENEMY);
  135. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_COVER_WAIT);
  136. AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RALLEY);
  137. }
  138. LINK_ENTITY_TO_CLASS( npc_houndeye, CNPC_Houndeye );
  139. IMPLEMENT_CUSTOM_AI( npc_houndeye, CNPC_Houndeye );
  140. BEGIN_DATADESC( CNPC_Houndeye )
  141. DEFINE_FIELD( m_fAsleep, FIELD_BOOLEAN ),
  142. DEFINE_FIELD( m_fDontBlink, FIELD_BOOLEAN ),
  143. DEFINE_FIELD( m_flNextSecondaryAttack, FIELD_TIME ),
  144. DEFINE_FIELD( m_bLoopClockwise, FIELD_BOOLEAN ),
  145. DEFINE_FIELD( m_pEnergyWave, FIELD_CLASSPTR ),
  146. DEFINE_FIELD( m_flEndEnergyWaveTime, FIELD_TIME ),
  147. END_DATADESC()
  148. //=========================================================
  149. // Classify - indicates this monster's place in the
  150. // relationship table.
  151. //=========================================================
  152. Class_T CNPC_Houndeye::Classify ( void )
  153. {
  154. return CLASS_HOUNDEYE;
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose:
  158. // Input :
  159. // Output :
  160. //-----------------------------------------------------------------------------
  161. int CNPC_Houndeye::RangeAttack1Conditions ( float flDot, float flDist )
  162. {
  163. // I'm not allowed to attack if standing in another hound eye
  164. // (note houndeyes allowed to interpenetrate)
  165. trace_t tr;
  166. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,0.1),
  167. GetHullMins(), GetHullMaxs(),
  168. MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  169. if (tr.startsolid)
  170. {
  171. CBaseEntity *pEntity = tr.m_pEnt;
  172. if (pEntity->Classify() == CLASS_HOUNDEYE)
  173. {
  174. return( COND_NONE );
  175. }
  176. }
  177. // If I'm really close to my enemy allow me to attack if
  178. // I'm facing regardless of next attack time
  179. if (flDist < 100 && flDot >= 0.3)
  180. {
  181. return COND_CAN_RANGE_ATTACK1;
  182. }
  183. if ( gpGlobals->curtime < m_flNextAttack )
  184. {
  185. return( COND_NONE );
  186. }
  187. if (flDist > ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 ))
  188. {
  189. return COND_TOO_FAR_TO_ATTACK;
  190. }
  191. if (flDot < 0.3)
  192. {
  193. return COND_NOT_FACING_ATTACK;
  194. }
  195. return COND_CAN_RANGE_ATTACK1;
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose: Overidden for human grunts because they hear the DANGER sound
  199. // Input :
  200. // Output :
  201. //-----------------------------------------------------------------------------
  202. int CNPC_Houndeye::GetSoundInterests( void )
  203. {
  204. return SOUND_WORLD |
  205. SOUND_COMBAT |
  206. SOUND_PLAYER |
  207. SOUND_DANGER;
  208. }
  209. //=========================================================
  210. // MaxYawSpeed - allows each sequence to have a different
  211. // turn rate associated with it.
  212. //=========================================================
  213. float CNPC_Houndeye::MaxYawSpeed ( void )
  214. {
  215. int ys = 90;
  216. switch ( GetActivity() )
  217. {
  218. case ACT_CROUCHIDLE://sleeping!
  219. ys = 0;
  220. break;
  221. case ACT_IDLE:
  222. ys = 60;
  223. break;
  224. case ACT_WALK:
  225. ys = 90;
  226. break;
  227. case ACT_RUN:
  228. ys = 90;
  229. break;
  230. case ACT_TURN_LEFT:
  231. case ACT_TURN_RIGHT:
  232. ys = 90;
  233. break;
  234. }
  235. return ys;
  236. }
  237. //=========================================================
  238. // HandleAnimEvent - catches the monster-specific messages
  239. // that occur when tagged animation frames are played.
  240. //=========================================================
  241. void CNPC_Houndeye::HandleAnimEvent( animevent_t *pEvent )
  242. {
  243. switch ( pEvent->event )
  244. {
  245. case HOUND_AE_WARN:
  246. // do stuff for this event.
  247. WarnSound();
  248. break;
  249. case HOUND_AE_STARTATTACK:
  250. WarmUpSound();
  251. break;
  252. case HOUND_AE_HOPBACK:
  253. {
  254. float flGravity = GetCurrentGravity();
  255. SetGroundEntity( NULL );
  256. Vector forward;
  257. AngleVectors( GetLocalAngles(), &forward );
  258. Vector vecNewVelocity = forward * -200;
  259. //jump up 36 inches
  260. vecNewVelocity.z += sqrt( 2 * flGravity * 36 );
  261. SetAbsVelocity( vecNewVelocity );
  262. break;
  263. }
  264. case HOUND_AE_THUMP:
  265. // emit the shockwaves
  266. SonicAttack();
  267. m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 5.0, 8.0 );
  268. break;
  269. case HOUND_AE_ANGERSOUND1:
  270. {
  271. EmitSound( "NPC_Houndeye.Anger1" );
  272. }
  273. break;
  274. case HOUND_AE_ANGERSOUND2:
  275. {
  276. EmitSound( "NPC_Houndeye.Anger2" );
  277. }
  278. break;
  279. case HOUND_AE_CLOSE_EYE:
  280. if ( !m_fDontBlink )
  281. {
  282. //<<TEMP>> pev->skin = HOUNDEYE_EYE_FRAMES - 1;
  283. }
  284. break;
  285. case HOUND_AE_LEAP_HIT:
  286. {
  287. //<<TEMP>>return;//<<TEMP>>
  288. SetGroundEntity( NULL );
  289. //
  290. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  291. //
  292. UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
  293. Vector vecJumpDir;
  294. if ( GetEnemy() != NULL )
  295. {
  296. Vector vecEnemyEyePos = GetEnemy()->EyePosition();
  297. float gravity = GetCurrentGravity();
  298. if ( gravity <= 1 )
  299. {
  300. gravity = 1;
  301. }
  302. //
  303. // How fast does the houndeye need to travel to reach my enemy's eyes given gravity?
  304. //
  305. float height = ( vecEnemyEyePos.z - GetAbsOrigin().z );
  306. if ( height < 16 )
  307. {
  308. height = 16;
  309. }
  310. else if ( height > 120 )
  311. {
  312. height = 120;
  313. }
  314. float speed = sqrt( 2 * gravity * height );
  315. float time = speed / gravity;
  316. //
  317. // Scale the sideways velocity to get there at the right time
  318. //
  319. vecJumpDir = vecEnemyEyePos - GetAbsOrigin();
  320. vecJumpDir = vecJumpDir / time;
  321. //
  322. // Speed to offset gravity at the desired height.
  323. //
  324. vecJumpDir.z = speed;
  325. //
  326. // Don't jump too far/fast.
  327. //
  328. float distance = vecJumpDir.Length();
  329. if ( distance > 650 )
  330. {
  331. vecJumpDir = vecJumpDir * ( 650.0 / distance );
  332. }
  333. }
  334. else
  335. {
  336. Vector forward, up;
  337. AngleVectors( GetLocalAngles(), &forward, NULL, &up );
  338. //
  339. // Jump hop, don't care where.
  340. //
  341. vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
  342. }
  343. SetAbsVelocity( vecJumpDir );
  344. m_flNextAttack = gpGlobals->curtime + 2;
  345. break;
  346. }
  347. default:
  348. BaseClass::HandleAnimEvent( pEvent );
  349. break;
  350. }
  351. }
  352. //=========================================================
  353. // Spawn
  354. //=========================================================
  355. void CNPC_Houndeye::Spawn()
  356. {
  357. Precache( );
  358. SetModel("models/houndeye.mdl");
  359. SetHullType(HULL_WIDE_SHORT);
  360. SetHullSizeNormal();
  361. SetSolid( SOLID_BBOX );
  362. AddSolidFlags( FSOLID_NOT_STANDABLE );
  363. SetMoveType( MOVETYPE_STEP );
  364. SetBloodColor( BLOOD_COLOR_YELLOW );
  365. m_iHealth = sk_Houndeye_health.GetFloat();
  366. m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
  367. m_NPCState = NPC_STATE_NONE;
  368. m_fAsleep = false; // everyone spawns awake
  369. m_fDontBlink = false;
  370. CapabilitiesAdd( bits_CAP_SQUAD );
  371. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  372. CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 );
  373. CapabilitiesAdd( bits_CAP_TURN_HEAD );
  374. m_flNextSecondaryAttack = 0;
  375. m_bLoopClockwise = random->RandomInt(0,1) ? true : false;
  376. m_pEnergyWave = NULL;
  377. m_flEndEnergyWaveTime = 0;
  378. SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
  379. NPCInit();
  380. }
  381. //=========================================================
  382. // Precache - precaches all resources this monster needs
  383. //=========================================================
  384. void CNPC_Houndeye::Precache()
  385. {
  386. PrecacheModel("models/houndeye.mdl");
  387. PrecacheScriptSound( "NPC_Houndeye.Anger1" );
  388. PrecacheScriptSound( "NPC_Houndeye.Anger2" );
  389. PrecacheScriptSound( "NPC_Houndeye.SpeakSentence" );
  390. PrecacheScriptSound( "NPC_Houndeye.Idle" );
  391. PrecacheScriptSound( "NPC_Houndeye.WarmUp" );
  392. PrecacheScriptSound( "NPC_Houndeye.Warn" );
  393. PrecacheScriptSound( "NPC_Houndeye.Alert" );
  394. PrecacheScriptSound( "NPC_Houndeye.Die" );
  395. PrecacheScriptSound( "NPC_Houndeye.Pain" );
  396. PrecacheScriptSound( "NPC_Houndeye.Retreat" );
  397. PrecacheScriptSound( "NPC_Houndeye.SonicAttack" );
  398. PrecacheScriptSound( "NPC_Houndeye.GroupAttack" );
  399. PrecacheScriptSound( "NPC_Houndeye.GroupFollow" );
  400. UTIL_PrecacheOther("grenade_energy");
  401. BaseClass::Precache();
  402. }
  403. //------------------------------------------------------------------------------
  404. // Purpose :
  405. // Input :
  406. // Output :
  407. //------------------------------------------------------------------------------
  408. void CNPC_Houndeye::SpeakSentence( int sentenceType )
  409. {
  410. if (gpGlobals->curtime > m_flSoundWaitTime)
  411. {
  412. EmitSound( "NPC_Houndeye.SpeakSentence" );
  413. m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  414. }
  415. }
  416. //=========================================================
  417. // IdleSound
  418. //=========================================================
  419. void CNPC_Houndeye::IdleSound ( void )
  420. {
  421. if (!FOkToMakeSound())
  422. {
  423. return;
  424. }
  425. CPASAttenuationFilter filter( this,"NPC_Houndeye.Idle" );
  426. EmitSound( filter, entindex(),"NPC_Houndeye.Idle" );
  427. }
  428. //=========================================================
  429. // IdleSound
  430. //=========================================================
  431. void CNPC_Houndeye::WarmUpSound ( void )
  432. {
  433. EmitSound( "NPC_Houndeye.WarmUp" );
  434. }
  435. //=========================================================
  436. // WarnSound
  437. //=========================================================
  438. void CNPC_Houndeye::WarnSound ( void )
  439. {
  440. EmitSound( "NPC_Houndeye.Warn" );
  441. }
  442. //=========================================================
  443. // AlertSound
  444. //=========================================================
  445. void CNPC_Houndeye::AlertSound ( void )
  446. {
  447. // only first squad member makes ALERT sound.
  448. if ( m_pSquad && !m_pSquad->IsLeader( this ) )
  449. {
  450. return;
  451. }
  452. EmitSound( "NPC_Houndeye.Alert" );
  453. }
  454. //=========================================================
  455. // DeathSound
  456. //=========================================================
  457. void CNPC_Houndeye::DeathSound ( void )
  458. {
  459. EmitSound( "NPC_Houndeye.Die" );
  460. }
  461. //=========================================================
  462. // PainSound
  463. //=========================================================
  464. void CNPC_Houndeye::PainSound ( void )
  465. {
  466. EmitSound( "NPC_Houndeye.Pain" );
  467. }
  468. //=========================================================
  469. // WriteBeamColor - writes a color vector to the network
  470. // based on the size of the group.
  471. //=========================================================
  472. void CNPC_Houndeye::WriteBeamColor ( void )
  473. {
  474. BYTE bRed, bGreen, bBlue;
  475. if ( m_pSquad )
  476. {
  477. switch ( m_pSquad->NumMembers() )
  478. {
  479. case 2:
  480. // no case for 0 or 1, cause those are impossible for monsters in Squads.
  481. bRed = 101;
  482. bGreen = 133;
  483. bBlue = 221;
  484. break;
  485. case 3:
  486. bRed = 67;
  487. bGreen = 85;
  488. bBlue = 255;
  489. break;
  490. case 4:
  491. bRed = 62;
  492. bGreen = 33;
  493. bBlue = 211;
  494. break;
  495. default:
  496. DevWarning( 2, "Unsupported Houndeye SquadSize!\n" );
  497. bRed = 188;
  498. bGreen = 220;
  499. bBlue = 255;
  500. break;
  501. }
  502. }
  503. else
  504. {
  505. // solo houndeye - weakest beam
  506. bRed = 188;
  507. bGreen = 220;
  508. bBlue = 255;
  509. }
  510. WRITE_BYTE( bRed );
  511. WRITE_BYTE( bGreen );
  512. WRITE_BYTE( bBlue );
  513. }
  514. //-----------------------------------------------------------------------------
  515. // Purpose: Plays the engine sound.
  516. //-----------------------------------------------------------------------------
  517. void CNPC_Houndeye::NPCThink(void)
  518. {
  519. if (m_pEnergyWave)
  520. {
  521. if (gpGlobals->curtime > m_flEndEnergyWaveTime)
  522. {
  523. UTIL_Remove(m_pEnergyWave);
  524. m_pEnergyWave = NULL;
  525. }
  526. }
  527. // -----------------------------------------------------
  528. // Update collision group
  529. // While I'm running I'm allowed to penetrate
  530. // other houndeyes
  531. // -----------------------------------------------------
  532. Vector vVelocity;
  533. GetVelocity( &vVelocity, NULL );
  534. if (vVelocity.Length() > 10)
  535. {
  536. SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
  537. }
  538. else
  539. {
  540. // Don't go solid if resting in another houndeye
  541. trace_t tr;
  542. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1),
  543. GetHullMins(), GetHullMaxs(),
  544. MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  545. if (!tr.startsolid)
  546. {
  547. SetCollisionGroup( COLLISION_GROUP_NONE );
  548. }
  549. else
  550. {
  551. SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
  552. }
  553. }
  554. /*
  555. if (GetCollisionGroup() == HL2COLLISION_GROUP_HOUNDEYE)
  556. {
  557. NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 0);
  558. }
  559. else
  560. {
  561. NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 0);
  562. }
  563. */
  564. BaseClass::NPCThink();
  565. }
  566. //------------------------------------------------------------------------------
  567. // Purpose : Broadcast retreat occasionally when hurt
  568. // Input :
  569. // Output :
  570. //------------------------------------------------------------------------------
  571. int CNPC_Houndeye::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  572. {
  573. if (m_pSquad && random->RandomInt(0,10) == 10)
  574. {
  575. EmitSound( "NPC_Houndeye.Retreat" );
  576. m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  577. m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRetreat, NULL, this );
  578. }
  579. return BaseClass::OnTakeDamage_Alive( info );
  580. }
  581. //------------------------------------------------------------------------------
  582. // Purpose : Broadcast retreat when member of squad killed
  583. // Input :
  584. // Output :
  585. //------------------------------------------------------------------------------
  586. void CNPC_Houndeye::Event_Killed( const CTakeDamageInfo &info )
  587. {
  588. EmitSound( "NPC_Houndeye.Retreat" );
  589. m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  590. if (m_pSquad)
  591. {
  592. m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRetreat, NULL, this );
  593. }
  594. BaseClass::Event_Killed( info );
  595. }
  596. //=========================================================
  597. // SonicAttack
  598. //=========================================================
  599. void CNPC_Houndeye::SonicAttack ( void )
  600. {
  601. EmitSound( "NPC_Houndeye.SonicAttack" );
  602. if (m_pEnergyWave)
  603. {
  604. UTIL_Remove(m_pEnergyWave);
  605. }
  606. Vector vFacingDir = EyeDirection3D( );
  607. m_pEnergyWave = (CEnergyWave*)Create( "energy_wave", EyePosition(), GetLocalAngles() );
  608. m_flEndEnergyWaveTime = gpGlobals->curtime + 1; //<<TEMP>> magic
  609. m_pEnergyWave->SetAbsVelocity( 100*vFacingDir );
  610. CBaseEntity *pEntity = NULL;
  611. // iterate on all entities in the vicinity.
  612. for ( CEntitySphereQuery sphere( GetAbsOrigin(), HOUNDEYE_MAX_ATTACK_RADIUS ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
  613. {
  614. if (pEntity->Classify() == CLASS_HOUNDEYE)
  615. {
  616. continue;
  617. }
  618. if (pEntity->GetFlags() & FL_NOTARGET)
  619. {
  620. continue;
  621. }
  622. IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  623. if ( pEntity->m_takedamage != DAMAGE_NO || pPhysicsObject)
  624. {
  625. // --------------------------
  626. // Adjust damage by distance
  627. // --------------------------
  628. float flDist = (pEntity->WorldSpaceCenter() - GetAbsOrigin()).Length();
  629. float flDamageAdjuster = 1-( flDist / HOUNDEYE_MAX_ATTACK_RADIUS );
  630. // --------------------------
  631. // Adjust damage by direction
  632. // --------------------------
  633. Vector forward;
  634. AngleVectors( GetAbsAngles(), &forward );
  635. Vector vEntDir = (pEntity->GetAbsOrigin() - GetAbsOrigin());
  636. VectorNormalize(vEntDir);
  637. float flDotPr = DotProduct(forward,vEntDir);
  638. flDamageAdjuster *= flDotPr;
  639. if (flDamageAdjuster < 0)
  640. {
  641. continue;
  642. }
  643. // --------------------------
  644. // Adjust damage by visibility
  645. // --------------------------
  646. if ( !FVisible( pEntity ) )
  647. {
  648. if ( pEntity->IsPlayer() )
  649. {
  650. // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still
  651. // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients
  652. // so that monsters in other parts of the level don't take the damage and get pissed.
  653. flDamageAdjuster *= 0.5;
  654. }
  655. else if ( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) )
  656. {
  657. // do not hurt nonclients through walls, but allow damage to be done to breakables
  658. continue;
  659. }
  660. }
  661. // ------------------------------
  662. // Apply the damage
  663. // ------------------------------
  664. if (pEntity->m_takedamage != DAMAGE_NO)
  665. {
  666. CTakeDamageInfo info( this, this, flDamageAdjuster * sk_Houndeye_dmg_blast.GetFloat(), DMG_SONIC | DMG_ALWAYSGIB );
  667. CalculateExplosiveDamageForce( &info, (pEntity->GetAbsOrigin() - GetAbsOrigin()), pEntity->GetAbsOrigin() );
  668. pEntity->TakeDamage( info );
  669. // Throw the player
  670. if ( pEntity->IsPlayer() )
  671. {
  672. Vector forward;
  673. AngleVectors( GetLocalAngles(), &forward );
  674. Vector vecVelocity = pEntity->GetAbsVelocity();
  675. vecVelocity += forward * 250 * flDamageAdjuster;
  676. vecVelocity.z = 300 * flDamageAdjuster;
  677. pEntity->SetAbsVelocity( vecVelocity );
  678. pEntity->ViewPunch( QAngle(random->RandomInt(-20,20), 0, random->RandomInt(-20,20)) );
  679. }
  680. }
  681. // ------------------------------
  682. // Apply physics foces
  683. // ------------------------------
  684. IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  685. if (pPhysicsObject)
  686. {
  687. float flForce = flDamageAdjuster * 8000;
  688. pPhysicsObject->ApplyForceCenter( (vEntDir+Vector(0,0,0.2)) * flForce );
  689. pPhysicsObject->ApplyTorqueCenter( vEntDir * flForce );
  690. }
  691. }
  692. }
  693. }
  694. //=========================================================
  695. // start task
  696. //=========================================================
  697. void CNPC_Houndeye::StartTask( const Task_t *pTask )
  698. {
  699. switch ( pTask->iTask )
  700. {
  701. case TASK_HOUND_GET_PATH_TO_CIRCLE:
  702. {
  703. if (GetEnemy() == NULL)
  704. {
  705. TaskFail(FAIL_NO_ENEMY);
  706. }
  707. else
  708. {
  709. Vector vTargetPos = GetEnemyLKP();
  710. vTargetPos.z = GetFloorZ(vTargetPos);
  711. if (GetNavigator()->SetRadialGoal(vTargetPos, random->RandomInt(50,500), 90, 175, m_bLoopClockwise))
  712. {
  713. TaskComplete();
  714. return;
  715. }
  716. TaskFail(FAIL_NO_ROUTE);
  717. }
  718. break;
  719. }
  720. case TASK_HOUND_REVERSE_STRAFE_DIR:
  721. {
  722. // Try the other direction
  723. m_bLoopClockwise = (m_bLoopClockwise) ? false : true;
  724. TaskComplete();
  725. break;
  726. }
  727. // Override to set appropriate distances
  728. case TASK_GET_PATH_TO_ENEMY_LOS:
  729. {
  730. float flMaxRange = HOUNDEYE_MAX_ATTACK_RADIUS * 0.9;
  731. float flMinRange = HOUNDEYE_MIN_ATTACK_RADIUS;
  732. Vector posLos;
  733. bool foundLos = false;
  734. if (GetEnemy() != NULL)
  735. {
  736. foundLos = GetTacticalServices()->FindLos(GetEnemyLKP(),GetEnemy()->EyePosition(), flMinRange, flMaxRange, 0.0, &posLos);
  737. }
  738. else
  739. {
  740. TaskFail(FAIL_NO_TARGET);
  741. return;
  742. }
  743. if (foundLos)
  744. {
  745. GetNavigator()->SetGoal( AI_NavGoal_t( posLos, ACT_RUN, AIN_HULL_TOLERANCE ) );
  746. }
  747. else
  748. {
  749. TaskFail(FAIL_NO_SHOOT);
  750. }
  751. break;
  752. }
  753. case TASK_HOUND_FALL_ASLEEP:
  754. {
  755. m_fAsleep = true; // signal that hound is lying down (must stand again before doing anything else!)
  756. TaskComplete( true );
  757. break;
  758. }
  759. case TASK_HOUND_WAKE_UP:
  760. {
  761. m_fAsleep = false; // signal that hound is standing again
  762. TaskComplete( true );
  763. break;
  764. }
  765. case TASK_HOUND_OPEN_EYE:
  766. {
  767. m_fDontBlink = false; // turn blinking back on and that code will automatically open the eye
  768. TaskComplete( true );
  769. break;
  770. }
  771. case TASK_HOUND_CLOSE_EYE:
  772. {
  773. //<<TEMP>> pev->skin = 0;
  774. m_fDontBlink = true; // tell blink code to leave the eye alone.
  775. break;
  776. }
  777. case TASK_HOUND_THREAT_DISPLAY:
  778. {
  779. SetIdealActivity( ACT_IDLE_ANGRY );
  780. break;
  781. }
  782. case TASK_HOUND_HOP_BACK:
  783. {
  784. SetIdealActivity( ACT_LEAP );
  785. break;
  786. }
  787. case TASK_RANGE_ATTACK1:
  788. {
  789. SetIdealActivity( ACT_RANGE_ATTACK1 );
  790. break;
  791. }
  792. default:
  793. {
  794. BaseClass::StartTask(pTask);
  795. break;
  796. }
  797. }
  798. }
  799. //=========================================================
  800. // RunTask
  801. //=========================================================
  802. void CNPC_Houndeye::RunTask( const Task_t *pTask )
  803. {
  804. switch ( pTask->iTask )
  805. {
  806. case TASK_HOUND_THREAT_DISPLAY:
  807. {
  808. GetMotor()->SetIdealYawToTargetAndUpdate( GetEnemyLKP(), AI_KEEP_YAW_SPEED );
  809. if ( IsActivityFinished() )
  810. {
  811. TaskComplete();
  812. }
  813. break;
  814. }
  815. case TASK_HOUND_CLOSE_EYE:
  816. {
  817. /*//<<TEMP>>
  818. if ( pev->skin < HOUNDEYE_EYE_FRAMES - 1 )
  819. {
  820. pev->skin++;
  821. }
  822. */
  823. break;
  824. }
  825. case TASK_HOUND_HOP_BACK:
  826. {
  827. if ( IsActivityFinished() )
  828. {
  829. TaskComplete();
  830. }
  831. break;
  832. }
  833. default:
  834. {
  835. BaseClass::RunTask(pTask);
  836. break;
  837. }
  838. }
  839. }
  840. //------------------------------------------------------------------------------
  841. // Purpose :
  842. // Input :
  843. // Output :
  844. //------------------------------------------------------------------------------
  845. void CNPC_Houndeye::PrescheduleThink ( void )
  846. {
  847. BaseClass::PrescheduleThink();
  848. // if the hound is mad and is running, make hunt noises.
  849. if ( m_NPCState == NPC_STATE_COMBAT && ( GetActivity() == ACT_RUN ) && random->RandomFloat( 0, 1 ) < 0.2 )
  850. {
  851. WarnSound();
  852. }
  853. // at random, initiate a blink if not already blinking or sleeping
  854. if ( !m_fDontBlink )
  855. {
  856. /*//<<TEMP>>//<<TEMP>>
  857. if ( ( pev->skin == 0 ) && random->RandomInt(0,0x7F) == 0 )
  858. {// start blinking!
  859. pev->skin = HOUNDEYE_EYE_FRAMES - 1;
  860. }
  861. else if ( pev->skin != 0 )
  862. {// already blinking
  863. pev->skin--;
  864. }
  865. */
  866. }
  867. }
  868. //-----------------------------------------------------------------------------
  869. // Purpose: Override base class activiites
  870. // Input :
  871. // Output :
  872. //-----------------------------------------------------------------------------
  873. Activity CNPC_Houndeye::NPC_TranslateActivity( Activity eNewActivity )
  874. {
  875. if ( eNewActivity == ACT_IDLE && m_NPCState == NPC_STATE_COMBAT )
  876. {
  877. return ACT_IDLE_ANGRY;
  878. }
  879. return eNewActivity;
  880. }
  881. //------------------------------------------------------------------------------
  882. // Purpose :
  883. // Input :
  884. // Output :
  885. //------------------------------------------------------------------------------
  886. int CNPC_Houndeye::TranslateSchedule( int scheduleType )
  887. {
  888. switch( scheduleType )
  889. {
  890. case SCHED_IDLE_STAND:
  891. {
  892. return BaseClass::TranslateSchedule( scheduleType );
  893. }
  894. case SCHED_RANGE_ATTACK1:
  895. {
  896. return SCHED_HOUND_RANGE_ATTACK1;
  897. }
  898. case SCHED_CHASE_ENEMY_FAILED:
  899. {
  900. return SCHED_COMBAT_FACE;
  901. }
  902. default:
  903. {
  904. return BaseClass::TranslateSchedule ( scheduleType );
  905. }
  906. }
  907. }
  908. //------------------------------------------------------------------------------
  909. // Purpose : Is anyone in the squad currently attacking
  910. // Input :
  911. // Output :
  912. //------------------------------------------------------------------------------
  913. bool CNPC_Houndeye::IsAnyoneInSquadAttacking( void )
  914. {
  915. if (!m_pSquad)
  916. {
  917. return false;
  918. }
  919. AISquadIter_t iter;
  920. for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
  921. {
  922. if (pSquadMember->IsCurSchedule(SCHED_HOUND_RANGE_ATTACK1))
  923. {
  924. return true;
  925. }
  926. }
  927. return false;
  928. }
  929. //=========================================================
  930. // SelectSchedule
  931. //=========================================================
  932. int CNPC_Houndeye::SelectSchedule( void )
  933. {
  934. switch ( m_NPCState )
  935. {
  936. case NPC_STATE_IDLE:
  937. case NPC_STATE_ALERT:
  938. {
  939. if ( HasCondition(COND_LIGHT_DAMAGE) ||
  940. HasCondition(COND_HEAVY_DAMAGE) )
  941. {
  942. return SCHED_TAKE_COVER_FROM_ORIGIN;
  943. }
  944. break;
  945. }
  946. case NPC_STATE_COMBAT:
  947. {
  948. // dead enemy
  949. if ( HasCondition( COND_ENEMY_DEAD ) )
  950. {
  951. // call base class, all code to handle dead enemies is centralized there.
  952. return BaseClass::SelectSchedule();
  953. }
  954. // If a group attack was requested attack even if attack conditions not met
  955. if ( HasCondition( COND_HOUND_GROUP_ATTACK ))
  956. {
  957. // Check that I'm not standing in another hound eye
  958. // before attacking
  959. trace_t tr;
  960. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1),
  961. GetHullMins(), GetHullMaxs(),
  962. MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  963. if (!tr.startsolid)
  964. {
  965. return SCHED_HOUND_GROUP_ATTACK;
  966. }
  967. // Otherwise attack as soon as I can
  968. else
  969. {
  970. m_flNextAttack = gpGlobals->curtime;
  971. SCHED_HOUND_ATTACK_STRAFE;
  972. }
  973. }
  974. // If a group retread was requested
  975. if ( HasCondition( COND_HOUND_GROUP_RETREAT ))
  976. {
  977. return SCHED_HOUND_GROUP_RETREAT;
  978. }
  979. if ( HasCondition( COND_LIGHT_DAMAGE ) |
  980. HasCondition( COND_HEAVY_DAMAGE ) )
  981. {
  982. if ( random->RandomFloat( 0 , 1 ) <= 0.4 )
  983. {
  984. trace_t tr;
  985. Vector forward;
  986. AngleVectors( GetAbsAngles(), &forward );
  987. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + forward * -128,
  988. GetHullMins(), GetHullMaxs(),
  989. MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  990. if ( tr.fraction == 1.0 )
  991. {
  992. // it's clear behind, so the hound will jump
  993. return SCHED_HOUND_HOP_RETREAT;
  994. }
  995. }
  996. return SCHED_TAKE_COVER_FROM_ENEMY;
  997. }
  998. // If a group rally was requested
  999. if ( HasCondition( COND_HOUND_GROUP_RALLEY ))
  1000. {
  1001. return SCHED_HOUND_GROUP_RALLEY;
  1002. }
  1003. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  1004. {
  1005. if (m_pSquad && random->RandomInt(0,4) == 0)
  1006. {
  1007. if (!IsAnyoneInSquadAttacking())
  1008. {
  1009. EmitSound( "NPC_Houndeye.GroupAttack" );
  1010. m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  1011. m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupAttack, NULL, this );
  1012. return SCHED_HOUND_GROUP_ATTACK;
  1013. }
  1014. }
  1015. //<<TEMP>>comment
  1016. SetCollisionGroup( COLLISION_GROUP_NONE );
  1017. return SCHED_RANGE_ATTACK1;
  1018. }
  1019. else
  1020. {
  1021. if (m_pSquad && random->RandomInt(0,5) == 0)
  1022. {
  1023. if (!IsAnyoneInSquadAttacking())
  1024. {
  1025. EmitSound( "NPC_Houndeye.GroupFollow" );
  1026. m_flSoundWaitTime = gpGlobals->curtime + 1.0;
  1027. m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRalley, NULL, this );
  1028. return SCHED_HOUND_ATTACK_STRAFE;
  1029. }
  1030. }
  1031. return SCHED_HOUND_ATTACK_STRAFE;
  1032. }
  1033. break;
  1034. }
  1035. }
  1036. return BaseClass::SelectSchedule();
  1037. }
  1038. //-----------------------------------------------------------------------------
  1039. // Purpose: This is a generic function (to be implemented by sub-classes) to
  1040. // handle specific interactions between different types of characters
  1041. // (For example the barnacle grabbing an NPC)
  1042. // Input : Constant for the type of interaction
  1043. // Output : true - if sub-class has a response for the interaction
  1044. // false - if sub-class has no response
  1045. //-----------------------------------------------------------------------------
  1046. bool CNPC_Houndeye::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
  1047. {
  1048. if (interactionType == g_interactionHoundeyeGroupAttack)
  1049. {
  1050. SetCondition(COND_HOUND_GROUP_ATTACK);
  1051. return true;
  1052. }
  1053. else if (interactionType == g_interactionHoundeyeGroupRetreat)
  1054. {
  1055. SetCondition(COND_HOUND_GROUP_RETREAT);
  1056. return true;
  1057. }
  1058. else if (interactionType == g_interactionHoundeyeGroupRalley)
  1059. {
  1060. SetCondition(COND_HOUND_GROUP_RALLEY);
  1061. SetTarget(sourceEnt);
  1062. m_bLoopClockwise = false;
  1063. return true;
  1064. }
  1065. return false;
  1066. }
  1067. //-----------------------------------------------------------------------------
  1068. //
  1069. // Schedules
  1070. //
  1071. //-----------------------------------------------------------------------------
  1072. //=========================================================
  1073. // SCHED_HOUND_ATTACK_STRAFE
  1074. //
  1075. // Run a cirle around my enemy
  1076. //=========================================================
  1077. AI_DEFINE_SCHEDULE
  1078. (
  1079. SCHED_HOUND_ATTACK_STRAFE ,
  1080. " Tasks "
  1081. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE_REVERSE"
  1082. " TASK_HOUND_GET_PATH_TO_CIRCLE 0"
  1083. " TASK_RUN_PATH 0"
  1084. " TASK_WAIT_FOR_MOVEMENT 0"
  1085. ""
  1086. " Interrupts "
  1087. " COND_WEAPON_SIGHT_OCCLUDED"
  1088. " COND_NEW_ENEMY"
  1089. " COND_ENEMY_DEAD"
  1090. " COND_HEAVY_DAMAGE"
  1091. " COND_CAN_RANGE_ATTACK1"
  1092. " COND_HOUND_GROUP_ATTACK"
  1093. " COND_HOUND_GROUP_RETREAT"
  1094. );
  1095. //=========================================================
  1096. // SCHED_HOUND_ATTACK_STRAFE_REVERSE
  1097. //
  1098. // Run a cirle around my enemy
  1099. //=========================================================
  1100. AI_DEFINE_SCHEDULE
  1101. (
  1102. SCHED_HOUND_ATTACK_STRAFE_REVERSE ,
  1103. " Tasks "
  1104. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_CHASE_ENEMY"
  1105. " TASK_HOUND_REVERSE_STRAFE_DIR 0"
  1106. " TASK_HOUND_GET_PATH_TO_CIRCLE 0"
  1107. " TASK_RUN_PATH 0"
  1108. " TASK_WAIT_FOR_MOVEMENT 0"
  1109. ""
  1110. " Interrupts "
  1111. " COND_WEAPON_SIGHT_OCCLUDED"
  1112. " COND_NEW_ENEMY"
  1113. " COND_ENEMY_DEAD"
  1114. " COND_HEAVY_DAMAGE"
  1115. " COND_CAN_RANGE_ATTACK1"
  1116. " COND_HOUND_GROUP_ATTACK"
  1117. " COND_HOUND_GROUP_RETREAT"
  1118. );
  1119. //========================================================
  1120. // SCHED_HOUND_CHASE_ENEMY
  1121. //=========================================================
  1122. AI_DEFINE_SCHEDULE
  1123. (
  1124. SCHED_HOUND_CHASE_ENEMY,
  1125. " Tasks"
  1126. " TASK_SET_TOLERANCE_DISTANCE 30 "
  1127. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  1128. " TASK_GET_PATH_TO_ENEMY 0"
  1129. " TASK_RUN_PATH 0"
  1130. " TASK_WAIT_FOR_MOVEMENT 0"
  1131. ""
  1132. " Interrupts"
  1133. " COND_NEW_ENEMY"
  1134. " COND_ENEMY_DEAD"
  1135. " COND_CAN_RANGE_ATTACK1"
  1136. " COND_HOUND_GROUP_ATTACK"
  1137. " COND_HOUND_GROUP_RETREAT"
  1138. );
  1139. //=========================================================
  1140. // SCHED_HOUND_GROUP_ATTACK
  1141. //
  1142. // Face enemy, pause, then attack
  1143. //=========================================================
  1144. AI_DEFINE_SCHEDULE
  1145. (
  1146. SCHED_HOUND_GROUP_ATTACK ,
  1147. " Tasks "
  1148. " TASK_STOP_MOVING 0"
  1149. " TASK_FACE_ENEMY 0"
  1150. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ANGRY"
  1151. " TASK_SPEAK_SENTENCE 0"
  1152. " TASK_WAIT 1"
  1153. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HOUND_RANGE_ATTACK1"
  1154. ""
  1155. " Interrupts "
  1156. " COND_NEW_ENEMY"
  1157. " COND_ENEMY_DEAD"
  1158. " COND_HEAVY_DAMAGE"
  1159. );
  1160. //=========================================================
  1161. // > SCHED_HOUND_GROUP_RETREAT
  1162. //
  1163. // Take cover from enemy!
  1164. //=========================================================
  1165. AI_DEFINE_SCHEDULE
  1166. (
  1167. SCHED_HOUND_GROUP_RETREAT,
  1168. " Tasks"
  1169. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
  1170. " TASK_STOP_MOVING 0"
  1171. " TASK_WAIT 0.2"
  1172. " TASK_SET_TOLERANCE_DISTANCE 24"
  1173. " TASK_FIND_COVER_FROM_ENEMY 0"
  1174. " TASK_RUN_PATH 0"
  1175. " TASK_WAIT_FOR_MOVEMENT 0"
  1176. " TASK_REMEMBER MEMORY:INCOVER"
  1177. " TASK_FACE_ENEMY 0"
  1178. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Translated to cover
  1179. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HOUND_COVER_WAIT"
  1180. ""
  1181. " Interrupts"
  1182. " COND_NEW_ENEMY"
  1183. );
  1184. //=========================================================
  1185. // > SCHED_HOUND_GROUP_RALLEY
  1186. //
  1187. // Run to rally hound!
  1188. //=========================================================
  1189. AI_DEFINE_SCHEDULE
  1190. (
  1191. SCHED_HOUND_GROUP_RALLEY,
  1192. " Tasks"
  1193. " TASK_SET_TOLERANCE_DISTANCE 30"
  1194. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
  1195. " TASK_GET_PATH_TO_TARGET 0"
  1196. " TASK_RUN_PATH 0"
  1197. " TASK_WAIT_FOR_MOVEMENT 0"
  1198. ""
  1199. " Interrupts"
  1200. " COND_NEW_ENEMY"
  1201. " COND_ENEMY_DEAD"
  1202. " COND_HEAVY_DAMAGE"
  1203. " COND_HOUND_GROUP_ATTACK"
  1204. " COND_HOUND_GROUP_RETREAT"
  1205. );
  1206. //=========================================================
  1207. // > SCHED_HOUND_COVER_WAIT
  1208. //
  1209. // Wait in cover till enemy see's me or I take damage
  1210. //=========================================================
  1211. AI_DEFINE_SCHEDULE
  1212. (
  1213. SCHED_HOUND_COVER_WAIT,
  1214. " Tasks"
  1215. " TASK_WAIT 2"
  1216. ""
  1217. " Interrupts"
  1218. " COND_SEE_ENEMY"
  1219. " COND_LIGHT_DAMAGE"
  1220. );
  1221. //=========================================================
  1222. // > SCHED_HOUND_RANGE_ATTACK1
  1223. //=========================================================
  1224. AI_DEFINE_SCHEDULE
  1225. (
  1226. SCHED_HOUND_RANGE_ATTACK1,
  1227. " Tasks"
  1228. " TASK_STOP_MOVING 0"
  1229. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ANGRY"
  1230. " TASK_FACE_IDEAL 0"
  1231. " TASK_RANGE_ATTACK1 0"
  1232. ""
  1233. " Interrupts"
  1234. //" COND_LIGHT_DAMAGE" // don't interupt on small damage
  1235. " COND_HEAVY_DAMAGE"
  1236. );
  1237. //=========================================================
  1238. // > SCHED_HOUND_HOP_RETREAT
  1239. //=========================================================
  1240. AI_DEFINE_SCHEDULE
  1241. (
  1242. SCHED_HOUND_HOP_RETREAT,
  1243. " Tasks"
  1244. " TASK_STOP_MOVING 0"
  1245. " TASK_HOUND_HOP_BACK 0"
  1246. " TASK_SET_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
  1247. ""
  1248. " Interrupts"
  1249. );