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.

776 lines
20 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Father Grigori, a benevolent monk who is the last remaining human
  4. // in Ravenholm. He keeps to the rooftops and uses a big ole elephant
  5. // gun to send his zombified former friends to a peaceful death.
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "ai_baseactor.h"
  10. #include "ai_hull.h"
  11. #include "ammodef.h"
  12. #include "gamerules.h"
  13. #include "IEffects.h"
  14. #include "engine/IEngineSound.h"
  15. #include "ai_behavior.h"
  16. #include "ai_behavior_assault.h"
  17. #include "ai_behavior_lead.h"
  18. #include "npcevent.h"
  19. #include "ai_playerally.h"
  20. #include "ai_senses.h"
  21. #include "soundent.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. ConVar monk_headshot_freq( "monk_headshot_freq", "2" );
  25. //-----------------------------------------------------------------------------
  26. // Activities.
  27. //-----------------------------------------------------------------------------
  28. int ACT_MONK_GUN_IDLE;
  29. class CNPC_Monk : public CAI_PlayerAlly
  30. {
  31. DECLARE_CLASS( CNPC_Monk, CAI_PlayerAlly );
  32. public:
  33. CNPC_Monk() {}
  34. void Spawn();
  35. void Precache();
  36. bool CreateBehaviors();
  37. int GetSoundInterests();
  38. void BuildScheduleTestBits( void );
  39. Class_T Classify( void );
  40. bool ShouldBackAway();
  41. bool IsValidEnemy( CBaseEntity *pEnemy );
  42. int TranslateSchedule( int scheduleType );
  43. int SelectSchedule ();
  44. void HandleAnimEvent( animevent_t *pEvent );
  45. Activity NPC_TranslateActivity( Activity eNewActivity );
  46. void PainSound( const CTakeDamageInfo &info );
  47. void DeathSound( const CTakeDamageInfo &info );
  48. WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon );
  49. Vector GetActualShootPosition( const Vector &shootOrigin );
  50. Vector GetActualShootTrajectory( const Vector &shootOrigin );
  51. void PrescheduleThink();
  52. void StartTask( const Task_t *pTask );
  53. void RunTask( const Task_t *pTask );
  54. void GatherConditions();
  55. bool PassesDamageFilter( const CTakeDamageInfo &info );
  56. void OnKilledNPC( CBaseCombatCharacter *pKilled );
  57. bool IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos ) const;
  58. int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
  59. DECLARE_DATADESC();
  60. private:
  61. //-----------------------------------------------------
  62. // Conditions, Schedules, Tasks
  63. //-----------------------------------------------------
  64. enum
  65. {
  66. SCHED_MONK_RANGE_ATTACK1 = BaseClass::NEXT_SCHEDULE,
  67. SCHED_MONK_BACK_AWAY_FROM_ENEMY,
  68. SCHED_MONK_BACK_AWAY_AND_RELOAD,
  69. SCHED_MONK_NORMAL_RELOAD,
  70. };
  71. /*enum
  72. {
  73. //TASK_MONK_FIRST_TASK = BaseClass::NEXT_TASK,
  74. };*/
  75. DEFINE_CUSTOM_AI;
  76. // Inputs
  77. void InputPerfectAccuracyOn( inputdata_t &inputdata );
  78. void InputPerfectAccuracyOff( inputdata_t &inputdata );
  79. CAI_AssaultBehavior m_AssaultBehavior;
  80. CAI_LeadBehavior m_LeadBehavior;
  81. int m_iNumZombies;
  82. int m_iDangerousZombies;
  83. bool m_bPerfectAccuracy;
  84. bool m_bMournedPlayer;
  85. };
  86. BEGIN_DATADESC( CNPC_Monk )
  87. // m_AssaultBehavior
  88. // m_LeadBehavior
  89. DEFINE_FIELD( m_iNumZombies, FIELD_INTEGER ),
  90. DEFINE_FIELD( m_iDangerousZombies, FIELD_INTEGER ),
  91. DEFINE_FIELD( m_bPerfectAccuracy, FIELD_BOOLEAN ),
  92. DEFINE_FIELD( m_bMournedPlayer, FIELD_BOOLEAN ),
  93. // Inputs
  94. DEFINE_INPUTFUNC( FIELD_VOID, "PerfectAccuracyOn", InputPerfectAccuracyOn ),
  95. DEFINE_INPUTFUNC( FIELD_VOID, "PerfectAccuracyOff", InputPerfectAccuracyOff ),
  96. END_DATADESC()
  97. LINK_ENTITY_TO_CLASS( npc_monk, CNPC_Monk );
  98. //-----------------------------------------------------------------------------
  99. //-----------------------------------------------------------------------------
  100. bool CNPC_Monk::CreateBehaviors()
  101. {
  102. AddBehavior( &m_LeadBehavior );
  103. AddBehavior( &m_AssaultBehavior );
  104. return BaseClass::CreateBehaviors();
  105. }
  106. //-----------------------------------------------------------------------------
  107. //-----------------------------------------------------------------------------
  108. int CNPC_Monk::GetSoundInterests()
  109. {
  110. return SOUND_WORLD |
  111. SOUND_COMBAT |
  112. SOUND_PLAYER |
  113. SOUND_DANGER;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Purpose:
  117. //-----------------------------------------------------------------------------
  118. void CNPC_Monk::BuildScheduleTestBits( void )
  119. {
  120. // FIXME: we need a way to make scenes non-interruptible
  121. #if 0
  122. if ( IsCurSchedule( SCHED_RANGE_ATTACK1 ) || IsCurSchedule( SCHED_SCENE_GENERIC ) )
  123. {
  124. ClearCustomInterruptCondition( COND_LIGHT_DAMAGE );
  125. ClearCustomInterruptCondition( COND_HEAVY_DAMAGE );
  126. ClearCustomInterruptCondition( COND_NEW_ENEMY );
  127. ClearCustomInterruptCondition( COND_HEAR_DANGER );
  128. }
  129. #endif
  130. // Don't interrupt while shooting the gun
  131. const Task_t* pTask = GetTask();
  132. if ( pTask && (pTask->iTask == TASK_RANGE_ATTACK1) )
  133. {
  134. ClearCustomInterruptCondition( COND_HEAVY_DAMAGE );
  135. ClearCustomInterruptCondition( COND_ENEMY_OCCLUDED );
  136. ClearCustomInterruptCondition( COND_HEAR_DANGER );
  137. ClearCustomInterruptCondition( COND_WEAPON_BLOCKED_BY_FRIEND );
  138. ClearCustomInterruptCondition( COND_WEAPON_SIGHT_OCCLUDED );
  139. }
  140. }
  141. //-----------------------------------------------------------------------------
  142. // Purpose:
  143. //-----------------------------------------------------------------------------
  144. Class_T CNPC_Monk::Classify( void )
  145. {
  146. return CLASS_PLAYER_ALLY_VITAL;
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose:
  150. //-----------------------------------------------------------------------------
  151. Activity CNPC_Monk::NPC_TranslateActivity( Activity eNewActivity )
  152. {
  153. eNewActivity = BaseClass::NPC_TranslateActivity( eNewActivity );
  154. if ( (m_NPCState == NPC_STATE_COMBAT || m_NPCState == NPC_STATE_ALERT) )
  155. {
  156. bool bGunUp = false;
  157. bGunUp = (gpGlobals->curtime - m_flLastAttackTime < 4);
  158. bGunUp = bGunUp || (GetEnemy() && !HasCondition( COND_TOO_FAR_TO_ATTACK ));
  159. if (bGunUp)
  160. {
  161. if ( eNewActivity == ACT_IDLE )
  162. {
  163. eNewActivity = ACT_IDLE_ANGRY;
  164. }
  165. // keep aiming a little longer than normal since the shot takes so long and there's no good way to do a transitions between movement types :/
  166. else if ( eNewActivity == ACT_WALK )
  167. {
  168. eNewActivity = ACT_WALK_AIM;
  169. }
  170. else if ( eNewActivity == ACT_RUN )
  171. {
  172. eNewActivity = ACT_RUN_AIM;
  173. }
  174. }
  175. }
  176. // We need these so that we can pick up the shotgun to throw it in the balcony scene
  177. if ( eNewActivity == ACT_IDLE_ANGRY_SHOTGUN )
  178. {
  179. eNewActivity = ACT_IDLE_ANGRY_SMG1;
  180. }
  181. else if ( eNewActivity == ACT_WALK_AIM_SHOTGUN )
  182. {
  183. eNewActivity = ACT_WALK_AIM_RIFLE;
  184. }
  185. else if ( eNewActivity == ACT_RUN_AIM_SHOTGUN )
  186. {
  187. eNewActivity = ACT_RUN_AIM_RIFLE;
  188. }
  189. else if ( eNewActivity == ACT_RANGE_ATTACK_SHOTGUN_LOW )
  190. {
  191. return ACT_RANGE_ATTACK_SMG1_LOW;
  192. }
  193. return eNewActivity;
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. //-----------------------------------------------------------------------------
  198. void CNPC_Monk::Precache()
  199. {
  200. PrecacheModel( "models/Monk.mdl" );
  201. PrecacheScriptSound( "NPC_Citizen.FootstepLeft" );
  202. PrecacheScriptSound( "NPC_Citizen.FootstepRight" );
  203. BaseClass::Precache();
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Purpose:
  207. //-----------------------------------------------------------------------------
  208. void CNPC_Monk::Spawn()
  209. {
  210. Precache();
  211. BaseClass::Spawn();
  212. SetModel( "models/Monk.mdl" );
  213. SetHullType(HULL_HUMAN);
  214. SetHullSizeNormal();
  215. SetSolid( SOLID_BBOX );
  216. AddSolidFlags( FSOLID_NOT_STANDABLE );
  217. SetMoveType( MOVETYPE_STEP );
  218. SetBloodColor( BLOOD_COLOR_RED );
  219. m_iHealth = 100;
  220. m_flFieldOfView = m_flFieldOfView = -0.707; // 270`
  221. m_NPCState = NPC_STATE_NONE;
  222. m_HackedGunPos = Vector ( 0, 0, 55 );
  223. CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP | bits_CAP_MOVE_GROUND );
  224. CapabilitiesAdd( bits_CAP_USE_WEAPONS );
  225. CapabilitiesAdd( bits_CAP_ANIMATEDFACE );
  226. CapabilitiesAdd( bits_CAP_FRIENDLY_DMG_IMMUNE );
  227. CapabilitiesAdd( bits_CAP_AIM_GUN );
  228. CapabilitiesAdd( bits_CAP_MOVE_SHOOT );
  229. NPCInit();
  230. }
  231. //------------------------------------------------------------------------------
  232. //------------------------------------------------------------------------------
  233. void CNPC_Monk::PainSound( const CTakeDamageInfo &info )
  234. {
  235. SpeakIfAllowed( TLK_WOUND );
  236. }
  237. //------------------------------------------------------------------------------
  238. //------------------------------------------------------------------------------
  239. void CNPC_Monk::DeathSound( const CTakeDamageInfo &info )
  240. {
  241. // Sentences don't play on dead NPCs
  242. SentenceStop();
  243. Speak( TLK_DEATH );
  244. }
  245. //------------------------------------------------------------------------------
  246. //------------------------------------------------------------------------------
  247. WeaponProficiency_t CNPC_Monk::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon )
  248. {
  249. return WEAPON_PROFICIENCY_PERFECT;
  250. }
  251. //-----------------------------------------------------------------------------
  252. //-----------------------------------------------------------------------------
  253. Vector CNPC_Monk::GetActualShootPosition( const Vector &shootOrigin )
  254. {
  255. return BaseClass::GetActualShootPosition( shootOrigin );
  256. }
  257. //-----------------------------------------------------------------------------
  258. //-----------------------------------------------------------------------------
  259. Vector CNPC_Monk::GetActualShootTrajectory( const Vector &shootOrigin )
  260. {
  261. if( GetEnemy() && GetEnemy()->Classify() == CLASS_ZOMBIE )
  262. {
  263. Vector vecShootDir;
  264. if( m_bPerfectAccuracy || random->RandomInt( 1, monk_headshot_freq.GetInt() ) == 1 )
  265. {
  266. vecShootDir = GetEnemy()->HeadTarget( shootOrigin ) - shootOrigin;
  267. }
  268. else
  269. {
  270. vecShootDir = GetEnemy()->BodyTarget( shootOrigin ) - shootOrigin;
  271. }
  272. VectorNormalize( vecShootDir );
  273. return vecShootDir;
  274. }
  275. return BaseClass::GetActualShootTrajectory( shootOrigin );
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose:
  279. // Input : pEvent -
  280. //-----------------------------------------------------------------------------
  281. void CNPC_Monk::HandleAnimEvent( animevent_t *pEvent )
  282. {
  283. switch( pEvent->event )
  284. {
  285. case NPC_EVENT_LEFTFOOT:
  286. {
  287. EmitSound( "NPC_Citizen.FootstepLeft", pEvent->eventtime );
  288. }
  289. break;
  290. case NPC_EVENT_RIGHTFOOT:
  291. {
  292. EmitSound( "NPC_Citizen.FootstepRight", pEvent->eventtime );
  293. }
  294. break;
  295. default:
  296. BaseClass::HandleAnimEvent( pEvent );
  297. break;
  298. }
  299. }
  300. //-------------------------------------
  301. // Grigori tries to stand his ground until
  302. // enemies are very close.
  303. //-------------------------------------
  304. #define MONK_STAND_GROUND_HEIGHT 24.0
  305. bool CNPC_Monk::ShouldBackAway()
  306. {
  307. if( !GetEnemy() )
  308. return false;
  309. if( GetAbsOrigin().z - GetEnemy()->GetAbsOrigin().z >= MONK_STAND_GROUND_HEIGHT )
  310. {
  311. // This is a fairly special case. Grigori looks better fighting from his assault points in the
  312. // elevated places of the Graveyard, so we prevent his back away behavior anytime he has a height
  313. // advantage on his enemy.
  314. return false;
  315. }
  316. float flDist;
  317. flDist = ( GetAbsOrigin() - GetEnemy()->GetAbsOrigin() ).Length();
  318. if( flDist <= 180 )
  319. return true;
  320. return false;
  321. }
  322. //-------------------------------------
  323. bool CNPC_Monk::IsValidEnemy( CBaseEntity *pEnemy )
  324. {
  325. if ( BaseClass::IsValidEnemy( pEnemy ) && GetActiveWeapon() )
  326. {
  327. float flDist;
  328. flDist = ( GetAbsOrigin() - pEnemy->GetAbsOrigin() ).Length();
  329. if( flDist <= GetActiveWeapon()->m_fMaxRange1 )
  330. return true;
  331. }
  332. return false;
  333. }
  334. //-------------------------------------
  335. int CNPC_Monk::TranslateSchedule( int scheduleType )
  336. {
  337. switch( scheduleType )
  338. {
  339. case SCHED_MOVE_AWAY_FAIL:
  340. // Our first method of backing away failed. Try another.
  341. return SCHED_MONK_BACK_AWAY_FROM_ENEMY;
  342. break;
  343. case SCHED_RANGE_ATTACK1:
  344. {
  345. if( ShouldBackAway() )
  346. {
  347. // Get some room, rely on move and shoot.
  348. return SCHED_MOVE_AWAY;
  349. }
  350. return SCHED_MONK_RANGE_ATTACK1;
  351. }
  352. break;
  353. case SCHED_HIDE_AND_RELOAD:
  354. case SCHED_RELOAD:
  355. if( ShouldBackAway() )
  356. {
  357. return SCHED_MONK_BACK_AWAY_AND_RELOAD;
  358. }
  359. return SCHED_RELOAD;
  360. break;
  361. }
  362. return BaseClass::TranslateSchedule( scheduleType );
  363. }
  364. //-------------------------------------
  365. void CNPC_Monk::PrescheduleThink()
  366. {
  367. BaseClass::PrescheduleThink();
  368. }
  369. //-------------------------------------
  370. int CNPC_Monk::SelectSchedule()
  371. {
  372. if( HasCondition( COND_HEAR_DANGER ) )
  373. {
  374. SpeakIfAllowed( TLK_DANGER );
  375. return SCHED_TAKE_COVER_FROM_BEST_SOUND;
  376. }
  377. if ( HasCondition( COND_TALKER_PLAYER_DEAD ) && !m_bMournedPlayer && IsOkToSpeak() )
  378. {
  379. m_bMournedPlayer = true;
  380. Speak( TLK_IDLE );
  381. }
  382. if( !BehaviorSelectSchedule() )
  383. {
  384. if ( HasCondition ( COND_NO_PRIMARY_AMMO ) )
  385. {
  386. return SCHED_HIDE_AND_RELOAD;
  387. }
  388. }
  389. return BaseClass::SelectSchedule();
  390. }
  391. //-------------------------------------
  392. void CNPC_Monk::StartTask( const Task_t *pTask )
  393. {
  394. switch( pTask->iTask )
  395. {
  396. case TASK_RELOAD:
  397. {
  398. if ( GetActiveWeapon() && GetActiveWeapon()->HasPrimaryAmmo() )
  399. {
  400. // Don't reload if you have done so while moving (See BACK_AWAY_AND_RELOAD schedule).
  401. TaskComplete();
  402. return;
  403. }
  404. if( m_iNumZombies >= 2 && random->RandomInt( 1, 3 ) == 1 )
  405. {
  406. SpeakIfAllowed( TLK_ATTACKING );
  407. }
  408. Activity reloadGesture = TranslateActivity( ACT_GESTURE_RELOAD );
  409. if ( reloadGesture != ACT_INVALID && IsPlayingGesture( reloadGesture ) )
  410. {
  411. ResetIdealActivity( ACT_IDLE );
  412. return;
  413. }
  414. BaseClass::StartTask( pTask );
  415. }
  416. break;
  417. default:
  418. BaseClass::StartTask( pTask );
  419. break;
  420. }
  421. }
  422. void CNPC_Monk::RunTask( const Task_t *pTask )
  423. {
  424. switch( pTask->iTask )
  425. {
  426. case TASK_RELOAD:
  427. {
  428. Activity reloadGesture = TranslateActivity( ACT_GESTURE_RELOAD );
  429. if ( GetIdealActivity() != ACT_RELOAD && reloadGesture != ACT_INVALID )
  430. {
  431. if ( !IsPlayingGesture( reloadGesture ) )
  432. {
  433. if ( GetShotRegulator() )
  434. {
  435. GetShotRegulator()->Reset( false );
  436. }
  437. TaskComplete();
  438. }
  439. return;
  440. }
  441. BaseClass::RunTask( pTask );
  442. }
  443. break;
  444. default:
  445. BaseClass::RunTask( pTask );
  446. break;
  447. }
  448. }
  449. //-----------------------------------------------------------------------------
  450. //-----------------------------------------------------------------------------
  451. void CNPC_Monk::GatherConditions()
  452. {
  453. BaseClass::GatherConditions();
  454. // Build my zombie danger index!
  455. m_iNumZombies = 0;
  456. m_iDangerousZombies = 0;
  457. AISightIter_t iter;
  458. CBaseEntity *pSightEnt;
  459. pSightEnt = GetSenses()->GetFirstSeenEntity( &iter );
  460. while( pSightEnt )
  461. {
  462. if( pSightEnt->Classify() == CLASS_ZOMBIE && pSightEnt->IsAlive() )
  463. {
  464. // Is this zombie coming for me?
  465. CAI_BaseNPC *pZombie = dynamic_cast<CAI_BaseNPC*>(pSightEnt);
  466. if( pZombie && pZombie->GetEnemy() == this )
  467. {
  468. m_iNumZombies++;
  469. // if this zombie is close enough to attack, add him to the zombie danger!
  470. float flDist;
  471. flDist = (pZombie->GetAbsOrigin() - GetAbsOrigin()).Length2DSqr();
  472. if( flDist <= 128.0f * 128.0f )
  473. {
  474. m_iDangerousZombies++;
  475. }
  476. }
  477. }
  478. pSightEnt = GetSenses()->GetNextSeenEntity( &iter );
  479. }
  480. if( m_iDangerousZombies >= 3 || (GetEnemy() && GetHealth() < 25) )
  481. {
  482. // I see many zombies, or I'm quite injured.
  483. SpeakIfAllowed( TLK_HELP_ME );
  484. }
  485. // NOTE!!!!!! This code assumes grigori is using annabelle!
  486. ClearCondition(COND_LOW_PRIMARY_AMMO);
  487. if ( GetActiveWeapon() )
  488. {
  489. if ( GetActiveWeapon()->UsesPrimaryAmmo() )
  490. {
  491. if (!GetActiveWeapon()->HasPrimaryAmmo() )
  492. {
  493. SetCondition(COND_NO_PRIMARY_AMMO);
  494. }
  495. else if ( m_NPCState != NPC_STATE_COMBAT && GetActiveWeapon()->UsesClipsForAmmo1() && GetActiveWeapon()->Clip1() < 2 )
  496. {
  497. // Don't send a low ammo message unless we're not in combat.
  498. SetCondition(COND_LOW_PRIMARY_AMMO);
  499. }
  500. }
  501. }
  502. }
  503. //-----------------------------------------------------------------------------
  504. //-----------------------------------------------------------------------------
  505. bool CNPC_Monk::PassesDamageFilter( const CTakeDamageInfo &info )
  506. {
  507. if ( info.GetAttacker()->ClassMatches( "npc_headcrab_black" ) || info.GetAttacker()->ClassMatches( "npc_headcrab_poison" ) )
  508. return false;
  509. return BaseClass::PassesDamageFilter( info );
  510. }
  511. //-----------------------------------------------------------------------------
  512. //-----------------------------------------------------------------------------
  513. void CNPC_Monk::OnKilledNPC( CBaseCombatCharacter *pKilled )
  514. {
  515. if ( !pKilled )
  516. {
  517. return;
  518. }
  519. if ( pKilled->Classify() == CLASS_ZOMBIE )
  520. {
  521. // Don't speak if the gun is empty, cause grigori will want to speak while he's reloading.
  522. if ( GetActiveWeapon() )
  523. {
  524. if ( GetActiveWeapon()->UsesPrimaryAmmo() && !GetActiveWeapon()->HasPrimaryAmmo() )
  525. {
  526. // Gun is empty. I'm about to reload.
  527. if( m_iNumZombies >= 2 )
  528. {
  529. // Don't talk about killing a single zombie if there are more coming.
  530. // the reload behavior will say "come to me, children", etc.
  531. return;
  532. }
  533. }
  534. }
  535. if( m_iNumZombies == 1 || random->RandomInt( 1, 3 ) == 1 )
  536. {
  537. SpeakIfAllowed( TLK_ENEMY_DEAD );
  538. }
  539. }
  540. }
  541. //-----------------------------------------------------------------------------
  542. //-----------------------------------------------------------------------------
  543. int CNPC_Monk::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  544. {
  545. if( failedSchedule == SCHED_MONK_BACK_AWAY_FROM_ENEMY )
  546. {
  547. if( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  548. {
  549. // Most likely backed into a corner. Just blaze away.
  550. return SCHED_MONK_RANGE_ATTACK1;
  551. }
  552. }
  553. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  554. }
  555. //-----------------------------------------------------------------------------
  556. //-----------------------------------------------------------------------------
  557. bool CNPC_Monk::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos ) const
  558. {
  559. if ( startPos.z - endPos.z < 0 )
  560. return false;
  561. return BaseClass::IsJumpLegal( startPos, apex, endPos );
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Every shot's a headshot. Useful for scripted Grigoris
  565. //-----------------------------------------------------------------------------
  566. void CNPC_Monk::InputPerfectAccuracyOn( inputdata_t &inputdata )
  567. {
  568. m_bPerfectAccuracy = true;
  569. }
  570. //-----------------------------------------------------------------------------
  571. // Turn off perfect accuracy.
  572. //-----------------------------------------------------------------------------
  573. void CNPC_Monk::InputPerfectAccuracyOff( inputdata_t &inputdata )
  574. {
  575. m_bPerfectAccuracy = false;
  576. }
  577. //-----------------------------------------------------------------------------
  578. //
  579. // CNPC_Monk Schedules
  580. //
  581. //-----------------------------------------------------------------------------
  582. AI_BEGIN_CUSTOM_NPC( npc_monk, CNPC_Monk )
  583. DECLARE_ACTIVITY( ACT_MONK_GUN_IDLE )
  584. DEFINE_SCHEDULE
  585. (
  586. SCHED_MONK_RANGE_ATTACK1,
  587. " Tasks"
  588. " TASK_STOP_MOVING 0"
  589. " TASK_FACE_ENEMY 0"
  590. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  591. " TASK_RANGE_ATTACK1 0"
  592. ""
  593. " Interrupts"
  594. " COND_HEAVY_DAMAGE"
  595. " COND_ENEMY_OCCLUDED"
  596. " COND_HEAR_DANGER"
  597. " COND_WEAPON_BLOCKED_BY_FRIEND"
  598. " COND_WEAPON_SIGHT_OCCLUDED"
  599. )
  600. DEFINE_SCHEDULE
  601. (
  602. SCHED_MONK_BACK_AWAY_FROM_ENEMY,
  603. " Tasks"
  604. " TASK_STOP_MOVING 0"
  605. " TASK_STORE_ENEMY_POSITION_IN_SAVEPOSITION 0"
  606. " TASK_FIND_BACKAWAY_FROM_SAVEPOSITION 0"
  607. " TASK_WALK_PATH_TIMED 4.0"
  608. " TASK_WAIT_FOR_MOVEMENT 0"
  609. ""
  610. " Interrupts"
  611. " COND_NEW_ENEMY"
  612. " COND_ENEMY_DEAD"
  613. );
  614. DEFINE_SCHEDULE
  615. (
  616. SCHED_MONK_BACK_AWAY_AND_RELOAD,
  617. " Tasks"
  618. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_MONK_NORMAL_RELOAD"
  619. " TASK_STOP_MOVING 0"
  620. " TASK_STORE_ENEMY_POSITION_IN_SAVEPOSITION 0"
  621. " TASK_FIND_BACKAWAY_FROM_SAVEPOSITION 0"
  622. " TASK_WALK_PATH_TIMED 2.0"
  623. " TASK_WAIT_FOR_MOVEMENT 0"
  624. " TASK_STOP_MOVING 0"
  625. " TASK_RELOAD 0"
  626. ""
  627. " Interrupts"
  628. " COND_ENEMY_DEAD"
  629. );
  630. DEFINE_SCHEDULE
  631. (
  632. SCHED_MONK_NORMAL_RELOAD,
  633. " Tasks"
  634. " TASK_STOP_MOVING 0"
  635. " TASK_RELOAD 0"
  636. ""
  637. " Interrupts"
  638. " COND_HEAR_DANGER"
  639. );
  640. AI_END_CUSTOM_NPC()