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.

4045 lines
111 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_hull.h"
  8. #include "ai_navigator.h"
  9. #include "ai_motor.h"
  10. #include "ai_squadslot.h"
  11. #include "ai_squad.h"
  12. #include "ai_route.h"
  13. #include "ai_interactions.h"
  14. #include "ai_tacticalservices.h"
  15. #include "soundent.h"
  16. #include "game.h"
  17. #include "npcevent.h"
  18. #include "npc_combine.h"
  19. #include "activitylist.h"
  20. #include "player.h"
  21. #include "basecombatweapon.h"
  22. #include "basegrenade_shared.h"
  23. #include "vstdlib/random.h"
  24. #include "engine/IEngineSound.h"
  25. #include "globals.h"
  26. #include "grenade_frag.h"
  27. #include "ndebugoverlay.h"
  28. #include "weapon_physcannon.h"
  29. #include "SoundEmitterSystem/isoundemittersystembase.h"
  30. #include "npc_headcrab.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. int g_fCombineQuestion; // true if an idle grunt asked a question. Cleared when someone answers. YUCK old global from grunt code
  34. #define COMBINE_SKIN_DEFAULT 0
  35. #define COMBINE_SKIN_SHOTGUNNER 1
  36. #define COMBINE_GRENADE_THROW_SPEED 650
  37. #define COMBINE_GRENADE_TIMER 3.5
  38. #define COMBINE_GRENADE_FLUSH_TIME 3.0 // Don't try to flush an enemy who has been out of sight for longer than this.
  39. #define COMBINE_GRENADE_FLUSH_DIST 256.0 // Don't try to flush an enemy who has moved farther than this distance from the last place I saw him.
  40. #define COMBINE_LIMP_HEALTH 20
  41. #define COMBINE_MIN_GRENADE_CLEAR_DIST 250
  42. #define COMBINE_EYE_STANDING_POSITION Vector( 0, 0, 66 )
  43. #define COMBINE_GUN_STANDING_POSITION Vector( 0, 0, 57 )
  44. #define COMBINE_EYE_CROUCHING_POSITION Vector( 0, 0, 40 )
  45. #define COMBINE_GUN_CROUCHING_POSITION Vector( 0, 0, 36 )
  46. #define COMBINE_SHOTGUN_STANDING_POSITION Vector( 0, 0, 36 )
  47. #define COMBINE_SHOTGUN_CROUCHING_POSITION Vector( 0, 0, 36 )
  48. #define COMBINE_MIN_CROUCH_DISTANCE 256.0
  49. //-----------------------------------------------------------------------------
  50. // Static stuff local to this file.
  51. //-----------------------------------------------------------------------------
  52. // This is the index to the name of the shotgun's classname in the string pool
  53. // so that we can get away with an integer compare rather than a string compare.
  54. string_t s_iszShotgunClassname;
  55. //-----------------------------------------------------------------------------
  56. // Interactions
  57. //-----------------------------------------------------------------------------
  58. int g_interactionCombineBash = 0; // melee bash attack
  59. //=========================================================
  60. // Combines's Anim Events Go Here
  61. //=========================================================
  62. #define COMBINE_AE_RELOAD ( 2 )
  63. #define COMBINE_AE_KICK ( 3 )
  64. #define COMBINE_AE_AIM ( 4 )
  65. #define COMBINE_AE_GREN_TOSS ( 7 )
  66. #define COMBINE_AE_GREN_LAUNCH ( 8 )
  67. #define COMBINE_AE_GREN_DROP ( 9 )
  68. #define COMBINE_AE_CAUGHT_ENEMY ( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad.
  69. int COMBINE_AE_BEGIN_ALTFIRE;
  70. int COMBINE_AE_ALTFIRE;
  71. //=========================================================
  72. // Combine activities
  73. //=========================================================
  74. //Activity ACT_COMBINE_STANDING_SMG1;
  75. //Activity ACT_COMBINE_CROUCHING_SMG1;
  76. //Activity ACT_COMBINE_STANDING_AR2;
  77. //Activity ACT_COMBINE_CROUCHING_AR2;
  78. //Activity ACT_COMBINE_WALKING_AR2;
  79. //Activity ACT_COMBINE_STANDING_SHOTGUN;
  80. //Activity ACT_COMBINE_CROUCHING_SHOTGUN;
  81. Activity ACT_COMBINE_THROW_GRENADE;
  82. Activity ACT_COMBINE_LAUNCH_GRENADE;
  83. Activity ACT_COMBINE_BUGBAIT;
  84. Activity ACT_COMBINE_AR2_ALTFIRE;
  85. Activity ACT_WALK_EASY;
  86. Activity ACT_WALK_MARCH;
  87. // -----------------------------------------------
  88. // > Squad slots
  89. // -----------------------------------------------
  90. enum SquadSlot_T
  91. {
  92. SQUAD_SLOT_GRENADE1 = LAST_SHARED_SQUADSLOT,
  93. SQUAD_SLOT_GRENADE2,
  94. SQUAD_SLOT_ATTACK_OCCLUDER,
  95. SQUAD_SLOT_OVERWATCH,
  96. };
  97. enum TacticalVariant_T
  98. {
  99. TACTICAL_VARIANT_DEFAULT = 0,
  100. TACTICAL_VARIANT_PRESSURE_ENEMY, // Always try to close in on the player.
  101. TACTICAL_VARIANT_PRESSURE_ENEMY_UNTIL_CLOSE, // Act like VARIANT_PRESSURE_ENEMY, but go to VARIANT_DEFAULT once within 30 feet
  102. };
  103. enum PathfindingVariant_T
  104. {
  105. PATHFINDING_VARIANT_DEFAULT = 0,
  106. };
  107. #define bits_MEMORY_PAIN_LIGHT_SOUND bits_MEMORY_CUSTOM1
  108. #define bits_MEMORY_PAIN_HEAVY_SOUND bits_MEMORY_CUSTOM2
  109. #define bits_MEMORY_PLAYER_HURT bits_MEMORY_CUSTOM3
  110. LINK_ENTITY_TO_CLASS( npc_combine, CNPC_Combine );
  111. //---------------------------------------------------------
  112. // Save/Restore
  113. //---------------------------------------------------------
  114. BEGIN_DATADESC( CNPC_Combine )
  115. DEFINE_FIELD( m_nKickDamage, FIELD_INTEGER ),
  116. DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ),
  117. DEFINE_FIELD( m_hForcedGrenadeTarget, FIELD_EHANDLE ),
  118. DEFINE_FIELD( m_bShouldPatrol, FIELD_BOOLEAN ),
  119. DEFINE_FIELD( m_bFirstEncounter, FIELD_BOOLEAN ),
  120. DEFINE_FIELD( m_flNextPainSoundTime, FIELD_TIME ),
  121. DEFINE_FIELD( m_flNextAlertSoundTime, FIELD_TIME ),
  122. DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ),
  123. DEFINE_FIELD( m_flNextLostSoundTime, FIELD_TIME ),
  124. DEFINE_FIELD( m_flAlertPatrolTime, FIELD_TIME ),
  125. DEFINE_FIELD( m_flNextAltFireTime, FIELD_TIME ),
  126. DEFINE_FIELD( m_nShots, FIELD_INTEGER ),
  127. DEFINE_FIELD( m_flShotDelay, FIELD_FLOAT ),
  128. DEFINE_FIELD( m_flStopMoveShootTime, FIELD_TIME ),
  129. DEFINE_KEYFIELD( m_iNumGrenades, FIELD_INTEGER, "NumGrenades" ),
  130. DEFINE_EMBEDDED( m_Sentences ),
  131. // m_AssaultBehavior (auto saved by AI)
  132. // m_StandoffBehavior (auto saved by AI)
  133. // m_FollowBehavior (auto saved by AI)
  134. // m_FuncTankBehavior (auto saved by AI)
  135. // m_RappelBehavior (auto saved by AI)
  136. // m_ActBusyBehavior (auto saved by AI)
  137. DEFINE_INPUTFUNC( FIELD_VOID, "LookOff", InputLookOff ),
  138. DEFINE_INPUTFUNC( FIELD_VOID, "LookOn", InputLookOn ),
  139. DEFINE_INPUTFUNC( FIELD_VOID, "StartPatrolling", InputStartPatrolling ),
  140. DEFINE_INPUTFUNC( FIELD_VOID, "StopPatrolling", InputStopPatrolling ),
  141. DEFINE_INPUTFUNC( FIELD_STRING, "Assault", InputAssault ),
  142. DEFINE_INPUTFUNC( FIELD_VOID, "HitByBugbait", InputHitByBugbait ),
  143. DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeAtTarget", InputThrowGrenadeAtTarget ),
  144. DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ),
  145. DEFINE_FIELD( m_fIsElite, FIELD_BOOLEAN ),
  146. DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ),
  147. DEFINE_KEYFIELD( m_iTacticalVariant, FIELD_INTEGER, "tacticalvariant" ),
  148. DEFINE_KEYFIELD( m_iPathfindingVariant, FIELD_INTEGER, "pathfindingvariant" ),
  149. END_DATADESC()
  150. //------------------------------------------------------------------------------
  151. // Constructor.
  152. //------------------------------------------------------------------------------
  153. CNPC_Combine::CNPC_Combine()
  154. {
  155. m_vecTossVelocity = vec3_origin;
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Create components
  159. //-----------------------------------------------------------------------------
  160. bool CNPC_Combine::CreateComponents()
  161. {
  162. if ( !BaseClass::CreateComponents() )
  163. return false;
  164. m_Sentences.Init( this, "NPC_Combine.SentenceParameters" );
  165. return true;
  166. }
  167. //------------------------------------------------------------------------------
  168. // Purpose: Don't look, only get info from squad.
  169. //------------------------------------------------------------------------------
  170. void CNPC_Combine::InputLookOff( inputdata_t &inputdata )
  171. {
  172. m_spawnflags |= SF_COMBINE_NO_LOOK;
  173. }
  174. //------------------------------------------------------------------------------
  175. // Purpose: Enable looking.
  176. //------------------------------------------------------------------------------
  177. void CNPC_Combine::InputLookOn( inputdata_t &inputdata )
  178. {
  179. m_spawnflags &= ~SF_COMBINE_NO_LOOK;
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. //-----------------------------------------------------------------------------
  184. void CNPC_Combine::InputStartPatrolling( inputdata_t &inputdata )
  185. {
  186. m_bShouldPatrol = true;
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Purpose:
  190. //-----------------------------------------------------------------------------
  191. void CNPC_Combine::InputStopPatrolling( inputdata_t &inputdata )
  192. {
  193. m_bShouldPatrol = false;
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. //-----------------------------------------------------------------------------
  198. void CNPC_Combine::InputAssault( inputdata_t &inputdata )
  199. {
  200. m_AssaultBehavior.SetParameters( AllocPooledString(inputdata.value.String()), CUE_DONT_WAIT, RALLY_POINT_SELECT_DEFAULT );
  201. }
  202. //-----------------------------------------------------------------------------
  203. // We were hit by bugbait
  204. //-----------------------------------------------------------------------------
  205. void CNPC_Combine::InputHitByBugbait( inputdata_t &inputdata )
  206. {
  207. SetCondition( COND_COMBINE_HIT_BY_BUGBAIT );
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: Force the combine soldier to throw a grenade at the target
  211. // If I'm a combine elite, fire my combine ball at the target instead.
  212. // Input : &inputdata -
  213. //-----------------------------------------------------------------------------
  214. void CNPC_Combine::InputThrowGrenadeAtTarget( inputdata_t &inputdata )
  215. {
  216. // Ignore if we're inside a scripted sequence
  217. if ( m_NPCState == NPC_STATE_SCRIPT && m_hCine )
  218. return;
  219. CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller );
  220. if ( !pEntity )
  221. {
  222. DevMsg("%s (%s) received ThrowGrenadeAtTarget input, but couldn't find target entity '%s'\n", GetClassname(), GetDebugName(), inputdata.value.String() );
  223. return;
  224. }
  225. m_hForcedGrenadeTarget = pEntity;
  226. m_flNextGrenadeCheck = 0;
  227. ClearSchedule( "Told to throw grenade via input" );
  228. }
  229. //-----------------------------------------------------------------------------
  230. // Purpose:
  231. //-----------------------------------------------------------------------------
  232. void CNPC_Combine::Precache()
  233. {
  234. PrecacheModel("models/Weapons/w_grenade.mdl");
  235. UTIL_PrecacheOther( "npc_handgrenade" );
  236. PrecacheScriptSound( "NPC_Combine.GrenadeLaunch" );
  237. PrecacheScriptSound( "NPC_Combine.WeaponBash" );
  238. PrecacheScriptSound( "Weapon_CombineGuard.Special1" );
  239. BaseClass::Precache();
  240. }
  241. //-----------------------------------------------------------------------------
  242. //-----------------------------------------------------------------------------
  243. void CNPC_Combine::Activate()
  244. {
  245. s_iszShotgunClassname = FindPooledString( "weapon_shotgun" );
  246. BaseClass::Activate();
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose:
  250. //
  251. //
  252. //-----------------------------------------------------------------------------
  253. void CNPC_Combine::Spawn( void )
  254. {
  255. SetHullType(HULL_HUMAN);
  256. SetHullSizeNormal();
  257. SetSolid( SOLID_BBOX );
  258. AddSolidFlags( FSOLID_NOT_STANDABLE );
  259. SetMoveType( MOVETYPE_STEP );
  260. SetBloodColor( BLOOD_COLOR_RED );
  261. m_flFieldOfView = -0.2;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
  262. m_NPCState = NPC_STATE_NONE;
  263. m_flNextGrenadeCheck = gpGlobals->curtime + 1;
  264. m_flNextPainSoundTime = 0;
  265. m_flNextAlertSoundTime = 0;
  266. m_bShouldPatrol = false;
  267. // CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_MOVE_GROUND | bits_CAP_MOVE_JUMP | bits_CAP_MOVE_CLIMB);
  268. // JAY: Disabled jump for now - hard to compare to HL1
  269. CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_MOVE_GROUND );
  270. CapabilitiesAdd( bits_CAP_AIM_GUN );
  271. // Innate range attack for grenade
  272. // CapabilitiesAdd(bits_CAP_INNATE_RANGE_ATTACK2 );
  273. // Innate range attack for kicking
  274. CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 );
  275. // Can be in a squad
  276. CapabilitiesAdd( bits_CAP_SQUAD);
  277. CapabilitiesAdd( bits_CAP_USE_WEAPONS );
  278. CapabilitiesAdd( bits_CAP_DUCK ); // In reloading and cover
  279. CapabilitiesAdd( bits_CAP_NO_HIT_SQUADMATES );
  280. m_bFirstEncounter = true;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
  281. m_HackedGunPos = Vector ( 0, 0, 55 );
  282. m_flStopMoveShootTime = FLT_MAX; // Move and shoot defaults on.
  283. m_MoveAndShootOverlay.SetInitialDelay( 0.75 ); // But with a bit of a delay.
  284. m_flNextLostSoundTime = 0;
  285. m_flAlertPatrolTime = 0;
  286. m_flNextAltFireTime = gpGlobals->curtime;
  287. NPCInit();
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose:
  291. // Output : Returns true on success, false on failure.
  292. //-----------------------------------------------------------------------------
  293. bool CNPC_Combine::CreateBehaviors()
  294. {
  295. AddBehavior( &m_RappelBehavior );
  296. AddBehavior( &m_ActBusyBehavior );
  297. AddBehavior( &m_AssaultBehavior );
  298. AddBehavior( &m_StandoffBehavior );
  299. AddBehavior( &m_FollowBehavior );
  300. AddBehavior( &m_FuncTankBehavior );
  301. return BaseClass::CreateBehaviors();
  302. }
  303. //-----------------------------------------------------------------------------
  304. //-----------------------------------------------------------------------------
  305. void CNPC_Combine::PostNPCInit()
  306. {
  307. if( IsElite() )
  308. {
  309. // Give a warning if a Combine Soldier is equipped with anything other than
  310. // an AR2.
  311. if( !GetActiveWeapon() || !FClassnameIs( GetActiveWeapon(), "weapon_ar2" ) )
  312. {
  313. DevWarning("**Combine Elite Soldier MUST be equipped with AR2\n");
  314. }
  315. }
  316. BaseClass::PostNPCInit();
  317. }
  318. //-----------------------------------------------------------------------------
  319. //-----------------------------------------------------------------------------
  320. void CNPC_Combine::GatherConditions()
  321. {
  322. BaseClass::GatherConditions();
  323. ClearCondition( COND_COMBINE_ATTACK_SLOT_AVAILABLE );
  324. if( GetState() == NPC_STATE_COMBAT )
  325. {
  326. if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) )
  327. {
  328. // Soldiers that are standing around doing nothing poll for attack slots so
  329. // that they can respond quickly when one comes available. If they can
  330. // occupy a vacant attack slot, they do so. This holds the slot until their
  331. // schedule breaks and schedule selection runs again, essentially reserving this
  332. // slot. If they do not select an attack schedule, then they'll release the slot.
  333. if( OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  334. {
  335. SetCondition( COND_COMBINE_ATTACK_SLOT_AVAILABLE );
  336. }
  337. }
  338. if( IsUsingTacticalVariant(TACTICAL_VARIANT_PRESSURE_ENEMY_UNTIL_CLOSE) )
  339. {
  340. if( GetEnemy() != NULL && !HasCondition(COND_ENEMY_OCCLUDED) )
  341. {
  342. // Now we're close to our enemy, stop using the tactical variant.
  343. if( GetAbsOrigin().DistToSqr(GetEnemy()->GetAbsOrigin()) < Square(30.0f * 12.0f) )
  344. m_iTacticalVariant = TACTICAL_VARIANT_DEFAULT;
  345. }
  346. }
  347. }
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Purpose:
  351. //-----------------------------------------------------------------------------
  352. void CNPC_Combine::PrescheduleThink()
  353. {
  354. BaseClass::PrescheduleThink();
  355. // Speak any queued sentences
  356. m_Sentences.UpdateSentenceQueue();
  357. if ( IsOnFire() )
  358. {
  359. SetCondition( COND_COMBINE_ON_FIRE );
  360. }
  361. else
  362. {
  363. ClearCondition( COND_COMBINE_ON_FIRE );
  364. }
  365. extern ConVar ai_debug_shoot_positions;
  366. if ( ai_debug_shoot_positions.GetBool() )
  367. NDebugOverlay::Cross3D( EyePosition(), 16, 0, 255, 0, false, 0.1 );
  368. if( gpGlobals->curtime >= m_flStopMoveShootTime )
  369. {
  370. // Time to stop move and shoot and start facing the way I'm running.
  371. // This makes the combine look attentive when disengaging, but prevents
  372. // them from always running around facing you.
  373. //
  374. // Only do this if it won't be immediately shut off again.
  375. if( GetNavigator()->GetPathTimeToGoal() > 1.0f )
  376. {
  377. m_MoveAndShootOverlay.SuspendMoveAndShoot( 5.0f );
  378. m_flStopMoveShootTime = FLT_MAX;
  379. }
  380. }
  381. if( m_flGroundSpeed > 0 && GetState() == NPC_STATE_COMBAT && m_MoveAndShootOverlay.IsSuspended() )
  382. {
  383. // Return to move and shoot when near my goal so that I 'tuck into' the location facing my enemy.
  384. if( GetNavigator()->GetPathTimeToGoal() <= 1.0f )
  385. {
  386. m_MoveAndShootOverlay.SuspendMoveAndShoot( 0 );
  387. }
  388. }
  389. }
  390. //-----------------------------------------------------------------------------
  391. //-----------------------------------------------------------------------------
  392. void CNPC_Combine::DelayAltFireAttack( float flDelay )
  393. {
  394. float flNextAltFire = gpGlobals->curtime + flDelay;
  395. if( flNextAltFire > m_flNextAltFireTime )
  396. {
  397. // Don't let this delay order preempt a previous request to wait longer.
  398. m_flNextAltFireTime = flNextAltFire;
  399. }
  400. }
  401. //-----------------------------------------------------------------------------
  402. //-----------------------------------------------------------------------------
  403. void CNPC_Combine::DelaySquadAltFireAttack( float flDelay )
  404. {
  405. // Make sure to delay my own alt-fire attack.
  406. DelayAltFireAttack( flDelay );
  407. AISquadIter_t iter;
  408. CAI_BaseNPC *pSquadmate = m_pSquad ? m_pSquad->GetFirstMember( &iter ) : NULL;
  409. while ( pSquadmate )
  410. {
  411. CNPC_Combine *pCombine = dynamic_cast<CNPC_Combine*>(pSquadmate);
  412. if( pCombine && pCombine->IsElite() )
  413. {
  414. pCombine->DelayAltFireAttack( flDelay );
  415. }
  416. pSquadmate = m_pSquad->GetNextMember( &iter );
  417. }
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose: degrees to turn in 0.1 seconds
  421. //-----------------------------------------------------------------------------
  422. float CNPC_Combine::MaxYawSpeed( void )
  423. {
  424. switch( GetActivity() )
  425. {
  426. case ACT_TURN_LEFT:
  427. case ACT_TURN_RIGHT:
  428. return 45;
  429. break;
  430. case ACT_RUN:
  431. case ACT_RUN_HURT:
  432. return 15;
  433. break;
  434. case ACT_WALK:
  435. case ACT_WALK_CROUCH:
  436. return 25;
  437. break;
  438. case ACT_RANGE_ATTACK1:
  439. case ACT_RANGE_ATTACK2:
  440. case ACT_MELEE_ATTACK1:
  441. case ACT_MELEE_ATTACK2:
  442. return 35;
  443. default:
  444. return 35;
  445. break;
  446. }
  447. }
  448. //-----------------------------------------------------------------------------
  449. //
  450. //-----------------------------------------------------------------------------
  451. bool CNPC_Combine::ShouldMoveAndShoot()
  452. {
  453. // Set this timer so that gpGlobals->curtime can't catch up to it.
  454. // Essentially, we're saying that we're not going to interfere with
  455. // what the AI wants to do with move and shoot.
  456. //
  457. // If any code below changes this timer, the code is saying
  458. // "It's OK to move and shoot until gpGlobals->curtime == m_flStopMoveShootTime"
  459. m_flStopMoveShootTime = FLT_MAX;
  460. if( IsCurSchedule( SCHED_COMBINE_HIDE_AND_RELOAD, false ) )
  461. m_flStopMoveShootTime = gpGlobals->curtime + random->RandomFloat( 0.4f, 0.6f );
  462. if( IsCurSchedule( SCHED_TAKE_COVER_FROM_BEST_SOUND, false ) )
  463. return false;
  464. if( IsCurSchedule( SCHED_COMBINE_TAKE_COVER_FROM_BEST_SOUND, false ) )
  465. return false;
  466. if( IsCurSchedule( SCHED_COMBINE_RUN_AWAY_FROM_BEST_SOUND, false ) )
  467. return false;
  468. if( HasCondition( COND_NO_PRIMARY_AMMO, false ) )
  469. m_flStopMoveShootTime = gpGlobals->curtime + random->RandomFloat( 0.4f, 0.6f );
  470. if( m_pSquad && IsCurSchedule( SCHED_COMBINE_TAKE_COVER1, false ) )
  471. m_flStopMoveShootTime = gpGlobals->curtime + random->RandomFloat( 0.4f, 0.6f );
  472. return BaseClass::ShouldMoveAndShoot();
  473. }
  474. //-----------------------------------------------------------------------------
  475. // Purpose: turn in the direction of movement
  476. // Output :
  477. //-----------------------------------------------------------------------------
  478. bool CNPC_Combine::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval )
  479. {
  480. return BaseClass::OverrideMoveFacing( move, flInterval );
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Purpose:
  484. //
  485. //
  486. //-----------------------------------------------------------------------------
  487. Class_T CNPC_Combine::Classify ( void )
  488. {
  489. return CLASS_COMBINE;
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Continuous movement tasks
  493. //-----------------------------------------------------------------------------
  494. bool CNPC_Combine::IsCurTaskContinuousMove()
  495. {
  496. const Task_t* pTask = GetTask();
  497. if ( pTask && (pTask->iTask == TASK_COMBINE_CHASE_ENEMY_CONTINUOUSLY) )
  498. return true;
  499. return BaseClass::IsCurTaskContinuousMove();
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Chase the enemy, updating the target position as the player moves
  503. //-----------------------------------------------------------------------------
  504. void CNPC_Combine::StartTaskChaseEnemyContinuously( const Task_t *pTask )
  505. {
  506. CBaseEntity *pEnemy = GetEnemy();
  507. if ( !pEnemy )
  508. {
  509. TaskFail( FAIL_NO_ENEMY );
  510. return;
  511. }
  512. // We're done once we get close enough
  513. if ( WorldSpaceCenter().DistToSqr( pEnemy->WorldSpaceCenter() ) <= pTask->flTaskData * pTask->flTaskData )
  514. {
  515. TaskComplete();
  516. return;
  517. }
  518. // TASK_GET_PATH_TO_ENEMY
  519. if ( IsUnreachable( pEnemy ) )
  520. {
  521. TaskFail(FAIL_NO_ROUTE);
  522. return;
  523. }
  524. if ( !GetNavigator()->SetGoal( GOALTYPE_ENEMY, AIN_NO_PATH_TASK_FAIL ) )
  525. {
  526. // no way to get there =(
  527. DevWarning( 2, "GetPathToEnemy failed!!\n" );
  528. RememberUnreachable( pEnemy );
  529. TaskFail(FAIL_NO_ROUTE);
  530. return;
  531. }
  532. // NOTE: This is TaskRunPath here.
  533. if ( TranslateActivity( ACT_RUN ) != ACT_INVALID )
  534. {
  535. GetNavigator()->SetMovementActivity( ACT_RUN );
  536. }
  537. else
  538. {
  539. GetNavigator()->SetMovementActivity(ACT_WALK);
  540. }
  541. // Cover is void once I move
  542. Forget( bits_MEMORY_INCOVER );
  543. if (GetNavigator()->GetGoalType() == GOALTYPE_NONE)
  544. {
  545. TaskComplete();
  546. GetNavigator()->ClearGoal(); // Clear residual state
  547. return;
  548. }
  549. // No shooting delay when in this mode
  550. m_MoveAndShootOverlay.SetInitialDelay( 0.0 );
  551. if (!GetNavigator()->IsGoalActive())
  552. {
  553. SetIdealActivity( GetStoppedActivity() );
  554. }
  555. else
  556. {
  557. // Check validity of goal type
  558. ValidateNavGoal();
  559. }
  560. // set that we're probably going to stop before the goal
  561. GetNavigator()->SetArrivalDistance( pTask->flTaskData );
  562. m_vSavePosition = GetEnemy()->WorldSpaceCenter();
  563. }
  564. void CNPC_Combine::RunTaskChaseEnemyContinuously( const Task_t *pTask )
  565. {
  566. if (!GetNavigator()->IsGoalActive())
  567. {
  568. SetIdealActivity( GetStoppedActivity() );
  569. }
  570. else
  571. {
  572. // Check validity of goal type
  573. ValidateNavGoal();
  574. }
  575. CBaseEntity *pEnemy = GetEnemy();
  576. if ( !pEnemy )
  577. {
  578. TaskFail( FAIL_NO_ENEMY );
  579. return;
  580. }
  581. // We're done once we get close enough
  582. if ( WorldSpaceCenter().DistToSqr( pEnemy->WorldSpaceCenter() ) <= pTask->flTaskData * pTask->flTaskData )
  583. {
  584. GetNavigator()->StopMoving();
  585. TaskComplete();
  586. return;
  587. }
  588. // Recompute path if the enemy has moved too much
  589. if ( m_vSavePosition.DistToSqr( pEnemy->WorldSpaceCenter() ) < (pTask->flTaskData * pTask->flTaskData) )
  590. return;
  591. if ( IsUnreachable( pEnemy ) )
  592. {
  593. TaskFail(FAIL_NO_ROUTE);
  594. return;
  595. }
  596. if ( !GetNavigator()->RefindPathToGoal() )
  597. {
  598. TaskFail(FAIL_NO_ROUTE);
  599. return;
  600. }
  601. m_vSavePosition = pEnemy->WorldSpaceCenter();
  602. }
  603. //=========================================================
  604. // start task
  605. //=========================================================
  606. void CNPC_Combine::StartTask( const Task_t *pTask )
  607. {
  608. // NOTE: This reset is required because we change it in TASK_COMBINE_CHASE_ENEMY_CONTINUOUSLY
  609. m_MoveAndShootOverlay.SetInitialDelay( 0.75 );
  610. switch ( pTask->iTask )
  611. {
  612. case TASK_COMBINE_SET_STANDING:
  613. {
  614. if ( pTask->flTaskData == 1.0f)
  615. {
  616. Stand();
  617. }
  618. else
  619. {
  620. Crouch();
  621. }
  622. TaskComplete();
  623. }
  624. break;
  625. case TASK_COMBINE_CHASE_ENEMY_CONTINUOUSLY:
  626. StartTaskChaseEnemyContinuously( pTask );
  627. break;
  628. case TASK_COMBINE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET:
  629. SetIdealActivity( (Activity)(int)pTask->flTaskData );
  630. GetMotor()->SetIdealYawToTargetAndUpdate( m_vecAltFireTarget, AI_KEEP_YAW_SPEED );
  631. break;
  632. case TASK_COMBINE_SIGNAL_BEST_SOUND:
  633. if( IsInSquad() && GetSquad()->NumMembers() > 1 )
  634. {
  635. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  636. if( pPlayer && OccupyStrategySlot( SQUAD_SLOT_EXCLUSIVE_HANDSIGN ) && pPlayer->FInViewCone( this ) )
  637. {
  638. CSound *pSound;
  639. pSound = GetBestSound();
  640. Assert( pSound != NULL );
  641. if ( pSound )
  642. {
  643. Vector right, tosound;
  644. GetVectors( NULL, &right, NULL );
  645. tosound = pSound->GetSoundReactOrigin() - GetAbsOrigin();
  646. VectorNormalize( tosound);
  647. tosound.z = 0;
  648. right.z = 0;
  649. if( DotProduct( right, tosound ) > 0 )
  650. {
  651. // Right
  652. SetIdealActivity( ACT_SIGNAL_RIGHT );
  653. }
  654. else
  655. {
  656. // Left
  657. SetIdealActivity( ACT_SIGNAL_LEFT );
  658. }
  659. break;
  660. }
  661. }
  662. }
  663. // Otherwise, just skip it.
  664. TaskComplete();
  665. break;
  666. case TASK_ANNOUNCE_ATTACK:
  667. {
  668. // If Primary Attack
  669. if ((int)pTask->flTaskData == 1)
  670. {
  671. // -----------------------------------------------------------
  672. // If enemy isn't facing me and I haven't attacked in a while
  673. // annouce my attack before I start wailing away
  674. // -----------------------------------------------------------
  675. CBaseCombatCharacter *pBCC = GetEnemyCombatCharacterPointer();
  676. if (pBCC && pBCC->IsPlayer() && (!pBCC->FInViewCone ( this )) &&
  677. (gpGlobals->curtime - m_flLastAttackTime > 3.0) )
  678. {
  679. m_flLastAttackTime = gpGlobals->curtime;
  680. m_Sentences.Speak( "COMBINE_ANNOUNCE", SENTENCE_PRIORITY_HIGH );
  681. // Wait two seconds
  682. SetWait( 2.0 );
  683. if ( !IsCrouching() )
  684. {
  685. SetActivity(ACT_IDLE);
  686. }
  687. else
  688. {
  689. SetActivity(ACT_COWER); // This is really crouch idle
  690. }
  691. }
  692. // -------------------------------------------------------------
  693. // Otherwise move on
  694. // -------------------------------------------------------------
  695. else
  696. {
  697. TaskComplete();
  698. }
  699. }
  700. else
  701. {
  702. m_Sentences.Speak( "COMBINE_THROW_GRENADE", SENTENCE_PRIORITY_MEDIUM );
  703. SetActivity(ACT_IDLE);
  704. // Wait two seconds
  705. SetWait( 2.0 );
  706. }
  707. break;
  708. }
  709. case TASK_WALK_PATH:
  710. case TASK_RUN_PATH:
  711. // grunt no longer assumes he is covered if he moves
  712. Forget( bits_MEMORY_INCOVER );
  713. BaseClass::StartTask( pTask );
  714. break;
  715. case TASK_COMBINE_FACE_TOSS_DIR:
  716. break;
  717. case TASK_COMBINE_GET_PATH_TO_FORCED_GREN_LOS:
  718. {
  719. if ( !m_hForcedGrenadeTarget )
  720. {
  721. TaskFail(FAIL_NO_ENEMY);
  722. return;
  723. }
  724. float flMaxRange = 2000;
  725. float flMinRange = 0;
  726. Vector vecEnemy = m_hForcedGrenadeTarget->GetAbsOrigin();
  727. Vector vecEnemyEye = vecEnemy + m_hForcedGrenadeTarget->GetViewOffset();
  728. Vector posLos;
  729. bool found = false;
  730. if ( GetTacticalServices()->FindLateralLos( vecEnemyEye, &posLos ) )
  731. {
  732. float dist = ( posLos - vecEnemyEye ).Length();
  733. if ( dist < flMaxRange && dist > flMinRange )
  734. found = true;
  735. }
  736. if ( !found && GetTacticalServices()->FindLos( vecEnemy, vecEnemyEye, flMinRange, flMaxRange, 1.0, &posLos ) )
  737. {
  738. found = true;
  739. }
  740. if ( !found )
  741. {
  742. TaskFail( FAIL_NO_SHOOT );
  743. }
  744. else
  745. {
  746. // else drop into run task to offer an interrupt
  747. m_vInterruptSavePosition = posLos;
  748. }
  749. }
  750. break;
  751. case TASK_COMBINE_IGNORE_ATTACKS:
  752. // must be in a squad
  753. if (m_pSquad && m_pSquad->NumMembers() > 2)
  754. {
  755. // the enemy must be far enough away
  756. if (GetEnemy() && (GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter()).Length() > 512.0 )
  757. {
  758. m_flNextAttack = gpGlobals->curtime + pTask->flTaskData;
  759. }
  760. }
  761. TaskComplete( );
  762. break;
  763. case TASK_COMBINE_DEFER_SQUAD_GRENADES:
  764. {
  765. if ( m_pSquad )
  766. {
  767. // iterate my squad and stop everyone from throwing grenades for a little while.
  768. AISquadIter_t iter;
  769. CAI_BaseNPC *pSquadmate = m_pSquad ? m_pSquad->GetFirstMember( &iter ) : NULL;
  770. while ( pSquadmate )
  771. {
  772. CNPC_Combine *pCombine = dynamic_cast<CNPC_Combine*>(pSquadmate);
  773. if( pCombine )
  774. {
  775. pCombine->m_flNextGrenadeCheck = gpGlobals->curtime + 5;
  776. }
  777. pSquadmate = m_pSquad->GetNextMember( &iter );
  778. }
  779. }
  780. TaskComplete();
  781. break;
  782. }
  783. case TASK_FACE_IDEAL:
  784. case TASK_FACE_ENEMY:
  785. {
  786. if( pTask->iTask == TASK_FACE_ENEMY && HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  787. {
  788. TaskComplete();
  789. return;
  790. }
  791. BaseClass::StartTask( pTask );
  792. bool bIsFlying = (GetMoveType() == MOVETYPE_FLY) || (GetMoveType() == MOVETYPE_FLYGRAVITY);
  793. if (bIsFlying)
  794. {
  795. SetIdealActivity( ACT_GLIDE );
  796. }
  797. }
  798. break;
  799. case TASK_FIND_COVER_FROM_ENEMY:
  800. {
  801. if (GetHintGroup() == NULL_STRING)
  802. {
  803. CBaseEntity *pEntity = GetEnemy();
  804. // FIXME: this should be generalized by the schedules that are selected, or in the definition of
  805. // what "cover" means (i.e., trace attack vulnerability vs. physical attack vulnerability
  806. if ( pEntity )
  807. {
  808. // NOTE: This is a good time to check to see if the player is hurt.
  809. // Have the combine notice this and call out
  810. if ( !HasMemory(bits_MEMORY_PLAYER_HURT) && pEntity->IsPlayer() && pEntity->GetHealth() <= 20 )
  811. {
  812. if ( m_pSquad )
  813. {
  814. m_pSquad->SquadRemember(bits_MEMORY_PLAYER_HURT);
  815. }
  816. m_Sentences.Speak( "COMBINE_PLAYERHIT", SENTENCE_PRIORITY_INVALID );
  817. JustMadeSound( SENTENCE_PRIORITY_HIGH );
  818. }
  819. if ( pEntity->MyNPCPointer() )
  820. {
  821. if ( !(pEntity->MyNPCPointer()->CapabilitiesGet( ) & bits_CAP_WEAPON_RANGE_ATTACK1) &&
  822. !(pEntity->MyNPCPointer()->CapabilitiesGet( ) & bits_CAP_INNATE_RANGE_ATTACK1) )
  823. {
  824. TaskComplete();
  825. return;
  826. }
  827. }
  828. }
  829. }
  830. BaseClass::StartTask( pTask );
  831. }
  832. break;
  833. case TASK_RANGE_ATTACK1:
  834. {
  835. m_nShots = GetActiveWeapon()->GetRandomBurst();
  836. m_flShotDelay = GetActiveWeapon()->GetFireRate();
  837. m_flNextAttack = gpGlobals->curtime + m_flShotDelay - 0.1;
  838. ResetIdealActivity( ACT_RANGE_ATTACK1 );
  839. m_flLastAttackTime = gpGlobals->curtime;
  840. }
  841. break;
  842. case TASK_COMBINE_DIE_INSTANTLY:
  843. {
  844. CTakeDamageInfo info;
  845. info.SetAttacker( this );
  846. info.SetInflictor( this );
  847. info.SetDamage( m_iHealth );
  848. info.SetDamageType( pTask->flTaskData );
  849. info.SetDamageForce( Vector( 0.1, 0.1, 0.1 ) );
  850. TakeDamage( info );
  851. TaskComplete();
  852. }
  853. break;
  854. default:
  855. BaseClass:: StartTask( pTask );
  856. break;
  857. }
  858. }
  859. //=========================================================
  860. // RunTask
  861. //=========================================================
  862. void CNPC_Combine::RunTask( const Task_t *pTask )
  863. {
  864. /*
  865. {
  866. CBaseEntity *pEnemy = GetEnemy();
  867. if (pEnemy)
  868. {
  869. NDebugOverlay::Line(Center(), pEnemy->Center(), 0,255,255, false, 0.1);
  870. }
  871. }
  872. */
  873. /*
  874. if (m_iMySquadSlot != SQUAD_SLOT_NONE)
  875. {
  876. char text[64];
  877. Q_snprintf( text, strlen( text ), "%d", m_iMySquadSlot );
  878. NDebugOverlay::Text( Center() + Vector( 0, 0, 72 ), text, false, 0.1 );
  879. }
  880. */
  881. switch ( pTask->iTask )
  882. {
  883. case TASK_COMBINE_CHASE_ENEMY_CONTINUOUSLY:
  884. RunTaskChaseEnemyContinuously( pTask );
  885. break;
  886. case TASK_COMBINE_SIGNAL_BEST_SOUND:
  887. AutoMovement( );
  888. if ( IsActivityFinished() )
  889. {
  890. TaskComplete();
  891. }
  892. break;
  893. case TASK_ANNOUNCE_ATTACK:
  894. {
  895. // Stop waiting if enemy facing me or lost enemy
  896. CBaseCombatCharacter* pBCC = GetEnemyCombatCharacterPointer();
  897. if (!pBCC || pBCC->FInViewCone( this ))
  898. {
  899. TaskComplete();
  900. }
  901. if ( IsWaitFinished() )
  902. {
  903. TaskComplete();
  904. }
  905. }
  906. break;
  907. case TASK_COMBINE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET:
  908. GetMotor()->SetIdealYawToTargetAndUpdate( m_vecAltFireTarget, AI_KEEP_YAW_SPEED );
  909. if ( IsActivityFinished() )
  910. {
  911. TaskComplete();
  912. }
  913. break;
  914. case TASK_COMBINE_FACE_TOSS_DIR:
  915. {
  916. // project a point along the toss vector and turn to face that point.
  917. GetMotor()->SetIdealYawToTargetAndUpdate( GetLocalOrigin() + m_vecTossVelocity * 64, AI_KEEP_YAW_SPEED );
  918. if ( FacingIdeal() )
  919. {
  920. TaskComplete( true );
  921. }
  922. break;
  923. }
  924. case TASK_COMBINE_GET_PATH_TO_FORCED_GREN_LOS:
  925. {
  926. if ( !m_hForcedGrenadeTarget )
  927. {
  928. TaskFail(FAIL_NO_ENEMY);
  929. return;
  930. }
  931. if ( GetTaskInterrupt() > 0 )
  932. {
  933. ClearTaskInterrupt();
  934. Vector vecEnemy = m_hForcedGrenadeTarget->GetAbsOrigin();
  935. AI_NavGoal_t goal( m_vInterruptSavePosition, ACT_RUN, AIN_HULL_TOLERANCE );
  936. GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET );
  937. GetNavigator()->SetArrivalDirection( vecEnemy - goal.dest );
  938. }
  939. else
  940. {
  941. TaskInterrupt();
  942. }
  943. }
  944. break;
  945. case TASK_RANGE_ATTACK1:
  946. {
  947. AutoMovement( );
  948. Vector vecEnemyLKP = GetEnemyLKP();
  949. if (!FInAimCone( vecEnemyLKP ))
  950. {
  951. GetMotor()->SetIdealYawToTargetAndUpdate( vecEnemyLKP, AI_KEEP_YAW_SPEED );
  952. }
  953. else
  954. {
  955. GetMotor()->SetIdealYawAndUpdate( GetMotor()->GetIdealYaw(), AI_KEEP_YAW_SPEED );
  956. }
  957. if ( gpGlobals->curtime >= m_flNextAttack )
  958. {
  959. if ( IsActivityFinished() )
  960. {
  961. if (--m_nShots > 0)
  962. {
  963. // DevMsg("ACT_RANGE_ATTACK1\n");
  964. ResetIdealActivity( ACT_RANGE_ATTACK1 );
  965. m_flLastAttackTime = gpGlobals->curtime;
  966. m_flNextAttack = gpGlobals->curtime + m_flShotDelay - 0.1;
  967. }
  968. else
  969. {
  970. // DevMsg("TASK_RANGE_ATTACK1 complete\n");
  971. TaskComplete();
  972. }
  973. }
  974. }
  975. else
  976. {
  977. // DevMsg("Wait\n");
  978. }
  979. }
  980. break;
  981. default:
  982. {
  983. BaseClass::RunTask( pTask );
  984. break;
  985. }
  986. }
  987. }
  988. //------------------------------------------------------------------------------
  989. // Purpose : Override to always shoot at eyes (for ducking behind things)
  990. // Input :
  991. // Output :
  992. //------------------------------------------------------------------------------
  993. Vector CNPC_Combine::BodyTarget( const Vector &posSrc, bool bNoisy )
  994. {
  995. Vector result = BaseClass::BodyTarget( posSrc, bNoisy );
  996. // @TODO (toml 02-02-04): this seems wrong. Isn't this already be accounted for
  997. // with the eye position used in the base BodyTarget()
  998. if ( GetFlags() & FL_DUCKING )
  999. result -= Vector(0,0,24);
  1000. return result;
  1001. }
  1002. //------------------------------------------------------------------------------
  1003. // Purpose:
  1004. //------------------------------------------------------------------------------
  1005. bool CNPC_Combine::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
  1006. {
  1007. if( m_spawnflags & SF_COMBINE_NO_LOOK )
  1008. {
  1009. // When no look is set, if enemy has eluded the squad,
  1010. // he's always invisble to me
  1011. if (GetEnemies()->HasEludedMe(pEntity))
  1012. {
  1013. return false;
  1014. }
  1015. }
  1016. return BaseClass::FVisible(pEntity, traceMask, ppBlocker);
  1017. }
  1018. //-----------------------------------------------------------------------------
  1019. //-----------------------------------------------------------------------------
  1020. void CNPC_Combine::Event_Killed( const CTakeDamageInfo &info )
  1021. {
  1022. // if I was killed before I could finish throwing my grenade, drop
  1023. // a grenade item that the player can retrieve.
  1024. if( GetActivity() == ACT_RANGE_ATTACK2 )
  1025. {
  1026. if( m_iLastAnimEventHandled != COMBINE_AE_GREN_TOSS )
  1027. {
  1028. // Drop the grenade as an item.
  1029. Vector vecStart;
  1030. GetAttachment( "lefthand", vecStart );
  1031. CBaseEntity *pItem = DropItem( "weapon_frag", vecStart, RandomAngle(0,360) );
  1032. if ( pItem )
  1033. {
  1034. IPhysicsObject *pObj = pItem->VPhysicsGetObject();
  1035. if ( pObj )
  1036. {
  1037. Vector vel;
  1038. vel.x = random->RandomFloat( -100.0f, 100.0f );
  1039. vel.y = random->RandomFloat( -100.0f, 100.0f );
  1040. vel.z = random->RandomFloat( 800.0f, 1200.0f );
  1041. AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f );
  1042. vel[2] = 0.0f;
  1043. pObj->AddVelocity( &vel, &angImp );
  1044. }
  1045. // In the Citadel we need to dissolve this
  1046. if ( PlayerHasMegaPhysCannon() )
  1047. {
  1048. CBaseCombatWeapon *pWeapon = static_cast<CBaseCombatWeapon *>(pItem);
  1049. pWeapon->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
  1050. }
  1051. }
  1052. }
  1053. }
  1054. BaseClass::Event_Killed( info );
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. // Purpose: Override. Don't update if I'm not looking
  1058. // Input :
  1059. // Output : Returns true is new enemy, false is known enemy
  1060. //-----------------------------------------------------------------------------
  1061. bool CNPC_Combine::UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position, CBaseEntity *pInformer )
  1062. {
  1063. if( m_spawnflags & SF_COMBINE_NO_LOOK )
  1064. {
  1065. return false;
  1066. }
  1067. return BaseClass::UpdateEnemyMemory( pEnemy, position, pInformer );
  1068. }
  1069. //-----------------------------------------------------------------------------
  1070. // Purpose: Allows for modification of the interrupt mask for the current schedule.
  1071. // In the most cases the base implementation should be called first.
  1072. //-----------------------------------------------------------------------------
  1073. void CNPC_Combine::BuildScheduleTestBits( void )
  1074. {
  1075. BaseClass::BuildScheduleTestBits();
  1076. if (gpGlobals->curtime < m_flNextAttack)
  1077. {
  1078. ClearCustomInterruptCondition( COND_CAN_RANGE_ATTACK1 );
  1079. ClearCustomInterruptCondition( COND_CAN_RANGE_ATTACK2 );
  1080. }
  1081. SetCustomInterruptCondition( COND_COMBINE_HIT_BY_BUGBAIT );
  1082. if ( !IsCurSchedule( SCHED_COMBINE_BURNING_STAND ) )
  1083. {
  1084. SetCustomInterruptCondition( COND_COMBINE_ON_FIRE );
  1085. }
  1086. }
  1087. //-----------------------------------------------------------------------------
  1088. // Purpose: Translate base class activities into combot activites
  1089. //-----------------------------------------------------------------------------
  1090. Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity )
  1091. {
  1092. //Slaming this back to ACT_COMBINE_BUGBAIT since we don't want ANYTHING to change our activity while we burn.
  1093. if ( HasCondition( COND_COMBINE_ON_FIRE ) )
  1094. return BaseClass::NPC_TranslateActivity( ACT_COMBINE_BUGBAIT );
  1095. if (eNewActivity == ACT_RANGE_ATTACK2)
  1096. {
  1097. // grunt is going to a secondary long range attack. This may be a thrown
  1098. // grenade or fired grenade, we must determine which and pick proper sequence
  1099. if (Weapon_OwnsThisType( "weapon_grenadelauncher" ) )
  1100. {
  1101. return ( Activity )ACT_COMBINE_LAUNCH_GRENADE;
  1102. }
  1103. else
  1104. {
  1105. return ( Activity )ACT_COMBINE_THROW_GRENADE;
  1106. }
  1107. }
  1108. else if (eNewActivity == ACT_IDLE)
  1109. {
  1110. if ( !IsCrouching() && ( m_NPCState == NPC_STATE_COMBAT || m_NPCState == NPC_STATE_ALERT ) )
  1111. {
  1112. eNewActivity = ACT_IDLE_ANGRY;
  1113. }
  1114. }
  1115. if ( m_AssaultBehavior.IsRunning() )
  1116. {
  1117. switch ( eNewActivity )
  1118. {
  1119. case ACT_IDLE:
  1120. eNewActivity = ACT_IDLE_ANGRY;
  1121. break;
  1122. case ACT_WALK:
  1123. eNewActivity = ACT_WALK_AIM;
  1124. break;
  1125. case ACT_RUN:
  1126. eNewActivity = ACT_RUN_AIM;
  1127. break;
  1128. }
  1129. }
  1130. return BaseClass::NPC_TranslateActivity( eNewActivity );
  1131. }
  1132. //-----------------------------------------------------------------------------
  1133. // Purpose: Overidden for human grunts because they hear the DANGER sound
  1134. // Input :
  1135. // Output :
  1136. //-----------------------------------------------------------------------------
  1137. int CNPC_Combine::GetSoundInterests( void )
  1138. {
  1139. return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_DANGER | SOUND_PHYSICS_DANGER | SOUND_BULLET_IMPACT | SOUND_MOVE_AWAY;
  1140. }
  1141. //-----------------------------------------------------------------------------
  1142. // Purpose: Return true if this NPC can hear the specified sound
  1143. //-----------------------------------------------------------------------------
  1144. bool CNPC_Combine::QueryHearSound( CSound *pSound )
  1145. {
  1146. if ( pSound->SoundContext() & SOUND_CONTEXT_COMBINE_ONLY )
  1147. return true;
  1148. if ( pSound->SoundContext() & SOUND_CONTEXT_EXCLUDE_COMBINE )
  1149. return false;
  1150. return BaseClass::QueryHearSound( pSound );
  1151. }
  1152. //-----------------------------------------------------------------------------
  1153. // Purpose: Announce an assault if the enemy can see me and we are pretty
  1154. // close to him/her
  1155. // Input :
  1156. // Output :
  1157. //-----------------------------------------------------------------------------
  1158. void CNPC_Combine::AnnounceAssault(void)
  1159. {
  1160. if (random->RandomInt(0,5) > 1)
  1161. return;
  1162. // If enemy can see me make assualt sound
  1163. CBaseCombatCharacter* pBCC = GetEnemyCombatCharacterPointer();
  1164. if (!pBCC)
  1165. return;
  1166. if (!FOkToMakeSound())
  1167. return;
  1168. // Make sure we are pretty close
  1169. if ( WorldSpaceCenter().DistToSqr( pBCC->WorldSpaceCenter() ) > (2000 * 2000))
  1170. return;
  1171. // Make sure we are in view cone of player
  1172. if (!pBCC->FInViewCone ( this ))
  1173. return;
  1174. // Make sure player can see me
  1175. if ( FVisible( pBCC ) )
  1176. {
  1177. m_Sentences.Speak( "COMBINE_ASSAULT" );
  1178. }
  1179. }
  1180. void CNPC_Combine::AnnounceEnemyType( CBaseEntity *pEnemy )
  1181. {
  1182. const char *pSentenceName = "COMBINE_MONST";
  1183. switch ( pEnemy->Classify() )
  1184. {
  1185. case CLASS_PLAYER:
  1186. pSentenceName = "COMBINE_ALERT";
  1187. break;
  1188. case CLASS_PLAYER_ALLY:
  1189. case CLASS_CITIZEN_REBEL:
  1190. case CLASS_CITIZEN_PASSIVE:
  1191. case CLASS_VORTIGAUNT:
  1192. pSentenceName = "COMBINE_MONST_CITIZENS";
  1193. break;
  1194. case CLASS_PLAYER_ALLY_VITAL:
  1195. pSentenceName = "COMBINE_MONST_CHARACTER";
  1196. break;
  1197. case CLASS_ANTLION:
  1198. pSentenceName = "COMBINE_MONST_BUGS";
  1199. break;
  1200. case CLASS_ZOMBIE:
  1201. pSentenceName = "COMBINE_MONST_ZOMBIES";
  1202. break;
  1203. case CLASS_HEADCRAB:
  1204. case CLASS_BARNACLE:
  1205. pSentenceName = "COMBINE_MONST_PARASITES";
  1206. break;
  1207. }
  1208. m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH );
  1209. }
  1210. void CNPC_Combine::AnnounceEnemyKill( CBaseEntity *pEnemy )
  1211. {
  1212. if (!pEnemy )
  1213. return;
  1214. const char *pSentenceName = "COMBINE_KILL_MONST";
  1215. switch ( pEnemy->Classify() )
  1216. {
  1217. case CLASS_PLAYER:
  1218. pSentenceName = "COMBINE_PLAYER_DEAD";
  1219. break;
  1220. // no sentences for these guys yet
  1221. case CLASS_PLAYER_ALLY:
  1222. case CLASS_CITIZEN_REBEL:
  1223. case CLASS_CITIZEN_PASSIVE:
  1224. case CLASS_VORTIGAUNT:
  1225. break;
  1226. case CLASS_PLAYER_ALLY_VITAL:
  1227. break;
  1228. case CLASS_ANTLION:
  1229. break;
  1230. case CLASS_ZOMBIE:
  1231. break;
  1232. case CLASS_HEADCRAB:
  1233. case CLASS_BARNACLE:
  1234. break;
  1235. }
  1236. m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH );
  1237. }
  1238. //-----------------------------------------------------------------------------
  1239. // Select the combat schedule
  1240. //-----------------------------------------------------------------------------
  1241. int CNPC_Combine::SelectCombatSchedule()
  1242. {
  1243. // -----------
  1244. // dead enemy
  1245. // -----------
  1246. if ( HasCondition( COND_ENEMY_DEAD ) )
  1247. {
  1248. // call base class, all code to handle dead enemies is centralized there.
  1249. return SCHED_NONE;
  1250. }
  1251. // -----------
  1252. // new enemy
  1253. // -----------
  1254. if ( HasCondition( COND_NEW_ENEMY ) )
  1255. {
  1256. CBaseEntity *pEnemy = GetEnemy();
  1257. bool bFirstContact = false;
  1258. float flTimeSinceFirstSeen = gpGlobals->curtime - GetEnemies()->FirstTimeSeen( pEnemy );
  1259. if( flTimeSinceFirstSeen < 3.0f )
  1260. bFirstContact = true;
  1261. if ( m_pSquad && pEnemy )
  1262. {
  1263. if ( HasCondition( COND_SEE_ENEMY ) )
  1264. {
  1265. AnnounceEnemyType( pEnemy );
  1266. }
  1267. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) && OccupyStrategySlot( SQUAD_SLOT_ATTACK1 ) )
  1268. {
  1269. // Start suppressing if someone isn't firing already (SLOT_ATTACK1). This means
  1270. // I'm the guy who spotted the enemy, I should react immediately.
  1271. return SCHED_COMBINE_SUPPRESS;
  1272. }
  1273. if ( m_pSquad->IsLeader( this ) || ( m_pSquad->GetLeader() && m_pSquad->GetLeader()->GetEnemy() != pEnemy ) )
  1274. {
  1275. // I'm the leader, but I didn't get the job suppressing the enemy. We know this because
  1276. // This code only runs if the code above didn't assign me SCHED_COMBINE_SUPPRESS.
  1277. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) && OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1278. {
  1279. return SCHED_RANGE_ATTACK1;
  1280. }
  1281. if( HasCondition(COND_WEAPON_HAS_LOS) && IsStrategySlotRangeOccupied( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1282. {
  1283. // If everyone else is attacking and I have line of fire, wait for a chance to cover someone.
  1284. if( OccupyStrategySlot( SQUAD_SLOT_OVERWATCH ) )
  1285. {
  1286. return SCHED_COMBINE_ENTER_OVERWATCH;
  1287. }
  1288. }
  1289. }
  1290. else
  1291. {
  1292. if ( m_pSquad->GetLeader() && FOkToMakeSound( SENTENCE_PRIORITY_MEDIUM ) )
  1293. {
  1294. JustMadeSound( SENTENCE_PRIORITY_MEDIUM ); // squelch anything that isn't high priority so the leader can speak
  1295. }
  1296. // First contact, and I'm solo, or not the squad leader.
  1297. if( HasCondition( COND_SEE_ENEMY ) && CanGrenadeEnemy() )
  1298. {
  1299. if( OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) )
  1300. {
  1301. return SCHED_RANGE_ATTACK2;
  1302. }
  1303. }
  1304. if( !bFirstContact && OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1305. {
  1306. if( random->RandomInt(0, 100) < 60 )
  1307. {
  1308. return SCHED_ESTABLISH_LINE_OF_FIRE;
  1309. }
  1310. else
  1311. {
  1312. return SCHED_COMBINE_PRESS_ATTACK;
  1313. }
  1314. }
  1315. return SCHED_TAKE_COVER_FROM_ENEMY;
  1316. }
  1317. }
  1318. }
  1319. // ---------------------
  1320. // no ammo
  1321. // ---------------------
  1322. if ( ( HasCondition ( COND_NO_PRIMARY_AMMO ) || HasCondition ( COND_LOW_PRIMARY_AMMO ) ) && !HasCondition( COND_CAN_MELEE_ATTACK1) )
  1323. {
  1324. return SCHED_HIDE_AND_RELOAD;
  1325. }
  1326. // ----------------------
  1327. // LIGHT DAMAGE
  1328. // ----------------------
  1329. if ( HasCondition( COND_LIGHT_DAMAGE ) )
  1330. {
  1331. if ( GetEnemy() != NULL )
  1332. {
  1333. // only try to take cover if we actually have an enemy!
  1334. // FIXME: need to take cover for enemy dealing the damage
  1335. // A standing guy will either crouch or run.
  1336. // A crouching guy tries to stay stuck in.
  1337. if( !IsCrouching() )
  1338. {
  1339. if( GetEnemy() && random->RandomFloat( 0, 100 ) < 50 && CouldShootIfCrouching( GetEnemy() ) )
  1340. {
  1341. Crouch();
  1342. }
  1343. else
  1344. {
  1345. //!!!KELLY - this grunt was hit and is going to run to cover.
  1346. // m_Sentences.Speak( "COMBINE_COVER" );
  1347. return SCHED_TAKE_COVER_FROM_ENEMY;
  1348. }
  1349. }
  1350. }
  1351. else
  1352. {
  1353. // How am I wounded in combat with no enemy?
  1354. Assert( GetEnemy() != NULL );
  1355. }
  1356. }
  1357. // If I'm scared of this enemy run away
  1358. if ( IRelationType( GetEnemy() ) == D_FR )
  1359. {
  1360. if (HasCondition( COND_SEE_ENEMY ) ||
  1361. HasCondition( COND_SEE_FEAR ) ||
  1362. HasCondition( COND_LIGHT_DAMAGE ) ||
  1363. HasCondition( COND_HEAVY_DAMAGE ))
  1364. {
  1365. FearSound();
  1366. //ClearCommandGoal();
  1367. return SCHED_RUN_FROM_ENEMY;
  1368. }
  1369. // If I've seen the enemy recently, cower. Ignore the time for unforgettable enemies.
  1370. AI_EnemyInfo_t *pMemory = GetEnemies()->Find( GetEnemy() );
  1371. if ( (pMemory && pMemory->bUnforgettable) || (GetEnemyLastTimeSeen() > (gpGlobals->curtime - 5.0)) )
  1372. {
  1373. // If we're facing him, just look ready. Otherwise, face him.
  1374. if ( FInAimCone( GetEnemy()->EyePosition() ) )
  1375. return SCHED_COMBAT_STAND;
  1376. return SCHED_FEAR_FACE;
  1377. }
  1378. }
  1379. int attackSchedule = SelectScheduleAttack();
  1380. if ( attackSchedule != SCHED_NONE )
  1381. return attackSchedule;
  1382. if (HasCondition(COND_ENEMY_OCCLUDED))
  1383. {
  1384. // stand up, just in case
  1385. Stand();
  1386. DesireStand();
  1387. if( GetEnemy() && !(GetEnemy()->GetFlags() & FL_NOTARGET) && OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1388. {
  1389. // Charge in and break the enemy's cover!
  1390. return SCHED_ESTABLISH_LINE_OF_FIRE;
  1391. }
  1392. // If I'm a long, long way away, establish a LOF anyway. Once I get there I'll
  1393. // start respecting the squad slots again.
  1394. float flDistSq = GetEnemy()->WorldSpaceCenter().DistToSqr( WorldSpaceCenter() );
  1395. if ( flDistSq > Square(3000) )
  1396. return SCHED_ESTABLISH_LINE_OF_FIRE;
  1397. // Otherwise tuck in.
  1398. Remember( bits_MEMORY_INCOVER );
  1399. return SCHED_COMBINE_WAIT_IN_COVER;
  1400. }
  1401. // --------------------------------------------------------------
  1402. // Enemy not occluded but isn't open to attack
  1403. // --------------------------------------------------------------
  1404. if ( HasCondition( COND_SEE_ENEMY ) && !HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  1405. {
  1406. if ( (HasCondition( COND_TOO_FAR_TO_ATTACK ) || IsUsingTacticalVariant(TACTICAL_VARIANT_PRESSURE_ENEMY) ) && OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ))
  1407. {
  1408. return SCHED_COMBINE_PRESS_ATTACK;
  1409. }
  1410. AnnounceAssault();
  1411. return SCHED_COMBINE_ASSAULT;
  1412. }
  1413. return SCHED_NONE;
  1414. }
  1415. //-----------------------------------------------------------------------------
  1416. // Purpose:
  1417. // Input :
  1418. // Output :
  1419. //-----------------------------------------------------------------------------
  1420. int CNPC_Combine::SelectSchedule( void )
  1421. {
  1422. if ( IsWaitingToRappel() && BehaviorSelectSchedule() )
  1423. {
  1424. return BaseClass::SelectSchedule();
  1425. }
  1426. if ( HasCondition(COND_COMBINE_ON_FIRE) )
  1427. return SCHED_COMBINE_BURNING_STAND;
  1428. int nSched = SelectFlinchSchedule();
  1429. if ( nSched != SCHED_NONE )
  1430. return nSched;
  1431. if ( m_hForcedGrenadeTarget )
  1432. {
  1433. if ( m_flNextGrenadeCheck < gpGlobals->curtime )
  1434. {
  1435. Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter();
  1436. if ( IsElite() )
  1437. {
  1438. if ( FVisible( m_hForcedGrenadeTarget ) )
  1439. {
  1440. m_vecAltFireTarget = vecTarget;
  1441. m_hForcedGrenadeTarget = NULL;
  1442. return SCHED_COMBINE_AR2_ALTFIRE;
  1443. }
  1444. }
  1445. else
  1446. {
  1447. // If we can, throw a grenade at the target.
  1448. // Ignore grenade count / distance / etc
  1449. if ( CheckCanThrowGrenade( vecTarget ) )
  1450. {
  1451. m_hForcedGrenadeTarget = NULL;
  1452. return SCHED_COMBINE_FORCED_GRENADE_THROW;
  1453. }
  1454. }
  1455. }
  1456. // Can't throw at the target, so lets try moving to somewhere where I can see it
  1457. if ( !FVisible( m_hForcedGrenadeTarget ) )
  1458. {
  1459. return SCHED_COMBINE_MOVE_TO_FORCED_GREN_LOS;
  1460. }
  1461. }
  1462. if ( m_NPCState != NPC_STATE_SCRIPT)
  1463. {
  1464. // If we're hit by bugbait, thrash around
  1465. if ( HasCondition( COND_COMBINE_HIT_BY_BUGBAIT ) )
  1466. {
  1467. // Don't do this if we're mounting a func_tank
  1468. if ( m_FuncTankBehavior.IsMounted() == true )
  1469. {
  1470. m_FuncTankBehavior.Dismount();
  1471. }
  1472. ClearCondition( COND_COMBINE_HIT_BY_BUGBAIT );
  1473. return SCHED_COMBINE_BUGBAIT_DISTRACTION;
  1474. }
  1475. // We've been told to move away from a target to make room for a grenade to be thrown at it
  1476. if ( HasCondition( COND_HEAR_MOVE_AWAY ) )
  1477. {
  1478. return SCHED_MOVE_AWAY;
  1479. }
  1480. // These things are done in any state but dead and prone
  1481. if (m_NPCState != NPC_STATE_DEAD && m_NPCState != NPC_STATE_PRONE )
  1482. {
  1483. // Cower when physics objects are thrown at me
  1484. if ( HasCondition( COND_HEAR_PHYSICS_DANGER ) )
  1485. {
  1486. return SCHED_FLINCH_PHYSICS;
  1487. }
  1488. // grunts place HIGH priority on running away from danger sounds.
  1489. if ( HasCondition(COND_HEAR_DANGER) )
  1490. {
  1491. CSound *pSound;
  1492. pSound = GetBestSound();
  1493. Assert( pSound != NULL );
  1494. if ( pSound)
  1495. {
  1496. if (pSound->m_iType & SOUND_DANGER)
  1497. {
  1498. // I hear something dangerous, probably need to take cover.
  1499. // dangerous sound nearby!, call it out
  1500. const char *pSentenceName = "COMBINE_DANGER";
  1501. CBaseEntity *pSoundOwner = pSound->m_hOwner;
  1502. if ( pSoundOwner )
  1503. {
  1504. CBaseGrenade *pGrenade = dynamic_cast<CBaseGrenade *>(pSoundOwner);
  1505. if ( pGrenade && pGrenade->GetThrower() )
  1506. {
  1507. if ( IRelationType( pGrenade->GetThrower() ) != D_LI )
  1508. {
  1509. // special case call out for enemy grenades
  1510. pSentenceName = "COMBINE_GREN";
  1511. }
  1512. }
  1513. }
  1514. m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL );
  1515. // If the sound is approaching danger, I have no enemy, and I don't see it, turn to face.
  1516. if( !GetEnemy() && pSound->IsSoundType(SOUND_CONTEXT_DANGER_APPROACH) && pSound->m_hOwner && !FInViewCone(pSound->GetSoundReactOrigin()) )
  1517. {
  1518. GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() );
  1519. return SCHED_COMBINE_FACE_IDEAL_YAW;
  1520. }
  1521. return SCHED_TAKE_COVER_FROM_BEST_SOUND;
  1522. }
  1523. // JAY: This was disabled in HL1. Test?
  1524. if (!HasCondition( COND_SEE_ENEMY ) && ( pSound->m_iType & (SOUND_PLAYER | SOUND_COMBAT) ))
  1525. {
  1526. GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() );
  1527. }
  1528. }
  1529. }
  1530. }
  1531. if( BehaviorSelectSchedule() )
  1532. {
  1533. return BaseClass::SelectSchedule();
  1534. }
  1535. }
  1536. switch ( m_NPCState )
  1537. {
  1538. case NPC_STATE_IDLE:
  1539. {
  1540. if ( m_bShouldPatrol )
  1541. return SCHED_COMBINE_PATROL;
  1542. }
  1543. // NOTE: Fall through!
  1544. case NPC_STATE_ALERT:
  1545. {
  1546. if( HasCondition(COND_LIGHT_DAMAGE) || HasCondition(COND_HEAVY_DAMAGE) )
  1547. {
  1548. AI_EnemyInfo_t *pDanger = GetEnemies()->GetDangerMemory();
  1549. if( pDanger && FInViewCone(pDanger->vLastKnownLocation) && !BaseClass::FVisible(pDanger->vLastKnownLocation) )
  1550. {
  1551. // I've been hurt, I'm facing the danger, but I don't see it, so move from this position.
  1552. return SCHED_TAKE_COVER_FROM_ORIGIN;
  1553. }
  1554. }
  1555. if( HasCondition( COND_HEAR_COMBAT ) )
  1556. {
  1557. CSound *pSound = GetBestSound();
  1558. if( pSound && pSound->IsSoundType( SOUND_COMBAT ) )
  1559. {
  1560. if( m_pSquad && m_pSquad->GetSquadMemberNearestTo( pSound->GetSoundReactOrigin() ) == this && OccupyStrategySlot( SQUAD_SLOT_INVESTIGATE_SOUND ) )
  1561. {
  1562. return SCHED_INVESTIGATE_SOUND;
  1563. }
  1564. }
  1565. }
  1566. // Don't patrol if I'm in the middle of an assault, because I'll never return to the assault.
  1567. if ( !m_AssaultBehavior.HasAssaultCue() )
  1568. {
  1569. if( m_bShouldPatrol || HasCondition( COND_COMBINE_SHOULD_PATROL ) )
  1570. return SCHED_COMBINE_PATROL;
  1571. }
  1572. }
  1573. break;
  1574. case NPC_STATE_COMBAT:
  1575. {
  1576. int nSched = SelectCombatSchedule();
  1577. if ( nSched != SCHED_NONE )
  1578. return nSched;
  1579. }
  1580. break;
  1581. }
  1582. // no special cases here, call the base class
  1583. return BaseClass::SelectSchedule();
  1584. }
  1585. //-----------------------------------------------------------------------------
  1586. //-----------------------------------------------------------------------------
  1587. int CNPC_Combine::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  1588. {
  1589. if( failedSchedule == SCHED_COMBINE_TAKE_COVER1 )
  1590. {
  1591. if( IsInSquad() && IsStrategySlotRangeOccupied(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2) && HasCondition(COND_SEE_ENEMY) )
  1592. {
  1593. // This eases the effects of an unfortunate bug that usually plagues shotgunners. Since their rate of fire is low,
  1594. // they spend relatively long periods of time without an attack squad slot. If you corner a shotgunner, usually
  1595. // the other memebers of the squad will hog all of the attack slots and pick schedules to move to establish line of
  1596. // fire. During this time, the shotgunner is prevented from attacking. If he also cannot find cover (the fallback case)
  1597. // he will stand around like an idiot, right in front of you. Instead of this, we have him run up to you for a melee attack.
  1598. return SCHED_COMBINE_MOVE_TO_MELEE;
  1599. }
  1600. }
  1601. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  1602. }
  1603. //-----------------------------------------------------------------------------
  1604. // Should we charge the player?
  1605. //-----------------------------------------------------------------------------
  1606. bool CNPC_Combine::ShouldChargePlayer()
  1607. {
  1608. return GetEnemy() && GetEnemy()->IsPlayer() && PlayerHasMegaPhysCannon() && !IsLimitingHintGroups();
  1609. }
  1610. //-----------------------------------------------------------------------------
  1611. // Select attack schedules
  1612. //-----------------------------------------------------------------------------
  1613. #define COMBINE_MEGA_PHYSCANNON_ATTACK_DISTANCE 192
  1614. #define COMBINE_MEGA_PHYSCANNON_ATTACK_DISTANCE_SQ (COMBINE_MEGA_PHYSCANNON_ATTACK_DISTANCE*COMBINE_MEGA_PHYSCANNON_ATTACK_DISTANCE)
  1615. int CNPC_Combine::SelectScheduleAttack()
  1616. {
  1617. // Drop a grenade?
  1618. if ( HasCondition( COND_COMBINE_DROP_GRENADE ) )
  1619. return SCHED_COMBINE_DROP_GRENADE;
  1620. // Kick attack?
  1621. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  1622. {
  1623. return SCHED_MELEE_ATTACK1;
  1624. }
  1625. // If I'm fighting a combine turret (it's been hacked to attack me), I can't really
  1626. // hurt it with bullets, so become grenade happy.
  1627. if ( GetEnemy() && GetEnemy()->Classify() == CLASS_COMBINE && FClassnameIs(GetEnemy(), "npc_turret_floor") )
  1628. {
  1629. // Don't do this until I've been fighting the turret for a few seconds
  1630. float flTimeAtFirstHand = GetEnemies()->TimeAtFirstHand(GetEnemy());
  1631. if ( flTimeAtFirstHand != AI_INVALID_TIME )
  1632. {
  1633. float flTimeEnemySeen = gpGlobals->curtime - flTimeAtFirstHand;
  1634. if ( flTimeEnemySeen > 4.0 )
  1635. {
  1636. if ( CanGrenadeEnemy() && OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) )
  1637. return SCHED_RANGE_ATTACK2;
  1638. }
  1639. }
  1640. // If we're not in the viewcone of the turret, run up and hit it. Do this a bit later to
  1641. // give other squadmembers a chance to throw a grenade before I run in.
  1642. if ( !GetEnemy()->MyNPCPointer()->FInViewCone( this ) && OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) )
  1643. return SCHED_COMBINE_CHARGE_TURRET;
  1644. }
  1645. // When fighting against the player who's wielding a mega-physcannon,
  1646. // always close the distance if possible
  1647. // But don't do it if you're in a nav-limited hint group
  1648. if ( ShouldChargePlayer() )
  1649. {
  1650. float flDistSq = GetEnemy()->WorldSpaceCenter().DistToSqr( WorldSpaceCenter() );
  1651. if ( flDistSq <= COMBINE_MEGA_PHYSCANNON_ATTACK_DISTANCE_SQ )
  1652. {
  1653. if( HasCondition(COND_SEE_ENEMY) )
  1654. {
  1655. if ( OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1656. return SCHED_RANGE_ATTACK1;
  1657. }
  1658. else
  1659. {
  1660. if ( OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1661. return SCHED_COMBINE_PRESS_ATTACK;
  1662. }
  1663. }
  1664. if ( HasCondition(COND_SEE_ENEMY) && !IsUnreachable( GetEnemy() ) )
  1665. {
  1666. return SCHED_COMBINE_CHARGE_PLAYER;
  1667. }
  1668. }
  1669. // Can I shoot?
  1670. if ( HasCondition(COND_CAN_RANGE_ATTACK1) )
  1671. {
  1672. // JAY: HL1 behavior missing?
  1673. #if 0
  1674. if ( m_pSquad )
  1675. {
  1676. // if the enemy has eluded the squad and a squad member has just located the enemy
  1677. // and the enemy does not see the squad member, issue a call to the squad to waste a
  1678. // little time and give the player a chance to turn.
  1679. if ( MySquadLeader()->m_fEnemyEluded && !HasConditions ( bits_COND_ENEMY_FACING_ME ) )
  1680. {
  1681. MySquadLeader()->m_fEnemyEluded = FALSE;
  1682. return SCHED_GRUNT_FOUND_ENEMY;
  1683. }
  1684. }
  1685. #endif
  1686. // Engage if allowed
  1687. if ( OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1688. {
  1689. return SCHED_RANGE_ATTACK1;
  1690. }
  1691. // Throw a grenade if not allowed to engage with weapon.
  1692. if ( CanGrenadeEnemy() )
  1693. {
  1694. if ( OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) )
  1695. {
  1696. return SCHED_RANGE_ATTACK2;
  1697. }
  1698. }
  1699. DesireCrouch();
  1700. return SCHED_TAKE_COVER_FROM_ENEMY;
  1701. }
  1702. if ( GetEnemy() && !HasCondition(COND_SEE_ENEMY) )
  1703. {
  1704. // We don't see our enemy. If it hasn't been long since I last saw him,
  1705. // and he's pretty close to the last place I saw him, throw a grenade in
  1706. // to flush him out. A wee bit of cheating here...
  1707. float flTime;
  1708. float flDist;
  1709. flTime = gpGlobals->curtime - GetEnemies()->LastTimeSeen( GetEnemy() );
  1710. flDist = ( GetEnemy()->GetAbsOrigin() - GetEnemies()->LastSeenPosition( GetEnemy() ) ).Length();
  1711. //Msg("Time: %f Dist: %f\n", flTime, flDist );
  1712. if ( flTime <= COMBINE_GRENADE_FLUSH_TIME && flDist <= COMBINE_GRENADE_FLUSH_DIST && CanGrenadeEnemy( false ) && OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) )
  1713. {
  1714. return SCHED_RANGE_ATTACK2;
  1715. }
  1716. }
  1717. if (HasCondition(COND_WEAPON_SIGHT_OCCLUDED))
  1718. {
  1719. // If they are hiding behind something that we can destroy, start shooting at it.
  1720. CBaseEntity *pBlocker = GetEnemyOccluder();
  1721. if ( pBlocker && pBlocker->GetHealth() > 0 && OccupyStrategySlot( SQUAD_SLOT_ATTACK_OCCLUDER ) )
  1722. {
  1723. return SCHED_SHOOT_ENEMY_COVER;
  1724. }
  1725. }
  1726. return SCHED_NONE;
  1727. }
  1728. //-----------------------------------------------------------------------------
  1729. // Purpose:
  1730. // Input :
  1731. // Output :
  1732. //-----------------------------------------------------------------------------
  1733. int CNPC_Combine::TranslateSchedule( int scheduleType )
  1734. {
  1735. switch( scheduleType )
  1736. {
  1737. case SCHED_TAKE_COVER_FROM_ENEMY:
  1738. {
  1739. if ( m_pSquad )
  1740. {
  1741. // Have to explicitly check innate range attack condition as may have weapon with range attack 2
  1742. if ( g_pGameRules->IsSkillLevel( SKILL_HARD ) &&
  1743. HasCondition(COND_CAN_RANGE_ATTACK2) &&
  1744. OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) )
  1745. {
  1746. m_Sentences.Speak( "COMBINE_THROW_GRENADE" );
  1747. return SCHED_COMBINE_TOSS_GRENADE_COVER1;
  1748. }
  1749. else
  1750. {
  1751. if ( ShouldChargePlayer() && !IsUnreachable( GetEnemy() ) )
  1752. return SCHED_COMBINE_CHARGE_PLAYER;
  1753. return SCHED_COMBINE_TAKE_COVER1;
  1754. }
  1755. }
  1756. else
  1757. {
  1758. // Have to explicitly check innate range attack condition as may have weapon with range attack 2
  1759. if ( random->RandomInt(0,1) && HasCondition(COND_CAN_RANGE_ATTACK2) )
  1760. {
  1761. return SCHED_COMBINE_GRENADE_COVER1;
  1762. }
  1763. else
  1764. {
  1765. if ( ShouldChargePlayer() && !IsUnreachable( GetEnemy() ) )
  1766. return SCHED_COMBINE_CHARGE_PLAYER;
  1767. return SCHED_COMBINE_TAKE_COVER1;
  1768. }
  1769. }
  1770. }
  1771. case SCHED_TAKE_COVER_FROM_BEST_SOUND:
  1772. {
  1773. return SCHED_COMBINE_TAKE_COVER_FROM_BEST_SOUND;
  1774. }
  1775. break;
  1776. case SCHED_COMBINE_TAKECOVER_FAILED:
  1777. {
  1778. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) && OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1779. {
  1780. return TranslateSchedule( SCHED_RANGE_ATTACK1 );
  1781. }
  1782. // Run somewhere randomly
  1783. return TranslateSchedule( SCHED_FAIL );
  1784. break;
  1785. }
  1786. break;
  1787. case SCHED_FAIL_ESTABLISH_LINE_OF_FIRE:
  1788. {
  1789. if( !IsCrouching() )
  1790. {
  1791. if( GetEnemy() && CouldShootIfCrouching( GetEnemy() ) )
  1792. {
  1793. Crouch();
  1794. return SCHED_COMBAT_FACE;
  1795. }
  1796. }
  1797. if( HasCondition( COND_SEE_ENEMY ) )
  1798. {
  1799. return TranslateSchedule( SCHED_TAKE_COVER_FROM_ENEMY );
  1800. }
  1801. else if ( !m_AssaultBehavior.HasAssaultCue() )
  1802. {
  1803. // Don't patrol if I'm in the middle of an assault, because
  1804. // I'll never return to the assault.
  1805. if ( GetEnemy() )
  1806. {
  1807. RememberUnreachable( GetEnemy() );
  1808. }
  1809. return TranslateSchedule( SCHED_COMBINE_PATROL );
  1810. }
  1811. }
  1812. break;
  1813. case SCHED_COMBINE_ASSAULT:
  1814. {
  1815. CBaseEntity *pEntity = GetEnemy();
  1816. // FIXME: this should be generalized by the schedules that are selected, or in the definition of
  1817. // what "cover" means (i.e., trace attack vulnerability vs. physical attack vulnerability
  1818. if (pEntity && pEntity->MyNPCPointer())
  1819. {
  1820. if ( !(pEntity->MyNPCPointer()->CapabilitiesGet( ) & bits_CAP_WEAPON_RANGE_ATTACK1))
  1821. {
  1822. return TranslateSchedule( SCHED_ESTABLISH_LINE_OF_FIRE );
  1823. }
  1824. }
  1825. // don't charge forward if there's a hint group
  1826. if (GetHintGroup() != NULL_STRING)
  1827. {
  1828. return TranslateSchedule( SCHED_ESTABLISH_LINE_OF_FIRE );
  1829. }
  1830. return SCHED_COMBINE_ASSAULT;
  1831. }
  1832. case SCHED_ESTABLISH_LINE_OF_FIRE:
  1833. {
  1834. // always assume standing
  1835. // Stand();
  1836. if( CanAltFireEnemy(true) && OccupyStrategySlot(SQUAD_SLOT_SPECIAL_ATTACK) )
  1837. {
  1838. // If an elite in the squad could fire a combine ball at the player's last known position,
  1839. // do so!
  1840. return SCHED_COMBINE_AR2_ALTFIRE;
  1841. }
  1842. if( IsUsingTacticalVariant( TACTICAL_VARIANT_PRESSURE_ENEMY ) && !IsRunningBehavior() )
  1843. {
  1844. if( OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  1845. {
  1846. return SCHED_COMBINE_PRESS_ATTACK;
  1847. }
  1848. }
  1849. return SCHED_COMBINE_ESTABLISH_LINE_OF_FIRE;
  1850. }
  1851. break;
  1852. case SCHED_HIDE_AND_RELOAD:
  1853. {
  1854. // stand up, just in case
  1855. // Stand();
  1856. // DesireStand();
  1857. if( CanGrenadeEnemy() && OccupyStrategySlot( SQUAD_SLOT_GRENADE1 ) && random->RandomInt( 0, 100 ) < 20 )
  1858. {
  1859. // If I COULD throw a grenade and I need to reload, 20% chance I'll throw a grenade before I hide to reload.
  1860. return SCHED_COMBINE_GRENADE_AND_RELOAD;
  1861. }
  1862. // No running away in the citadel!
  1863. if ( ShouldChargePlayer() )
  1864. return SCHED_RELOAD;
  1865. return SCHED_COMBINE_HIDE_AND_RELOAD;
  1866. }
  1867. break;
  1868. case SCHED_RANGE_ATTACK1:
  1869. {
  1870. if ( HasCondition( COND_NO_PRIMARY_AMMO ) || HasCondition( COND_LOW_PRIMARY_AMMO ) )
  1871. {
  1872. // Ditch the strategy slot for attacking (which we just reserved!)
  1873. VacateStrategySlot();
  1874. return TranslateSchedule( SCHED_HIDE_AND_RELOAD );
  1875. }
  1876. if( CanAltFireEnemy(true) && OccupyStrategySlot(SQUAD_SLOT_SPECIAL_ATTACK) )
  1877. {
  1878. // Since I'm holding this squadslot, no one else can try right now. If I die before the shot
  1879. // goes off, I won't have affected anyone else's ability to use this attack at their nearest
  1880. // convenience.
  1881. return SCHED_COMBINE_AR2_ALTFIRE;
  1882. }
  1883. if ( IsCrouching() || ( CrouchIsDesired() && !HasCondition( COND_HEAVY_DAMAGE ) ) )
  1884. {
  1885. // See if we can crouch and shoot
  1886. if (GetEnemy() != NULL)
  1887. {
  1888. float dist = (GetLocalOrigin() - GetEnemy()->GetLocalOrigin()).Length();
  1889. // only crouch if they are relatively far away
  1890. if (dist > COMBINE_MIN_CROUCH_DISTANCE)
  1891. {
  1892. // try crouching
  1893. Crouch();
  1894. Vector targetPos = GetEnemy()->BodyTarget(GetActiveWeapon()->GetLocalOrigin());
  1895. // if we can't see it crouched, stand up
  1896. if (!WeaponLOSCondition(GetLocalOrigin(),targetPos,false))
  1897. {
  1898. Stand();
  1899. }
  1900. }
  1901. }
  1902. }
  1903. else
  1904. {
  1905. // always assume standing
  1906. Stand();
  1907. }
  1908. return SCHED_COMBINE_RANGE_ATTACK1;
  1909. }
  1910. case SCHED_RANGE_ATTACK2:
  1911. {
  1912. // If my weapon can range attack 2 use the weapon
  1913. if (GetActiveWeapon() && GetActiveWeapon()->CapabilitiesGet() & bits_CAP_WEAPON_RANGE_ATTACK2)
  1914. {
  1915. return SCHED_RANGE_ATTACK2;
  1916. }
  1917. // Otherwise use innate attack
  1918. else
  1919. {
  1920. return SCHED_COMBINE_RANGE_ATTACK2;
  1921. }
  1922. }
  1923. // SCHED_COMBAT_FACE:
  1924. // SCHED_COMBINE_WAIT_FACE_ENEMY:
  1925. // SCHED_COMBINE_SWEEP:
  1926. // SCHED_COMBINE_COVER_AND_RELOAD:
  1927. // SCHED_COMBINE_FOUND_ENEMY:
  1928. case SCHED_VICTORY_DANCE:
  1929. {
  1930. return SCHED_COMBINE_VICTORY_DANCE;
  1931. }
  1932. case SCHED_COMBINE_SUPPRESS:
  1933. {
  1934. #define MIN_SIGNAL_DIST 256
  1935. if ( GetEnemy() != NULL && GetEnemy()->IsPlayer() && m_bFirstEncounter )
  1936. {
  1937. float flDistToEnemy = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).Length();
  1938. if( flDistToEnemy >= MIN_SIGNAL_DIST )
  1939. {
  1940. m_bFirstEncounter = false;// after first encounter, leader won't issue handsigns anymore when he has a new enemy
  1941. return SCHED_COMBINE_SIGNAL_SUPPRESS;
  1942. }
  1943. }
  1944. return SCHED_COMBINE_SUPPRESS;
  1945. }
  1946. case SCHED_FAIL:
  1947. {
  1948. if ( GetEnemy() != NULL )
  1949. {
  1950. return SCHED_COMBINE_COMBAT_FAIL;
  1951. }
  1952. return SCHED_FAIL;
  1953. }
  1954. case SCHED_COMBINE_PATROL:
  1955. {
  1956. // If I have an enemy, don't go off into random patrol mode.
  1957. if ( GetEnemy() && GetEnemy()->IsAlive() )
  1958. return SCHED_COMBINE_PATROL_ENEMY;
  1959. return SCHED_COMBINE_PATROL;
  1960. }
  1961. }
  1962. return BaseClass::TranslateSchedule( scheduleType );
  1963. }
  1964. //=========================================================
  1965. //=========================================================
  1966. void CNPC_Combine::OnStartSchedule( int scheduleType )
  1967. {
  1968. }
  1969. //=========================================================
  1970. // HandleAnimEvent - catches the monster-specific messages
  1971. // that occur when tagged animation frames are played.
  1972. //=========================================================
  1973. void CNPC_Combine::HandleAnimEvent( animevent_t *pEvent )
  1974. {
  1975. Vector vecShootDir;
  1976. Vector vecShootOrigin;
  1977. bool handledEvent = false;
  1978. if (pEvent->type & AE_TYPE_NEWEVENTSYSTEM)
  1979. {
  1980. if ( pEvent->event == COMBINE_AE_BEGIN_ALTFIRE )
  1981. {
  1982. EmitSound( "Weapon_CombineGuard.Special1" );
  1983. handledEvent = true;
  1984. }
  1985. else if ( pEvent->event == COMBINE_AE_ALTFIRE )
  1986. {
  1987. if( IsElite() )
  1988. {
  1989. animevent_t fakeEvent;
  1990. fakeEvent.pSource = this;
  1991. fakeEvent.event = EVENT_WEAPON_AR2_ALTFIRE;
  1992. GetActiveWeapon()->Operator_HandleAnimEvent( &fakeEvent, this );
  1993. // Stop other squad members from combine balling for a while.
  1994. DelaySquadAltFireAttack( 10.0f );
  1995. // I'm disabling this decrementor. At the time of this change, the elites
  1996. // don't bother to check if they have grenades anyway. This means that all
  1997. // elites have infinite combine balls, even if the designer marks the elite
  1998. // as having 0 grenades. By disabling this decrementor, yet enabling the code
  1999. // that makes sure the elite has grenades in order to fire a combine ball, we
  2000. // preserve the legacy behavior while making it possible for a designer to prevent
  2001. // elites from shooting combine balls by setting grenades to '0' in hammer. (sjb) EP2_OUTLAND_10
  2002. // m_iNumGrenades--;
  2003. }
  2004. handledEvent = true;
  2005. }
  2006. else
  2007. {
  2008. BaseClass::HandleAnimEvent( pEvent );
  2009. }
  2010. }
  2011. else
  2012. {
  2013. switch( pEvent->event )
  2014. {
  2015. case COMBINE_AE_AIM:
  2016. {
  2017. handledEvent = true;
  2018. break;
  2019. }
  2020. case COMBINE_AE_RELOAD:
  2021. // We never actually run out of ammo, just need to refill the clip
  2022. if (GetActiveWeapon())
  2023. {
  2024. GetActiveWeapon()->WeaponSound( RELOAD_NPC );
  2025. GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1();
  2026. GetActiveWeapon()->m_iClip2 = GetActiveWeapon()->GetMaxClip2();
  2027. }
  2028. ClearCondition(COND_LOW_PRIMARY_AMMO);
  2029. ClearCondition(COND_NO_PRIMARY_AMMO);
  2030. ClearCondition(COND_NO_SECONDARY_AMMO);
  2031. handledEvent = true;
  2032. break;
  2033. case COMBINE_AE_GREN_TOSS:
  2034. {
  2035. Vector vecSpin;
  2036. vecSpin.x = random->RandomFloat( -1000.0, 1000.0 );
  2037. vecSpin.y = random->RandomFloat( -1000.0, 1000.0 );
  2038. vecSpin.z = random->RandomFloat( -1000.0, 1000.0 );
  2039. Vector vecStart;
  2040. GetAttachment( "lefthand", vecStart );
  2041. if( m_NPCState == NPC_STATE_SCRIPT )
  2042. {
  2043. // Use a fixed velocity for grenades thrown in scripted state.
  2044. // Grenades thrown from a script do not count against grenades remaining for the AI to use.
  2045. Vector forward, up, vecThrow;
  2046. GetVectors( &forward, NULL, &up );
  2047. vecThrow = forward * 750 + up * 175;
  2048. Fraggrenade_Create( vecStart, vec3_angle, vecThrow, vecSpin, this, COMBINE_GRENADE_TIMER, true );
  2049. }
  2050. else
  2051. {
  2052. // Use the Velocity that AI gave us.
  2053. Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vecSpin, this, COMBINE_GRENADE_TIMER, true );
  2054. m_iNumGrenades--;
  2055. }
  2056. // wait six seconds before even looking again to see if a grenade can be thrown.
  2057. m_flNextGrenadeCheck = gpGlobals->curtime + 6;
  2058. }
  2059. handledEvent = true;
  2060. break;
  2061. case COMBINE_AE_GREN_LAUNCH:
  2062. {
  2063. EmitSound( "NPC_Combine.GrenadeLaunch" );
  2064. CBaseEntity *pGrenade = CreateNoSpawn( "npc_contactgrenade", Weapon_ShootPosition(), vec3_angle, this );
  2065. pGrenade->KeyValue( "velocity", m_vecTossVelocity );
  2066. pGrenade->Spawn( );
  2067. if ( g_pGameRules->IsSkillLevel(SKILL_HARD) )
  2068. m_flNextGrenadeCheck = gpGlobals->curtime + random->RandomFloat( 2, 5 );// wait a random amount of time before shooting again
  2069. else
  2070. m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
  2071. }
  2072. handledEvent = true;
  2073. break;
  2074. case COMBINE_AE_GREN_DROP:
  2075. {
  2076. Vector vecStart;
  2077. GetAttachment( "lefthand", vecStart );
  2078. Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vec3_origin, this, COMBINE_GRENADE_TIMER, true );
  2079. m_iNumGrenades--;
  2080. }
  2081. handledEvent = true;
  2082. break;
  2083. case COMBINE_AE_KICK:
  2084. {
  2085. // Does no damage, because damage is applied based upon whether the target can handle the interaction
  2086. CBaseEntity *pHurt = CheckTraceHullAttack( 70, -Vector(16,16,18), Vector(16,16,18), 0, DMG_CLUB );
  2087. CBaseCombatCharacter* pBCC = ToBaseCombatCharacter( pHurt );
  2088. if (pBCC)
  2089. {
  2090. Vector forward, up;
  2091. AngleVectors( GetLocalAngles(), &forward, NULL, &up );
  2092. if ( !pBCC->DispatchInteraction( g_interactionCombineBash, NULL, this ) )
  2093. {
  2094. if ( pBCC->IsPlayer() )
  2095. {
  2096. pBCC->ViewPunch( QAngle(-12,-7,0) );
  2097. pHurt->ApplyAbsVelocityImpulse( forward * 100 + up * 50 );
  2098. }
  2099. CTakeDamageInfo info( this, this, m_nKickDamage, DMG_CLUB );
  2100. CalculateMeleeDamageForce( &info, forward, pBCC->GetAbsOrigin() );
  2101. pBCC->TakeDamage( info );
  2102. EmitSound( "NPC_Combine.WeaponBash" );
  2103. }
  2104. }
  2105. m_Sentences.Speak( "COMBINE_KICK" );
  2106. handledEvent = true;
  2107. break;
  2108. }
  2109. case COMBINE_AE_CAUGHT_ENEMY:
  2110. m_Sentences.Speak( "COMBINE_ALERT" );
  2111. handledEvent = true;
  2112. break;
  2113. default:
  2114. BaseClass::HandleAnimEvent( pEvent );
  2115. break;
  2116. }
  2117. }
  2118. if( handledEvent )
  2119. {
  2120. m_iLastAnimEventHandled = pEvent->event;
  2121. }
  2122. }
  2123. //-----------------------------------------------------------------------------
  2124. // Purpose: Get shoot position of BCC at an arbitrary position
  2125. // Input :
  2126. // Output :
  2127. //-----------------------------------------------------------------------------
  2128. Vector CNPC_Combine::Weapon_ShootPosition( )
  2129. {
  2130. bool bStanding = !IsCrouching();
  2131. Vector right;
  2132. GetVectors( NULL, &right, NULL );
  2133. if ((CapabilitiesGet() & bits_CAP_DUCK) )
  2134. {
  2135. if ( IsCrouchedActivity( GetActivity() ) )
  2136. {
  2137. bStanding = false;
  2138. }
  2139. }
  2140. // FIXME: rename this "estimated" since it's not based on animation
  2141. // FIXME: the orientation won't be correct when testing from arbitary positions for arbitary angles
  2142. if ( bStanding )
  2143. {
  2144. if( HasShotgun() )
  2145. {
  2146. return GetAbsOrigin() + COMBINE_SHOTGUN_STANDING_POSITION + right * 8;
  2147. }
  2148. else
  2149. {
  2150. return GetAbsOrigin() + COMBINE_GUN_STANDING_POSITION + right * 8;
  2151. }
  2152. }
  2153. else
  2154. {
  2155. if( HasShotgun() )
  2156. {
  2157. return GetAbsOrigin() + COMBINE_SHOTGUN_CROUCHING_POSITION + right * 8;
  2158. }
  2159. else
  2160. {
  2161. return GetAbsOrigin() + COMBINE_GUN_CROUCHING_POSITION + right * 8;
  2162. }
  2163. }
  2164. }
  2165. //=========================================================
  2166. // Speak Sentence - say your cued up sentence.
  2167. //
  2168. // Some grunt sentences (take cover and charge) rely on actually
  2169. // being able to execute the intended action. It's really lame
  2170. // when a grunt says 'COVER ME' and then doesn't move. The problem
  2171. // is that the sentences were played when the decision to TRY
  2172. // to move to cover was made. Now the sentence is played after
  2173. // we know for sure that there is a valid path. The schedule
  2174. // may still fail but in most cases, well after the grunt has
  2175. // started moving.
  2176. //=========================================================
  2177. void CNPC_Combine::SpeakSentence( int sentenceType )
  2178. {
  2179. switch( sentenceType )
  2180. {
  2181. case 0: // assault
  2182. AnnounceAssault();
  2183. break;
  2184. case 1: // Flanking the player
  2185. // If I'm moving more than 20ft, I need to talk about it
  2186. if ( GetNavigator()->GetPath()->GetPathLength() > 20 * 12.0f )
  2187. {
  2188. m_Sentences.Speak( "COMBINE_FLANK" );
  2189. }
  2190. break;
  2191. }
  2192. }
  2193. //=========================================================
  2194. // PainSound
  2195. //=========================================================
  2196. void CNPC_Combine::PainSound ( void )
  2197. {
  2198. // NOTE: The response system deals with this at the moment
  2199. if ( GetFlags() & FL_DISSOLVING )
  2200. return;
  2201. if ( gpGlobals->curtime > m_flNextPainSoundTime )
  2202. {
  2203. const char *pSentenceName = "COMBINE_PAIN";
  2204. float healthRatio = (float)GetHealth() / (float)GetMaxHealth();
  2205. if ( !HasMemory(bits_MEMORY_PAIN_LIGHT_SOUND) && healthRatio > 0.9 )
  2206. {
  2207. Remember( bits_MEMORY_PAIN_LIGHT_SOUND );
  2208. pSentenceName = "COMBINE_TAUNT";
  2209. }
  2210. else if ( !HasMemory(bits_MEMORY_PAIN_HEAVY_SOUND) && healthRatio < 0.25 )
  2211. {
  2212. Remember( bits_MEMORY_PAIN_HEAVY_SOUND );
  2213. pSentenceName = "COMBINE_COVER";
  2214. }
  2215. m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
  2216. m_flNextPainSoundTime = gpGlobals->curtime + 1;
  2217. }
  2218. }
  2219. //-----------------------------------------------------------------------------
  2220. // Purpose: implemented by subclasses to give them an opportunity to make
  2221. // a sound when they lose their enemy
  2222. // Input :
  2223. // Output :
  2224. //-----------------------------------------------------------------------------
  2225. void CNPC_Combine::LostEnemySound( void)
  2226. {
  2227. if ( gpGlobals->curtime <= m_flNextLostSoundTime )
  2228. return;
  2229. const char *pSentence;
  2230. if (!(CBaseEntity*)GetEnemy() || gpGlobals->curtime - GetEnemyLastTimeSeen() > 10)
  2231. {
  2232. pSentence = "COMBINE_LOST_LONG";
  2233. }
  2234. else
  2235. {
  2236. pSentence = "COMBINE_LOST_SHORT";
  2237. }
  2238. if ( m_Sentences.Speak( pSentence ) >= 0 )
  2239. {
  2240. m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0);
  2241. }
  2242. }
  2243. //-----------------------------------------------------------------------------
  2244. // Purpose: implemented by subclasses to give them an opportunity to make
  2245. // a sound when they lose their enemy
  2246. // Input :
  2247. // Output :
  2248. //-----------------------------------------------------------------------------
  2249. void CNPC_Combine::FoundEnemySound( void)
  2250. {
  2251. m_Sentences.Speak( "COMBINE_REFIND_ENEMY", SENTENCE_PRIORITY_HIGH );
  2252. }
  2253. //-----------------------------------------------------------------------------
  2254. // Purpose: Implemented by subclasses to give them an opportunity to make
  2255. // a sound before they attack
  2256. // Input :
  2257. // Output :
  2258. //-----------------------------------------------------------------------------
  2259. // BUGBUG: It looks like this is never played because combine don't do SCHED_WAKE_ANGRY or anything else that does a TASK_SOUND_WAKE
  2260. void CNPC_Combine::AlertSound( void)
  2261. {
  2262. if ( gpGlobals->curtime > m_flNextAlertSoundTime )
  2263. {
  2264. m_Sentences.Speak( "COMBINE_GO_ALERT", SENTENCE_PRIORITY_HIGH );
  2265. m_flNextAlertSoundTime = gpGlobals->curtime + 10.0f;
  2266. }
  2267. }
  2268. //=========================================================
  2269. // NotifyDeadFriend
  2270. //=========================================================
  2271. void CNPC_Combine::NotifyDeadFriend ( CBaseEntity* pFriend )
  2272. {
  2273. if ( GetSquad()->NumMembers() < 2 )
  2274. {
  2275. m_Sentences.Speak( "COMBINE_LAST_OF_SQUAD", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_NORMAL );
  2276. JustMadeSound();
  2277. return;
  2278. }
  2279. // relaxed visibility test so that guys say this more often
  2280. //if( FInViewCone( pFriend ) && FVisible( pFriend ) )
  2281. {
  2282. m_Sentences.Speak( "COMBINE_MAN_DOWN" );
  2283. }
  2284. BaseClass::NotifyDeadFriend(pFriend);
  2285. }
  2286. //=========================================================
  2287. // DeathSound
  2288. //=========================================================
  2289. void CNPC_Combine::DeathSound ( void )
  2290. {
  2291. // NOTE: The response system deals with this at the moment
  2292. if ( GetFlags() & FL_DISSOLVING )
  2293. return;
  2294. m_Sentences.Speak( "COMBINE_DIE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
  2295. }
  2296. //=========================================================
  2297. // IdleSound
  2298. //=========================================================
  2299. void CNPC_Combine::IdleSound( void )
  2300. {
  2301. if (g_fCombineQuestion || random->RandomInt(0,1))
  2302. {
  2303. if (!g_fCombineQuestion)
  2304. {
  2305. // ask question or make statement
  2306. switch (random->RandomInt(0,2))
  2307. {
  2308. case 0: // check in
  2309. if ( m_Sentences.Speak( "COMBINE_CHECK" ) >= 0 )
  2310. {
  2311. g_fCombineQuestion = 1;
  2312. }
  2313. break;
  2314. case 1: // question
  2315. if ( m_Sentences.Speak( "COMBINE_QUEST" ) >= 0 )
  2316. {
  2317. g_fCombineQuestion = 2;
  2318. }
  2319. break;
  2320. case 2: // statement
  2321. m_Sentences.Speak( "COMBINE_IDLE" );
  2322. break;
  2323. }
  2324. }
  2325. else
  2326. {
  2327. switch (g_fCombineQuestion)
  2328. {
  2329. case 1: // check in
  2330. if ( m_Sentences.Speak( "COMBINE_CLEAR" ) >= 0 )
  2331. {
  2332. g_fCombineQuestion = 0;
  2333. }
  2334. break;
  2335. case 2: // question
  2336. if ( m_Sentences.Speak( "COMBINE_ANSWER" ) >= 0 )
  2337. {
  2338. g_fCombineQuestion = 0;
  2339. }
  2340. break;
  2341. }
  2342. }
  2343. }
  2344. }
  2345. //-----------------------------------------------------------------------------
  2346. // Purpose:
  2347. //
  2348. // This is for Grenade attacks. As the test for grenade attacks
  2349. // is expensive we don't want to do it every frame. Return true
  2350. // if we meet minimum set of requirements and then test for actual
  2351. // throw later if we actually decide to do a grenade attack.
  2352. // Input :
  2353. // Output :
  2354. //-----------------------------------------------------------------------------
  2355. int CNPC_Combine::RangeAttack2Conditions( float flDot, float flDist )
  2356. {
  2357. return COND_NONE;
  2358. }
  2359. //-----------------------------------------------------------------------------
  2360. // Purpose: Return true if the combine has grenades, hasn't checked lately, and
  2361. // can throw a grenade at the target point.
  2362. // Input : &vecTarget -
  2363. // Output : Returns true on success, false on failure.
  2364. //-----------------------------------------------------------------------------
  2365. bool CNPC_Combine::CanThrowGrenade( const Vector &vecTarget )
  2366. {
  2367. if( m_iNumGrenades < 1 )
  2368. {
  2369. // Out of grenades!
  2370. return false;
  2371. }
  2372. if (gpGlobals->curtime < m_flNextGrenadeCheck )
  2373. {
  2374. // Not allowed to throw another grenade right now.
  2375. return false;
  2376. }
  2377. float flDist;
  2378. flDist = ( vecTarget - GetAbsOrigin() ).Length();
  2379. if( flDist > 1024 || flDist < 128 )
  2380. {
  2381. // Too close or too far!
  2382. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  2383. return false;
  2384. }
  2385. // -----------------------
  2386. // If moving, don't check.
  2387. // -----------------------
  2388. if ( m_flGroundSpeed != 0 )
  2389. return false;
  2390. #if 0
  2391. Vector vecEnemyLKP = GetEnemyLKP();
  2392. if ( !( GetEnemy()->GetFlags() & FL_ONGROUND ) && GetEnemy()->GetWaterLevel() == 0 && vecEnemyLKP.z > (GetAbsOrigin().z + WorldAlignMaxs().z) )
  2393. {
  2394. //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to
  2395. // be grenaded.
  2396. // don't throw grenades at anything that isn't on the ground!
  2397. return COND_NONE;
  2398. }
  2399. #endif
  2400. // ---------------------------------------------------------------------
  2401. // Are any of my squad members near the intended grenade impact area?
  2402. // ---------------------------------------------------------------------
  2403. if ( m_pSquad )
  2404. {
  2405. if (m_pSquad->SquadMemberInRange( vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST ))
  2406. {
  2407. // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while.
  2408. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  2409. // Tell my squad members to clear out so I can get a grenade in
  2410. CSoundEnt::InsertSound( SOUND_MOVE_AWAY | SOUND_CONTEXT_COMBINE_ONLY, vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST, 0.1 );
  2411. return false;
  2412. }
  2413. }
  2414. return CheckCanThrowGrenade( vecTarget );
  2415. }
  2416. //-----------------------------------------------------------------------------
  2417. // Purpose: Returns true if the combine can throw a grenade at the specified target point
  2418. // Input : &vecTarget -
  2419. // Output : Returns true on success, false on failure.
  2420. //-----------------------------------------------------------------------------
  2421. bool CNPC_Combine::CheckCanThrowGrenade( const Vector &vecTarget )
  2422. {
  2423. //NDebugOverlay::Line( EyePosition(), vecTarget, 0, 255, 0, false, 5 );
  2424. // ---------------------------------------------------------------------
  2425. // Check that throw is legal and clear
  2426. // ---------------------------------------------------------------------
  2427. // FIXME: this is only valid for hand grenades, not RPG's
  2428. Vector vecToss;
  2429. Vector vecMins = -Vector(4,4,4);
  2430. Vector vecMaxs = Vector(4,4,4);
  2431. if( FInViewCone( vecTarget ) && CBaseEntity::FVisible( vecTarget ) )
  2432. {
  2433. vecToss = VecCheckThrow( this, EyePosition(), vecTarget, COMBINE_GRENADE_THROW_SPEED, 1.0, &vecMins, &vecMaxs );
  2434. }
  2435. else
  2436. {
  2437. // Have to try a high toss. Do I have enough room?
  2438. trace_t tr;
  2439. AI_TraceLine( EyePosition(), EyePosition() + Vector( 0, 0, 64 ), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  2440. if( tr.fraction != 1.0 )
  2441. {
  2442. return false;
  2443. }
  2444. vecToss = VecCheckToss( this, EyePosition(), vecTarget, -1, 1.0, true, &vecMins, &vecMaxs );
  2445. }
  2446. if ( vecToss != vec3_origin )
  2447. {
  2448. m_vecTossVelocity = vecToss;
  2449. // don't check again for a while.
  2450. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // 1/3 second.
  2451. return true;
  2452. }
  2453. else
  2454. {
  2455. // don't check again for a while.
  2456. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
  2457. return false;
  2458. }
  2459. }
  2460. //-----------------------------------------------------------------------------
  2461. //-----------------------------------------------------------------------------
  2462. bool CNPC_Combine::CanAltFireEnemy( bool bUseFreeKnowledge )
  2463. {
  2464. if (!IsElite() )
  2465. return false;
  2466. if (IsCrouching())
  2467. return false;
  2468. if( gpGlobals->curtime < m_flNextAltFireTime )
  2469. return false;
  2470. if( !GetEnemy() )
  2471. return false;
  2472. if (gpGlobals->curtime < m_flNextGrenadeCheck )
  2473. return false;
  2474. // See Steve Bond if you plan on changing this next piece of code!! (SJB) EP2_OUTLAND_10
  2475. if (m_iNumGrenades < 1)
  2476. return false;
  2477. CBaseEntity *pEnemy = GetEnemy();
  2478. if( !pEnemy->IsPlayer() && (!pEnemy->IsNPC() || !pEnemy->MyNPCPointer()->IsPlayerAlly()) )
  2479. return false;
  2480. Vector vecTarget;
  2481. // Determine what point we're shooting at
  2482. if( bUseFreeKnowledge )
  2483. {
  2484. vecTarget = GetEnemies()->LastKnownPosition( pEnemy ) + (pEnemy->GetViewOffset()*0.75);// approximates the chest
  2485. }
  2486. else
  2487. {
  2488. vecTarget = GetEnemies()->LastSeenPosition( pEnemy ) + (pEnemy->GetViewOffset()*0.75);// approximates the chest
  2489. }
  2490. // Trace a hull about the size of the combine ball (don't shoot through grates!)
  2491. trace_t tr;
  2492. Vector mins( -12, -12, -12 );
  2493. Vector maxs( 12, 12, 12 );
  2494. Vector vShootPosition = EyePosition();
  2495. if ( GetActiveWeapon() )
  2496. {
  2497. GetActiveWeapon()->GetAttachment( "muzzle", vShootPosition );
  2498. }
  2499. // Trace a hull about the size of the combine ball.
  2500. UTIL_TraceHull( vShootPosition, vecTarget, mins, maxs, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  2501. float flLength = (vShootPosition - vecTarget).Length();
  2502. flLength *= tr.fraction;
  2503. //If the ball can travel at least 65% of the distance to the player then let the NPC shoot it.
  2504. if( tr.fraction >= 0.65 && flLength > 128.0f )
  2505. {
  2506. // Target is valid
  2507. m_vecAltFireTarget = vecTarget;
  2508. return true;
  2509. }
  2510. // Check again later
  2511. m_vecAltFireTarget = vec3_origin;
  2512. m_flNextGrenadeCheck = gpGlobals->curtime + 1.0f;
  2513. return false;
  2514. }
  2515. //-----------------------------------------------------------------------------
  2516. //-----------------------------------------------------------------------------
  2517. bool CNPC_Combine::CanGrenadeEnemy( bool bUseFreeKnowledge )
  2518. {
  2519. if( IsElite() )
  2520. return false;
  2521. CBaseEntity *pEnemy = GetEnemy();
  2522. Assert( pEnemy != NULL );
  2523. if( pEnemy )
  2524. {
  2525. // I'm not allowed to throw grenades during dustoff
  2526. if ( IsCurSchedule(SCHED_DROPSHIP_DUSTOFF) )
  2527. return false;
  2528. if( bUseFreeKnowledge )
  2529. {
  2530. // throw to where we think they are.
  2531. return CanThrowGrenade( GetEnemies()->LastKnownPosition( pEnemy ) );
  2532. }
  2533. else
  2534. {
  2535. // hafta throw to where we last saw them.
  2536. return CanThrowGrenade( GetEnemies()->LastSeenPosition( pEnemy ) );
  2537. }
  2538. }
  2539. return false;
  2540. }
  2541. //-----------------------------------------------------------------------------
  2542. // Purpose: For combine melee attack (kick/hit)
  2543. // Input :
  2544. // Output :
  2545. //-----------------------------------------------------------------------------
  2546. int CNPC_Combine::MeleeAttack1Conditions ( float flDot, float flDist )
  2547. {
  2548. if (flDist > 64)
  2549. {
  2550. return COND_NONE; // COND_TOO_FAR_TO_ATTACK;
  2551. }
  2552. else if (flDot < 0.7)
  2553. {
  2554. return COND_NONE; // COND_NOT_FACING_ATTACK;
  2555. }
  2556. // Check Z
  2557. if ( GetEnemy() && fabs(GetEnemy()->GetAbsOrigin().z - GetAbsOrigin().z) > 64 )
  2558. return COND_NONE;
  2559. if ( dynamic_cast<CBaseHeadcrab *>(GetEnemy()) != NULL )
  2560. {
  2561. return COND_NONE;
  2562. }
  2563. // Make sure not trying to kick through a window or something.
  2564. trace_t tr;
  2565. Vector vecSrc, vecEnd;
  2566. vecSrc = WorldSpaceCenter();
  2567. vecEnd = GetEnemy()->WorldSpaceCenter();
  2568. AI_TraceLine(vecSrc, vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr);
  2569. if( tr.m_pEnt != GetEnemy() )
  2570. {
  2571. return COND_NONE;
  2572. }
  2573. return COND_CAN_MELEE_ATTACK1;
  2574. }
  2575. //-----------------------------------------------------------------------------
  2576. // Purpose:
  2577. // Output : Vector
  2578. //-----------------------------------------------------------------------------
  2579. Vector CNPC_Combine::EyePosition( void )
  2580. {
  2581. if ( !IsCrouching() )
  2582. {
  2583. return GetAbsOrigin() + COMBINE_EYE_STANDING_POSITION;
  2584. }
  2585. else
  2586. {
  2587. return GetAbsOrigin() + COMBINE_EYE_CROUCHING_POSITION;
  2588. }
  2589. /*
  2590. Vector m_EyePos;
  2591. GetAttachment( "eyes", m_EyePos );
  2592. return m_EyePos;
  2593. */
  2594. }
  2595. //-----------------------------------------------------------------------------
  2596. //-----------------------------------------------------------------------------
  2597. Vector CNPC_Combine::GetAltFireTarget()
  2598. {
  2599. Assert( IsElite() );
  2600. return m_vecAltFireTarget;
  2601. }
  2602. //-----------------------------------------------------------------------------
  2603. // Purpose:
  2604. // Input : nActivity -
  2605. // Output : Vector
  2606. //-----------------------------------------------------------------------------
  2607. Vector CNPC_Combine::EyeOffset( Activity nActivity )
  2608. {
  2609. if (CapabilitiesGet() & bits_CAP_DUCK)
  2610. {
  2611. if ( IsCrouchedActivity( nActivity ) )
  2612. return COMBINE_EYE_CROUCHING_POSITION;
  2613. }
  2614. // if the hint doesn't tell anything, assume current state
  2615. if ( !IsCrouching() )
  2616. {
  2617. return COMBINE_EYE_STANDING_POSITION;
  2618. }
  2619. else
  2620. {
  2621. return COMBINE_EYE_CROUCHING_POSITION;
  2622. }
  2623. }
  2624. //-----------------------------------------------------------------------------
  2625. // Purpose:
  2626. //-----------------------------------------------------------------------------
  2627. Vector CNPC_Combine::GetCrouchEyeOffset( void )
  2628. {
  2629. return COMBINE_EYE_CROUCHING_POSITION;
  2630. }
  2631. //-----------------------------------------------------------------------------
  2632. //-----------------------------------------------------------------------------
  2633. void CNPC_Combine::SetActivity( Activity NewActivity )
  2634. {
  2635. BaseClass::SetActivity( NewActivity );
  2636. m_iLastAnimEventHandled = -1;
  2637. }
  2638. //-----------------------------------------------------------------------------
  2639. //-----------------------------------------------------------------------------
  2640. NPC_STATE CNPC_Combine::SelectIdealState( void )
  2641. {
  2642. switch ( m_NPCState )
  2643. {
  2644. case NPC_STATE_COMBAT:
  2645. {
  2646. if ( GetEnemy() == NULL )
  2647. {
  2648. if ( !HasCondition( COND_ENEMY_DEAD ) )
  2649. {
  2650. // Lost track of my enemy. Patrol.
  2651. SetCondition( COND_COMBINE_SHOULD_PATROL );
  2652. }
  2653. return NPC_STATE_ALERT;
  2654. }
  2655. else if ( HasCondition( COND_ENEMY_DEAD ) )
  2656. {
  2657. AnnounceEnemyKill(GetEnemy());
  2658. }
  2659. }
  2660. default:
  2661. {
  2662. return BaseClass::SelectIdealState();
  2663. }
  2664. }
  2665. return GetIdealState();
  2666. }
  2667. //-----------------------------------------------------------------------------
  2668. //-----------------------------------------------------------------------------
  2669. bool CNPC_Combine::OnBeginMoveAndShoot()
  2670. {
  2671. if ( BaseClass::OnBeginMoveAndShoot() )
  2672. {
  2673. if( HasStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  2674. return true; // already have the slot I need
  2675. if( !HasStrategySlotRange( SQUAD_SLOT_GRENADE1, SQUAD_SLOT_ATTACK_OCCLUDER ) && OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  2676. return true;
  2677. }
  2678. return false;
  2679. }
  2680. //-----------------------------------------------------------------------------
  2681. //-----------------------------------------------------------------------------
  2682. void CNPC_Combine::OnEndMoveAndShoot()
  2683. {
  2684. VacateStrategySlot();
  2685. }
  2686. //-----------------------------------------------------------------------------
  2687. //-----------------------------------------------------------------------------
  2688. WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon )
  2689. {
  2690. if( FClassnameIs( pWeapon, "weapon_ar2" ) )
  2691. {
  2692. if( hl2_episodic.GetBool() )
  2693. {
  2694. return WEAPON_PROFICIENCY_VERY_GOOD;
  2695. }
  2696. else
  2697. {
  2698. return WEAPON_PROFICIENCY_GOOD;
  2699. }
  2700. }
  2701. else if( FClassnameIs( pWeapon, "weapon_shotgun" ) )
  2702. {
  2703. if( m_nSkin != COMBINE_SKIN_SHOTGUNNER )
  2704. {
  2705. m_nSkin = COMBINE_SKIN_SHOTGUNNER;
  2706. }
  2707. return WEAPON_PROFICIENCY_PERFECT;
  2708. }
  2709. else if( FClassnameIs( pWeapon, "weapon_smg1" ) )
  2710. {
  2711. return WEAPON_PROFICIENCY_GOOD;
  2712. }
  2713. return BaseClass::CalcWeaponProficiency( pWeapon );
  2714. }
  2715. //-----------------------------------------------------------------------------
  2716. //-----------------------------------------------------------------------------
  2717. bool CNPC_Combine::HasShotgun()
  2718. {
  2719. if( GetActiveWeapon() && GetActiveWeapon()->m_iClassname == s_iszShotgunClassname )
  2720. {
  2721. return true;
  2722. }
  2723. return false;
  2724. }
  2725. //-----------------------------------------------------------------------------
  2726. // Only supports weapons that use clips.
  2727. //-----------------------------------------------------------------------------
  2728. bool CNPC_Combine::ActiveWeaponIsFullyLoaded()
  2729. {
  2730. CBaseCombatWeapon *pWeapon = GetActiveWeapon();
  2731. if( !pWeapon )
  2732. return false;
  2733. if( !pWeapon->UsesClipsForAmmo1() )
  2734. return false;
  2735. return ( pWeapon->Clip1() >= pWeapon->GetMaxClip1() );
  2736. }
  2737. //-----------------------------------------------------------------------------
  2738. // Purpose: This is a generic function (to be implemented by sub-classes) to
  2739. // handle specific interactions between different types of characters
  2740. // (For example the barnacle grabbing an NPC)
  2741. // Input : The type of interaction, extra info pointer, and who started it
  2742. // Output : true - if sub-class has a response for the interaction
  2743. // false - if sub-class has no response
  2744. //-----------------------------------------------------------------------------
  2745. bool CNPC_Combine::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter *sourceEnt)
  2746. {
  2747. if ( interactionType == g_interactionTurretStillStanding )
  2748. {
  2749. // A turret that I've kicked recently is still standing 5 seconds later.
  2750. if ( sourceEnt == GetEnemy() )
  2751. {
  2752. // It's still my enemy. Time to grenade it.
  2753. Vector forward, up;
  2754. AngleVectors( GetLocalAngles(), &forward, NULL, &up );
  2755. m_vecTossVelocity = forward * 10;
  2756. SetCondition( COND_COMBINE_DROP_GRENADE );
  2757. ClearSchedule( "Failed to kick over turret" );
  2758. }
  2759. return true;
  2760. }
  2761. return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
  2762. }
  2763. //-----------------------------------------------------------------------------
  2764. //
  2765. //-----------------------------------------------------------------------------
  2766. const char* CNPC_Combine::GetSquadSlotDebugName( int iSquadSlot )
  2767. {
  2768. switch( iSquadSlot )
  2769. {
  2770. case SQUAD_SLOT_GRENADE1: return "SQUAD_SLOT_GRENADE1";
  2771. break;
  2772. case SQUAD_SLOT_GRENADE2: return "SQUAD_SLOT_GRENADE2";
  2773. break;
  2774. case SQUAD_SLOT_ATTACK_OCCLUDER: return "SQUAD_SLOT_ATTACK_OCCLUDER";
  2775. break;
  2776. case SQUAD_SLOT_OVERWATCH: return "SQUAD_SLOT_OVERWATCH";
  2777. break;
  2778. }
  2779. return BaseClass::GetSquadSlotDebugName( iSquadSlot );
  2780. }
  2781. //-----------------------------------------------------------------------------
  2782. //-----------------------------------------------------------------------------
  2783. bool CNPC_Combine::IsUsingTacticalVariant( int variant )
  2784. {
  2785. if( variant == TACTICAL_VARIANT_PRESSURE_ENEMY && m_iTacticalVariant == TACTICAL_VARIANT_PRESSURE_ENEMY_UNTIL_CLOSE )
  2786. {
  2787. // Essentially, fib. Just say that we are a 'pressure enemy' soldier.
  2788. return true;
  2789. }
  2790. return m_iTacticalVariant == variant;
  2791. }
  2792. //-----------------------------------------------------------------------------
  2793. // For the purpose of determining whether to use a pathfinding variant, this
  2794. // function determines whether the current schedule is a schedule that
  2795. // 'approaches' the enemy.
  2796. //-----------------------------------------------------------------------------
  2797. bool CNPC_Combine::IsRunningApproachEnemySchedule()
  2798. {
  2799. if( IsCurSchedule( SCHED_CHASE_ENEMY ) )
  2800. return true;
  2801. if( IsCurSchedule( SCHED_ESTABLISH_LINE_OF_FIRE ) )
  2802. return true;
  2803. if( IsCurSchedule( SCHED_COMBINE_PRESS_ATTACK, false ) )
  2804. return true;
  2805. return false;
  2806. }
  2807. bool CNPC_Combine::ShouldPickADeathPose( void )
  2808. {
  2809. return !IsCrouching();
  2810. }
  2811. //-----------------------------------------------------------------------------
  2812. //
  2813. // Schedules
  2814. //
  2815. //-----------------------------------------------------------------------------
  2816. AI_BEGIN_CUSTOM_NPC( npc_combine, CNPC_Combine )
  2817. //Tasks
  2818. DECLARE_TASK( TASK_COMBINE_FACE_TOSS_DIR )
  2819. DECLARE_TASK( TASK_COMBINE_IGNORE_ATTACKS )
  2820. DECLARE_TASK( TASK_COMBINE_SIGNAL_BEST_SOUND )
  2821. DECLARE_TASK( TASK_COMBINE_DEFER_SQUAD_GRENADES )
  2822. DECLARE_TASK( TASK_COMBINE_CHASE_ENEMY_CONTINUOUSLY )
  2823. DECLARE_TASK( TASK_COMBINE_DIE_INSTANTLY )
  2824. DECLARE_TASK( TASK_COMBINE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET )
  2825. DECLARE_TASK( TASK_COMBINE_GET_PATH_TO_FORCED_GREN_LOS )
  2826. DECLARE_TASK( TASK_COMBINE_SET_STANDING )
  2827. //Activities
  2828. DECLARE_ACTIVITY( ACT_COMBINE_THROW_GRENADE )
  2829. DECLARE_ACTIVITY( ACT_COMBINE_LAUNCH_GRENADE )
  2830. DECLARE_ACTIVITY( ACT_COMBINE_BUGBAIT )
  2831. DECLARE_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE )
  2832. DECLARE_ACTIVITY( ACT_WALK_EASY )
  2833. DECLARE_ACTIVITY( ACT_WALK_MARCH )
  2834. DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE )
  2835. DECLARE_ANIMEVENT( COMBINE_AE_ALTFIRE )
  2836. DECLARE_SQUADSLOT( SQUAD_SLOT_GRENADE1 )
  2837. DECLARE_SQUADSLOT( SQUAD_SLOT_GRENADE2 )
  2838. DECLARE_CONDITION( COND_COMBINE_NO_FIRE )
  2839. DECLARE_CONDITION( COND_COMBINE_DEAD_FRIEND )
  2840. DECLARE_CONDITION( COND_COMBINE_SHOULD_PATROL )
  2841. DECLARE_CONDITION( COND_COMBINE_HIT_BY_BUGBAIT )
  2842. DECLARE_CONDITION( COND_COMBINE_DROP_GRENADE )
  2843. DECLARE_CONDITION( COND_COMBINE_ON_FIRE )
  2844. DECLARE_CONDITION( COND_COMBINE_ATTACK_SLOT_AVAILABLE )
  2845. DECLARE_INTERACTION( g_interactionCombineBash );
  2846. //=========================================================
  2847. // SCHED_COMBINE_TAKE_COVER_FROM_BEST_SOUND
  2848. //
  2849. // hide from the loudest sound source (to run from grenade)
  2850. //=========================================================
  2851. DEFINE_SCHEDULE
  2852. (
  2853. SCHED_COMBINE_TAKE_COVER_FROM_BEST_SOUND,
  2854. " Tasks"
  2855. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBINE_RUN_AWAY_FROM_BEST_SOUND"
  2856. " TASK_STOP_MOVING 0"
  2857. " TASK_COMBINE_SIGNAL_BEST_SOUND 0"
  2858. " TASK_FIND_COVER_FROM_BEST_SOUND 0"
  2859. " TASK_RUN_PATH 0"
  2860. " TASK_WAIT_FOR_MOVEMENT 0"
  2861. " TASK_REMEMBER MEMORY:INCOVER"
  2862. " TASK_FACE_REASONABLE 0"
  2863. ""
  2864. " Interrupts"
  2865. )
  2866. DEFINE_SCHEDULE
  2867. (
  2868. SCHED_COMBINE_RUN_AWAY_FROM_BEST_SOUND,
  2869. " Tasks"
  2870. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COWER"
  2871. " TASK_GET_PATH_AWAY_FROM_BEST_SOUND 600"
  2872. " TASK_RUN_PATH_TIMED 2"
  2873. " TASK_STOP_MOVING 0"
  2874. ""
  2875. " Interrupts"
  2876. )
  2877. //=========================================================
  2878. // SCHED_COMBINE_COMBAT_FAIL
  2879. //=========================================================
  2880. DEFINE_SCHEDULE
  2881. (
  2882. SCHED_COMBINE_COMBAT_FAIL,
  2883. " Tasks"
  2884. " TASK_STOP_MOVING 0"
  2885. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE "
  2886. " TASK_WAIT_FACE_ENEMY 2"
  2887. " TASK_WAIT_PVS 0"
  2888. ""
  2889. " Interrupts"
  2890. " COND_CAN_RANGE_ATTACK1"
  2891. " COND_CAN_RANGE_ATTACK2"
  2892. " COND_CAN_MELEE_ATTACK1"
  2893. " COND_CAN_MELEE_ATTACK2"
  2894. )
  2895. //=========================================================
  2896. // SCHED_COMBINE_VICTORY_DANCE
  2897. //=========================================================
  2898. DEFINE_SCHEDULE
  2899. (
  2900. SCHED_COMBINE_VICTORY_DANCE,
  2901. " Tasks"
  2902. " TASK_STOP_MOVING 0"
  2903. " TASK_FACE_ENEMY 0"
  2904. " TASK_WAIT 1.5"
  2905. " TASK_GET_PATH_TO_ENEMY_CORPSE 0"
  2906. " TASK_WALK_PATH 0"
  2907. " TASK_WAIT_FOR_MOVEMENT 0"
  2908. " TASK_FACE_ENEMY 0"
  2909. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_VICTORY_DANCE"
  2910. ""
  2911. " Interrupts"
  2912. " COND_NEW_ENEMY"
  2913. " COND_LIGHT_DAMAGE"
  2914. " COND_HEAVY_DAMAGE"
  2915. )
  2916. //=========================================================
  2917. // SCHED_COMBINE_ASSAULT
  2918. //=========================================================
  2919. DEFINE_SCHEDULE
  2920. (
  2921. SCHED_COMBINE_ASSAULT,
  2922. " Tasks "
  2923. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBINE_ESTABLISH_LINE_OF_FIRE"
  2924. " TASK_SET_TOLERANCE_DISTANCE 48"
  2925. " TASK_GET_PATH_TO_ENEMY_LKP 0"
  2926. " TASK_COMBINE_IGNORE_ATTACKS 0.2"
  2927. " TASK_SPEAK_SENTENCE 0"
  2928. " TASK_RUN_PATH 0"
  2929. // " TASK_COMBINE_MOVE_AND_AIM 0"
  2930. " TASK_WAIT_FOR_MOVEMENT 0"
  2931. " TASK_COMBINE_IGNORE_ATTACKS 0.0"
  2932. ""
  2933. " Interrupts "
  2934. " COND_NEW_ENEMY"
  2935. " COND_ENEMY_DEAD"
  2936. " COND_ENEMY_UNREACHABLE"
  2937. " COND_CAN_RANGE_ATTACK1"
  2938. " COND_CAN_MELEE_ATTACK1"
  2939. " COND_CAN_RANGE_ATTACK2"
  2940. " COND_CAN_MELEE_ATTACK2"
  2941. " COND_TOO_FAR_TO_ATTACK"
  2942. " COND_HEAR_DANGER"
  2943. " COND_HEAR_MOVE_AWAY"
  2944. )
  2945. DEFINE_SCHEDULE
  2946. (
  2947. SCHED_COMBINE_ESTABLISH_LINE_OF_FIRE,
  2948. " Tasks "
  2949. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_FAIL_ESTABLISH_LINE_OF_FIRE"
  2950. " TASK_SET_TOLERANCE_DISTANCE 48"
  2951. " TASK_GET_PATH_TO_ENEMY_LKP_LOS 0"
  2952. " TASK_COMBINE_SET_STANDING 1"
  2953. " TASK_SPEAK_SENTENCE 1"
  2954. " TASK_RUN_PATH 0"
  2955. " TASK_WAIT_FOR_MOVEMENT 0"
  2956. " TASK_COMBINE_IGNORE_ATTACKS 0.0"
  2957. " TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
  2958. " "
  2959. " Interrupts "
  2960. " COND_NEW_ENEMY"
  2961. " COND_ENEMY_DEAD"
  2962. //" COND_CAN_RANGE_ATTACK1"
  2963. //" COND_CAN_RANGE_ATTACK2"
  2964. " COND_CAN_MELEE_ATTACK1"
  2965. " COND_CAN_MELEE_ATTACK2"
  2966. " COND_HEAR_DANGER"
  2967. " COND_HEAR_MOVE_AWAY"
  2968. " COND_HEAVY_DAMAGE"
  2969. )
  2970. //=========================================================
  2971. // SCHED_COMBINE_PRESS_ATTACK
  2972. //=========================================================
  2973. DEFINE_SCHEDULE
  2974. (
  2975. SCHED_COMBINE_PRESS_ATTACK,
  2976. " Tasks "
  2977. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBINE_ESTABLISH_LINE_OF_FIRE"
  2978. " TASK_SET_TOLERANCE_DISTANCE 72"
  2979. " TASK_GET_PATH_TO_ENEMY_LKP 0"
  2980. " TASK_COMBINE_SET_STANDING 1"
  2981. " TASK_RUN_PATH 0"
  2982. " TASK_WAIT_FOR_MOVEMENT 0"
  2983. ""
  2984. " Interrupts "
  2985. " COND_NEW_ENEMY"
  2986. " COND_ENEMY_DEAD"
  2987. " COND_ENEMY_UNREACHABLE"
  2988. " COND_NO_PRIMARY_AMMO"
  2989. " COND_LOW_PRIMARY_AMMO"
  2990. " COND_TOO_CLOSE_TO_ATTACK"
  2991. " COND_CAN_MELEE_ATTACK1"
  2992. " COND_CAN_MELEE_ATTACK2"
  2993. " COND_HEAR_DANGER"
  2994. " COND_HEAR_MOVE_AWAY"
  2995. )
  2996. //=========================================================
  2997. // SCHED_COMBINE_COMBAT_FACE
  2998. //=========================================================
  2999. DEFINE_SCHEDULE
  3000. (
  3001. SCHED_COMBINE_COMBAT_FACE,
  3002. " Tasks"
  3003. " TASK_STOP_MOVING 0"
  3004. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  3005. " TASK_FACE_ENEMY 0"
  3006. " TASK_WAIT 1.5"
  3007. //" TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBINE_SWEEP"
  3008. ""
  3009. " Interrupts"
  3010. " COND_NEW_ENEMY"
  3011. " COND_ENEMY_DEAD"
  3012. " COND_CAN_RANGE_ATTACK1"
  3013. " COND_CAN_RANGE_ATTACK2"
  3014. )
  3015. //=========================================================
  3016. // SCHED_HIDE_AND_RELOAD
  3017. //=========================================================
  3018. DEFINE_SCHEDULE
  3019. (
  3020. SCHED_COMBINE_HIDE_AND_RELOAD,
  3021. " Tasks"
  3022. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RELOAD"
  3023. " TASK_FIND_COVER_FROM_ENEMY 0"
  3024. " TASK_RUN_PATH 0"
  3025. " TASK_WAIT_FOR_MOVEMENT 0"
  3026. " TASK_REMEMBER MEMORY:INCOVER"
  3027. " TASK_FACE_ENEMY 0"
  3028. " TASK_RELOAD 0"
  3029. ""
  3030. " Interrupts"
  3031. " COND_CAN_MELEE_ATTACK1"
  3032. " COND_CAN_MELEE_ATTACK2"
  3033. " COND_HEAVY_DAMAGE"
  3034. " COND_HEAR_DANGER"
  3035. " COND_HEAR_MOVE_AWAY"
  3036. )
  3037. //=========================================================
  3038. // SCHED_COMBINE_SIGNAL_SUPPRESS
  3039. // don't stop shooting until the clip is
  3040. // empty or combine gets hurt.
  3041. //=========================================================
  3042. DEFINE_SCHEDULE
  3043. (
  3044. SCHED_COMBINE_SIGNAL_SUPPRESS,
  3045. " Tasks"
  3046. " TASK_STOP_MOVING 0"
  3047. " TASK_FACE_IDEAL 0"
  3048. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_SIGNAL_GROUP"
  3049. " TASK_COMBINE_SET_STANDING 0"
  3050. " TASK_RANGE_ATTACK1 0"
  3051. ""
  3052. " Interrupts"
  3053. " COND_ENEMY_DEAD"
  3054. " COND_LIGHT_DAMAGE"
  3055. " COND_HEAVY_DAMAGE"
  3056. " COND_NO_PRIMARY_AMMO"
  3057. " COND_WEAPON_BLOCKED_BY_FRIEND"
  3058. " COND_WEAPON_SIGHT_OCCLUDED"
  3059. " COND_HEAR_DANGER"
  3060. " COND_HEAR_MOVE_AWAY"
  3061. " COND_COMBINE_NO_FIRE"
  3062. )
  3063. //=========================================================
  3064. // SCHED_COMBINE_SUPPRESS
  3065. //=========================================================
  3066. DEFINE_SCHEDULE
  3067. (
  3068. SCHED_COMBINE_SUPPRESS,
  3069. " Tasks"
  3070. " TASK_STOP_MOVING 0"
  3071. " TASK_FACE_ENEMY 0"
  3072. " TASK_COMBINE_SET_STANDING 0"
  3073. " TASK_RANGE_ATTACK1 0"
  3074. ""
  3075. " Interrupts"
  3076. " COND_ENEMY_DEAD"
  3077. " COND_LIGHT_DAMAGE"
  3078. " COND_HEAVY_DAMAGE"
  3079. " COND_NO_PRIMARY_AMMO"
  3080. " COND_HEAR_DANGER"
  3081. " COND_HEAR_MOVE_AWAY"
  3082. " COND_COMBINE_NO_FIRE"
  3083. " COND_WEAPON_BLOCKED_BY_FRIEND"
  3084. )
  3085. //=========================================================
  3086. // SCHED_COMBINE_ENTER_OVERWATCH
  3087. //
  3088. // Parks a combine soldier in place looking at the player's
  3089. // last known position, ready to attack if the player pops out
  3090. //=========================================================
  3091. DEFINE_SCHEDULE
  3092. (
  3093. SCHED_COMBINE_ENTER_OVERWATCH,
  3094. " Tasks"
  3095. " TASK_STOP_MOVING 0"
  3096. " TASK_COMBINE_SET_STANDING 0"
  3097. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  3098. " TASK_FACE_ENEMY 0"
  3099. " TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBINE_OVERWATCH"
  3100. ""
  3101. " Interrupts"
  3102. " COND_HEAR_DANGER"
  3103. " COND_NEW_ENEMY"
  3104. )
  3105. //=========================================================
  3106. // SCHED_COMBINE_OVERWATCH
  3107. //
  3108. // Parks a combine soldier in place looking at the player's
  3109. // last known position, ready to attack if the player pops out
  3110. //=========================================================
  3111. DEFINE_SCHEDULE
  3112. (
  3113. SCHED_COMBINE_OVERWATCH,
  3114. " Tasks"
  3115. " TASK_WAIT_FACE_ENEMY 10"
  3116. ""
  3117. " Interrupts"
  3118. " COND_CAN_RANGE_ATTACK1"
  3119. " COND_ENEMY_DEAD"
  3120. " COND_LIGHT_DAMAGE"
  3121. " COND_HEAVY_DAMAGE"
  3122. " COND_NO_PRIMARY_AMMO"
  3123. " COND_HEAR_DANGER"
  3124. " COND_HEAR_MOVE_AWAY"
  3125. " COND_NEW_ENEMY"
  3126. )
  3127. //=========================================================
  3128. // SCHED_COMBINE_WAIT_IN_COVER
  3129. // we don't allow danger or the ability
  3130. // to attack to break a combine's run to cover schedule but
  3131. // when a combine is in cover we do want them to attack if they can.
  3132. //=========================================================
  3133. DEFINE_SCHEDULE
  3134. (
  3135. SCHED_COMBINE_WAIT_IN_COVER,
  3136. " Tasks"
  3137. " TASK_STOP_MOVING 0"
  3138. " TASK_COMBINE_SET_STANDING 0"
  3139. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Translated to cover
  3140. " TASK_WAIT_FACE_ENEMY 1"
  3141. ""
  3142. " Interrupts"
  3143. " COND_NEW_ENEMY"
  3144. " COND_CAN_RANGE_ATTACK1"
  3145. " COND_CAN_RANGE_ATTACK2"
  3146. " COND_CAN_MELEE_ATTACK1"
  3147. " COND_CAN_MELEE_ATTACK2"
  3148. " COND_HEAR_DANGER"
  3149. " COND_HEAR_MOVE_AWAY"
  3150. " COND_COMBINE_ATTACK_SLOT_AVAILABLE"
  3151. )
  3152. //=========================================================
  3153. // SCHED_COMBINE_TAKE_COVER1
  3154. //=========================================================
  3155. DEFINE_SCHEDULE
  3156. (
  3157. SCHED_COMBINE_TAKE_COVER1 ,
  3158. " Tasks"
  3159. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBINE_TAKECOVER_FAILED"
  3160. " TASK_STOP_MOVING 0"
  3161. " TASK_WAIT 0.2"
  3162. " TASK_FIND_COVER_FROM_ENEMY 0"
  3163. " TASK_RUN_PATH 0"
  3164. " TASK_WAIT_FOR_MOVEMENT 0"
  3165. " TASK_REMEMBER MEMORY:INCOVER"
  3166. " TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBINE_WAIT_IN_COVER"
  3167. ""
  3168. " Interrupts"
  3169. )
  3170. DEFINE_SCHEDULE
  3171. (
  3172. SCHED_COMBINE_TAKECOVER_FAILED,
  3173. " Tasks"
  3174. " TASK_STOP_MOVING 0"
  3175. ""
  3176. " Interrupts"
  3177. )
  3178. //=========================================================
  3179. // SCHED_COMBINE_GRENADE_COVER1
  3180. //=========================================================
  3181. DEFINE_SCHEDULE
  3182. (
  3183. SCHED_COMBINE_GRENADE_COVER1,
  3184. " Tasks"
  3185. " TASK_STOP_MOVING 0"
  3186. " TASK_FIND_COVER_FROM_ENEMY 99"
  3187. " TASK_FIND_FAR_NODE_COVER_FROM_ENEMY 384"
  3188. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SPECIAL_ATTACK2"
  3189. " TASK_CLEAR_MOVE_WAIT 0"
  3190. " TASK_RUN_PATH 0"
  3191. " TASK_WAIT_FOR_MOVEMENT 0"
  3192. " TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBINE_WAIT_IN_COVER"
  3193. ""
  3194. " Interrupts"
  3195. )
  3196. //=========================================================
  3197. // SCHED_COMBINE_TOSS_GRENADE_COVER1
  3198. //
  3199. // drop grenade then run to cover.
  3200. //=========================================================
  3201. DEFINE_SCHEDULE
  3202. (
  3203. SCHED_COMBINE_TOSS_GRENADE_COVER1,
  3204. " Tasks"
  3205. " TASK_FACE_ENEMY 0"
  3206. " TASK_RANGE_ATTACK2 0"
  3207. " TASK_SET_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
  3208. ""
  3209. " Interrupts"
  3210. )
  3211. //=========================================================
  3212. // SCHED_COMBINE_RANGE_ATTACK1
  3213. //=========================================================
  3214. DEFINE_SCHEDULE
  3215. (
  3216. SCHED_COMBINE_RANGE_ATTACK1,
  3217. " Tasks"
  3218. " TASK_STOP_MOVING 0"
  3219. " TASK_FACE_ENEMY 0"
  3220. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  3221. " TASK_WAIT_RANDOM 0.3"
  3222. " TASK_RANGE_ATTACK1 0"
  3223. " TASK_COMBINE_IGNORE_ATTACKS 0.5"
  3224. ""
  3225. " Interrupts"
  3226. " COND_NEW_ENEMY"
  3227. " COND_ENEMY_DEAD"
  3228. " COND_HEAVY_DAMAGE"
  3229. " COND_LIGHT_DAMAGE"
  3230. " COND_LOW_PRIMARY_AMMO"
  3231. " COND_NO_PRIMARY_AMMO"
  3232. " COND_WEAPON_BLOCKED_BY_FRIEND"
  3233. " COND_TOO_CLOSE_TO_ATTACK"
  3234. " COND_GIVE_WAY"
  3235. " COND_HEAR_DANGER"
  3236. " COND_HEAR_MOVE_AWAY"
  3237. " COND_COMBINE_NO_FIRE"
  3238. ""
  3239. // Enemy_Occluded Don't interrupt on this. Means
  3240. // comibine will fire where player was after
  3241. // he has moved for a little while. Good effect!!
  3242. // WEAPON_SIGHT_OCCLUDED Don't block on this! Looks better for railings, etc.
  3243. )
  3244. //=========================================================
  3245. // AR2 Alt Fire Attack
  3246. //=========================================================
  3247. DEFINE_SCHEDULE
  3248. (
  3249. SCHED_COMBINE_AR2_ALTFIRE,
  3250. " Tasks"
  3251. " TASK_STOP_MOVING 0"
  3252. " TASK_ANNOUNCE_ATTACK 1"
  3253. " TASK_COMBINE_PLAY_SEQUENCE_FACE_ALTFIRE_TARGET ACTIVITY:ACT_COMBINE_AR2_ALTFIRE"
  3254. ""
  3255. " Interrupts"
  3256. )
  3257. //=========================================================
  3258. // Mapmaker forced grenade throw
  3259. //=========================================================
  3260. DEFINE_SCHEDULE
  3261. (
  3262. SCHED_COMBINE_FORCED_GRENADE_THROW,
  3263. " Tasks"
  3264. " TASK_STOP_MOVING 0"
  3265. " TASK_COMBINE_FACE_TOSS_DIR 0"
  3266. " TASK_ANNOUNCE_ATTACK 2" // 2 = grenade
  3267. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK2"
  3268. " TASK_COMBINE_DEFER_SQUAD_GRENADES 0"
  3269. ""
  3270. " Interrupts"
  3271. )
  3272. //=========================================================
  3273. // Move to LOS of the mapmaker's forced grenade throw target
  3274. //=========================================================
  3275. DEFINE_SCHEDULE
  3276. (
  3277. SCHED_COMBINE_MOVE_TO_FORCED_GREN_LOS,
  3278. " Tasks "
  3279. " TASK_SET_TOLERANCE_DISTANCE 48"
  3280. " TASK_COMBINE_GET_PATH_TO_FORCED_GREN_LOS 0"
  3281. " TASK_SPEAK_SENTENCE 1"
  3282. " TASK_RUN_PATH 0"
  3283. " TASK_WAIT_FOR_MOVEMENT 0"
  3284. " "
  3285. " Interrupts "
  3286. " COND_NEW_ENEMY"
  3287. " COND_ENEMY_DEAD"
  3288. " COND_CAN_MELEE_ATTACK1"
  3289. " COND_CAN_MELEE_ATTACK2"
  3290. " COND_HEAR_DANGER"
  3291. " COND_HEAR_MOVE_AWAY"
  3292. " COND_HEAVY_DAMAGE"
  3293. )
  3294. //=========================================================
  3295. // SCHED_COMBINE_RANGE_ATTACK2
  3296. //
  3297. // secondary range attack. Overriden because base class stops attacking when the enemy is occluded.
  3298. // combines's grenade toss requires the enemy be occluded.
  3299. //=========================================================
  3300. DEFINE_SCHEDULE
  3301. (
  3302. SCHED_COMBINE_RANGE_ATTACK2,
  3303. " Tasks"
  3304. " TASK_STOP_MOVING 0"
  3305. " TASK_COMBINE_FACE_TOSS_DIR 0"
  3306. " TASK_ANNOUNCE_ATTACK 2" // 2 = grenade
  3307. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK2"
  3308. " TASK_COMBINE_DEFER_SQUAD_GRENADES 0"
  3309. " TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBINE_WAIT_IN_COVER" // don't run immediately after throwing grenade.
  3310. ""
  3311. " Interrupts"
  3312. )
  3313. //=========================================================
  3314. // Throw a grenade, then run off and reload.
  3315. //=========================================================
  3316. DEFINE_SCHEDULE
  3317. (
  3318. SCHED_COMBINE_GRENADE_AND_RELOAD,
  3319. " Tasks"
  3320. " TASK_STOP_MOVING 0"
  3321. " TASK_COMBINE_FACE_TOSS_DIR 0"
  3322. " TASK_ANNOUNCE_ATTACK 2" // 2 = grenade
  3323. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK2"
  3324. " TASK_COMBINE_DEFER_SQUAD_GRENADES 0"
  3325. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HIDE_AND_RELOAD" // don't run immediately after throwing grenade.
  3326. ""
  3327. " Interrupts"
  3328. )
  3329. DEFINE_SCHEDULE
  3330. (
  3331. SCHED_COMBINE_PATROL,
  3332. " Tasks"
  3333. " TASK_STOP_MOVING 0"
  3334. " TASK_WANDER 900540"
  3335. " TASK_WALK_PATH 0"
  3336. " TASK_WAIT_FOR_MOVEMENT 0"
  3337. " TASK_STOP_MOVING 0"
  3338. " TASK_FACE_REASONABLE 0"
  3339. " TASK_WAIT 3"
  3340. " TASK_WAIT_RANDOM 3"
  3341. " TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBINE_PATROL" // keep doing it
  3342. ""
  3343. " Interrupts"
  3344. " COND_ENEMY_DEAD"
  3345. " COND_LIGHT_DAMAGE"
  3346. " COND_HEAVY_DAMAGE"
  3347. " COND_HEAR_DANGER"
  3348. " COND_HEAR_MOVE_AWAY"
  3349. " COND_NEW_ENEMY"
  3350. " COND_SEE_ENEMY"
  3351. " COND_CAN_RANGE_ATTACK1"
  3352. " COND_CAN_RANGE_ATTACK2"
  3353. )
  3354. DEFINE_SCHEDULE
  3355. (
  3356. SCHED_COMBINE_BUGBAIT_DISTRACTION,
  3357. " Tasks"
  3358. " TASK_STOP_MOVING 0"
  3359. " TASK_RESET_ACTIVITY 0"
  3360. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_COMBINE_BUGBAIT"
  3361. ""
  3362. " Interrupts"
  3363. ""
  3364. )
  3365. //=========================================================
  3366. // SCHED_COMBINE_CHARGE_TURRET
  3367. //
  3368. // Used to run straight at enemy turrets to knock them over.
  3369. // Prevents squadmates from throwing grenades during.
  3370. //=========================================================
  3371. DEFINE_SCHEDULE
  3372. (
  3373. SCHED_COMBINE_CHARGE_TURRET,
  3374. " Tasks"
  3375. " TASK_COMBINE_DEFER_SQUAD_GRENADES 0"
  3376. " TASK_STOP_MOVING 0"
  3377. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  3378. " TASK_GET_CHASE_PATH_TO_ENEMY 300"
  3379. " TASK_RUN_PATH 0"
  3380. " TASK_WAIT_FOR_MOVEMENT 0"
  3381. " TASK_FACE_ENEMY 0"
  3382. ""
  3383. " Interrupts"
  3384. " COND_NEW_ENEMY"
  3385. " COND_ENEMY_DEAD"
  3386. " COND_ENEMY_UNREACHABLE"
  3387. " COND_CAN_MELEE_ATTACK1"
  3388. " COND_CAN_MELEE_ATTACK2"
  3389. " COND_TOO_CLOSE_TO_ATTACK"
  3390. " COND_TASK_FAILED"
  3391. " COND_LOST_ENEMY"
  3392. " COND_BETTER_WEAPON_AVAILABLE"
  3393. " COND_HEAR_DANGER"
  3394. )
  3395. //=========================================================
  3396. // SCHED_COMBINE_CHARGE_PLAYER
  3397. //
  3398. // Used to run straight at enemy player since physgun combat
  3399. // is more fun when the enemies are close
  3400. //=========================================================
  3401. DEFINE_SCHEDULE
  3402. (
  3403. SCHED_COMBINE_CHARGE_PLAYER,
  3404. " Tasks"
  3405. " TASK_STOP_MOVING 0"
  3406. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  3407. " TASK_COMBINE_CHASE_ENEMY_CONTINUOUSLY 192"
  3408. " TASK_FACE_ENEMY 0"
  3409. ""
  3410. " Interrupts"
  3411. " COND_NEW_ENEMY"
  3412. " COND_ENEMY_DEAD"
  3413. " COND_ENEMY_UNREACHABLE"
  3414. " COND_CAN_MELEE_ATTACK1"
  3415. " COND_CAN_MELEE_ATTACK2"
  3416. " COND_TASK_FAILED"
  3417. " COND_LOST_ENEMY"
  3418. " COND_HEAR_DANGER"
  3419. )
  3420. //=========================================================
  3421. // SCHED_COMBINE_DROP_GRENADE
  3422. //
  3423. // Place a grenade at my feet
  3424. //=========================================================
  3425. DEFINE_SCHEDULE
  3426. (
  3427. SCHED_COMBINE_DROP_GRENADE,
  3428. " Tasks"
  3429. " TASK_STOP_MOVING 0"
  3430. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SPECIAL_ATTACK2"
  3431. " TASK_FIND_COVER_FROM_ENEMY 99"
  3432. " TASK_FIND_FAR_NODE_COVER_FROM_ENEMY 384"
  3433. " TASK_CLEAR_MOVE_WAIT 0"
  3434. " TASK_RUN_PATH 0"
  3435. " TASK_WAIT_FOR_MOVEMENT 0"
  3436. ""
  3437. " Interrupts"
  3438. )
  3439. //=========================================================
  3440. // SCHED_COMBINE_PATROL_ENEMY
  3441. //
  3442. // Used instead if SCHED_COMBINE_PATROL if I have an enemy.
  3443. // Wait for the enemy a bit in the hopes of ambushing him.
  3444. //=========================================================
  3445. DEFINE_SCHEDULE
  3446. (
  3447. SCHED_COMBINE_PATROL_ENEMY,
  3448. " Tasks"
  3449. " TASK_STOP_MOVING 0"
  3450. " TASK_WAIT_FACE_ENEMY 1"
  3451. " TASK_WAIT_FACE_ENEMY_RANDOM 3"
  3452. ""
  3453. " Interrupts"
  3454. " COND_ENEMY_DEAD"
  3455. " COND_LIGHT_DAMAGE"
  3456. " COND_HEAVY_DAMAGE"
  3457. " COND_HEAR_DANGER"
  3458. " COND_HEAR_MOVE_AWAY"
  3459. " COND_NEW_ENEMY"
  3460. " COND_SEE_ENEMY"
  3461. " COND_CAN_RANGE_ATTACK1"
  3462. " COND_CAN_RANGE_ATTACK2"
  3463. )
  3464. DEFINE_SCHEDULE
  3465. (
  3466. SCHED_COMBINE_BURNING_STAND,
  3467. " Tasks"
  3468. " TASK_SET_ACTIVITY ACTIVITY:ACT_COMBINE_BUGBAIT"
  3469. " TASK_RANDOMIZE_FRAMERATE 20"
  3470. " TASK_WAIT 2"
  3471. " TASK_WAIT_RANDOM 3"
  3472. " TASK_COMBINE_DIE_INSTANTLY DMG_BURN"
  3473. " TASK_WAIT 1.0"
  3474. " "
  3475. " Interrupts"
  3476. )
  3477. DEFINE_SCHEDULE
  3478. (
  3479. SCHED_COMBINE_FACE_IDEAL_YAW,
  3480. " Tasks"
  3481. " TASK_FACE_IDEAL 0"
  3482. " "
  3483. " Interrupts"
  3484. )
  3485. DEFINE_SCHEDULE
  3486. (
  3487. SCHED_COMBINE_MOVE_TO_MELEE,
  3488. " Tasks"
  3489. " TASK_STORE_ENEMY_POSITION_IN_SAVEPOSITION 0"
  3490. " TASK_GET_PATH_TO_SAVEPOSITION 0"
  3491. " TASK_RUN_PATH 0"
  3492. " TASK_WAIT_FOR_MOVEMENT 0"
  3493. " "
  3494. " Interrupts"
  3495. " COND_NEW_ENEMY"
  3496. " COND_ENEMY_DEAD"
  3497. " COND_CAN_MELEE_ATTACK1"
  3498. )
  3499. AI_END_CUSTOM_NPC()