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.

2645 lines
69 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 "beam_shared.h"
  16. #include "ai_default.h"
  17. #include "ai_task.h"
  18. #include "ai_schedule.h"
  19. #include "ai_node.h"
  20. #include "ai_hull.h"
  21. #include "ai_hint.h"
  22. #include "ai_memory.h"
  23. #include "ai_route.h"
  24. #include "ai_motor.h"
  25. #include "hl1_npc_hgrunt.h"
  26. #include "soundent.h"
  27. #include "game.h"
  28. #include "npcevent.h"
  29. #include "entitylist.h"
  30. #include "activitylist.h"
  31. #include "animation.h"
  32. #include "engine/IEngineSound.h"
  33. #include "ammodef.h"
  34. #include "basecombatweapon.h"
  35. #include "hl1_basegrenade.h"
  36. #include "ai_interactions.h"
  37. #include "scripted.h"
  38. #include "hl1_basegrenade.h"
  39. #include "hl1_grenade_mp5.h"
  40. ConVar sk_hgrunt_health( "sk_hgrunt_health","0");
  41. ConVar sk_hgrunt_kick ( "sk_hgrunt_kick", "0" );
  42. ConVar sk_hgrunt_pellets ( "sk_hgrunt_pellets", "0" );
  43. ConVar sk_hgrunt_gspeed ( "sk_hgrunt_gspeed", "0" );
  44. extern ConVar sk_plr_dmg_grenade;
  45. extern ConVar sk_plr_dmg_mp5_grenade;
  46. #define SF_GRUNT_LEADER ( 1 << 5 )
  47. int g_fGruntQuestion; // true if an idle grunt asked a question. Cleared when someone answers.
  48. int g_iSquadIndex = 0;
  49. #define HGRUNT_GUN_SPREAD 0.08716f
  50. //=========================================================
  51. // monster-specific DEFINE's
  52. //=========================================================
  53. #define GRUNT_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
  54. #define GRUNT_VOL 0.35 // volume of grunt sounds
  55. #define GRUNT_SNDLVL SNDLVL_NORM // soundlevel of grunt sentences
  56. #define HGRUNT_LIMP_HEALTH 20
  57. #define HGRUNT_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot.
  58. #define HGRUNT_NUM_HEADS 2 // how many grunt heads are there?
  59. #define HGRUNT_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill
  60. #define HGRUNT_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences
  61. #define HGRUNT_9MMAR ( 1 << 0)
  62. #define HGRUNT_HANDGRENADE ( 1 << 1)
  63. #define HGRUNT_GRENADELAUNCHER ( 1 << 2)
  64. #define HGRUNT_SHOTGUN ( 1 << 3)
  65. #define HEAD_GROUP 1
  66. #define HEAD_GRUNT 0
  67. #define HEAD_COMMANDER 1
  68. #define HEAD_SHOTGUN 2
  69. #define HEAD_M203 3
  70. #define GUN_GROUP 2
  71. #define GUN_MP5 0
  72. #define GUN_SHOTGUN 1
  73. #define GUN_NONE 2
  74. //=========================================================
  75. // Monster's Anim Events Go Here
  76. //=========================================================
  77. #define HGRUNT_AE_RELOAD ( 2 )
  78. #define HGRUNT_AE_KICK ( 3 )
  79. #define HGRUNT_AE_BURST1 ( 4 )
  80. #define HGRUNT_AE_BURST2 ( 5 )
  81. #define HGRUNT_AE_BURST3 ( 6 )
  82. #define HGRUNT_AE_GREN_TOSS ( 7 )
  83. #define HGRUNT_AE_GREN_LAUNCH ( 8 )
  84. #define HGRUNT_AE_GREN_DROP ( 9 )
  85. #define HGRUNT_AE_CAUGHT_ENEMY ( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad.
  86. #define HGRUNT_AE_DROP_GUN ( 11) // grunt (probably dead) is dropping his mp5.
  87. const char *CNPC_HGrunt::pGruntSentences[] =
  88. {
  89. "HG_GREN", // grenade scared grunt
  90. "HG_ALERT", // sees player
  91. "HG_MONSTER", // sees monster
  92. "HG_COVER", // running to cover
  93. "HG_THROW", // about to throw grenade
  94. "HG_CHARGE", // running out to get the enemy
  95. "HG_TAUNT", // say rude things
  96. };
  97. enum HGRUNT_SENTENCE_TYPES
  98. {
  99. HGRUNT_SENT_NONE = -1,
  100. HGRUNT_SENT_GREN = 0,
  101. HGRUNT_SENT_ALERT,
  102. HGRUNT_SENT_MONSTER,
  103. HGRUNT_SENT_COVER,
  104. HGRUNT_SENT_THROW,
  105. HGRUNT_SENT_CHARGE,
  106. HGRUNT_SENT_TAUNT,
  107. } ;
  108. LINK_ENTITY_TO_CLASS( monster_human_grunt, CNPC_HGrunt );
  109. //=========================================================
  110. // monster-specific schedule types
  111. //=========================================================
  112. enum
  113. {
  114. SCHED_GRUNT_FAIL = LAST_SHARED_SCHEDULE,
  115. SCHED_GRUNT_COMBAT_FAIL,
  116. SCHED_GRUNT_VICTORY_DANCE,
  117. SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,
  118. SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE_RETRY,
  119. SCHED_GRUNT_FOUND_ENEMY,
  120. SCHED_GRUNT_COMBAT_FACE,
  121. SCHED_GRUNT_SIGNAL_SUPPRESS,
  122. SCHED_GRUNT_SUPPRESS,
  123. SCHED_GRUNT_WAIT_IN_COVER,
  124. SCHED_GRUNT_TAKE_COVER,
  125. SCHED_GRUNT_GRENADE_COVER,
  126. SCHED_GRUNT_TOSS_GRENADE_COVER,
  127. SCHED_GRUNT_HIDE_RELOAD,
  128. SCHED_GRUNT_SWEEP,
  129. SCHED_GRUNT_RANGE_ATTACK1A,
  130. SCHED_GRUNT_RANGE_ATTACK1B,
  131. SCHED_GRUNT_RANGE_ATTACK2,
  132. SCHED_GRUNT_REPEL,
  133. SCHED_GRUNT_REPEL_ATTACK,
  134. SCHED_GRUNT_REPEL_LAND,
  135. SCHED_GRUNT_TAKE_COVER_FAILED,
  136. SCHED_GRUNT_RELOAD,
  137. SCHED_GRUNT_TAKE_COVER_FROM_ENEMY,
  138. SCHED_GRUNT_BARNACLE_HIT,
  139. SCHED_GRUNT_BARNACLE_PULL,
  140. SCHED_GRUNT_BARNACLE_CHOMP,
  141. SCHED_GRUNT_BARNACLE_CHEW,
  142. };
  143. //=========================================================
  144. // monster-specific tasks
  145. //=========================================================
  146. enum
  147. {
  148. TASK_GRUNT_FACE_TOSS_DIR = LAST_SHARED_TASK + 1,
  149. TASK_GRUNT_SPEAK_SENTENCE,
  150. TASK_GRUNT_CHECK_FIRE,
  151. };
  152. //=========================================================
  153. // monster-specific conditions
  154. //=========================================================
  155. enum
  156. {
  157. COND_GRUNT_NOFIRE = LAST_SHARED_CONDITION + 1,
  158. };
  159. // -----------------------------------------------
  160. // > Squad slots
  161. // -----------------------------------------------
  162. enum SquadSlot_T
  163. {
  164. SQUAD_SLOT_GRENADE1 = LAST_SHARED_SQUADSLOT,
  165. SQUAD_SLOT_GRENADE2,
  166. SQUAD_SLOT_ENGAGE1,
  167. SQUAD_SLOT_ENGAGE2,
  168. };
  169. int ACT_GRUNT_LAUNCH_GRENADE;
  170. int ACT_GRUNT_TOSS_GRENADE;
  171. int ACT_GRUNT_MP5_STANDING;
  172. int ACT_GRUNT_MP5_CROUCHING;
  173. int ACT_GRUNT_SHOTGUN_STANDING;
  174. int ACT_GRUNT_SHOTGUN_CROUCHING;
  175. //---------------------------------------------------------
  176. // Save/Restore
  177. //---------------------------------------------------------
  178. BEGIN_DATADESC( CNPC_HGrunt )
  179. DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ),
  180. DEFINE_FIELD( m_flNextPainTime, FIELD_TIME ),
  181. DEFINE_FIELD( m_flCheckAttackTime, FIELD_FLOAT ),
  182. DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ),
  183. DEFINE_FIELD( m_iLastGrenadeCondition, FIELD_INTEGER ),
  184. DEFINE_FIELD( m_fStanding, FIELD_BOOLEAN ),
  185. DEFINE_FIELD( m_fFirstEncounter, FIELD_BOOLEAN ),
  186. DEFINE_FIELD( m_iClipSize, FIELD_INTEGER ),
  187. DEFINE_FIELD( m_voicePitch, FIELD_INTEGER ),
  188. DEFINE_FIELD( m_iSentence, FIELD_INTEGER ),
  189. DEFINE_KEYFIELD( m_iWeapons, FIELD_INTEGER, "weapons" ),
  190. DEFINE_KEYFIELD( m_SquadName, FIELD_STRING, "netname" ),
  191. DEFINE_FIELD( m_bInBarnacleMouth, FIELD_BOOLEAN ),
  192. DEFINE_FIELD( m_flLastEnemySightTime, FIELD_TIME ),
  193. DEFINE_FIELD( m_flTalkWaitTime, FIELD_TIME ),
  194. //DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
  195. END_DATADESC()
  196. //=========================================================
  197. // Spawn
  198. //=========================================================
  199. void CNPC_HGrunt::Spawn()
  200. {
  201. Precache( );
  202. SetModel( "models/hgrunt.mdl" );
  203. SetHullType(HULL_HUMAN);
  204. SetHullSizeNormal();
  205. SetSolid( SOLID_BBOX );
  206. AddSolidFlags( FSOLID_NOT_STANDABLE );
  207. SetMoveType( MOVETYPE_STEP );
  208. m_bloodColor = BLOOD_COLOR_RED;
  209. ClearEffects();
  210. m_iHealth = sk_hgrunt_health.GetFloat();
  211. m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
  212. m_NPCState = NPC_STATE_NONE;
  213. m_flNextGrenadeCheck = gpGlobals->curtime + 1;
  214. m_flNextPainTime = gpGlobals->curtime;
  215. m_iSentence = HGRUNT_SENT_NONE;
  216. CapabilitiesClear();
  217. CapabilitiesAdd ( bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP | bits_CAP_MOVE_GROUND );
  218. CapabilitiesAdd(bits_CAP_INNATE_RANGE_ATTACK1 );
  219. // Innate range attack for grenade
  220. CapabilitiesAdd(bits_CAP_INNATE_RANGE_ATTACK2 );
  221. // Innate range attack for kicking
  222. CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 );
  223. m_fFirstEncounter = true;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
  224. m_HackedGunPos = Vector ( 0, 0, 55 );
  225. if ( m_iWeapons == 0)
  226. {
  227. // initialize to original values
  228. m_iWeapons = HGRUNT_9MMAR | HGRUNT_HANDGRENADE;
  229. // pev->weapons = HGRUNT_SHOTGUN;
  230. // pev->weapons = HGRUNT_9MMAR | HGRUNT_GRENADELAUNCHER;
  231. }
  232. if (FBitSet( m_iWeapons, HGRUNT_SHOTGUN ))
  233. {
  234. SetBodygroup( GUN_GROUP, GUN_SHOTGUN );
  235. m_iClipSize = 8;
  236. }
  237. else
  238. {
  239. m_iClipSize = GRUNT_CLIP_SIZE;
  240. }
  241. m_cAmmoLoaded = m_iClipSize;
  242. if ( random->RandomInt( 0, 99 ) < 80)
  243. m_nSkin = 0; // light skin
  244. else
  245. m_nSkin = 1; // dark skin
  246. if (FBitSet( m_iWeapons, HGRUNT_SHOTGUN ))
  247. {
  248. SetBodygroup( HEAD_GROUP, HEAD_SHOTGUN);
  249. }
  250. else if (FBitSet( m_iWeapons, HGRUNT_GRENADELAUNCHER ))
  251. {
  252. SetBodygroup( HEAD_GROUP, HEAD_M203 );
  253. m_nSkin = 1; // alway dark skin
  254. }
  255. m_flTalkWaitTime = 0;
  256. //HACK
  257. g_iSquadIndex = 0;
  258. BaseClass::Spawn();
  259. NPCInit();
  260. }
  261. int CNPC_HGrunt::IRelationPriority( CBaseEntity *pTarget )
  262. {
  263. //I hate alien grunts more than anything.
  264. if ( pTarget->Classify() == CLASS_ALIEN_MILITARY )
  265. {
  266. if ( FClassnameIs( pTarget, "monster_alien_grunt" ) )
  267. {
  268. return ( BaseClass::IRelationPriority ( pTarget ) + 1 );
  269. }
  270. }
  271. return BaseClass::IRelationPriority( pTarget );
  272. }
  273. //=========================================================
  274. // Precache - precaches all resources this monster needs
  275. //=========================================================
  276. void CNPC_HGrunt::Precache()
  277. {
  278. m_iAmmoType = GetAmmoDef()->Index("9mmRound");
  279. PrecacheModel("models/hgrunt.mdl");
  280. // get voice pitch
  281. if ( random->RandomInt(0,1))
  282. m_voicePitch = 109 + random->RandomInt(0,7);
  283. else
  284. m_voicePitch = 100;
  285. PrecacheScriptSound( "HGrunt.Reload" );
  286. PrecacheScriptSound( "HGrunt.GrenadeLaunch" );
  287. PrecacheScriptSound( "HGrunt.9MM" );
  288. PrecacheScriptSound( "HGrunt.Shotgun" );
  289. PrecacheScriptSound( "HGrunt.Pain" );
  290. PrecacheScriptSound( "HGrunt.Die" );
  291. BaseClass::Precache();
  292. UTIL_PrecacheOther( "grenade_hand" );
  293. UTIL_PrecacheOther( "grenade_mp5" );
  294. }
  295. //=========================================================
  296. // someone else is talking - don't speak
  297. //=========================================================
  298. bool CNPC_HGrunt::FOkToSpeak( void )
  299. {
  300. // if someone else is talking, don't speak
  301. if ( gpGlobals->curtime <= m_flTalkWaitTime )
  302. return FALSE;
  303. if ( m_spawnflags & SF_NPC_GAG )
  304. {
  305. if ( m_NPCState != NPC_STATE_COMBAT )
  306. {
  307. // no talking outside of combat if gagged.
  308. return FALSE;
  309. }
  310. }
  311. return TRUE;
  312. }
  313. //=========================================================
  314. // Speak Sentence - say your cued up sentence.
  315. //
  316. // Some grunt sentences (take cover and charge) rely on actually
  317. // being able to execute the intended action. It's really lame
  318. // when a grunt says 'COVER ME' and then doesn't move. The problem
  319. // is that the sentences were played when the decision to TRY
  320. // to move to cover was made. Now the sentence is played after
  321. // we know for sure that there is a valid path. The schedule
  322. // may still fail but in most cases, well after the grunt has
  323. // started moving.
  324. //=========================================================
  325. void CNPC_HGrunt::SpeakSentence( void )
  326. {
  327. if ( m_iSentence == HGRUNT_SENT_NONE )
  328. {
  329. // no sentence cued up.
  330. return;
  331. }
  332. if ( FOkToSpeak() )
  333. {
  334. SENTENCEG_PlayRndSz( edict(), pGruntSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  335. JustSpoke();
  336. }
  337. }
  338. //=========================================================
  339. //=========================================================
  340. void CNPC_HGrunt::JustSpoke( void )
  341. {
  342. m_flTalkWaitTime = gpGlobals->curtime + random->RandomFloat( 1.5f, 2.0f );
  343. m_iSentence = HGRUNT_SENT_NONE;
  344. }
  345. //=========================================================
  346. // PrescheduleThink - this function runs after conditions
  347. // are collected and before scheduling code is run.
  348. //=========================================================
  349. void CNPC_HGrunt::PrescheduleThink ( void )
  350. {
  351. BaseClass::PrescheduleThink();
  352. if ( m_pSquad && GetEnemy() != NULL )
  353. {
  354. if ( m_pSquad->GetLeader() == NULL )
  355. return;
  356. CNPC_HGrunt *pSquadLeader = (CNPC_HGrunt*)m_pSquad->GetLeader()->MyNPCPointer();
  357. if ( pSquadLeader == NULL )
  358. return; //Paranoid, so making sure it's ok.
  359. if ( HasCondition ( COND_SEE_ENEMY ) )
  360. {
  361. // update the squad's last enemy sighting time.
  362. pSquadLeader->m_flLastEnemySightTime = gpGlobals->curtime;
  363. }
  364. else
  365. {
  366. if ( gpGlobals->curtime - pSquadLeader->m_flLastEnemySightTime > 5 )
  367. {
  368. // been a while since we've seen the enemy
  369. pSquadLeader->GetEnemies()->MarkAsEluded( GetEnemy() );
  370. }
  371. }
  372. }
  373. }
  374. Class_T CNPC_HGrunt::Classify ( void )
  375. {
  376. return CLASS_HUMAN_MILITARY;
  377. }
  378. //=========================================================
  379. //
  380. // SquadRecruit(), get some monsters of my classification and
  381. // link them as a group. returns the group size
  382. //
  383. //=========================================================
  384. int CNPC_HGrunt::SquadRecruit( int searchRadius, int maxMembers )
  385. {
  386. int squadCount;
  387. int iMyClass = Classify();// cache this monster's class
  388. if ( maxMembers < 2 )
  389. return 0;
  390. // I am my own leader
  391. squadCount = 1;
  392. CBaseEntity *pEntity = NULL;
  393. if ( m_SquadName != NULL_STRING )
  394. {
  395. // I have a netname, so unconditionally recruit everyone else with that name.
  396. pEntity = gEntList.FindEntityByClassname( pEntity, "monster_human_grunt" );
  397. while ( pEntity )
  398. {
  399. CNPC_HGrunt *pRecruit = (CNPC_HGrunt*)pEntity->MyNPCPointer();
  400. if ( pRecruit )
  401. {
  402. if ( !pRecruit->m_pSquad && pRecruit->Classify() == iMyClass && pRecruit != this )
  403. {
  404. // minimum protection here against user error.in worldcraft.
  405. if ( pRecruit->m_SquadName != NULL_STRING && FStrEq( STRING( m_SquadName ), STRING( pRecruit->m_SquadName ) ) )
  406. {
  407. pRecruit->InitSquad();
  408. squadCount++;
  409. }
  410. }
  411. }
  412. pEntity = gEntList.FindEntityByClassname( pEntity, "monster_human_grunt" );
  413. }
  414. return squadCount;
  415. }
  416. else
  417. {
  418. char szSquadName[64];
  419. Q_snprintf( szSquadName, sizeof( szSquadName ), "squad%d\n", g_iSquadIndex );
  420. m_SquadName = MAKE_STRING( szSquadName );
  421. while ( ( pEntity = gEntList.FindEntityInSphere( pEntity, GetAbsOrigin(), searchRadius ) ) != NULL )
  422. {
  423. if ( !FClassnameIs ( pEntity, "monster_human_grunt" ) )
  424. continue;
  425. CNPC_HGrunt *pRecruit = (CNPC_HGrunt*)pEntity->MyNPCPointer();
  426. if ( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_hCine )
  427. {
  428. // Can we recruit this guy?
  429. if ( !pRecruit->m_pSquad && pRecruit->Classify() == iMyClass &&
  430. ( (iMyClass != CLASS_ALIEN_MONSTER) || FClassnameIs( this, pRecruit->GetClassname() ) ) &&
  431. !pRecruit->m_SquadName )
  432. {
  433. trace_t tr;
  434. UTIL_TraceLine( GetAbsOrigin() + GetViewOffset(), pRecruit->GetAbsOrigin() + GetViewOffset(), MASK_NPCSOLID_BRUSHONLY, pRecruit, COLLISION_GROUP_NONE, &tr );// try to hit recruit with a traceline.
  435. if ( tr.fraction == 1.0 )
  436. {
  437. //We're ready to recruit people, so start a squad if I don't have one.
  438. if ( !m_pSquad )
  439. {
  440. InitSquad();
  441. }
  442. pRecruit->m_SquadName = m_SquadName;
  443. pRecruit->CapabilitiesAdd ( bits_CAP_SQUAD );
  444. pRecruit->InitSquad();
  445. squadCount++;
  446. }
  447. }
  448. }
  449. }
  450. if ( squadCount > 1 )
  451. {
  452. g_iSquadIndex++;
  453. }
  454. }
  455. return squadCount;
  456. }
  457. void CNPC_HGrunt::StartNPC ( void )
  458. {
  459. if ( !m_pSquad )
  460. {
  461. if ( m_SquadName != NULL_STRING )
  462. {
  463. // if I have a groupname, I can only recruit if I'm flagged as leader
  464. if ( GetSpawnFlags() & SF_GRUNT_LEADER )
  465. {
  466. InitSquad();
  467. // try to form squads now.
  468. int iSquadSize = SquadRecruit( 1024, 4 );
  469. if ( iSquadSize )
  470. {
  471. Msg ( "Squad of %d %s formed\n", iSquadSize, GetClassname() );
  472. }
  473. }
  474. else
  475. {
  476. //Hacky.
  477. //Revisit me later.
  478. const char *pSquadName = STRING( m_SquadName );
  479. m_SquadName = NULL_STRING;
  480. BaseClass::StartNPC();
  481. m_SquadName = MAKE_STRING( pSquadName );
  482. return;
  483. }
  484. }
  485. else
  486. {
  487. int iSquadSize = SquadRecruit( 1024, 4 );
  488. if ( iSquadSize )
  489. {
  490. Msg ( "Squad of %d %s formed\n", iSquadSize, GetClassname() );
  491. }
  492. }
  493. }
  494. BaseClass::StartNPC();
  495. if ( m_pSquad && m_pSquad->IsLeader( this ) )
  496. {
  497. SetBodygroup( 1, 1 ); // UNDONE: truly ugly hack
  498. m_nSkin = 0;
  499. }
  500. }
  501. //=========================================================
  502. // CheckMeleeAttack1
  503. //=========================================================
  504. int CNPC_HGrunt::MeleeAttack1Conditions ( float flDot, float flDist )
  505. {
  506. if (flDist > 64)
  507. return COND_TOO_FAR_TO_ATTACK;
  508. else if (flDot < 0.7)
  509. return COND_NOT_FACING_ATTACK;
  510. return COND_CAN_MELEE_ATTACK1;
  511. }
  512. //=========================================================
  513. // CheckRangeAttack1 - overridden for HGrunt, cause
  514. // FCanCheckAttacks() doesn't disqualify all attacks based
  515. // on whether or not the enemy is occluded because unlike
  516. // the base class, the HGrunt can attack when the enemy is
  517. // occluded (throw grenade over wall, etc). We must
  518. // disqualify the machine gun attack if the enemy is occluded.
  519. //=========================================================
  520. int CNPC_HGrunt::RangeAttack1Conditions ( float flDot, float flDist )
  521. {
  522. if ( !HasCondition( COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 && NoFriendlyFire() )
  523. {
  524. trace_t tr;
  525. if ( !GetEnemy()->IsPlayer() && flDist <= 64 )
  526. {
  527. // kick nonclients, but don't shoot at them.
  528. return COND_NONE;
  529. }
  530. Vector vecSrc;
  531. QAngle angAngles;
  532. GetAttachment( "0", vecSrc, angAngles );
  533. //NDebugOverlay::Line( GetAbsOrigin() + GetViewOffset(), GetEnemy()->BodyTarget(GetAbsOrigin() + GetViewOffset()), 255, 0, 0, false, 0.1 );
  534. // verify that a bullet fired from the gun will hit the enemy before the world.
  535. UTIL_TraceLine( GetAbsOrigin() + GetViewOffset(), GetEnemy()->BodyTarget(GetAbsOrigin() + GetViewOffset()), MASK_SHOT, this/*pentIgnore*/, COLLISION_GROUP_NONE, &tr);
  536. if ( tr.fraction == 1.0 || tr.m_pEnt == GetEnemy() )
  537. {
  538. //NDebugOverlay::Line( tr.startpos, tr.endpos, 0, 255, 0, false, 1.0 );
  539. return COND_CAN_RANGE_ATTACK1;
  540. }
  541. //NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 1.0 );
  542. }
  543. if ( !NoFriendlyFire() )
  544. return COND_WEAPON_BLOCKED_BY_FRIEND; //err =|
  545. return COND_NONE;
  546. }
  547. int CNPC_HGrunt::RangeAttack2Conditions( float flDot, float flDist )
  548. {
  549. m_iLastGrenadeCondition = GetGrenadeConditions( flDot, flDist );
  550. return m_iLastGrenadeCondition;
  551. }
  552. int CNPC_HGrunt::GetGrenadeConditions( float flDot, float flDist )
  553. {
  554. if ( !FBitSet( m_iWeapons, ( HGRUNT_HANDGRENADE | HGRUNT_GRENADELAUNCHER ) ) )
  555. return COND_NONE;
  556. // assume things haven't changed too much since last time
  557. if (gpGlobals->curtime < m_flNextGrenadeCheck )
  558. return m_iLastGrenadeCondition;
  559. if ( m_flGroundSpeed != 0 )
  560. return COND_NONE;
  561. CBaseEntity *pEnemy = GetEnemy();
  562. if (!pEnemy)
  563. return COND_NONE;
  564. Vector flEnemyLKP = GetEnemyLKP();
  565. if ( !(pEnemy->GetFlags() & FL_ONGROUND) && pEnemy->GetWaterLevel() == 0 && flEnemyLKP.z > (GetAbsOrigin().z + WorldAlignMaxs().z) )
  566. {
  567. //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to
  568. // be grenaded.
  569. // don't throw grenades at anything that isn't on the ground!
  570. return COND_NONE;
  571. }
  572. Vector vecTarget;
  573. if (FBitSet( m_iWeapons, HGRUNT_HANDGRENADE))
  574. {
  575. // find feet
  576. if ( random->RandomInt( 0,1 ) )
  577. {
  578. // magically know where they are
  579. pEnemy->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecTarget );
  580. }
  581. else
  582. {
  583. // toss it to where you last saw them
  584. vecTarget = flEnemyLKP;
  585. }
  586. }
  587. else
  588. {
  589. // find target
  590. // vecTarget = GetEnemy()->BodyTarget( GetAbsOrigin() );
  591. vecTarget = GetEnemy()->GetAbsOrigin() + (GetEnemy()->BodyTarget( GetAbsOrigin() ) - GetEnemy()->GetAbsOrigin());
  592. // estimate position
  593. if ( HasCondition( COND_SEE_ENEMY))
  594. {
  595. vecTarget = vecTarget + ((vecTarget - GetAbsOrigin()).Length() / sk_hgrunt_gspeed.GetFloat()) * GetEnemy()->GetAbsVelocity();
  596. }
  597. }
  598. // are any of my squad members near the intended grenade impact area?
  599. if ( m_pSquad )
  600. {
  601. if ( m_pSquad->SquadMemberInRange( vecTarget, 256 ) )
  602. {
  603. // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while.
  604. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  605. return COND_NONE;
  606. }
  607. }
  608. if ( ( vecTarget - GetAbsOrigin() ).Length2D() <= 256 )
  609. {
  610. // crap, I don't want to blow myself up
  611. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  612. return COND_NONE;
  613. }
  614. if (FBitSet( m_iWeapons, HGRUNT_HANDGRENADE))
  615. {
  616. Vector vGunPos;
  617. QAngle angGunAngles;
  618. GetAttachment( "0", vGunPos, angGunAngles );
  619. Vector vecToss = VecCheckToss( this, vGunPos, vecTarget, -1, 0.5, false );
  620. if ( vecToss != vec3_origin )
  621. {
  622. m_vecTossVelocity = vecToss;
  623. // don't check again for a while.
  624. m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second.
  625. return COND_CAN_RANGE_ATTACK2;
  626. }
  627. else
  628. {
  629. // don't check again for a while.
  630. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  631. return COND_NONE;
  632. }
  633. }
  634. else
  635. {
  636. Vector vGunPos;
  637. QAngle angGunAngles;
  638. GetAttachment( "0", vGunPos, angGunAngles );
  639. Vector vecToss = VecCheckThrow( this, vGunPos, vecTarget, sk_hgrunt_gspeed.GetFloat(), 0.5 );
  640. if ( vecToss != vec3_origin )
  641. {
  642. m_vecTossVelocity = vecToss;
  643. // don't check again for a while.
  644. m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second.
  645. return COND_CAN_RANGE_ATTACK2;
  646. }
  647. else
  648. {
  649. // don't check again for a while.
  650. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  651. return COND_NONE;
  652. }
  653. }
  654. }
  655. //=========================================================
  656. // FCanCheckAttacks - this is overridden for human grunts
  657. // because they can throw/shoot grenades when they can't see their
  658. // target and the base class doesn't check attacks if the monster
  659. // cannot see its enemy.
  660. //
  661. // !!!BUGBUG - this gets called before a 3-round burst is fired
  662. // which means that a friendly can still be hit with up to 2 rounds.
  663. // ALSO, grenades will not be tossed if there is a friendly in front,
  664. // this is a bad bug. Friendly machine gun fire avoidance
  665. // will unecessarily prevent the throwing of a grenade as well.
  666. //=========================================================
  667. bool CNPC_HGrunt::FCanCheckAttacks( void )
  668. {
  669. // This condition set when too close to a grenade to blow it up
  670. if ( !HasCondition( COND_TOO_CLOSE_TO_ATTACK ) )
  671. {
  672. return true;
  673. }
  674. else
  675. {
  676. return false;
  677. }
  678. }
  679. int CNPC_HGrunt::GetSoundInterests( void )
  680. {
  681. return SOUND_WORLD |
  682. SOUND_COMBAT |
  683. SOUND_PLAYER |
  684. SOUND_BULLET_IMPACT |
  685. SOUND_DANGER;
  686. }
  687. //=========================================================
  688. // TraceAttack - make sure we're not taking it in the helmet
  689. //=========================================================
  690. void CNPC_HGrunt::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  691. {
  692. CTakeDamageInfo info = inputInfo;
  693. // check for helmet shot
  694. if (ptr->hitgroup == 11)
  695. {
  696. // make sure we're wearing one
  697. if ( GetBodygroup( 1 ) == HEAD_GRUNT && (info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB)))
  698. {
  699. // absorb damage
  700. info.SetDamage( info.GetDamage() - 20 );
  701. if ( info.GetDamage() <= 0 )
  702. info.SetDamage( 0.01 );
  703. }
  704. // it's head shot anyways
  705. ptr->hitgroup = HITGROUP_HEAD;
  706. }
  707. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  708. }
  709. //=========================================================
  710. // TakeDamage - overridden for the grunt because the grunt
  711. // needs to forget that he is in cover if he's hurt. (Obviously
  712. // not in a safe place anymore).
  713. //=========================================================
  714. int CNPC_HGrunt::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  715. {
  716. Forget( bits_MEMORY_INCOVER );
  717. return BaseClass::OnTakeDamage_Alive ( inputInfo );
  718. }
  719. //=========================================================
  720. // SetYawSpeed - allows each sequence to have a different
  721. // turn rate associated with it.
  722. //=========================================================
  723. float CNPC_HGrunt::MaxYawSpeed( void )
  724. {
  725. float flYS;
  726. switch ( GetActivity() )
  727. {
  728. case ACT_IDLE:
  729. flYS = 150;
  730. break;
  731. case ACT_RUN:
  732. flYS = 150;
  733. break;
  734. case ACT_WALK:
  735. flYS = 180;
  736. break;
  737. case ACT_RANGE_ATTACK1:
  738. flYS = 120;
  739. break;
  740. case ACT_RANGE_ATTACK2:
  741. flYS = 120;
  742. break;
  743. case ACT_MELEE_ATTACK1:
  744. flYS = 120;
  745. break;
  746. case ACT_MELEE_ATTACK2:
  747. flYS = 120;
  748. break;
  749. case ACT_TURN_LEFT:
  750. case ACT_TURN_RIGHT:
  751. flYS = 180;
  752. break;
  753. case ACT_GLIDE:
  754. case ACT_FLY:
  755. flYS = 30;
  756. break;
  757. default:
  758. flYS = 90;
  759. break;
  760. }
  761. // Yaw speed is handled differently now!
  762. return flYS * 0.5f;
  763. }
  764. void CNPC_HGrunt::IdleSound( void )
  765. {
  766. if (FOkToSpeak() && ( g_fGruntQuestion || random->RandomInt( 0,1 ) ) )
  767. {
  768. if (!g_fGruntQuestion)
  769. {
  770. // ask question or make statement
  771. switch ( random->RandomInt( 0,2 ) )
  772. {
  773. case 0: // check in
  774. SENTENCEG_PlayRndSz( edict(), "HG_CHECK", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
  775. g_fGruntQuestion = 1;
  776. break;
  777. case 1: // question
  778. SENTENCEG_PlayRndSz( edict(), "HG_QUEST", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
  779. g_fGruntQuestion = 2;
  780. break;
  781. case 2: // statement
  782. SENTENCEG_PlayRndSz( edict(), "HG_IDLE", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
  783. break;
  784. }
  785. }
  786. else
  787. {
  788. switch (g_fGruntQuestion)
  789. {
  790. case 1: // check in
  791. SENTENCEG_PlayRndSz( edict(), "HG_CLEAR", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
  792. break;
  793. case 2: // question
  794. SENTENCEG_PlayRndSz( edict(), "HG_ANSWER", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
  795. break;
  796. }
  797. g_fGruntQuestion = 0;
  798. }
  799. JustSpoke();
  800. }
  801. }
  802. bool CNPC_HGrunt::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
  803. {
  804. if (interactionType == g_interactionBarnacleVictimDangle)
  805. {
  806. // Force choosing of a new schedule
  807. ClearSchedule( "Soldier being eaten by a barnacle" );
  808. m_bInBarnacleMouth = true;
  809. return true;
  810. }
  811. else if ( interactionType == g_interactionBarnacleVictimReleased )
  812. {
  813. SetState ( NPC_STATE_IDLE );
  814. m_bInBarnacleMouth = false;
  815. SetAbsVelocity( vec3_origin );
  816. SetMoveType( MOVETYPE_STEP );
  817. return true;
  818. }
  819. else if ( interactionType == g_interactionBarnacleVictimGrab )
  820. {
  821. if ( GetFlags() & FL_ONGROUND )
  822. {
  823. SetGroundEntity( NULL );
  824. }
  825. //Maybe this will break something else.
  826. if ( GetState() == NPC_STATE_SCRIPT )
  827. {
  828. m_hCine->CancelScript();
  829. ClearSchedule( "Soldier grabbed by a barnacle" );
  830. }
  831. SetState( NPC_STATE_PRONE );
  832. CTakeDamageInfo info;
  833. PainSound( info );
  834. return true;
  835. }
  836. return false;
  837. }
  838. //-----------------------------------------------------------------------------
  839. // Purpose: Combine needs to check ammo
  840. // Input :
  841. // Output :
  842. //-----------------------------------------------------------------------------
  843. void CNPC_HGrunt::CheckAmmo ( void )
  844. {
  845. if ( m_cAmmoLoaded <= 0 )
  846. SetCondition( COND_NO_PRIMARY_AMMO );
  847. }
  848. //=========================================================
  849. //=========================================================
  850. CBaseEntity *CNPC_HGrunt::Kick( void )
  851. {
  852. trace_t tr;
  853. Vector forward;
  854. AngleVectors( GetAbsAngles(), &forward );
  855. Vector vecStart = GetAbsOrigin();
  856. vecStart.z += WorldAlignSize().z * 0.5;
  857. Vector vecEnd = vecStart + (forward * 70);
  858. UTIL_TraceHull( vecStart, vecEnd, Vector(-16,-16,-18), Vector(16,16,18), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, &tr );
  859. if ( tr.m_pEnt )
  860. {
  861. CBaseEntity *pEntity = tr.m_pEnt;
  862. return pEntity;
  863. }
  864. return NULL;
  865. }
  866. Vector CNPC_HGrunt::Weapon_ShootPosition( void )
  867. {
  868. if ( m_fStanding )
  869. return GetAbsOrigin() + Vector( 0, 0, 60 );
  870. else
  871. return GetAbsOrigin() + Vector( 0, 0, 48 );
  872. }
  873. void CNPC_HGrunt::Event_Killed( const CTakeDamageInfo &info )
  874. {
  875. Vector vecGunPos;
  876. QAngle vecGunAngles;
  877. GetAttachment( "0", vecGunPos, vecGunAngles );
  878. // switch to body group with no gun.
  879. SetBodygroup( GUN_GROUP, GUN_NONE );
  880. // If the gun would drop into a wall, spawn it at our origin
  881. if( UTIL_PointContents( vecGunPos ) & CONTENTS_SOLID )
  882. {
  883. vecGunPos = GetAbsOrigin();
  884. }
  885. // now spawn a gun.
  886. if (FBitSet( m_iWeapons, HGRUNT_SHOTGUN ))
  887. {
  888. DropItem( "weapon_shotgun", vecGunPos, vecGunAngles );
  889. }
  890. else
  891. {
  892. DropItem( "weapon_mp5", vecGunPos, vecGunAngles );
  893. }
  894. if (FBitSet( m_iWeapons, HGRUNT_GRENADELAUNCHER ))
  895. {
  896. DropItem( "ammo_ARgrenades", BodyTarget( GetAbsOrigin() ), vecGunAngles );
  897. }
  898. BaseClass::Event_Killed( info );
  899. }
  900. //=========================================================
  901. // HandleAnimEvent - catches the monster-specific messages
  902. // that occur when tagged animation frames are played.
  903. //=========================================================
  904. void CNPC_HGrunt::HandleAnimEvent( animevent_t *pEvent )
  905. {
  906. Vector vecShootDir;
  907. Vector vecShootOrigin;
  908. switch( pEvent->event )
  909. {
  910. case HGRUNT_AE_RELOAD:
  911. {
  912. CPASAttenuationFilter filter( this );
  913. EmitSound( filter, entindex(), "HGrunt.Reload" );
  914. m_cAmmoLoaded = m_iClipSize;
  915. ClearCondition( COND_NO_PRIMARY_AMMO);
  916. }
  917. break;
  918. case HGRUNT_AE_GREN_TOSS:
  919. {
  920. CHandGrenade *pGrenade = (CHandGrenade*)Create( "grenade_hand", GetAbsOrigin() + Vector(0,0,60), vec3_angle );
  921. if ( pGrenade )
  922. {
  923. pGrenade->ShootTimed( this, m_vecTossVelocity, 3.5 );
  924. }
  925. m_iLastGrenadeCondition = COND_NONE;
  926. m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
  927. Msg( "Tossing a grenade to flush you out!\n" );
  928. }
  929. break;
  930. case HGRUNT_AE_GREN_LAUNCH:
  931. {
  932. CPASAttenuationFilter filter2( this );
  933. EmitSound( filter2, entindex(), "HGrunt.GrenadeLaunch" );
  934. Vector vecSrc;
  935. QAngle angAngles;
  936. GetAttachment( "0", vecSrc, angAngles );
  937. CGrenadeMP5 * m_pMyGrenade = (CGrenadeMP5*)Create( "grenade_mp5", vecSrc, angAngles, this );
  938. m_pMyGrenade->SetAbsVelocity( m_vecTossVelocity );
  939. m_pMyGrenade->SetLocalAngularVelocity( QAngle( random->RandomFloat( -100, -500 ), 0, 0 ) );
  940. m_pMyGrenade->SetMoveType( MOVETYPE_FLYGRAVITY );
  941. m_pMyGrenade->SetThrower( this );
  942. m_pMyGrenade->SetDamage( sk_plr_dmg_mp5_grenade.GetFloat() );
  943. if (g_iSkillLevel == SKILL_HARD)
  944. m_flNextGrenadeCheck = gpGlobals->curtime + random->RandomFloat( 2, 5 );// wait a random amount of time before shooting again
  945. else
  946. m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
  947. m_iLastGrenadeCondition = COND_NONE;
  948. Msg( "Using grenade launcer to flush you out!\n" );
  949. }
  950. break;
  951. case HGRUNT_AE_GREN_DROP:
  952. {
  953. CHandGrenade *pGrenade = (CHandGrenade*)Create( "grenade_hand", Weapon_ShootPosition(), vec3_angle );
  954. if ( pGrenade )
  955. {
  956. pGrenade->ShootTimed( this, m_vecTossVelocity, 3.5 );
  957. }
  958. m_iLastGrenadeCondition = COND_NONE;
  959. Msg( "Dropping a grenade!\n" );
  960. }
  961. break;
  962. case HGRUNT_AE_BURST1:
  963. {
  964. if ( FBitSet( m_iWeapons, HGRUNT_9MMAR ) )
  965. {
  966. Shoot();
  967. CPASAttenuationFilter filter3( this );
  968. // the first round of the three round burst plays the sound and puts a sound in the world sound list.
  969. EmitSound( filter3, entindex(), "HGrunt.9MM" );
  970. }
  971. else
  972. {
  973. Shotgun( );
  974. CPASAttenuationFilter filter4( this );
  975. EmitSound( filter4, entindex(), "HGrunt.Shotgun" );
  976. }
  977. CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), 384, 0.3 );
  978. }
  979. break;
  980. case HGRUNT_AE_BURST2:
  981. case HGRUNT_AE_BURST3:
  982. Shoot();
  983. break;
  984. case HGRUNT_AE_KICK:
  985. {
  986. CBaseEntity *pHurt = Kick();
  987. if ( pHurt )
  988. {
  989. // SOUND HERE!
  990. Vector forward, up;
  991. AngleVectors( GetAbsAngles(), &forward, NULL, &up );
  992. if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
  993. pHurt->ViewPunch( QAngle( 15, 0, 0) );
  994. // Don't give velocity or damage to the world
  995. if( pHurt->entindex() > 0 )
  996. {
  997. pHurt->ApplyAbsVelocityImpulse( forward * 100 + up * 50 );
  998. CTakeDamageInfo info( this, this, sk_hgrunt_kick.GetFloat(), DMG_CLUB );
  999. CalculateMeleeDamageForce( &info, forward, pHurt->GetAbsOrigin() );
  1000. pHurt->TakeDamage( info );
  1001. }
  1002. }
  1003. }
  1004. break;
  1005. case HGRUNT_AE_CAUGHT_ENEMY:
  1006. {
  1007. if ( FOkToSpeak() )
  1008. {
  1009. SENTENCEG_PlayRndSz( edict(), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1010. JustSpoke();
  1011. }
  1012. }
  1013. default:
  1014. BaseClass::HandleAnimEvent( pEvent );
  1015. break;
  1016. }
  1017. }
  1018. void CNPC_HGrunt::SetAim( const Vector &aimDir )
  1019. {
  1020. QAngle angDir;
  1021. VectorAngles( aimDir, angDir );
  1022. float curPitch = GetPoseParameter( "XR" );
  1023. float newPitch = curPitch + UTIL_AngleDiff( UTIL_ApproachAngle( angDir.x, curPitch, 60 ), curPitch );
  1024. SetPoseParameter( "XR", -newPitch );
  1025. }
  1026. //=========================================================
  1027. // Shoot
  1028. //=========================================================
  1029. void CNPC_HGrunt::Shoot ( void )
  1030. {
  1031. if ( GetEnemy() == NULL )
  1032. return;
  1033. Vector vecShootOrigin = Weapon_ShootPosition();
  1034. Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
  1035. Vector forward, right, up;
  1036. AngleVectors( GetAbsAngles(), &forward, &right, &up );
  1037. Vector vecShellVelocity = right * random->RandomFloat(40,90) + up * random->RandomFloat( 75,200 ) + forward * random->RandomFloat( -40, 40 );
  1038. EjectShell( vecShootOrigin - vecShootDir * 24, vecShellVelocity, GetAbsAngles().y, 0 );
  1039. FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, m_iAmmoType ); // shoot +-5 degrees
  1040. DoMuzzleFlash();
  1041. m_cAmmoLoaded--;// take away a bullet!
  1042. SetAim( vecShootDir );
  1043. }
  1044. //=========================================================
  1045. // Shoot
  1046. //=========================================================
  1047. void CNPC_HGrunt::Shotgun ( void )
  1048. {
  1049. if ( GetEnemy() == NULL )
  1050. return;
  1051. Vector vecShootOrigin = Weapon_ShootPosition();
  1052. Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
  1053. Vector forward, right, up;
  1054. AngleVectors( GetAbsAngles(), &forward, &right, &up );
  1055. Vector vecShellVelocity = right * random->RandomFloat(40,90) + up * random->RandomFloat( 75,200 ) + forward * random->RandomFloat( -40, 40 );
  1056. EjectShell( vecShootOrigin - vecShootDir * 24, vecShellVelocity, GetAbsAngles().y, 1 );
  1057. FireBullets( sk_hgrunt_pellets.GetFloat(), vecShootOrigin, vecShootDir, VECTOR_CONE_15DEGREES, 2048, m_iAmmoType, 0 ); // shoot +-7.5 degrees
  1058. DoMuzzleFlash();
  1059. m_cAmmoLoaded--;// take away a bullet!
  1060. SetAim( vecShootDir );
  1061. }
  1062. //=========================================================
  1063. // start task
  1064. //=========================================================
  1065. void CNPC_HGrunt::StartTask ( const Task_t *pTask )
  1066. {
  1067. switch ( pTask->iTask )
  1068. {
  1069. case TASK_GRUNT_CHECK_FIRE:
  1070. if ( !NoFriendlyFire() )
  1071. {
  1072. SetCondition( COND_WEAPON_BLOCKED_BY_FRIEND );
  1073. }
  1074. TaskComplete();
  1075. break;
  1076. case TASK_GRUNT_SPEAK_SENTENCE:
  1077. SpeakSentence();
  1078. TaskComplete();
  1079. break;
  1080. case TASK_WALK_PATH:
  1081. case TASK_RUN_PATH:
  1082. // grunt no longer assumes he is covered if he moves
  1083. Forget( bits_MEMORY_INCOVER );
  1084. BaseClass ::StartTask( pTask );
  1085. break;
  1086. case TASK_RELOAD:
  1087. SetIdealActivity( ACT_RELOAD );
  1088. break;
  1089. case TASK_GRUNT_FACE_TOSS_DIR:
  1090. break;
  1091. case TASK_FACE_IDEAL:
  1092. case TASK_FACE_ENEMY:
  1093. BaseClass::StartTask( pTask );
  1094. if (GetMoveType() == MOVETYPE_FLYGRAVITY)
  1095. {
  1096. SetIdealActivity( ACT_GLIDE );
  1097. }
  1098. break;
  1099. default:
  1100. BaseClass::StartTask( pTask );
  1101. break;
  1102. }
  1103. }
  1104. //=========================================================
  1105. // RunTask
  1106. //=========================================================
  1107. void CNPC_HGrunt::RunTask( const Task_t *pTask )
  1108. {
  1109. switch ( pTask->iTask )
  1110. {
  1111. case TASK_GRUNT_FACE_TOSS_DIR:
  1112. {
  1113. // project a point along the toss vector and turn to face that point.
  1114. GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + m_vecTossVelocity * 64, AI_KEEP_YAW_SPEED );
  1115. if ( FacingIdeal() )
  1116. {
  1117. TaskComplete();
  1118. }
  1119. break;
  1120. }
  1121. default:
  1122. {
  1123. BaseClass::RunTask( pTask );
  1124. break;
  1125. }
  1126. }
  1127. }
  1128. //=========================================================
  1129. // PainSound
  1130. //=========================================================
  1131. void CNPC_HGrunt::PainSound( const CTakeDamageInfo &info )
  1132. {
  1133. if ( gpGlobals->curtime > m_flNextPainTime )
  1134. {
  1135. CPASAttenuationFilter filter( this );
  1136. EmitSound( filter, entindex(), "HGrunt.Pain" );
  1137. m_flNextPainTime = gpGlobals->curtime + 1;
  1138. }
  1139. }
  1140. //=========================================================
  1141. // DeathSound
  1142. //=========================================================
  1143. void CNPC_HGrunt::DeathSound( const CTakeDamageInfo &info )
  1144. {
  1145. CPASAttenuationFilter filter( this, ATTN_IDLE );
  1146. EmitSound( filter, entindex(), "HGrunt.Die" );
  1147. }
  1148. //=========================================================
  1149. // SetActivity
  1150. //=========================================================
  1151. Activity CNPC_HGrunt::NPC_TranslateActivity( Activity eNewActivity )
  1152. {
  1153. switch ( eNewActivity)
  1154. {
  1155. case ACT_RANGE_ATTACK1:
  1156. // grunt is either shooting standing or shooting crouched
  1157. if (FBitSet( m_iWeapons, HGRUNT_9MMAR))
  1158. {
  1159. if ( m_fStanding )
  1160. {
  1161. // get aimable sequence
  1162. return (Activity)ACT_GRUNT_MP5_STANDING;
  1163. }
  1164. else
  1165. {
  1166. // get crouching shoot
  1167. return (Activity)ACT_GRUNT_MP5_CROUCHING;
  1168. }
  1169. }
  1170. else
  1171. {
  1172. if ( m_fStanding )
  1173. {
  1174. // get aimable sequence
  1175. return (Activity)ACT_GRUNT_SHOTGUN_STANDING;
  1176. }
  1177. else
  1178. {
  1179. // get crouching shoot
  1180. return (Activity)ACT_GRUNT_SHOTGUN_CROUCHING;
  1181. }
  1182. }
  1183. break;
  1184. case ACT_RANGE_ATTACK2:
  1185. // grunt is going to a secondary long range attack. This may be a thrown
  1186. // grenade or fired grenade, we must determine which and pick proper sequence
  1187. if ( m_iWeapons & HGRUNT_HANDGRENADE )
  1188. {
  1189. // get toss anim
  1190. return (Activity)ACT_GRUNT_TOSS_GRENADE;
  1191. }
  1192. else
  1193. {
  1194. // get launch anim
  1195. return (Activity)ACT_GRUNT_LAUNCH_GRENADE;
  1196. }
  1197. break;
  1198. case ACT_RUN:
  1199. if ( m_iHealth <= HGRUNT_LIMP_HEALTH )
  1200. {
  1201. // limp!
  1202. return ACT_RUN_HURT;
  1203. }
  1204. else
  1205. {
  1206. return eNewActivity;
  1207. }
  1208. break;
  1209. case ACT_WALK:
  1210. if ( m_iHealth <= HGRUNT_LIMP_HEALTH )
  1211. {
  1212. // limp!
  1213. return ACT_WALK_HURT;
  1214. }
  1215. else
  1216. {
  1217. return eNewActivity;
  1218. }
  1219. break;
  1220. case ACT_IDLE:
  1221. if ( m_NPCState == NPC_STATE_COMBAT )
  1222. {
  1223. eNewActivity = ACT_IDLE_ANGRY;
  1224. }
  1225. break;
  1226. }
  1227. return BaseClass::NPC_TranslateActivity( eNewActivity );
  1228. }
  1229. void CNPC_HGrunt::ClearAttackConditions( void )
  1230. {
  1231. bool fCanRangeAttack2 = HasCondition( COND_CAN_RANGE_ATTACK2 );
  1232. // Call the base class.
  1233. BaseClass::ClearAttackConditions();
  1234. if( fCanRangeAttack2 )
  1235. {
  1236. // We don't allow the base class to clear this condition because we
  1237. // don't sense for it every frame.
  1238. SetCondition( COND_CAN_RANGE_ATTACK2 );
  1239. }
  1240. }
  1241. int CNPC_HGrunt::SelectSchedule( void )
  1242. {
  1243. // clear old sentence
  1244. m_iSentence = HGRUNT_SENT_NONE;
  1245. // flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling.
  1246. if ( GetMoveType() == MOVETYPE_FLYGRAVITY && m_NPCState != NPC_STATE_PRONE )
  1247. {
  1248. if (GetFlags() & FL_ONGROUND)
  1249. {
  1250. // just landed
  1251. SetMoveType( MOVETYPE_STEP );
  1252. SetGravity( 1.0 );
  1253. return SCHED_GRUNT_REPEL_LAND;
  1254. }
  1255. else
  1256. {
  1257. // repel down a rope,
  1258. if ( m_NPCState == NPC_STATE_COMBAT )
  1259. return SCHED_GRUNT_REPEL_ATTACK;
  1260. else
  1261. return SCHED_GRUNT_REPEL;
  1262. }
  1263. }
  1264. // grunts place HIGH priority on running away from danger sounds.
  1265. if ( HasCondition ( COND_HEAR_DANGER ) )
  1266. {
  1267. // dangerous sound nearby!
  1268. //!!!KELLY - currently, this is the grunt's signal that a grenade has landed nearby,
  1269. // and the grunt should find cover from the blast
  1270. // good place for "SHIT!" or some other colorful verbal indicator of dismay.
  1271. // It's not safe to play a verbal order here "Scatter", etc cause
  1272. // this may only affect a single individual in a squad.
  1273. if (FOkToSpeak())
  1274. {
  1275. SENTENCEG_PlayRndSz( edict(), "HG_GREN", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1276. JustSpoke();
  1277. }
  1278. return SCHED_TAKE_COVER_FROM_BEST_SOUND;
  1279. }
  1280. switch ( m_NPCState )
  1281. {
  1282. case NPC_STATE_PRONE:
  1283. {
  1284. if (m_bInBarnacleMouth)
  1285. {
  1286. return SCHED_GRUNT_BARNACLE_CHOMP;
  1287. }
  1288. else
  1289. {
  1290. return SCHED_GRUNT_BARNACLE_HIT;
  1291. }
  1292. }
  1293. case NPC_STATE_COMBAT:
  1294. {
  1295. // dead enemy
  1296. if ( HasCondition( COND_ENEMY_DEAD ) )
  1297. {
  1298. // call base class, all code to handle dead enemies is centralized there.
  1299. return BaseClass::SelectSchedule();
  1300. }
  1301. // new enemy
  1302. if ( HasCondition( COND_NEW_ENEMY) )
  1303. {
  1304. if ( m_pSquad )
  1305. {
  1306. if ( !m_pSquad->IsLeader( this ) )
  1307. {
  1308. return SCHED_TAKE_COVER_FROM_ENEMY;
  1309. }
  1310. else
  1311. {
  1312. //!!!KELLY - the leader of a squad of grunts has just seen the player or a
  1313. // monster and has made it the squad's enemy. You
  1314. // can check pev->flags for FL_CLIENT to determine whether this is the player
  1315. // or a monster. He's going to immediately start
  1316. // firing, though. If you'd like, we can make an alternate "first sight"
  1317. // schedule where the leader plays a handsign anim
  1318. // that gives us enough time to hear a short sentence or spoken command
  1319. // before he starts pluggin away.
  1320. if (FOkToSpeak())// && RANDOM_LONG(0,1))
  1321. {
  1322. if ((GetEnemy() != NULL) && GetEnemy()->IsPlayer())
  1323. // player
  1324. SENTENCEG_PlayRndSz( edict(), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1325. else if ((GetEnemy() != NULL) &&
  1326. (GetEnemy()->Classify() != CLASS_PLAYER_ALLY) &&
  1327. (GetEnemy()->Classify() != CLASS_HUMAN_PASSIVE) &&
  1328. (GetEnemy()->Classify() != CLASS_MACHINE) )
  1329. // monster
  1330. SENTENCEG_PlayRndSz( edict(), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1331. JustSpoke();
  1332. }
  1333. if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
  1334. {
  1335. return SCHED_GRUNT_SUPPRESS;
  1336. }
  1337. else
  1338. {
  1339. return SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE;
  1340. }
  1341. }
  1342. }
  1343. }
  1344. // no ammo
  1345. else if ( HasCondition ( COND_NO_PRIMARY_AMMO ) )
  1346. {
  1347. //!!!KELLY - this individual just realized he's out of bullet ammo.
  1348. // He's going to try to find cover to run to and reload, but rarely, if
  1349. // none is available, he'll drop and reload in the open here.
  1350. return SCHED_GRUNT_HIDE_RELOAD;
  1351. }
  1352. // damaged just a little
  1353. else if ( HasCondition( COND_LIGHT_DAMAGE ) )
  1354. {
  1355. // if hurt:
  1356. // 90% chance of taking cover
  1357. // 10% chance of flinch.
  1358. int iPercent = random->RandomInt(0,99);
  1359. if ( iPercent <= 90 && GetEnemy() != NULL )
  1360. {
  1361. // only try to take cover if we actually have an enemy!
  1362. //!!!KELLY - this grunt was hit and is going to run to cover.
  1363. if (FOkToSpeak()) // && RANDOM_LONG(0,1))
  1364. {
  1365. //SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1366. m_iSentence = HGRUNT_SENT_COVER;
  1367. //JustSpoke();
  1368. }
  1369. return SCHED_TAKE_COVER_FROM_ENEMY;
  1370. }
  1371. else
  1372. {
  1373. return SCHED_SMALL_FLINCH;
  1374. }
  1375. }
  1376. // can kick
  1377. else if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  1378. {
  1379. return SCHED_MELEE_ATTACK1;
  1380. }
  1381. // can grenade launch
  1382. else if ( FBitSet( m_iWeapons, HGRUNT_GRENADELAUNCHER) && HasCondition ( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_GRENADE2 ) )
  1383. {
  1384. // shoot a grenade if you can
  1385. return SCHED_RANGE_ATTACK2;
  1386. }
  1387. // can shoot
  1388. else if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
  1389. {
  1390. if ( m_pSquad )
  1391. {
  1392. if ( m_pSquad->GetLeader() != NULL )
  1393. {
  1394. CAI_BaseNPC *pSquadLeader = m_pSquad->GetLeader()->MyNPCPointer();
  1395. // if the enemy has eluded the squad and a squad member has just located the enemy
  1396. // and the enemy does not see the squad member, issue a call to the squad to waste a
  1397. // little time and give the player a chance to turn.
  1398. if ( pSquadLeader && pSquadLeader->EnemyHasEludedMe() && !HasCondition ( COND_ENEMY_FACING_ME ) )
  1399. {
  1400. return SCHED_GRUNT_FOUND_ENEMY;
  1401. }
  1402. }
  1403. }
  1404. if ( OccupyStrategySlotRange ( SQUAD_SLOT_ENGAGE1, SQUAD_SLOT_ENGAGE2 ) )
  1405. {
  1406. // try to take an available ENGAGE slot
  1407. return SCHED_RANGE_ATTACK1;
  1408. }
  1409. else if ( HasCondition ( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_GRENADE2 ) )
  1410. {
  1411. // throw a grenade if can and no engage slots are available
  1412. return SCHED_RANGE_ATTACK2;
  1413. }
  1414. else
  1415. {
  1416. // hide!
  1417. return SCHED_TAKE_COVER_FROM_ENEMY;
  1418. }
  1419. }
  1420. // can't see enemy
  1421. else if ( HasCondition( COND_ENEMY_OCCLUDED ) )
  1422. {
  1423. if ( HasCondition( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_GRENADE2 ) )
  1424. {
  1425. //!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc
  1426. if (FOkToSpeak())
  1427. {
  1428. SENTENCEG_PlayRndSz( edict(), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1429. JustSpoke();
  1430. }
  1431. return SCHED_RANGE_ATTACK2;
  1432. }
  1433. else if ( OccupyStrategySlotRange ( SQUAD_SLOT_ENGAGE1, SQUAD_SLOT_ENGAGE2 ) )
  1434. {
  1435. //!!!KELLY - grunt cannot see the enemy and has just decided to
  1436. // charge the enemy's position.
  1437. if (FOkToSpeak())// && RANDOM_LONG(0,1))
  1438. {
  1439. //SENTENCEG_PlayRndSz( ENT(pev), "HG_CHARGE", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1440. m_iSentence = HGRUNT_SENT_CHARGE;
  1441. //JustSpoke();
  1442. }
  1443. return SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE;
  1444. }
  1445. else
  1446. {
  1447. //!!!KELLY - grunt is going to stay put for a couple seconds to see if
  1448. // the enemy wanders back out into the open, or approaches the
  1449. // grunt's covered position. Good place for a taunt, I guess?
  1450. if (FOkToSpeak() && random->RandomInt(0,1))
  1451. {
  1452. SENTENCEG_PlayRndSz( edict(), "HG_TAUNT", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1453. JustSpoke();
  1454. }
  1455. return SCHED_STANDOFF;
  1456. }
  1457. }
  1458. if ( HasCondition( COND_SEE_ENEMY ) && !HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
  1459. {
  1460. return SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE;
  1461. }
  1462. }
  1463. case NPC_STATE_ALERT:
  1464. if ( HasCondition( COND_ENEMY_DEAD ) && SelectWeightedSequence( ACT_VICTORY_DANCE ) != ACTIVITY_NOT_AVAILABLE )
  1465. {
  1466. // Scan around for new enemies
  1467. return SCHED_VICTORY_DANCE;
  1468. }
  1469. break;
  1470. }
  1471. return BaseClass::SelectSchedule();
  1472. }
  1473. int CNPC_HGrunt::TranslateSchedule( int scheduleType )
  1474. {
  1475. if ( scheduleType == SCHED_CHASE_ENEMY_FAILED )
  1476. {
  1477. return SCHED_ESTABLISH_LINE_OF_FIRE;
  1478. }
  1479. switch ( scheduleType )
  1480. {
  1481. case SCHED_TAKE_COVER_FROM_ENEMY:
  1482. {
  1483. if ( m_pSquad )
  1484. {
  1485. if ( g_iSkillLevel == SKILL_HARD && HasCondition( COND_CAN_RANGE_ATTACK2 ) && OccupyStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_GRENADE2 ) )
  1486. {
  1487. if (FOkToSpeak())
  1488. {
  1489. SENTENCEG_PlayRndSz( edict(), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
  1490. JustSpoke();
  1491. }
  1492. return SCHED_GRUNT_TOSS_GRENADE_COVER;
  1493. }
  1494. else
  1495. {
  1496. return SCHED_GRUNT_TAKE_COVER;
  1497. }
  1498. }
  1499. else
  1500. {
  1501. if ( random->RandomInt(0,1) )
  1502. {
  1503. return SCHED_GRUNT_TAKE_COVER;
  1504. }
  1505. else
  1506. {
  1507. return SCHED_GRUNT_GRENADE_COVER;
  1508. }
  1509. }
  1510. }
  1511. case SCHED_GRUNT_TAKE_COVER_FAILED:
  1512. {
  1513. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) && OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1514. {
  1515. return SCHED_RANGE_ATTACK1;
  1516. }
  1517. return SCHED_FAIL;
  1518. }
  1519. break;
  1520. case SCHED_RANGE_ATTACK1:
  1521. {
  1522. // randomly stand or crouch
  1523. if ( random->RandomInt( 0,9 ) == 0)
  1524. {
  1525. m_fStanding = random->RandomInt( 0, 1 ) != 0;
  1526. }
  1527. if ( m_fStanding )
  1528. return SCHED_GRUNT_RANGE_ATTACK1B;
  1529. else
  1530. return SCHED_GRUNT_RANGE_ATTACK1A;
  1531. }
  1532. case SCHED_RANGE_ATTACK2:
  1533. {
  1534. return SCHED_GRUNT_RANGE_ATTACK2;
  1535. }
  1536. case SCHED_VICTORY_DANCE:
  1537. {
  1538. if ( m_pSquad )
  1539. {
  1540. if ( !m_pSquad->IsLeader( this ) )
  1541. {
  1542. return SCHED_GRUNT_FAIL;
  1543. }
  1544. }
  1545. return SCHED_GRUNT_VICTORY_DANCE;
  1546. }
  1547. case SCHED_GRUNT_SUPPRESS:
  1548. {
  1549. if ( GetEnemy()->IsPlayer() && m_fFirstEncounter )
  1550. {
  1551. m_fFirstEncounter = FALSE;// after first encounter, leader won't issue handsigns anymore when he has a new enemy
  1552. return SCHED_GRUNT_SIGNAL_SUPPRESS;
  1553. }
  1554. else
  1555. {
  1556. return SCHED_GRUNT_SUPPRESS;
  1557. }
  1558. }
  1559. case SCHED_FAIL:
  1560. {
  1561. if ( GetEnemy() != NULL )
  1562. {
  1563. // grunt has an enemy, so pick a different default fail schedule most likely to help recover.
  1564. return SCHED_GRUNT_COMBAT_FAIL;
  1565. }
  1566. return SCHED_GRUNT_FAIL;
  1567. }
  1568. case SCHED_GRUNT_REPEL:
  1569. {
  1570. Vector vecVel = GetAbsVelocity();
  1571. if ( vecVel.z > -128 )
  1572. {
  1573. vecVel.z -= 32;
  1574. SetAbsVelocity( vecVel );
  1575. }
  1576. return SCHED_GRUNT_REPEL;
  1577. }
  1578. case SCHED_GRUNT_REPEL_ATTACK:
  1579. {
  1580. Vector vecVel = GetAbsVelocity();
  1581. if ( vecVel.z > -128 )
  1582. {
  1583. vecVel.z -= 32;
  1584. SetAbsVelocity( vecVel );
  1585. }
  1586. return SCHED_GRUNT_REPEL_ATTACK;
  1587. }
  1588. default:
  1589. {
  1590. return BaseClass::TranslateSchedule( scheduleType );
  1591. }
  1592. }
  1593. }
  1594. //=========================================================
  1595. // CHGruntRepel - when triggered, spawns a monster_human_grunt
  1596. // repelling down a line.
  1597. //=========================================================
  1598. class CNPC_HGruntRepel:public CAI_BaseNPC
  1599. {
  1600. DECLARE_CLASS( CNPC_HGruntRepel, CAI_BaseNPC );
  1601. public:
  1602. void Spawn( void );
  1603. void Precache( void );
  1604. void RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  1605. int m_iSpriteTexture; // Don't save, precache
  1606. DECLARE_DATADESC();
  1607. };
  1608. LINK_ENTITY_TO_CLASS( monster_grunt_repel, CNPC_HGruntRepel );
  1609. //---------------------------------------------------------
  1610. // Save/Restore
  1611. //---------------------------------------------------------
  1612. BEGIN_DATADESC( CNPC_HGruntRepel )
  1613. DEFINE_USEFUNC( RepelUse ),
  1614. //DEFINE_FIELD( m_iSpriteTexture, FIELD_INTEGER ),
  1615. END_DATADESC()
  1616. void CNPC_HGruntRepel::Spawn( void )
  1617. {
  1618. Precache( );
  1619. SetSolid( SOLID_NONE );
  1620. SetUse( &CNPC_HGruntRepel::RepelUse );
  1621. }
  1622. void CNPC_HGruntRepel::Precache( void )
  1623. {
  1624. UTIL_PrecacheOther( "monster_human_grunt" );
  1625. m_iSpriteTexture = PrecacheModel( "sprites/rope.vmt" );
  1626. }
  1627. void CNPC_HGruntRepel::RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  1628. {
  1629. trace_t tr;
  1630. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -4096.0), MASK_NPCSOLID, this,COLLISION_GROUP_NONE, &tr);
  1631. CBaseEntity *pEntity = Create( "monster_human_grunt", GetAbsOrigin(), GetAbsAngles() );
  1632. CAI_BaseNPC *pGrunt = pEntity->MyNPCPointer( );
  1633. pGrunt->SetMoveType( MOVETYPE_FLYGRAVITY );
  1634. pGrunt->SetGravity( 0.001 );
  1635. pGrunt->SetAbsVelocity( Vector( 0, 0, random->RandomFloat( -196, -128 ) ) );
  1636. pGrunt->SetActivity( ACT_GLIDE );
  1637. // UNDONE: position?
  1638. pGrunt->m_vecLastPosition = tr.endpos;
  1639. CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.vmt", 10 );
  1640. pBeam->PointEntInit( GetAbsOrigin() + Vector(0,0,112), pGrunt );
  1641. pBeam->SetBeamFlags( FBEAM_SOLID );
  1642. pBeam->SetColor( 255, 255, 255 );
  1643. pBeam->SetThink( &CBaseEntity::SUB_Remove );
  1644. SetNextThink( gpGlobals->curtime + -4096.0 * tr.fraction / pGrunt->GetAbsVelocity().z + 0.5 );
  1645. UTIL_Remove( this );
  1646. }
  1647. //------------------------------------------------------------------------------
  1648. //
  1649. // Schedules
  1650. //
  1651. //------------------------------------------------------------------------------
  1652. AI_BEGIN_CUSTOM_NPC( monster_human_grunt, CNPC_HGrunt )
  1653. DECLARE_ACTIVITY( ACT_GRUNT_LAUNCH_GRENADE )
  1654. DECLARE_ACTIVITY( ACT_GRUNT_TOSS_GRENADE )
  1655. DECLARE_ACTIVITY( ACT_GRUNT_MP5_STANDING );
  1656. DECLARE_ACTIVITY( ACT_GRUNT_MP5_CROUCHING );
  1657. DECLARE_ACTIVITY( ACT_GRUNT_SHOTGUN_STANDING );
  1658. DECLARE_ACTIVITY( ACT_GRUNT_SHOTGUN_CROUCHING );
  1659. DECLARE_CONDITION( COND_GRUNT_NOFIRE )
  1660. DECLARE_TASK( TASK_GRUNT_FACE_TOSS_DIR )
  1661. DECLARE_TASK( TASK_GRUNT_SPEAK_SENTENCE )
  1662. DECLARE_TASK( TASK_GRUNT_CHECK_FIRE )
  1663. DECLARE_SQUADSLOT( SQUAD_SLOT_GRENADE1 )
  1664. DECLARE_SQUADSLOT( SQUAD_SLOT_GRENADE2 )
  1665. DECLARE_SQUADSLOT( SQUAD_SLOT_ENGAGE1 )
  1666. DECLARE_SQUADSLOT( SQUAD_SLOT_ENGAGE2 )
  1667. //=========================================================
  1668. // > SCHED_GRUNT_FAIL
  1669. //=========================================================
  1670. DEFINE_SCHEDULE
  1671. (
  1672. SCHED_GRUNT_FAIL,
  1673. " Tasks"
  1674. " TASK_STOP_MOVING 0"
  1675. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1676. " TASK_WAIT 0"
  1677. " TASK_WAIT_PVS 0"
  1678. " "
  1679. " Interrupts"
  1680. " COND_CAN_RANGE_ATTACK1"
  1681. " COND_CAN_MELEE_ATTACK1"
  1682. )
  1683. //=========================================================
  1684. // > SCHED_GRUNT_COMBAT_FAIL
  1685. //=========================================================
  1686. DEFINE_SCHEDULE
  1687. (
  1688. SCHED_GRUNT_COMBAT_FAIL,
  1689. " Tasks"
  1690. " TASK_STOP_MOVING 0"
  1691. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1692. " TASK_WAIT_FACE_ENEMY 2"
  1693. " TASK_WAIT_PVS 0"
  1694. " "
  1695. " Interrupts"
  1696. " COND_CAN_RANGE_ATTACK1"
  1697. )
  1698. //=========================================================
  1699. // > SCHED_GRUNT_VICTORY_DANCE
  1700. // Victory dance!
  1701. //=========================================================
  1702. DEFINE_SCHEDULE
  1703. (
  1704. SCHED_GRUNT_VICTORY_DANCE,
  1705. " Tasks"
  1706. " TASK_STOP_MOVING 0"
  1707. " TASK_FACE_ENEMY 0"
  1708. " TASK_WAIT 1.5"
  1709. " TASK_GET_PATH_TO_ENEMY_CORPSE 0"
  1710. " TASK_WALK_PATH 0"
  1711. " TASK_WAIT_FOR_MOVEMENT 0"
  1712. " TASK_FACE_ENEMY 0"
  1713. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_VICTORY_DANCE"
  1714. " "
  1715. " Interrupts"
  1716. " COND_NEW_ENEMY"
  1717. " COND_LIGHT_DAMAGE"
  1718. " COND_HEAVY_DAMAGE"
  1719. )
  1720. //=========================================================
  1721. // > SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE
  1722. // Establish line of fire - move to a position that allows
  1723. // the grunt to attack.
  1724. //=========================================================
  1725. DEFINE_SCHEDULE
  1726. (
  1727. SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,
  1728. " Tasks"
  1729. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE_RETRY"
  1730. " TASK_GET_PATH_TO_ENEMY 0"
  1731. " TASK_GRUNT_SPEAK_SENTENCE 0"
  1732. " TASK_RUN_PATH 0"
  1733. " TASK_WAIT_FOR_MOVEMENT 0"
  1734. " "
  1735. " Interrupts"
  1736. " COND_NEW_ENEMY"
  1737. " COND_ENEMY_DEAD"
  1738. " COND_LIGHT_DAMAGE"
  1739. " COND_HEAVY_DAMAGE"
  1740. " COND_CAN_RANGE_ATTACK1"
  1741. " COND_CAN_MELEE_ATTACK1"
  1742. " COND_CAN_RANGE_ATTACK2"
  1743. " COND_CAN_MELEE_ATTACK2"
  1744. " COND_HEAR_DANGER"
  1745. )
  1746. //=========================================================
  1747. // This is a schedule I added that borrows some HL2 technology
  1748. // to be smarter in cases where HL1 was pretty dumb. I've wedged
  1749. // this between ESTABLISH_LINE_OF_FIRE and TAKE_COVER_FROM_ENEMY (sjb)
  1750. //=========================================================
  1751. DEFINE_SCHEDULE
  1752. (
  1753. SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE_RETRY,
  1754. " Tasks"
  1755. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_GRUNT_TAKE_COVER_FROM_ENEMY"
  1756. " TASK_GET_PATH_TO_ENEMY_LKP_LOS 0"
  1757. " TASK_GRUNT_SPEAK_SENTENCE 0"
  1758. " TASK_RUN_PATH 0"
  1759. " TASK_WAIT_FOR_MOVEMENT 0"
  1760. " "
  1761. " Interrupts"
  1762. " COND_NEW_ENEMY"
  1763. " COND_ENEMY_DEAD"
  1764. " COND_LIGHT_DAMAGE"
  1765. " COND_HEAVY_DAMAGE"
  1766. " COND_CAN_RANGE_ATTACK1"
  1767. " COND_CAN_MELEE_ATTACK1"
  1768. " COND_CAN_RANGE_ATTACK2"
  1769. " COND_CAN_MELEE_ATTACK2"
  1770. " COND_HEAR_DANGER"
  1771. )
  1772. //=========================================================
  1773. // > SCHED_GRUNT_FOUND_ENEMY
  1774. // Grunt established sight with an enemy
  1775. // that was hiding from the squad.
  1776. //=========================================================
  1777. DEFINE_SCHEDULE
  1778. (
  1779. SCHED_GRUNT_FOUND_ENEMY,
  1780. " Tasks"
  1781. " TASK_STOP_MOVING 0"
  1782. " TASK_FACE_ENEMY 0"
  1783. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_SIGNAL1"
  1784. " "
  1785. " Interrupts"
  1786. " COND_HEAR_DANGER"
  1787. )
  1788. //=========================================================
  1789. // > SCHED_GRUNT_COMBAT_FACE
  1790. //=========================================================
  1791. DEFINE_SCHEDULE
  1792. (
  1793. SCHED_GRUNT_COMBAT_FACE,
  1794. " Tasks"
  1795. " TASK_STOP_MOVING 0"
  1796. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1797. " TASK_FACE_ENEMY 0"
  1798. " TASK_WAIT 1.5"
  1799. " TASK_SET_SCHEDULE SCHEDULE:SCHED_GRUNT_SWEEP"
  1800. " "
  1801. " Interrupts"
  1802. " COND_NEW_ENEMY"
  1803. " COND_ENEMY_DEAD"
  1804. " COND_CAN_RANGE_ATTACK1"
  1805. " COND_CAN_RANGE_ATTACK2"
  1806. )
  1807. //=========================================================
  1808. // > SCHED_GRUNT_SIGNAL_SUPPRESS
  1809. // Suppressing fire - don't stop shooting until the clip is
  1810. // empty or grunt gets hurt.
  1811. //=========================================================
  1812. DEFINE_SCHEDULE
  1813. (
  1814. SCHED_GRUNT_SIGNAL_SUPPRESS,
  1815. " Tasks"
  1816. " TASK_STOP_MOVING 0"
  1817. " TASK_FACE_IDEAL 0"
  1818. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_SIGNAL2"
  1819. " TASK_FACE_ENEMY 0"
  1820. " TASK_GRUNT_CHECK_FIRE 0"
  1821. " TASK_RANGE_ATTACK1 0"
  1822. " TASK_FACE_ENEMY 0"
  1823. " TASK_GRUNT_CHECK_FIRE 0"
  1824. " TASK_RANGE_ATTACK1 0"
  1825. " TASK_FACE_ENEMY 0"
  1826. " TASK_GRUNT_CHECK_FIRE 0"
  1827. " TASK_RANGE_ATTACK1 0"
  1828. " TASK_FACE_ENEMY 0"
  1829. " TASK_GRUNT_CHECK_FIRE 0"
  1830. " TASK_RANGE_ATTACK1 0"
  1831. " TASK_FACE_ENEMY 0"
  1832. " TASK_GRUNT_CHECK_FIRE 0"
  1833. " TASK_RANGE_ATTACK1 0"
  1834. " "
  1835. " Interrupts"
  1836. " COND_ENEMY_DEAD"
  1837. " COND_LIGHT_DAMAGE"
  1838. " COND_HEAVY_DAMAGE"
  1839. " COND_GRUNT_NOFIRE"
  1840. " COND_NO_PRIMARY_AMMO"
  1841. " COND_HEAR_DANGER"
  1842. )
  1843. //=========================================================
  1844. // > SCHED_GRUNT_SUPPRESS
  1845. //=========================================================
  1846. DEFINE_SCHEDULE
  1847. (
  1848. SCHED_GRUNT_SUPPRESS,
  1849. " Tasks"
  1850. " TASK_STOP_MOVING 0"
  1851. " TASK_FACE_ENEMY 0"
  1852. " TASK_GRUNT_CHECK_FIRE 0"
  1853. " TASK_RANGE_ATTACK1 0"
  1854. " TASK_FACE_ENEMY 0"
  1855. " TASK_GRUNT_CHECK_FIRE 0"
  1856. " TASK_RANGE_ATTACK1 0"
  1857. " TASK_FACE_ENEMY 0"
  1858. " TASK_GRUNT_CHECK_FIRE 0"
  1859. " TASK_RANGE_ATTACK1 0"
  1860. " TASK_FACE_ENEMY 0"
  1861. " TASK_GRUNT_CHECK_FIRE 0"
  1862. " TASK_RANGE_ATTACK1 0"
  1863. " TASK_FACE_ENEMY 0"
  1864. " TASK_GRUNT_CHECK_FIRE 0"
  1865. " TASK_RANGE_ATTACK1 0"
  1866. " "
  1867. " Interrupts"
  1868. " COND_ENEMY_DEAD"
  1869. " COND_LIGHT_DAMAGE"
  1870. " COND_HEAVY_DAMAGE"
  1871. " COND_GRUNT_NOFIRE"
  1872. " COND_NO_PRIMARY_AMMO"
  1873. " COND_HEAR_DANGER"
  1874. )
  1875. //=========================================================
  1876. // > SCHED_GRUNT_WAIT_IN_COVER
  1877. // grunt wait in cover - we don't allow danger or the ability
  1878. // to attack to break a grunt's run to cover schedule, but
  1879. // when a grunt is in cover, we do want them to attack if they can.
  1880. //=========================================================
  1881. DEFINE_SCHEDULE
  1882. (
  1883. SCHED_GRUNT_WAIT_IN_COVER,
  1884. " Tasks"
  1885. " TASK_STOP_MOVING 0"
  1886. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1887. " TASK_WAIT_FACE_ENEMY 1"
  1888. " "
  1889. " Interrupts"
  1890. " COND_NEW_ENEMY"
  1891. " COND_HEAR_DANGER"
  1892. " COND_CAN_RANGE_ATTACK1"
  1893. " COND_CAN_RANGE_ATTACK2"
  1894. " COND_CAN_MELEE_ATTACK1"
  1895. " COND_CAN_MELEE_ATTACK2"
  1896. )
  1897. //=========================================================
  1898. // > SCHED_GRUNT_TAKE_COVER
  1899. // !!!BUGBUG - set a decent fail schedule here.
  1900. //=========================================================
  1901. DEFINE_SCHEDULE
  1902. (
  1903. SCHED_GRUNT_TAKE_COVER,
  1904. " Tasks"
  1905. " TASK_STOP_MOVING 0"
  1906. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_GRUNT_TAKE_COVER_FAILED"
  1907. " TASK_WAIT 0.2"
  1908. " TASK_FIND_COVER_FROM_ENEMY 0"
  1909. " TASK_GRUNT_SPEAK_SENTENCE 0"
  1910. " TASK_RUN_PATH 0"
  1911. " TASK_WAIT_FOR_MOVEMENT 0"
  1912. " TASK_REMEMBER MEMORY:INCOVER"
  1913. " TASK_SET_SCHEDULE SCHEDULE:SCHED_GRUNT_WAIT_IN_COVER"
  1914. " "
  1915. " Interrupts"
  1916. )
  1917. //=========================================================
  1918. // > SCHED_GRUNT_GRENADE_COVER
  1919. // drop grenade then run to cover.
  1920. //=========================================================
  1921. DEFINE_SCHEDULE
  1922. (
  1923. SCHED_GRUNT_GRENADE_COVER,
  1924. " Tasks"
  1925. " TASK_STOP_MOVING 0"
  1926. " TASK_FIND_COVER_FROM_ENEMY 99"
  1927. " TASK_FIND_FAR_NODE_COVER_FROM_ENEMY 384"
  1928. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SPECIAL_ATTACK1"
  1929. " TASK_CLEAR_MOVE_WAIT 0"
  1930. " TASK_RUN_PATH 0"
  1931. " TASK_WAIT_FOR_MOVEMENT 0"
  1932. " TASK_SET_SCHEDULE SCHEDULE:SCHED_GRUNT_WAIT_IN_COVER"
  1933. " "
  1934. " Interrupts"
  1935. )
  1936. //=========================================================
  1937. // > SCHED_GRUNT_TOSS_GRENADE_COVER
  1938. // drop grenade then run to cover.
  1939. //=========================================================
  1940. DEFINE_SCHEDULE
  1941. (
  1942. SCHED_GRUNT_TOSS_GRENADE_COVER,
  1943. " Tasks"
  1944. " TASK_FACE_ENEMY 0"
  1945. " TASK_RANGE_ATTACK2 0"
  1946. " TASK_SET_SCHEDULE SCHEDULE:SCHED_GRUNT_TAKE_COVER_FROM_ENEMY"
  1947. " "
  1948. " Interrupts"
  1949. )
  1950. //=========================================================
  1951. // > SCHED_GRUNT_HIDE_RELOAD
  1952. // Grunt reload schedule
  1953. //=========================================================
  1954. DEFINE_SCHEDULE
  1955. (
  1956. SCHED_GRUNT_HIDE_RELOAD,
  1957. " Tasks"
  1958. " TASK_STOP_MOVING 0"
  1959. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_GRUNT_RELOAD"
  1960. " TASK_FIND_COVER_FROM_ENEMY 0"
  1961. " TASK_RUN_PATH 0"
  1962. " TASK_WAIT_FOR_MOVEMENT 0"
  1963. " TASK_REMEMBER MEMORY:INCOVER"
  1964. " TASK_FACE_ENEMY 0"
  1965. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RELOAD"
  1966. " "
  1967. " Interrupts"
  1968. " COND_HEAVY_DAMAGE"
  1969. " COND_HEAR_DANGER"
  1970. )
  1971. //=========================================================
  1972. // > SCHED_GRUNT_SWEEP
  1973. // Do a turning sweep of the area
  1974. //=========================================================
  1975. DEFINE_SCHEDULE
  1976. (
  1977. SCHED_GRUNT_SWEEP,
  1978. " Tasks"
  1979. " TASK_TURN_LEFT 179"
  1980. " TASK_WAIT 1"
  1981. " TASK_TURN_LEFT 179"
  1982. " TASK_WAIT 1"
  1983. " "
  1984. " Interrupts"
  1985. " COND_NEW_ENEMY"
  1986. " COND_LIGHT_DAMAGE"
  1987. " COND_HEAVY_DAMAGE"
  1988. " COND_CAN_RANGE_ATTACK1"
  1989. " COND_CAN_RANGE_ATTACK2"
  1990. " COND_HEAR_WORLD"
  1991. " COND_HEAR_DANGER"
  1992. " COND_HEAR_PLAYER"
  1993. )
  1994. //=========================================================
  1995. // > SCHED_GRUNT_RANGE_ATTACK1A
  1996. // primary range attack. Overriden because base class stops attacking when the enemy is occluded.
  1997. // grunt's grenade toss requires the enemy be occluded.
  1998. //=========================================================
  1999. DEFINE_SCHEDULE
  2000. (
  2001. SCHED_GRUNT_RANGE_ATTACK1A,
  2002. " Tasks"
  2003. " TASK_STOP_MOVING 0"
  2004. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_CROUCH"
  2005. " TASK_GRUNT_CHECK_FIRE 0"
  2006. " TASK_RANGE_ATTACK1 0"
  2007. " TASK_FACE_ENEMY 0"
  2008. " TASK_GRUNT_CHECK_FIRE 0"
  2009. " TASK_RANGE_ATTACK1 0"
  2010. " TASK_FACE_ENEMY 0"
  2011. " TASK_GRUNT_CHECK_FIRE 0"
  2012. " TASK_RANGE_ATTACK1 0"
  2013. " TASK_FACE_ENEMY 0"
  2014. " TASK_GRUNT_CHECK_FIRE 0"
  2015. " TASK_RANGE_ATTACK1 0"
  2016. " "
  2017. " Interrupts"
  2018. " COND_NEW_ENEMY"
  2019. " COND_ENEMY_DEAD"
  2020. " COND_HEAVY_DAMAGE"
  2021. " COND_ENEMY_OCCLUDED"
  2022. " COND_HEAR_DANGER"
  2023. " COND_GRUNT_NOFIRE"
  2024. " COND_NO_PRIMARY_AMMO"
  2025. )
  2026. //=========================================================
  2027. // > SCHED_GRUNT_RANGE_ATTACK1B
  2028. // primary range attack. Overriden because base class stops attacking when the enemy is occluded.
  2029. // grunt's grenade toss requires the enemy be occluded.
  2030. //=========================================================
  2031. DEFINE_SCHEDULE
  2032. (
  2033. SCHED_GRUNT_RANGE_ATTACK1B,
  2034. " Tasks"
  2035. " TASK_STOP_MOVING 0"
  2036. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_IDLE_ANGRY"
  2037. " TASK_GRUNT_CHECK_FIRE 0"
  2038. " TASK_RANGE_ATTACK1 0"
  2039. " TASK_FACE_ENEMY 0"
  2040. " TASK_GRUNT_CHECK_FIRE 0"
  2041. " TASK_RANGE_ATTACK1 0"
  2042. " TASK_FACE_ENEMY 0"
  2043. " TASK_GRUNT_CHECK_FIRE 0"
  2044. " TASK_RANGE_ATTACK1 0"
  2045. " TASK_FACE_ENEMY 0"
  2046. " TASK_GRUNT_CHECK_FIRE 0"
  2047. " TASK_RANGE_ATTACK1 0"
  2048. " "
  2049. " Interrupts"
  2050. " COND_NEW_ENEMY"
  2051. " COND_ENEMY_DEAD"
  2052. " COND_HEAVY_DAMAGE"
  2053. " COND_ENEMY_OCCLUDED"
  2054. " COND_HEAR_DANGER"
  2055. " COND_GRUNT_NOFIRE"
  2056. " COND_NO_PRIMARY_AMMO"
  2057. )
  2058. //=========================================================
  2059. // > SCHED_GRUNT_RANGE_ATTACK2
  2060. // secondary range attack. Overriden because base class stops attacking when the enemy is occluded.
  2061. // grunt's grenade toss requires the enemy be occluded.
  2062. //=========================================================
  2063. DEFINE_SCHEDULE
  2064. (
  2065. SCHED_GRUNT_RANGE_ATTACK2,
  2066. " Tasks"
  2067. " TASK_STOP_MOVING 0"
  2068. " TASK_GRUNT_FACE_TOSS_DIR 0"
  2069. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK2"
  2070. " TASK_SET_SCHEDULE SCHEDULE:SCHED_GRUNT_WAIT_IN_COVER" // don't run immediately after throwing grenade.
  2071. " "
  2072. " Interrupts"
  2073. )
  2074. //=========================================================
  2075. // > SCHED_GRUNT_REPEL
  2076. // secondary range attack. Overriden because base class stops attacking when the enemy is occluded.
  2077. // grunt's grenade toss requires the enemy be occluded.
  2078. //=========================================================
  2079. DEFINE_SCHEDULE
  2080. (
  2081. SCHED_GRUNT_REPEL,
  2082. " Tasks"
  2083. " TASK_STOP_MOVING 0"
  2084. " TASK_FACE_IDEAL 0"
  2085. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_GLIDE"
  2086. " "
  2087. " Interrupts"
  2088. " COND_SEE_ENEMY"
  2089. " COND_NEW_ENEMY"
  2090. " COND_LIGHT_DAMAGE"
  2091. " COND_HEAVY_DAMAGE"
  2092. " COND_HEAR_DANGER"
  2093. " COND_HEAR_PLAYER"
  2094. " COND_HEAR_COMBAT"
  2095. )
  2096. //=========================================================
  2097. // > SCHED_GRUNT_REPEL_ATTACK
  2098. //=========================================================
  2099. DEFINE_SCHEDULE
  2100. (
  2101. SCHED_GRUNT_REPEL_ATTACK,
  2102. " Tasks"
  2103. " TASK_STOP_MOVING 0"
  2104. " TASK_FACE_ENEMY 0"
  2105. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_FLY"
  2106. " "
  2107. " Interrupts"
  2108. " COND_ENEMY_OCCLUDED"
  2109. )
  2110. //=========================================================
  2111. // > SCHED_GRUNT_REPEL_LAND
  2112. //=========================================================
  2113. DEFINE_SCHEDULE
  2114. (
  2115. SCHED_GRUNT_REPEL_LAND,
  2116. " Tasks"
  2117. " TASK_STOP_MOVING 0"
  2118. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_LAND"
  2119. " TASK_GET_PATH_TO_LASTPOSITION 0"
  2120. " TASK_RUN_PATH 0"
  2121. " TASK_WAIT_FOR_MOVEMENT 0"
  2122. " TASK_CLEAR_LASTPOSITION 0"
  2123. " "
  2124. " Interrupts"
  2125. " COND_SEE_ENEMY"
  2126. " COND_NEW_ENEMY"
  2127. " COND_LIGHT_DAMAGE"
  2128. " COND_HEAVY_DAMAGE"
  2129. " COND_HEAR_DANGER"
  2130. " COND_HEAR_COMBAT"
  2131. " COND_HEAR_PLAYER"
  2132. )
  2133. //=========================================================
  2134. // > SCHED_GRUNT_RELOAD
  2135. //=========================================================
  2136. DEFINE_SCHEDULE
  2137. (
  2138. SCHED_GRUNT_RELOAD,
  2139. " Tasks"
  2140. " TASK_STOP_MOVING 0"
  2141. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RELOAD"
  2142. " "
  2143. " Interrupts"
  2144. " COND_HEAVY_DAMAGE"
  2145. )
  2146. //=========================================================
  2147. // > SCHED_GRUNT_TAKE_COVER_FROM_ENEMY
  2148. //=========================================================
  2149. DEFINE_SCHEDULE
  2150. (
  2151. SCHED_GRUNT_TAKE_COVER_FROM_ENEMY,
  2152. " Tasks"
  2153. " TASK_STOP_MOVING 0"
  2154. " TASK_WAIT 0.2"
  2155. " TASK_FIND_COVER_FROM_ENEMY 0"
  2156. " TASK_RUN_PATH 0"
  2157. " TASK_WAIT_FOR_MOVEMENT 0"
  2158. " TASK_REMEMBER MEMORY:INCOVER"
  2159. " TASK_FACE_ENEMY 0"
  2160. " TASK_WAIT 1"
  2161. " "
  2162. " Interrupts"
  2163. " COND_NEW_ENEMY"
  2164. )
  2165. //=========================================================
  2166. // > SCHED_GRUNT_TAKE_COVER_FAILED
  2167. // special schedule type that forces analysis of conditions and picks
  2168. // the best possible schedule to recover from this type of failure.
  2169. //=========================================================
  2170. DEFINE_SCHEDULE
  2171. (
  2172. SCHED_GRUNT_TAKE_COVER_FAILED,
  2173. " Tasks"
  2174. " Interrupts"
  2175. )
  2176. //=========================================================
  2177. // > SCHED_GRUNT_BARNACLE_HIT
  2178. //=========================================================
  2179. DEFINE_SCHEDULE
  2180. (
  2181. SCHED_GRUNT_BARNACLE_HIT,
  2182. " Tasks"
  2183. " TASK_STOP_MOVING 0"
  2184. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_HIT"
  2185. " TASK_SET_SCHEDULE SCHEDULE:SCHED_GRUNT_BARNACLE_PULL"
  2186. ""
  2187. " Interrupts"
  2188. )
  2189. //=========================================================
  2190. // > SCHED_GRUNT_BARNACLE_PULL
  2191. //=========================================================
  2192. DEFINE_SCHEDULE
  2193. (
  2194. SCHED_GRUNT_BARNACLE_PULL,
  2195. " Tasks"
  2196. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_PULL"
  2197. ""
  2198. " Interrupts"
  2199. )
  2200. //=========================================================
  2201. // > SCHED_GRUNT_BARNACLE_CHOMP
  2202. //=========================================================
  2203. DEFINE_SCHEDULE
  2204. (
  2205. SCHED_GRUNT_BARNACLE_CHOMP,
  2206. " Tasks"
  2207. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHOMP"
  2208. " TASK_SET_SCHEDULE SCHEDULE:SCHED_GRUNT_BARNACLE_CHEW"
  2209. ""
  2210. " Interrupts"
  2211. )
  2212. //=========================================================
  2213. // > SCHED_GRUNT_BARNACLE_CHEW
  2214. //=========================================================
  2215. DEFINE_SCHEDULE
  2216. (
  2217. SCHED_GRUNT_BARNACLE_CHEW,
  2218. " Tasks"
  2219. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHEW"
  2220. )
  2221. AI_END_CUSTOM_NPC()
  2222. //=========================================================
  2223. // DEAD HGRUNT PROP
  2224. //=========================================================
  2225. class CNPC_DeadHGrunt : public CAI_BaseNPC
  2226. {
  2227. DECLARE_CLASS( CNPC_DeadHGrunt, CAI_BaseNPC );
  2228. public:
  2229. void Spawn( void );
  2230. Class_T Classify ( void ) { return CLASS_HUMAN_MILITARY; }
  2231. float MaxYawSpeed( void ) { return 8.0f; }
  2232. bool KeyValue( const char *szKeyName, const char *szValue );
  2233. int m_iPose;// which sequence to display -- temporary, don't need to save
  2234. static char *m_szPoses[3];
  2235. };
  2236. char *CNPC_DeadHGrunt::m_szPoses[] = { "deadstomach", "deadside", "deadsitting" };
  2237. bool CNPC_DeadHGrunt::KeyValue( const char *szKeyName, const char *szValue )
  2238. {
  2239. if ( FStrEq( szKeyName, "pose" ) )
  2240. m_iPose = atoi( szValue );
  2241. else
  2242. CAI_BaseNPC::KeyValue( szKeyName, szValue );
  2243. return true;
  2244. }
  2245. LINK_ENTITY_TO_CLASS( monster_hgrunt_dead, CNPC_DeadHGrunt );
  2246. //=========================================================
  2247. // ********** DeadHGrunt SPAWN **********
  2248. //=========================================================
  2249. void CNPC_DeadHGrunt::Spawn( void )
  2250. {
  2251. PrecacheModel("models/hgrunt.mdl");
  2252. SetModel( "models/hgrunt.mdl" );
  2253. ClearEffects();
  2254. SetSequence( 0 );
  2255. m_bloodColor = BLOOD_COLOR_RED;
  2256. SetSequence( LookupSequence( m_szPoses[m_iPose] ) );
  2257. if ( GetSequence() == -1 )
  2258. {
  2259. Msg ( "Dead hgrunt with bad pose\n" );
  2260. }
  2261. // Corpses have less health
  2262. m_iHealth = 8;
  2263. // map old bodies onto new bodies
  2264. switch( m_nBody )
  2265. {
  2266. case 0: // Grunt with Gun
  2267. m_nBody = 0;
  2268. m_nSkin = 0;
  2269. SetBodygroup( HEAD_GROUP, HEAD_GRUNT );
  2270. SetBodygroup( GUN_GROUP, GUN_MP5 );
  2271. break;
  2272. case 1: // Commander with Gun
  2273. m_nBody = 0;
  2274. m_nSkin = 0;
  2275. SetBodygroup( HEAD_GROUP, HEAD_COMMANDER );
  2276. SetBodygroup( GUN_GROUP, GUN_MP5 );
  2277. break;
  2278. case 2: // Grunt no Gun
  2279. m_nBody = 0;
  2280. m_nSkin = 0;
  2281. SetBodygroup( HEAD_GROUP, HEAD_GRUNT );
  2282. SetBodygroup( GUN_GROUP, GUN_NONE );
  2283. break;
  2284. case 3: // Commander no Gun
  2285. m_nBody = 0;
  2286. m_nSkin = 0;
  2287. SetBodygroup( HEAD_GROUP, HEAD_COMMANDER );
  2288. SetBodygroup( GUN_GROUP, GUN_NONE );
  2289. break;
  2290. }
  2291. NPCInitDead();
  2292. }