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.

1410 lines
33 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
  4. // events
  5. //
  6. // $Workfile: $
  7. // $Date: $
  8. //
  9. //-----------------------------------------------------------------------------
  10. // $Log: $
  11. //
  12. // $NoKeywords: $
  13. //=============================================================================//
  14. #include "cbase.h"
  15. #include "ai_default.h"
  16. #include "ai_task.h"
  17. #include "ai_schedule.h"
  18. #include "ai_node.h"
  19. #include "ai_hull.h"
  20. #include "ai_hint.h"
  21. #include "ai_memory.h"
  22. #include "ai_route.h"
  23. #include "ai_motor.h"
  24. #include "hl1_npc_scientist.h"
  25. #include "soundent.h"
  26. #include "game.h"
  27. #include "npcevent.h"
  28. #include "entitylist.h"
  29. #include "activitylist.h"
  30. #include "animation.h"
  31. #include "engine/IEngineSound.h"
  32. #include "ai_navigator.h"
  33. #include "ai_behavior_follow.h"
  34. #include "AI_Criteria.h"
  35. #include "SoundEmitterSystem/isoundemittersystembase.h"
  36. #define SC_PLFEAR "SC_PLFEAR"
  37. #define SC_FEAR "SC_FEAR"
  38. #define SC_HEAL "SC_HEAL"
  39. #define SC_SCREAM "SC_SCREAM"
  40. #define SC_POK "SC_POK"
  41. ConVar sk_scientist_health( "sk_scientist_health","20");
  42. ConVar sk_scientist_heal( "sk_scientist_heal","25");
  43. #define NUM_SCIENTIST_HEADS 4 // four heads available for scientist model
  44. enum { HEAD_GLASSES = 0, HEAD_EINSTEIN = 1, HEAD_LUTHER = 2, HEAD_SLICK = 3 };
  45. int ACT_EXCITED;
  46. //=========================================================
  47. // Makes it fast to check barnacle classnames in
  48. // IsValidEnemy()
  49. //=========================================================
  50. string_t s_iszBarnacleClassname;
  51. //=========================================================
  52. // Monster's Anim Events Go Here
  53. //=========================================================
  54. #define SCIENTIST_AE_HEAL ( 1 )
  55. #define SCIENTIST_AE_NEEDLEON ( 2 )
  56. #define SCIENTIST_AE_NEEDLEOFF ( 3 )
  57. //=======================================================
  58. // Scientist
  59. //=======================================================
  60. LINK_ENTITY_TO_CLASS( monster_scientist, CNPC_Scientist );
  61. //IMPLEMENT_SERVERCLASS_ST( CNPC_Scientist, DT_NPC_Scientist )
  62. //END_SEND_TABLE()
  63. //---------------------------------------------------------
  64. // Save/Restore
  65. //---------------------------------------------------------
  66. BEGIN_DATADESC( CNPC_Scientist )
  67. DEFINE_FIELD( m_flFearTime, FIELD_TIME ),
  68. DEFINE_FIELD( m_flHealTime, FIELD_TIME ),
  69. DEFINE_FIELD( m_flPainTime, FIELD_TIME ),
  70. DEFINE_THINKFUNC( SUB_LVFadeOut ),
  71. END_DATADESC()
  72. //-----------------------------------------------------------------------------
  73. // Purpose:
  74. //
  75. //
  76. //-----------------------------------------------------------------------------
  77. void CNPC_Scientist::Precache( void )
  78. {
  79. PrecacheModel( "models/scientist.mdl" );
  80. PrecacheScriptSound( "Scientist.Pain" );
  81. TalkInit();
  82. BaseClass::Precache();
  83. }
  84. void CNPC_Scientist::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
  85. {
  86. BaseClass::ModifyOrAppendCriteria( criteriaSet );
  87. bool predisaster = FBitSet( m_spawnflags, SF_NPC_PREDISASTER ) ? true : false;
  88. criteriaSet.AppendCriteria( "disaster", predisaster ? "[disaster::pre]" : "[disaster::post]" );
  89. }
  90. // Init talk data
  91. void CNPC_Scientist::TalkInit()
  92. {
  93. BaseClass::TalkInit();
  94. // scientist will try to talk to friends in this order:
  95. m_szFriends[0] = "monster_scientist";
  96. m_szFriends[1] = "monster_sitting_scientist";
  97. m_szFriends[2] = "monster_barney";
  98. // get voice for head
  99. switch (m_nBody % 3)
  100. {
  101. default:
  102. case HEAD_GLASSES: GetExpresser()->SetVoicePitch( 105 ); break; //glasses
  103. case HEAD_EINSTEIN: GetExpresser()->SetVoicePitch( 100 ); break; //einstein
  104. case HEAD_LUTHER: GetExpresser()->SetVoicePitch( 95 ); break; //luther
  105. case HEAD_SLICK: GetExpresser()->SetVoicePitch( 100 ); break;//slick
  106. }
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose:
  110. //
  111. //
  112. //-----------------------------------------------------------------------------
  113. void CNPC_Scientist::Spawn( void )
  114. {
  115. //Select the body first if it's going to be random cause we set his voice pitch in Precache.
  116. if ( m_nBody == -1 )
  117. m_nBody = random->RandomInt( 0, NUM_SCIENTIST_HEADS-1 );// pick a head, any head
  118. SetRenderColor( 255, 255, 255, 255 );
  119. Precache();
  120. SetModel( "models/scientist.mdl" );
  121. SetHullType(HULL_HUMAN);
  122. SetHullSizeNormal();
  123. SetSolid( SOLID_BBOX );
  124. AddSolidFlags( FSOLID_NOT_STANDABLE );
  125. SetMoveType( MOVETYPE_STEP );
  126. m_bloodColor = BLOOD_COLOR_RED;
  127. ClearEffects();
  128. m_iHealth = sk_scientist_health.GetFloat();
  129. m_flFieldOfView = VIEW_FIELD_WIDE;
  130. m_NPCState = NPC_STATE_NONE;
  131. CapabilitiesClear();
  132. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE );
  133. CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );
  134. // White hands
  135. m_nSkin = 0;
  136. // Luther is black, make his hands black
  137. if ( m_nBody == HEAD_LUTHER )
  138. m_nSkin = 1;
  139. NPCInit();
  140. SetUse( &CNPC_Scientist::FollowerUse );
  141. }
  142. //-----------------------------------------------------------------------------
  143. //-----------------------------------------------------------------------------
  144. void CNPC_Scientist::Activate()
  145. {
  146. s_iszBarnacleClassname = FindPooledString( "monster_barnacle" );
  147. BaseClass::Activate();
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose:
  151. //
  152. //
  153. // Output :
  154. //-----------------------------------------------------------------------------
  155. Class_T CNPC_Scientist::Classify( void )
  156. {
  157. return CLASS_HUMAN_PASSIVE;
  158. }
  159. int CNPC_Scientist::GetSoundInterests ( void )
  160. {
  161. return SOUND_WORLD |
  162. SOUND_COMBAT |
  163. SOUND_DANGER |
  164. SOUND_PLAYER;
  165. }
  166. //=========================================================
  167. // HandleAnimEvent - catches the monster-specific messages
  168. // that occur when tagged animation frames are played.
  169. //=========================================================
  170. void CNPC_Scientist::HandleAnimEvent( animevent_t *pEvent )
  171. {
  172. switch( pEvent->event )
  173. {
  174. case SCIENTIST_AE_HEAL: // Heal my target (if within range)
  175. Heal();
  176. break;
  177. case SCIENTIST_AE_NEEDLEON:
  178. {
  179. int oldBody = m_nBody;
  180. m_nBody = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 1;
  181. }
  182. break;
  183. case SCIENTIST_AE_NEEDLEOFF:
  184. {
  185. int oldBody = m_nBody;
  186. m_nBody = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 0;
  187. }
  188. break;
  189. default:
  190. BaseClass::HandleAnimEvent( pEvent );
  191. break;
  192. }
  193. }
  194. void CNPC_Scientist::DeclineFollowing( void )
  195. {
  196. if ( CanSpeakAfterMyself() )
  197. {
  198. Speak( SC_POK );
  199. }
  200. }
  201. bool CNPC_Scientist::CanBecomeRagdoll( void )
  202. {
  203. if ( UTIL_IsLowViolence() )
  204. {
  205. return false;
  206. }
  207. return BaseClass::CanBecomeRagdoll();
  208. }
  209. bool CNPC_Scientist::ShouldGib( const CTakeDamageInfo &info )
  210. {
  211. if ( UTIL_IsLowViolence() )
  212. {
  213. return false;
  214. }
  215. return BaseClass::ShouldGib( info );
  216. }
  217. void CNPC_Scientist::SUB_StartLVFadeOut( float delay, bool notSolid )
  218. {
  219. SetThink( &CNPC_Scientist::SUB_LVFadeOut );
  220. SetNextThink( gpGlobals->curtime + delay );
  221. SetRenderColorA( 255 );
  222. m_nRenderMode = kRenderNormal;
  223. if ( notSolid )
  224. {
  225. AddSolidFlags( FSOLID_NOT_SOLID );
  226. SetLocalAngularVelocity( vec3_angle );
  227. }
  228. }
  229. void CNPC_Scientist::SUB_LVFadeOut( void )
  230. {
  231. if( VPhysicsGetObject() )
  232. {
  233. if( VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD || GetEFlags() & EFL_IS_BEING_LIFTED_BY_BARNACLE )
  234. {
  235. // Try again in a few seconds.
  236. SetNextThink( gpGlobals->curtime + 5 );
  237. SetRenderColorA( 255 );
  238. return;
  239. }
  240. }
  241. float dt = gpGlobals->frametime;
  242. if ( dt > 0.1f )
  243. {
  244. dt = 0.1f;
  245. }
  246. m_nRenderMode = kRenderTransTexture;
  247. int speed = MAX(3,256*dt); // fade out over 3 seconds
  248. SetRenderColorA( UTIL_Approach( 0, m_clrRender->a, speed ) );
  249. NetworkStateChanged();
  250. if ( m_clrRender->a == 0 )
  251. {
  252. UTIL_Remove(this);
  253. }
  254. else
  255. {
  256. SetNextThink( gpGlobals->curtime );
  257. }
  258. }
  259. void CNPC_Scientist::Scream( void )
  260. {
  261. if ( IsOkToSpeak() )
  262. {
  263. GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + 10 );
  264. SetSpeechTarget( GetEnemy() );
  265. Speak( SC_SCREAM );
  266. }
  267. }
  268. Activity CNPC_Scientist::GetStoppedActivity( void )
  269. {
  270. if ( GetEnemy() != NULL )
  271. return (Activity)ACT_EXCITED;
  272. return BaseClass::GetStoppedActivity();
  273. }
  274. float CNPC_Scientist::MaxYawSpeed( void )
  275. {
  276. switch( GetActivity() )
  277. {
  278. case ACT_TURN_LEFT:
  279. case ACT_TURN_RIGHT:
  280. return 160;
  281. break;
  282. case ACT_RUN:
  283. return 160;
  284. break;
  285. default:
  286. return 60;
  287. break;
  288. }
  289. }
  290. void CNPC_Scientist::StartTask( const Task_t *pTask )
  291. {
  292. switch( pTask->iTask )
  293. {
  294. case TASK_SAY_HEAL:
  295. GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + 2 );
  296. SetSpeechTarget( GetTarget() );
  297. Speak( SC_HEAL );
  298. TaskComplete();
  299. break;
  300. case TASK_SCREAM:
  301. Scream();
  302. TaskComplete();
  303. break;
  304. case TASK_RANDOM_SCREAM:
  305. if ( random->RandomFloat( 0, 1 ) < pTask->flTaskData )
  306. Scream();
  307. TaskComplete();
  308. break;
  309. case TASK_SAY_FEAR:
  310. if ( IsOkToSpeak() )
  311. {
  312. GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + 2 );
  313. SetSpeechTarget( GetEnemy() );
  314. if ( GetEnemy() && GetEnemy()->IsPlayer() )
  315. Speak( SC_PLFEAR );
  316. else
  317. Speak( SC_FEAR );
  318. }
  319. TaskComplete();
  320. break;
  321. case TASK_HEAL:
  322. SetIdealActivity( ACT_MELEE_ATTACK1 );
  323. break;
  324. case TASK_RUN_PATH_SCARED:
  325. GetNavigator()->SetMovementActivity( ACT_RUN_SCARED );
  326. break;
  327. case TASK_MOVE_TO_TARGET_RANGE_SCARED:
  328. {
  329. if ( GetTarget() == NULL)
  330. {
  331. TaskFail(FAIL_NO_TARGET);
  332. }
  333. else if ( (GetTarget()->GetAbsOrigin() - GetAbsOrigin()).Length() < 1 )
  334. {
  335. TaskComplete();
  336. }
  337. }
  338. break;
  339. default:
  340. BaseClass::StartTask( pTask );
  341. break;
  342. }
  343. }
  344. void CNPC_Scientist::RunTask( const Task_t *pTask )
  345. {
  346. switch ( pTask->iTask )
  347. {
  348. case TASK_RUN_PATH_SCARED:
  349. if ( !IsMoving() )
  350. TaskComplete();
  351. if ( random->RandomInt(0,31) < 8 )
  352. Scream();
  353. break;
  354. case TASK_MOVE_TO_TARGET_RANGE_SCARED:
  355. {
  356. float distance;
  357. if ( GetTarget() == NULL )
  358. {
  359. TaskFail(FAIL_NO_TARGET);
  360. }
  361. else
  362. {
  363. distance = ( GetNavigator()->GetPath()->ActualGoalPosition() - GetAbsOrigin() ).Length2D();
  364. // Re-evaluate when you think your finished, or the target has moved too far
  365. if ( (distance < pTask->flTaskData) || (GetNavigator()->GetPath()->ActualGoalPosition() - GetTarget()->GetAbsOrigin()).Length() > pTask->flTaskData * 0.5 )
  366. {
  367. GetNavigator()->GetPath()->ResetGoalPosition(GetTarget()->GetAbsOrigin());
  368. distance = ( GetNavigator()->GetPath()->ActualGoalPosition() - GetAbsOrigin() ).Length2D();
  369. // GetNavigator()->GetPath()->Find();
  370. GetNavigator()->SetGoal( GOALTYPE_TARGETENT );
  371. }
  372. // Set the appropriate activity based on an overlapping range
  373. // overlap the range to prevent oscillation
  374. // BUGBUG: this is checking linear distance (ie. through walls) and not path distance or even visibility
  375. if ( distance < pTask->flTaskData )
  376. {
  377. TaskComplete();
  378. GetNavigator()->GetPath()->Clear(); // Stop moving
  379. }
  380. else
  381. {
  382. if ( distance < 190 && GetNavigator()->GetMovementActivity() != ACT_WALK_SCARED )
  383. GetNavigator()->SetMovementActivity( ACT_WALK_SCARED );
  384. else if ( distance >= 270 && GetNavigator()->GetMovementActivity() != ACT_RUN_SCARED )
  385. GetNavigator()->SetMovementActivity( ACT_RUN_SCARED );
  386. }
  387. }
  388. }
  389. break;
  390. case TASK_HEAL:
  391. if ( IsSequenceFinished() )
  392. {
  393. TaskComplete();
  394. }
  395. else
  396. {
  397. if ( TargetDistance() > 90 )
  398. TaskComplete();
  399. if ( GetTarget() )
  400. GetMotor()->SetIdealYaw( UTIL_VecToYaw( GetTarget()->GetAbsOrigin() - GetAbsOrigin() ) );
  401. //GetMotor()->SetYawSpeed( m_YawSpeed );
  402. }
  403. break;
  404. default:
  405. BaseClass::RunTask( pTask );
  406. break;
  407. }
  408. }
  409. int CNPC_Scientist::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  410. {
  411. if ( inputInfo.GetInflictor() && inputInfo.GetInflictor()->GetFlags() & FL_CLIENT )
  412. {
  413. Remember( bits_MEMORY_PROVOKED );
  414. StopFollowing();
  415. }
  416. // make sure friends talk about it if player hurts scientist...
  417. return BaseClass::OnTakeDamage_Alive( inputInfo );
  418. }
  419. void CNPC_Scientist::Event_Killed( const CTakeDamageInfo &info )
  420. {
  421. SetUse( NULL );
  422. BaseClass::Event_Killed( info );
  423. if ( UTIL_IsLowViolence() )
  424. {
  425. SUB_StartLVFadeOut( 0.0f );
  426. }
  427. }
  428. bool CNPC_Scientist::CanHeal( void )
  429. {
  430. CBaseEntity *pTarget = GetFollowTarget();
  431. if ( pTarget == NULL )
  432. return false;
  433. if ( pTarget->IsPlayer() == false )
  434. return false;
  435. if ( (m_flHealTime > gpGlobals->curtime) || (pTarget->m_iHealth > (pTarget->m_iMaxHealth * 0.5)) )
  436. return false;
  437. return true;
  438. }
  439. //=========================================================
  440. // PainSound
  441. //=========================================================
  442. void CNPC_Scientist::PainSound ( const CTakeDamageInfo &info )
  443. {
  444. if (gpGlobals->curtime < m_flPainTime )
  445. return;
  446. m_flPainTime = gpGlobals->curtime + random->RandomFloat( 0.5, 0.75 );
  447. CPASAttenuationFilter filter( this );
  448. CSoundParameters params;
  449. if ( GetParametersForSound( "Scientist.Pain", params, NULL ) )
  450. {
  451. EmitSound_t ep( params );
  452. params.pitch = GetExpresser()->GetVoicePitch();
  453. EmitSound( filter, entindex(), ep );
  454. }
  455. }
  456. //=========================================================
  457. // DeathSound
  458. //=========================================================
  459. void CNPC_Scientist::DeathSound( const CTakeDamageInfo &info )
  460. {
  461. PainSound( info );
  462. }
  463. void CNPC_Scientist::Heal( void )
  464. {
  465. if ( !CanHeal() )
  466. return;
  467. Vector target = GetFollowTarget()->GetAbsOrigin() - GetAbsOrigin();
  468. if ( target.Length() > 100 )
  469. return;
  470. GetTarget()->TakeHealth( sk_scientist_heal.GetFloat(), DMG_GENERIC );
  471. // Don't heal again for 1 minute
  472. m_flHealTime = gpGlobals->curtime + 60;
  473. }
  474. int CNPC_Scientist::TranslateSchedule( int scheduleType )
  475. {
  476. switch( scheduleType )
  477. {
  478. // Hook these to make a looping schedule
  479. case SCHED_TARGET_FACE:
  480. {
  481. int baseType;
  482. // call base class default so that scientist will talk
  483. // when 'used'
  484. baseType = BaseClass::TranslateSchedule( scheduleType );
  485. if (baseType == SCHED_IDLE_STAND)
  486. return SCHED_TARGET_FACE; // override this for different target face behavior
  487. else
  488. return baseType;
  489. }
  490. break;
  491. case SCHED_TARGET_CHASE:
  492. return SCHED_SCI_FOLLOWTARGET;
  493. break;
  494. case SCHED_IDLE_STAND:
  495. {
  496. int baseType;
  497. baseType = BaseClass::TranslateSchedule( scheduleType );
  498. if (baseType == SCHED_IDLE_STAND)
  499. return SCHED_SCI_IDLESTAND; // override this for different target face behavior
  500. else
  501. return baseType;
  502. }
  503. break;
  504. }
  505. return BaseClass::TranslateSchedule( scheduleType );
  506. }
  507. Activity CNPC_Scientist::NPC_TranslateActivity( Activity newActivity )
  508. {
  509. if ( GetFollowTarget() && GetEnemy() )
  510. {
  511. CBaseEntity *pEnemy = GetEnemy();
  512. int relationship = D_NU;
  513. // Nothing scary, just me and the player
  514. if ( pEnemy != NULL )
  515. relationship = IRelationType( pEnemy );
  516. if ( relationship == D_HT || relationship == D_FR )
  517. {
  518. if ( newActivity == ACT_WALK )
  519. return ACT_WALK_SCARED;
  520. else if ( newActivity == ACT_RUN )
  521. return ACT_RUN_SCARED;
  522. }
  523. }
  524. return BaseClass::NPC_TranslateActivity( newActivity );
  525. }
  526. int CNPC_Scientist::SelectSchedule( void )
  527. {
  528. if( m_NPCState == NPC_STATE_PRONE )
  529. {
  530. // Immediately call up to the talker code. Barnacle death is priority schedule.
  531. return BaseClass::SelectSchedule();
  532. }
  533. // so we don't keep calling through the EHANDLE stuff
  534. CBaseEntity *pEnemy = GetEnemy();
  535. if ( GetFollowTarget() )
  536. {
  537. // so we don't keep calling through the EHANDLE stuff
  538. CBaseEntity *pEnemy = GetEnemy();
  539. int relationship = D_NU;
  540. // Nothing scary, just me and the player
  541. if ( pEnemy != NULL )
  542. relationship = IRelationType( pEnemy );
  543. if ( relationship != D_HT && relationship != D_FR )
  544. {
  545. // If I'm already close enough to my target
  546. if ( TargetDistance() <= 128 )
  547. {
  548. if ( CanHeal() ) // Heal opportunistically
  549. {
  550. SetTarget( GetFollowTarget() );
  551. return SCHED_SCI_HEAL;
  552. }
  553. }
  554. }
  555. }
  556. else if ( HasCondition( COND_PLAYER_PUSHING ) && !(GetSpawnFlags() & SF_NPC_PREDISASTER ) )
  557. { // Player wants me to move
  558. return SCHED_HL1TALKER_FOLLOW_MOVE_AWAY;
  559. }
  560. if ( BehaviorSelectSchedule() )
  561. {
  562. return BaseClass::SelectSchedule();
  563. }
  564. if ( HasCondition( COND_HEAR_DANGER ) && m_NPCState != NPC_STATE_PRONE )
  565. {
  566. CSound *pSound;
  567. pSound = GetBestSound();
  568. if ( pSound && pSound->IsSoundType(SOUND_DANGER) )
  569. return SCHED_TAKE_COVER_FROM_BEST_SOUND;
  570. }
  571. switch( m_NPCState )
  572. {
  573. case NPC_STATE_ALERT:
  574. case NPC_STATE_IDLE:
  575. if ( pEnemy )
  576. {
  577. if ( HasCondition( COND_SEE_ENEMY ) )
  578. m_flFearTime = gpGlobals->curtime;
  579. else if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert
  580. {
  581. SetEnemy( NULL );
  582. pEnemy = NULL;
  583. }
  584. }
  585. if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
  586. {
  587. // flinch if hurt
  588. return SCHED_SMALL_FLINCH;
  589. }
  590. // Cower when you hear something scary
  591. if ( HasCondition( COND_HEAR_DANGER ) || HasCondition( COND_HEAR_COMBAT ) )
  592. {
  593. CSound *pSound;
  594. pSound = GetBestSound();
  595. if ( pSound )
  596. {
  597. if ( pSound->IsSoundType(SOUND_DANGER | SOUND_COMBAT) )
  598. {
  599. if ( gpGlobals->curtime - m_flFearTime > 3 ) // Only cower every 3 seconds or so
  600. {
  601. m_flFearTime = gpGlobals->curtime; // Update last fear
  602. return SCHED_SCI_STARTLE; // This will just duck for a second
  603. }
  604. }
  605. }
  606. }
  607. if ( GetFollowTarget() )
  608. {
  609. if ( !GetFollowTarget()->IsAlive() )
  610. {
  611. // UNDONE: Comment about the recently dead player here?
  612. StopFollowing();
  613. break;
  614. }
  615. int relationship = D_NU;
  616. // Nothing scary, just me and the player
  617. if ( pEnemy != NULL )
  618. relationship = IRelationType( pEnemy );
  619. if ( relationship != D_HT )
  620. {
  621. return SCHED_TARGET_FACE; // Just face and follow.
  622. }
  623. else // UNDONE: When afraid, scientist won't move out of your way. Keep This? If not, write move away scared
  624. {
  625. if ( HasCondition( COND_NEW_ENEMY ) ) // I just saw something new and scary, react
  626. return SCHED_SCI_FEAR; // React to something scary
  627. return SCHED_SCI_FACETARGETSCARED; // face and follow, but I'm scared!
  628. }
  629. }
  630. // try to say something about smells
  631. TrySmellTalk();
  632. break;
  633. case NPC_STATE_COMBAT:
  634. if ( HasCondition( COND_NEW_ENEMY ) )
  635. return SCHED_SCI_FEAR; // Point and scream!
  636. if ( HasCondition( COND_SEE_ENEMY ) )
  637. return SCHED_SCI_COVER; // Take Cover
  638. if ( HasCondition( COND_HEAR_COMBAT ) || HasCondition( COND_HEAR_DANGER ) )
  639. return SCHED_TAKE_COVER_FROM_BEST_SOUND; // Cower and panic from the scary sound!
  640. return SCHED_SCI_COVER; // Run & Cower
  641. break;
  642. }
  643. return BaseClass::SelectSchedule();
  644. }
  645. NPC_STATE CNPC_Scientist::SelectIdealState ( void )
  646. {
  647. switch ( m_NPCState )
  648. {
  649. case NPC_STATE_ALERT:
  650. case NPC_STATE_IDLE:
  651. if ( HasCondition( COND_NEW_ENEMY ) )
  652. {
  653. if ( GetFollowTarget() && GetEnemy() )
  654. {
  655. int relationship = IRelationType( GetEnemy() );
  656. if ( relationship != D_FR || relationship != D_HT && ( !HasCondition( COND_LIGHT_DAMAGE ) || !HasCondition( COND_HEAVY_DAMAGE ) ) )
  657. {
  658. // Don't go to combat if you're following the player
  659. return NPC_STATE_ALERT;
  660. }
  661. StopFollowing();
  662. }
  663. }
  664. else if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
  665. {
  666. // Stop following if you take damage
  667. if ( GetFollowTarget() )
  668. StopFollowing();
  669. }
  670. break;
  671. case NPC_STATE_COMBAT:
  672. {
  673. CBaseEntity *pEnemy = GetEnemy();
  674. if ( pEnemy != NULL )
  675. {
  676. if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert
  677. {
  678. // Strip enemy when going to alert
  679. SetEnemy( NULL );
  680. return NPC_STATE_ALERT;
  681. }
  682. // Follow if only scared a little
  683. if ( GetFollowTarget() )
  684. {
  685. return NPC_STATE_ALERT;
  686. }
  687. if ( HasCondition( COND_SEE_ENEMY ) )
  688. {
  689. m_flFearTime = gpGlobals->curtime;
  690. return NPC_STATE_COMBAT;
  691. }
  692. }
  693. }
  694. break;
  695. }
  696. return BaseClass::SelectIdealState();
  697. }
  698. int CNPC_Scientist::FriendNumber( int arrayNumber )
  699. {
  700. static int array[3] = { 1, 2, 0 };
  701. if ( arrayNumber < 3 )
  702. return array[ arrayNumber ];
  703. return arrayNumber;
  704. }
  705. float CNPC_Scientist::TargetDistance( void )
  706. {
  707. CBaseEntity *pFollowTarget = GetFollowTarget();
  708. // If we lose the player, or he dies, return a really large distance
  709. if ( pFollowTarget == NULL || !pFollowTarget->IsAlive() )
  710. return 1e6;
  711. return (pFollowTarget->WorldSpaceCenter() - WorldSpaceCenter()).Length();
  712. }
  713. bool CNPC_Scientist::IsValidEnemy( CBaseEntity *pEnemy )
  714. {
  715. if( pEnemy->m_iClassname == s_iszBarnacleClassname )
  716. {
  717. // Scientists ignore barnacles rather than freak out.(sjb)
  718. return false;
  719. }
  720. return BaseClass::IsValidEnemy(pEnemy);
  721. }
  722. //=========================================================
  723. // Dead Scientist PROP
  724. //=========================================================
  725. class CNPC_DeadScientist : public CAI_BaseNPC
  726. {
  727. DECLARE_CLASS( CNPC_DeadScientist, CAI_BaseNPC );
  728. public:
  729. void Spawn( void );
  730. Class_T Classify ( void ) { return CLASS_NONE; }
  731. bool KeyValue( const char *szKeyName, const char *szValue );
  732. float MaxYawSpeed ( void ) { return 8.0f; }
  733. int m_iPose;// which sequence to display -- temporary, don't need to save
  734. int m_iDesiredSequence;
  735. static char *m_szPoses[7];
  736. };
  737. char *CNPC_DeadScientist::m_szPoses[] = { "lying_on_back", "lying_on_stomach", "dead_sitting", "dead_hang", "dead_table1", "dead_table2", "dead_table3" };
  738. bool CNPC_DeadScientist::KeyValue( const char *szKeyName, const char *szValue )
  739. {
  740. if ( FStrEq( szKeyName, "pose" ) )
  741. m_iPose = atoi( szValue );
  742. else
  743. BaseClass::KeyValue( szKeyName, szValue );
  744. return true;
  745. }
  746. LINK_ENTITY_TO_CLASS( monster_scientist_dead, CNPC_DeadScientist );
  747. //
  748. // ********** DeadScientist SPAWN **********
  749. //
  750. void CNPC_DeadScientist::Spawn( void )
  751. {
  752. PrecacheModel("models/scientist.mdl");
  753. SetModel( "models/scientist.mdl" );
  754. ClearEffects();
  755. SetSequence( 0 );
  756. m_bloodColor = BLOOD_COLOR_RED;
  757. SetRenderColor( 255, 255, 255, 255 );
  758. if ( m_nBody == -1 )
  759. {// -1 chooses a random head
  760. m_nBody = random->RandomInt( 0, NUM_SCIENTIST_HEADS-1);// pick a head, any head
  761. }
  762. // Luther is black, make his hands black
  763. if ( m_nBody == HEAD_LUTHER )
  764. m_nSkin = 1;
  765. else
  766. m_nSkin = 0;
  767. SetSequence( LookupSequence( m_szPoses[m_iPose] ) );
  768. if ( GetSequence() == -1)
  769. {
  770. Msg ( "Dead scientist with bad pose\n" );
  771. }
  772. m_iHealth = 0.0;//gSkillData.barneyHealth;
  773. NPCInitDead();
  774. }
  775. //=========================================================
  776. // Sitting Scientist PROP
  777. //=========================================================
  778. LINK_ENTITY_TO_CLASS( monster_sitting_scientist, CNPC_SittingScientist );
  779. //IMPLEMENT_CUSTOM_AI( monster_sitting_scientist, CNPC_SittingScientist );
  780. //IMPLEMENT_SERVERCLASS_ST( CNPC_SittingScientist, DT_NPC_SittingScientist )
  781. //END_SEND_TABLE()
  782. //---------------------------------------------------------
  783. // Save/Restore
  784. //---------------------------------------------------------
  785. BEGIN_DATADESC( CNPC_SittingScientist )
  786. DEFINE_FIELD( m_iHeadTurn, FIELD_INTEGER ),
  787. DEFINE_FIELD( m_flResponseDelay, FIELD_FLOAT ),
  788. //DEFINE_FIELD( m_baseSequence, FIELD_INTEGER ),
  789. DEFINE_THINKFUNC( SittingThink ),
  790. END_DATADESC()
  791. // animation sequence aliases
  792. typedef enum
  793. {
  794. SITTING_ANIM_sitlookleft,
  795. SITTING_ANIM_sitlookright,
  796. SITTING_ANIM_sitscared,
  797. SITTING_ANIM_sitting2,
  798. SITTING_ANIM_sitting3
  799. } SITTING_ANIM;
  800. //
  801. // ********** Scientist SPAWN **********
  802. //
  803. void CNPC_SittingScientist::Spawn( )
  804. {
  805. PrecacheModel("models/scientist.mdl");
  806. SetModel("models/scientist.mdl");
  807. Precache();
  808. InitBoneControllers();
  809. SetHullType(HULL_HUMAN);
  810. SetHullSizeNormal();
  811. SetSolid( SOLID_BBOX );
  812. AddSolidFlags( FSOLID_NOT_STANDABLE );
  813. SetMoveType( MOVETYPE_STEP );
  814. m_iHealth = 50;
  815. m_bloodColor = BLOOD_COLOR_RED;
  816. m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result )
  817. m_NPCState = NPC_STATE_NONE;
  818. CapabilitiesClear();
  819. CapabilitiesAdd( bits_CAP_TURN_HEAD );
  820. m_spawnflags |= SF_NPC_PREDISASTER; // predisaster only!
  821. if ( m_nBody == -1 )
  822. {// -1 chooses a random head
  823. m_nBody = random->RandomInt( 0, NUM_SCIENTIST_HEADS-1 );// pick a head, any head
  824. }
  825. // Luther is black, make his hands black
  826. if ( m_nBody == HEAD_LUTHER )
  827. m_nBody = 1;
  828. UTIL_DropToFloor( this,MASK_SOLID );
  829. NPCInit();
  830. SetThink (&CNPC_SittingScientist::SittingThink);
  831. SetNextThink( gpGlobals->curtime + 0.1f );
  832. m_baseSequence = LookupSequence( "sitlookleft" );
  833. SetSequence( m_baseSequence + random->RandomInt(0,4) );
  834. ResetSequenceInfo( );
  835. }
  836. void CNPC_SittingScientist::Precache( void )
  837. {
  838. m_baseSequence = LookupSequence( "sitlookleft" );
  839. TalkInit();
  840. }
  841. int CNPC_SittingScientist::FriendNumber( int arrayNumber )
  842. {
  843. static int array[3] = { 2, 1, 0 };
  844. if ( arrayNumber < 3 )
  845. return array[ arrayNumber ];
  846. return arrayNumber;
  847. }
  848. //=========================================================
  849. // sit, do stuff
  850. //=========================================================
  851. void CNPC_SittingScientist::SittingThink( void )
  852. {
  853. CBaseEntity *pent;
  854. StudioFrameAdvance( );
  855. // try to greet player
  856. //FIXMEFIXME
  857. //MB - don't greet, done by base talker
  858. if ( 0 && GetExpresser()->CanSpeakConcept( TLK_HELLO ) )
  859. {
  860. pent = FindNearestFriend(true);
  861. if (pent)
  862. {
  863. float yaw = VecToYaw(pent->GetAbsOrigin() - GetAbsOrigin()) - GetAbsAngles().y;
  864. if (yaw > 180) yaw -= 360;
  865. if (yaw < -180) yaw += 360;
  866. if (yaw > 0)
  867. SetSequence( m_baseSequence + SITTING_ANIM_sitlookleft );
  868. else
  869. SetSequence ( m_baseSequence + SITTING_ANIM_sitlookright );
  870. ResetSequenceInfo( );
  871. SetCycle( 0 );
  872. SetBoneController( 0, 0 );
  873. GetExpresser()->Speak( TLK_HELLO );
  874. }
  875. }
  876. else if ( IsSequenceFinished() )
  877. {
  878. int i = random->RandomInt(0,99);
  879. m_iHeadTurn = 0;
  880. if (m_flResponseDelay && gpGlobals->curtime > m_flResponseDelay)
  881. {
  882. // respond to question
  883. GetExpresser()->Speak( TLK_QUESTION );
  884. SetSequence( m_baseSequence + SITTING_ANIM_sitscared );
  885. m_flResponseDelay = 0;
  886. }
  887. else if (i < 30)
  888. {
  889. SetSequence( m_baseSequence + SITTING_ANIM_sitting3 );
  890. // turn towards player or nearest friend and speak
  891. //FIXME
  892. /*/ if (!FBitSet(m_nSpeak, bit_saidHelloPlayer))
  893. pent = FindNearestFriend(TRUE);
  894. else*/
  895. pent = FindNamedEntity( "!nearestfriend" );
  896. if (!FIdleSpeak() || !pent)
  897. {
  898. m_iHeadTurn = random->RandomInt(0,8) * 10 - 40;
  899. SetSequence( m_baseSequence + SITTING_ANIM_sitting3 );
  900. }
  901. else
  902. {
  903. // only turn head if we spoke
  904. float yaw = VecToYaw(pent->GetAbsOrigin() - GetAbsOrigin()) - GetAbsAngles().y;
  905. if (yaw > 180) yaw -= 360;
  906. if (yaw < -180) yaw += 360;
  907. if (yaw > 0)
  908. SetSequence( m_baseSequence + SITTING_ANIM_sitlookleft );
  909. else
  910. SetSequence( m_baseSequence + SITTING_ANIM_sitlookright );
  911. //ALERT(at_console, "sitting speak\n");
  912. }
  913. }
  914. else if (i < 60)
  915. {
  916. SetSequence( m_baseSequence + SITTING_ANIM_sitting3 );
  917. m_iHeadTurn = random->RandomInt(0,8) * 10 - 40;
  918. if ( random->RandomInt(0,99) < 5)
  919. {
  920. //ALERT(at_console, "sitting speak2\n");
  921. FIdleSpeak();
  922. }
  923. }
  924. else if (i < 80)
  925. {
  926. SetSequence( m_baseSequence + SITTING_ANIM_sitting2 );
  927. }
  928. else if (i < 100)
  929. {
  930. SetSequence( m_baseSequence + SITTING_ANIM_sitscared );
  931. }
  932. ResetSequenceInfo( );
  933. SetCycle( 0 );
  934. SetBoneController( 0, m_iHeadTurn );
  935. }
  936. SetNextThink( gpGlobals->curtime + 0.1f );
  937. }
  938. // prepare sitting scientist to answer a question
  939. void CNPC_SittingScientist::SetAnswerQuestion( CNPCSimpleTalker *pSpeaker )
  940. {
  941. m_flResponseDelay = gpGlobals->curtime + random->RandomFloat(3, 4);
  942. SetSpeechTarget( (CNPCSimpleTalker *)pSpeaker );
  943. }
  944. //------------------------------------------------------------------------------
  945. //
  946. // Schedules
  947. //
  948. //------------------------------------------------------------------------------
  949. AI_BEGIN_CUSTOM_NPC( monster_scientist, CNPC_Scientist )
  950. DECLARE_TASK( TASK_SAY_HEAL )
  951. DECLARE_TASK( TASK_HEAL )
  952. DECLARE_TASK( TASK_SAY_FEAR )
  953. DECLARE_TASK( TASK_RUN_PATH_SCARED )
  954. DECLARE_TASK( TASK_SCREAM )
  955. DECLARE_TASK( TASK_RANDOM_SCREAM )
  956. DECLARE_TASK( TASK_MOVE_TO_TARGET_RANGE_SCARED )
  957. DECLARE_ACTIVITY( ACT_EXCITED )
  958. //=========================================================
  959. // > SCHED_SCI_HEAL
  960. //=========================================================
  961. DEFINE_SCHEDULE
  962. (
  963. SCHED_SCI_HEAL,
  964. " Tasks"
  965. " TASK_GET_PATH_TO_TARGET 0"
  966. " TASK_MOVE_TO_TARGET_RANGE 50"
  967. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCI_FOLLOWTARGET"
  968. " TASK_FACE_IDEAL 0"
  969. " TASK_SAY_HEAL 0"
  970. " TASK_PLAY_SEQUENCE_FACE_TARGET ACTIVITY:ACT_ARM"
  971. " TASK_HEAL 0"
  972. " TASK_PLAY_SEQUENCE_FACE_TARGET ACTIVITY:ACT_DISARM"
  973. " "
  974. " Interrupts"
  975. )
  976. //=========================================================
  977. // > SCHED_SCI_FOLLOWTARGET
  978. //=========================================================
  979. DEFINE_SCHEDULE
  980. (
  981. SCHED_SCI_FOLLOWTARGET,
  982. " Tasks"
  983. // " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCI_STOPFOLLOWING"
  984. " TASK_GET_PATH_TO_TARGET 0"
  985. " TASK_MOVE_TO_TARGET_RANGE 128"
  986. " TASK_SET_SCHEDULE SCHEDULE:SCHED_TARGET_FACE"
  987. " "
  988. " Interrupts"
  989. " COND_NEW_ENEMY"
  990. " COND_LIGHT_DAMAGE"
  991. " COND_HEAVY_DAMAGE"
  992. " COND_HEAR_DANGER"
  993. " COND_HEAR_COMBAT"
  994. )
  995. //=========================================================
  996. // > SCHED_SCI_STOPFOLLOWING
  997. //=========================================================
  998. // DEFINE_SCHEDULE
  999. // (
  1000. // SCHED_SCI_STOPFOLLOWING,
  1001. //
  1002. // " Tasks"
  1003. // " TASK_TALKER_CANT_FOLLOW 0"
  1004. // " "
  1005. // " Interrupts"
  1006. // )
  1007. //=========================================================
  1008. // > SCHED_SCI_FACETARGET
  1009. //=========================================================
  1010. DEFINE_SCHEDULE
  1011. (
  1012. SCHED_SCI_FACETARGET,
  1013. " Tasks"
  1014. " TASK_STOP_MOVING 0"
  1015. " TASK_FACE_TARGET 0"
  1016. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1017. " TASK_SET_SCHEDULE SCHEDULE:SCHED_SCI_FOLLOWTARGET"
  1018. " "
  1019. " Interrupts"
  1020. " COND_NEW_ENEMY"
  1021. " COND_HEAR_DANGER"
  1022. " COND_HEAR_COMBAT"
  1023. " COND_GIVE_WAY"
  1024. )
  1025. //=========================================================
  1026. // > SCHED_SCI_COVER
  1027. //=========================================================
  1028. DEFINE_SCHEDULE
  1029. (
  1030. SCHED_SCI_COVER,
  1031. " Tasks"
  1032. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCI_PANIC"
  1033. " TASK_STOP_MOVING 0"
  1034. " TASK_FIND_COVER_FROM_ENEMY 0"
  1035. " TASK_RUN_PATH_SCARED 0"
  1036. " TASK_TURN_LEFT 179"
  1037. " TASK_SET_SCHEDULE SCHEDULE:SCHED_SCI_HIDE"
  1038. " "
  1039. " Interrupts"
  1040. " COND_NEW_ENEMY"
  1041. )
  1042. //=========================================================
  1043. // > SCHED_SCI_HIDE
  1044. //=========================================================
  1045. DEFINE_SCHEDULE
  1046. (
  1047. SCHED_SCI_HIDE,
  1048. " Tasks"
  1049. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCI_PANIC"
  1050. " TASK_STOP_MOVING 0"
  1051. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_CROUCHIDLE"
  1052. " TASK_SET_ACTIVITY ACTIVITY:ACT_CROUCHIDLE"
  1053. " TASK_WAIT_RANDOM 10"
  1054. " "
  1055. " Interrupts"
  1056. " COND_NEW_ENEMY"
  1057. " COND_SEE_ENEMY"
  1058. " COND_SEE_HATE"
  1059. " COND_SEE_FEAR"
  1060. " COND_SEE_DISLIKE"
  1061. " COND_HEAR_DANGER"
  1062. )
  1063. //=========================================================
  1064. // > SCHED_SCI_IDLESTAND
  1065. //=========================================================
  1066. DEFINE_SCHEDULE
  1067. (
  1068. SCHED_SCI_IDLESTAND,
  1069. " Tasks"
  1070. " TASK_STOP_MOVING 0"
  1071. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1072. " TASK_WAIT 2"
  1073. " TASK_TALKER_HEADRESET 0"
  1074. " "
  1075. " Interrupts"
  1076. " COND_NEW_ENEMY"
  1077. " COND_LIGHT_DAMAGE"
  1078. " COND_HEAVY_DAMAGE"
  1079. " COND_SMELL"
  1080. " COND_PROVOKED"
  1081. " COND_HEAR_COMBAT"
  1082. " COND_GIVE_WAY"
  1083. )
  1084. //=========================================================
  1085. // > SCHED_SCI_PANIC
  1086. //=========================================================
  1087. DEFINE_SCHEDULE
  1088. (
  1089. SCHED_SCI_PANIC,
  1090. " Tasks"
  1091. " TASK_STOP_MOVING 0"
  1092. " TASK_FACE_ENEMY 0"
  1093. " TASK_SCREAM 0"
  1094. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_EXCITED"
  1095. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1096. " "
  1097. " Interrupts"
  1098. )
  1099. //=========================================================
  1100. // > SCHED_SCI_FOLLOWSCARED
  1101. //=========================================================
  1102. DEFINE_SCHEDULE
  1103. (
  1104. SCHED_SCI_FOLLOWSCARED,
  1105. " Tasks"
  1106. " TASK_GET_PATH_TO_TARGET 0"
  1107. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCI_FOLLOWTARGET"
  1108. " TASK_MOVE_TO_TARGET_RANGE_SCARED 128"
  1109. " "
  1110. " Interrupts"
  1111. " COND_NEW_ENEMY"
  1112. " COND_LIGHT_DAMAGE"
  1113. " COND_HEAVY_DAMAGE"
  1114. " COND_HEAR_DANGER"
  1115. )
  1116. //=========================================================
  1117. // > SCHED_SCI_FACETARGETSCARED
  1118. //=========================================================
  1119. DEFINE_SCHEDULE
  1120. (
  1121. SCHED_SCI_FACETARGETSCARED,
  1122. " Tasks"
  1123. " TASK_FACE_TARGET 0"
  1124. " TASK_SET_ACTIVITY ACTIVITY:ACT_CROUCHIDLE"
  1125. " TASK_SET_SCHEDULE SCHEDULE:SCHED_SCI_FOLLOWSCARED"
  1126. " "
  1127. " Interrupts"
  1128. " COND_NEW_ENEMY"
  1129. " COND_HEAR_DANGER"
  1130. )
  1131. //=========================================================
  1132. // > SCHED_FEAR
  1133. //=========================================================
  1134. DEFINE_SCHEDULE
  1135. (
  1136. SCHED_SCI_FEAR,
  1137. " Tasks"
  1138. " TASK_STOP_MOVING 0"
  1139. " TASK_FACE_ENEMY 0"
  1140. " TASK_SAY_FEAR 0"
  1141. " "
  1142. " Interrupts"
  1143. " COND_NEW_ENEMY"
  1144. )
  1145. //=========================================================
  1146. // > SCHED_SCI_STARTLE
  1147. //=========================================================
  1148. DEFINE_SCHEDULE
  1149. (
  1150. SCHED_SCI_STARTLE,
  1151. " Tasks"
  1152. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCI_PANIC"
  1153. " TASK_RANDOM_SCREAM 0.3"
  1154. " TASK_STOP_MOVING 0"
  1155. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_CROUCH"
  1156. " TASK_RANDOM_SCREAM 0.1"
  1157. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_CROUCHIDLE"
  1158. " TASK_WAIT_RANDOM 1"
  1159. " "
  1160. " Interrupts"
  1161. " COND_NEW_ENEMY"
  1162. " COND_SEE_ENEMY"
  1163. " COND_SEE_HATE"
  1164. " COND_SEE_FEAR"
  1165. " COND_SEE_DISLIKE"
  1166. )
  1167. AI_END_CUSTOM_NPC()