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.

5846 lines
171 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "soundent.h"
  8. #include "npcevent.h"
  9. #include "globalstate.h"
  10. #include "ai_squad.h"
  11. #include "ai_tacticalservices.h"
  12. #include "npc_manhack.h"
  13. #include "npc_metropolice.h"
  14. #include "weapon_stunstick.h"
  15. #include "basegrenade_shared.h"
  16. #include "ai_route.h"
  17. #include "hl2_player.h"
  18. #include "iservervehicle.h"
  19. #include "items.h"
  20. #include "hl2_gamerules.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. //#define SF_METROPOLICE_ 0x00010000
  24. #define SF_METROPOLICE_SIMPLE_VERSION 0x00020000
  25. #define SF_METROPOLICE_ALWAYS_STITCH 0x00080000
  26. #define SF_METROPOLICE_NOCHATTER 0x00100000
  27. #define SF_METROPOLICE_ARREST_ENEMY 0x00200000
  28. #define SF_METROPOLICE_NO_FAR_STITCH 0x00400000
  29. #define SF_METROPOLICE_NO_MANHACK_DEPLOY 0x00800000
  30. #define SF_METROPOLICE_ALLOWED_TO_RESPOND 0x01000000
  31. #define SF_METROPOLICE_MID_RANGE_ATTACK 0x02000000
  32. #define METROPOLICE_MID_RANGE_ATTACK_RANGE 3500.0f
  33. #define METROPOLICE_SQUAD_STITCH_MIN_INTERVAL 1.0f
  34. #define METROPOLICE_SQUAD_STITCH_MAX_INTERVAL 1.2f
  35. #define AIM_ALONG_SIDE_LINE_OF_DEATH_DISTANCE 300.0f
  36. #define AIM_ALONG_SIDE_STEER_DISTANCE 200.0f
  37. #define AIM_ALONG_SIDE_DEFAULT_STITCH_LENGTH 750.0f
  38. #define AIM_ALONG_SIDE_LINE_OF_DEATH_LEAD_TIME 0.0f
  39. #define AIM_ALONG_SIDE_LINE_INITIAL_DRAW_FRACTION 0.2f
  40. #define AIM_BEHIND_DEFAULT_STITCH_LENGTH 1000.0f
  41. #define AIM_BEHIND_MINIMUM_DISTANCE 650.0f
  42. #define AIM_BEHIND_STEER_DISTANCE 150.0f
  43. #define RECENT_DAMAGE_INTERVAL 3.0f
  44. #define RECENT_DAMAGE_THRESHOLD 0.2f
  45. #define VEHICLE_PREDICT_ACCELERATION 333.0f
  46. #define VEHICLE_PREDICT_MAX_SPEED 600.0f
  47. #define METROPOLICE_MAX_WARNINGS 3
  48. #define METROPOLICE_BODYGROUP_MANHACK 1
  49. enum
  50. {
  51. // NOTE: Exact #s are important, since they are referred to by number in schedules below
  52. METROPOLICE_SENTENCE_FREEZE = 0,
  53. METROPOLICE_SENTENCE_HES_OVER_HERE = 1,
  54. METROPOLICE_SENTENCE_HES_RUNNING = 2,
  55. METROPOLICE_SENTENCE_TAKE_HIM_DOWN = 3,
  56. METROPOLICE_SENTENCE_ARREST_IN_POSITION = 4,
  57. METROPOLICE_SENTENCE_DEPLOY_MANHACK = 5,
  58. METROPOLICE_SENTENCE_MOVE_INTO_POSITION = 6,
  59. METROPOLICE_SENTENCE_HEARD_SOMETHING = 7,
  60. };
  61. enum
  62. {
  63. METROPOLICE_ANNOUNCE_ATTACK_PRIMARY = 1,
  64. METROPOLICE_ANNOUNCE_ATTACK_SECONDARY,
  65. METROPOLICE_ANNOUNCE_ATTACK_HARASS,
  66. };
  67. enum
  68. {
  69. METROPOLICE_CHATTER_WAIT_FOR_RESPONSE = 0,
  70. METROPOLICE_CHATTER_ASK_QUESTION = 1,
  71. METROPOLICE_CHATTER_RESPONSE = 2,
  72. METROPOLICE_CHATTER_RESPONSE_TYPE_COUNT = 2,
  73. };
  74. enum SpeechMemory_t
  75. {
  76. bits_MEMORY_PAIN_LIGHT_SOUND = bits_MEMORY_CUSTOM1,
  77. bits_MEMORY_PAIN_HEAVY_SOUND = bits_MEMORY_CUSTOM2,
  78. bits_MEMORY_PLAYER_HURT = bits_MEMORY_CUSTOM3,
  79. bits_MEMORY_PLAYER_HARASSED = bits_MEMORY_CUSTOM4,
  80. };
  81. //Metrocop
  82. int g_interactionMetrocopStartedStitch = 0;
  83. int g_interactionMetrocopIdleChatter = 0;
  84. int g_interactionMetrocopClearSentenceQueues = 0;
  85. extern int g_interactionHitByPlayerThrownPhysObj;
  86. ConVar sk_metropolice_stitch_reaction( "sk_metropolice_stitch_reaction","1.0");
  87. ConVar sk_metropolice_stitch_tight_hitcount( "sk_metropolice_stitch_tight_hitcount","2");
  88. ConVar sk_metropolice_stitch_at_hitcount( "sk_metropolice_stitch_at_hitcount","1");
  89. ConVar sk_metropolice_stitch_behind_hitcount( "sk_metropolice_stitch_behind_hitcount","3");
  90. ConVar sk_metropolice_stitch_along_hitcount( "sk_metropolice_stitch_along_hitcount","2");
  91. ConVar sk_metropolice_health( "sk_metropolice_health","0");
  92. ConVar sk_metropolice_simple_health( "sk_metropolice_simple_health","26");
  93. ConVar sk_metropolice_stitch_distance( "sk_metropolice_stitch_distance","1000");
  94. ConVar metropolice_chase_use_follow( "metropolice_chase_use_follow", "0" );
  95. ConVar metropolice_move_and_melee("metropolice_move_and_melee", "1" );
  96. ConVar metropolice_charge("metropolice_charge", "1" );
  97. // How many clips of pistol ammo a metropolice carries.
  98. #define METROPOLICE_NUM_CLIPS 5
  99. #define METROPOLICE_BURST_RELOAD_COUNT 20
  100. int AE_METROPOLICE_BATON_ON;
  101. int AE_METROPOLICE_BATON_OFF;
  102. int AE_METROPOLICE_SHOVE;
  103. int AE_METROPOLICE_START_DEPLOY;
  104. int AE_METROPOLICE_DRAW_PISTOL; // was 50
  105. int AE_METROPOLICE_DEPLOY_MANHACK; // was 51
  106. // -----------------------------------------------
  107. // > Squad slots
  108. // -----------------------------------------------
  109. enum SquadSlot_T
  110. {
  111. SQUAD_SLOT_POLICE_CHARGE_ENEMY = LAST_SHARED_SQUADSLOT,
  112. SQUAD_SLOT_POLICE_HARASS, // Yell at the player with a megaphone, etc.
  113. SQUAD_SLOT_POLICE_DEPLOY_MANHACK,
  114. SQUAD_SLOT_POLICE_ADVANCE,
  115. SQUAD_SLOT_POLICE_ATTACK_OCCLUDER1,
  116. SQUAD_SLOT_POLICE_ATTACK_OCCLUDER2,
  117. SQUAD_SLOT_POLICE_COVERING_FIRE1,
  118. SQUAD_SLOT_POLICE_COVERING_FIRE2,
  119. SQUAD_SLOT_POLICE_ARREST_ENEMY,
  120. };
  121. //=========================================================
  122. // Metro Police Activities
  123. //=========================================================
  124. int ACT_METROPOLICE_DRAW_PISTOL;
  125. int ACT_METROPOLICE_DEPLOY_MANHACK;
  126. int ACT_METROPOLICE_FLINCH_BEHIND;
  127. int ACT_WALK_BATON;
  128. int ACT_IDLE_ANGRY_BATON;
  129. int ACT_PUSH_PLAYER;
  130. int ACT_MELEE_ATTACK_THRUST;
  131. int ACT_ACTIVATE_BATON;
  132. int ACT_DEACTIVATE_BATON;
  133. LINK_ENTITY_TO_CLASS( npc_metropolice, CNPC_MetroPolice );
  134. BEGIN_DATADESC( CNPC_MetroPolice )
  135. DEFINE_EMBEDDED( m_BatonSwingTimer ),
  136. DEFINE_EMBEDDED( m_NextChargeTimer ),
  137. DEFINE_FIELD( m_flBatonDebounceTime, FIELD_FLOAT ),
  138. DEFINE_FIELD( m_bShouldActivateBaton, FIELD_BOOLEAN ),
  139. DEFINE_FIELD( m_iPistolClips, FIELD_INTEGER ),
  140. DEFINE_KEYFIELD( m_fWeaponDrawn, FIELD_BOOLEAN, "weapondrawn" ),
  141. DEFINE_FIELD( m_LastShootSlot, FIELD_INTEGER ),
  142. DEFINE_EMBEDDED( m_TimeYieldShootSlot ),
  143. DEFINE_EMBEDDED( m_Sentences ),
  144. DEFINE_FIELD( m_bPlayerIsNear, FIELD_BOOLEAN ),
  145. DEFINE_FIELD( m_vecBurstTargetPos, FIELD_POSITION_VECTOR ),
  146. DEFINE_FIELD( m_vecBurstDelta, FIELD_VECTOR ),
  147. DEFINE_FIELD( m_nBurstHits, FIELD_INTEGER ),
  148. DEFINE_FIELD( m_nMaxBurstHits, FIELD_INTEGER ),
  149. DEFINE_FIELD( m_flBurstPredictTime, FIELD_TIME ),
  150. DEFINE_FIELD( m_nBurstReloadCount, FIELD_INTEGER ),
  151. DEFINE_FIELD( m_vecBurstLineOfDeathDelta, FIELD_VECTOR ),
  152. DEFINE_FIELD( m_vecBurstLineOfDeathOrigin, FIELD_POSITION_VECTOR ),
  153. DEFINE_FIELD( m_flBurstSteerDistance, FIELD_FLOAT ),
  154. DEFINE_FIELD( m_nBurstMode, FIELD_INTEGER ),
  155. DEFINE_FIELD( m_nBurstSteerMode, FIELD_INTEGER ),
  156. DEFINE_FIELD( m_vecBurstPredictedVelocityDir, FIELD_VECTOR ),
  157. DEFINE_FIELD( m_vecBurstPredictedSpeed, FIELD_FLOAT ),
  158. DEFINE_FIELD( m_flValidStitchTime, FIELD_TIME ),
  159. DEFINE_FIELD( m_flNextLedgeCheckTime, FIELD_TIME ),
  160. DEFINE_FIELD( m_flTaskCompletionTime, FIELD_TIME ),
  161. DEFINE_FIELD( m_flLastPhysicsFlinchTime, FIELD_TIME ),
  162. DEFINE_FIELD( m_flLastDamageFlinchTime, FIELD_TIME ),
  163. DEFINE_FIELD( m_hManhack, FIELD_EHANDLE ),
  164. DEFINE_FIELD( m_hBlockingProp, FIELD_EHANDLE ),
  165. DEFINE_FIELD( m_nRecentDamage, FIELD_INTEGER ),
  166. DEFINE_FIELD( m_flRecentDamageTime, FIELD_TIME ),
  167. DEFINE_FIELD( m_flNextPainSoundTime, FIELD_TIME ),
  168. DEFINE_FIELD( m_flNextLostSoundTime, FIELD_TIME ),
  169. DEFINE_FIELD( m_nIdleChatterType, FIELD_INTEGER ),
  170. DEFINE_FIELD( m_bSimpleCops, FIELD_BOOLEAN ),
  171. DEFINE_FIELD( m_flLastHitYaw, FIELD_FLOAT ),
  172. DEFINE_FIELD( m_bPlayerTooClose, FIELD_BOOLEAN ),
  173. DEFINE_FIELD( m_bKeepFacingPlayer, FIELD_BOOLEAN ),
  174. DEFINE_FIELD( m_flChasePlayerTime, FIELD_TIME ),
  175. DEFINE_FIELD( m_vecPreChaseOrigin, FIELD_VECTOR ),
  176. DEFINE_FIELD( m_flPreChaseYaw, FIELD_FLOAT ),
  177. DEFINE_FIELD( m_nNumWarnings, FIELD_INTEGER ),
  178. DEFINE_FIELD( m_iNumPlayerHits, FIELD_INTEGER ),
  179. // m_ActBusyBehavior (auto saved by AI)
  180. // m_StandoffBehavior (auto saved by AI)
  181. // m_AssaultBehavior (auto saved by AI)
  182. // m_FuncTankBehavior (auto saved by AI)
  183. // m_RappelBehavior (auto saved by AI)
  184. // m_PolicingBehavior (auto saved by AI)
  185. // m_FollowBehavior (auto saved by AI)
  186. DEFINE_KEYFIELD( m_iManhacks, FIELD_INTEGER, "manhacks" ),
  187. DEFINE_INPUTFUNC( FIELD_VOID, "EnableManhackToss", InputEnableManhackToss ),
  188. DEFINE_INPUTFUNC( FIELD_STRING, "SetPoliceGoal", InputSetPoliceGoal ),
  189. DEFINE_INPUTFUNC( FIELD_VOID, "ActivateBaton", InputActivateBaton ),
  190. DEFINE_USEFUNC( PrecriminalUse ),
  191. DEFINE_OUTPUT( m_OnStunnedPlayer, "OnStunnedPlayer" ),
  192. DEFINE_OUTPUT( m_OnCupCopped, "OnCupCopped" ),
  193. END_DATADESC()
  194. //------------------------------------------------------------------------------
  195. float CNPC_MetroPolice::gm_flTimeLastSpokePeek;
  196. //------------------------------------------------------------------------------
  197. // Purpose
  198. //------------------------------------------------------------------------------
  199. CBaseEntity *CNPC_MetroPolice::CheckTraceHullAttack( float flDist, const Vector &mins, const Vector &maxs, int iDamage, int iDmgType, float forceScale, bool bDamageAnyNPC )
  200. {
  201. // If only a length is given assume we want to trace in our facing direction
  202. Vector forward;
  203. AngleVectors( GetAbsAngles(), &forward );
  204. Vector vStart = GetAbsOrigin();
  205. // The ideal place to start the trace is in the center of the attacker's bounding box.
  206. // however, we need to make sure there's enough clearance. Some of the smaller monsters aren't
  207. // as big as the hull we try to trace with. (SJB)
  208. float flVerticalOffset = WorldAlignSize().z * 0.5;
  209. if( flVerticalOffset < maxs.z )
  210. {
  211. // There isn't enough room to trace this hull, it's going to drag the ground.
  212. // so make the vertical offset just enough to clear the ground.
  213. flVerticalOffset = maxs.z + 1.0;
  214. }
  215. vStart.z += flVerticalOffset;
  216. Vector vEnd = vStart + (forward * flDist );
  217. return CheckTraceHullAttack( vStart, vEnd, mins, maxs, iDamage, iDmgType, forceScale, bDamageAnyNPC );
  218. }
  219. //------------------------------------------------------------------------------
  220. // Melee filter for police
  221. //------------------------------------------------------------------------------
  222. class CTraceFilterMetroPolice : public CTraceFilterEntitiesOnly
  223. {
  224. public:
  225. // It does have a base, but we'll never network anything below here..
  226. DECLARE_CLASS_NOBASE( CTraceFilterMetroPolice );
  227. CTraceFilterMetroPolice( const IHandleEntity *passentity, int collisionGroup, CTakeDamageInfo *dmgInfo, float flForceScale, bool bDamageAnyNPC )
  228. : m_pPassEnt(passentity), m_collisionGroup(collisionGroup), m_dmgInfo(dmgInfo), m_pHit(NULL), m_flForceScale(flForceScale), m_bDamageAnyNPC(bDamageAnyNPC)
  229. {
  230. }
  231. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  232. {
  233. if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
  234. return false;
  235. if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
  236. return false;
  237. // Don't test if the game code tells us we should ignore this collision...
  238. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  239. if ( pEntity )
  240. {
  241. if ( !pEntity->ShouldCollide( m_collisionGroup, contentsMask ) )
  242. return false;
  243. if ( !g_pGameRules->ShouldCollide( m_collisionGroup, pEntity->GetCollisionGroup() ) )
  244. return false;
  245. if ( pEntity->m_takedamage == DAMAGE_NO )
  246. return false;
  247. // Translate the vehicle into its driver for damage
  248. if ( pEntity->GetServerVehicle() != NULL )
  249. {
  250. CBaseEntity *pDriver = pEntity->GetServerVehicle()->GetPassenger();
  251. if ( pDriver != NULL )
  252. {
  253. pEntity = pDriver;
  254. }
  255. }
  256. Vector attackDir = pEntity->WorldSpaceCenter() - m_dmgInfo->GetAttacker()->WorldSpaceCenter();
  257. VectorNormalize( attackDir );
  258. CTakeDamageInfo info = (*m_dmgInfo);
  259. CalculateMeleeDamageForce( &info, attackDir, info.GetAttacker()->WorldSpaceCenter(), m_flForceScale );
  260. if( !(pEntity->GetFlags() & FL_ONGROUND) )
  261. {
  262. // Don't hit airborne entities so hard. They fly farther since
  263. // there's no friction with the ground.
  264. info.ScaleDamageForce( 0.001 );
  265. }
  266. CBaseCombatCharacter *pBCC = info.GetAttacker()->MyCombatCharacterPointer();
  267. CBaseCombatCharacter *pVictimBCC = pEntity->MyCombatCharacterPointer();
  268. // Only do these comparisons between NPCs
  269. if ( pBCC && pVictimBCC )
  270. {
  271. // Can only damage other NPCs that we hate
  272. if ( m_bDamageAnyNPC || pBCC->IRelationType( pEntity ) == D_HT || pEntity->IsPlayer() )
  273. {
  274. if ( info.GetDamage() )
  275. {
  276. // If gordon's a criminal, do damage now
  277. if ( !pEntity->IsPlayer() || GlobalEntity_GetState( "gordon_precriminal" ) == GLOBAL_OFF )
  278. {
  279. if ( pEntity->IsPlayer() && ((CBasePlayer *)pEntity)->IsSuitEquipped() )
  280. {
  281. info.ScaleDamage( .25 );
  282. info.ScaleDamageForce( .25 );
  283. }
  284. pEntity->TakeDamage( info );
  285. }
  286. }
  287. m_pHit = pEntity;
  288. return true;
  289. }
  290. }
  291. else
  292. {
  293. // Make sure if the player is holding this, he drops it
  294. Pickup_ForcePlayerToDropThisObject( pEntity );
  295. // Otherwise just damage passive objects in our way
  296. if ( info.GetDamage() )
  297. {
  298. pEntity->TakeDamage( info );
  299. }
  300. }
  301. }
  302. return false;
  303. }
  304. public:
  305. const IHandleEntity *m_pPassEnt;
  306. int m_collisionGroup;
  307. CTakeDamageInfo *m_dmgInfo;
  308. CBaseEntity *m_pHit;
  309. float m_flForceScale;
  310. bool m_bDamageAnyNPC;
  311. };
  312. //------------------------------------------------------------------------------
  313. // Purpose : start and end trace position, amount
  314. // of damage to do, and damage type. Returns a pointer to
  315. // the damaged entity in case the NPC wishes to do
  316. // other stuff to the victim (punchangle, etc)
  317. //
  318. // Used for many contact-range melee attacks. Bites, claws, etc.
  319. // Input :
  320. // Output :
  321. //------------------------------------------------------------------------------
  322. CBaseEntity *CNPC_MetroPolice::CheckTraceHullAttack( const Vector &vStart, const Vector &vEnd, const Vector &mins, const Vector &maxs, int iDamage, int iDmgType, float flForceScale, bool bDamageAnyNPC )
  323. {
  324. CTakeDamageInfo dmgInfo( this, this, iDamage, DMG_SLASH );
  325. CTraceFilterMetroPolice traceFilter( this, COLLISION_GROUP_NONE, &dmgInfo, flForceScale, bDamageAnyNPC );
  326. Ray_t ray;
  327. ray.Init( vStart, vEnd, mins, maxs );
  328. trace_t tr;
  329. enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
  330. CBaseEntity *pEntity = traceFilter.m_pHit;
  331. if ( pEntity == NULL )
  332. {
  333. // See if perhaps I'm trying to claw/bash someone who is standing on my head.
  334. Vector vecTopCenter;
  335. Vector vecEnd;
  336. Vector vecMins, vecMaxs;
  337. // Do a tracehull from the top center of my bounding box.
  338. vecTopCenter = GetAbsOrigin();
  339. CollisionProp()->WorldSpaceAABB( &vecMins, &vecMaxs );
  340. vecTopCenter.z = vecMaxs.z + 1.0f;
  341. vecEnd = vecTopCenter;
  342. vecEnd.z += 2.0f;
  343. ray.Init( vecTopCenter, vEnd, mins, maxs );
  344. enginetrace->TraceRay( ray, MASK_SHOT_HULL, &traceFilter, &tr );
  345. pEntity = traceFilter.m_pHit;
  346. }
  347. return pEntity;
  348. }
  349. //-----------------------------------------------------------------------------
  350. // My buddies got killed!
  351. //-----------------------------------------------------------------------------
  352. void CNPC_MetroPolice::NotifyDeadFriend( CBaseEntity* pFriend )
  353. {
  354. BaseClass::NotifyDeadFriend(pFriend);
  355. if ( pFriend == m_hManhack )
  356. {
  357. m_Sentences.Speak( "METROPOLICE_MANHACK_KILLED", SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL );
  358. DevMsg("My manhack died!\n");
  359. m_hManhack = NULL;
  360. return;
  361. }
  362. // No notifications for squadmates' dead manhacks
  363. if ( FClassnameIs( pFriend, "npc_manhack" ) )
  364. return;
  365. // Reset idle chatter, we may never get a response back
  366. if ( m_nIdleChatterType == METROPOLICE_CHATTER_WAIT_FOR_RESPONSE )
  367. {
  368. m_nIdleChatterType = METROPOLICE_CHATTER_ASK_QUESTION;
  369. }
  370. if ( GetSquad()->NumMembers() < 2 )
  371. {
  372. m_Sentences.Speak( "METROPOLICE_LAST_OF_SQUAD", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
  373. return;
  374. }
  375. m_Sentences.Speak( "METROPOLICE_MAN_DOWN", SENTENCE_PRIORITY_MEDIUM );
  376. }
  377. //-----------------------------------------------------------------------------
  378. //-----------------------------------------------------------------------------
  379. CNPC_MetroPolice::CNPC_MetroPolice()
  380. {
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Purpose:
  384. //-----------------------------------------------------------------------------
  385. void CNPC_MetroPolice::OnScheduleChange()
  386. {
  387. BaseClass::OnScheduleChange();
  388. if ( GetEnemy() && HasCondition( COND_ENEMY_DEAD ) )
  389. {
  390. AnnounceEnemyKill( GetEnemy() );
  391. }
  392. }
  393. //-----------------------------------------------------------------------------
  394. // Purpose:
  395. //-----------------------------------------------------------------------------
  396. void CNPC_MetroPolice::PrescheduleThink( void )
  397. {
  398. BaseClass::PrescheduleThink();
  399. // Speak any queued sentences
  400. m_Sentences.UpdateSentenceQueue();
  401. // Look at near players, always
  402. m_bPlayerIsNear = false;
  403. if ( PlayerIsCriminal() == false )
  404. {
  405. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  406. if ( pPlayer && ( pPlayer->WorldSpaceCenter() - WorldSpaceCenter() ).LengthSqr() < (128*128) )
  407. {
  408. m_bPlayerIsNear = true;
  409. AddLookTarget( pPlayer, 0.75f, 5.0f );
  410. if ( ( m_PolicingBehavior.IsEnabled() == false ) && ( m_nNumWarnings >= METROPOLICE_MAX_WARNINGS ) )
  411. {
  412. m_flBatonDebounceTime = gpGlobals->curtime + random->RandomFloat( 2.5f, 4.0f );
  413. SetTarget( pPlayer );
  414. SetBatonState( true );
  415. }
  416. }
  417. else
  418. {
  419. if ( m_PolicingBehavior.IsEnabled() == false && gpGlobals->curtime > m_flBatonDebounceTime )
  420. {
  421. SetBatonState( false );
  422. }
  423. m_bKeepFacingPlayer = false;
  424. }
  425. }
  426. if( IsOnFire() )
  427. {
  428. SetCondition( COND_METROPOLICE_ON_FIRE );
  429. }
  430. else
  431. {
  432. ClearCondition( COND_METROPOLICE_ON_FIRE );
  433. }
  434. if (gpGlobals->curtime > m_flRecentDamageTime + RECENT_DAMAGE_INTERVAL)
  435. {
  436. m_nRecentDamage = 0;
  437. m_flRecentDamageTime = 0;
  438. }
  439. }
  440. //-----------------------------------------------------------------------------
  441. // Purpose:
  442. // Input : &move -
  443. // flInterval -
  444. // Output : Returns true on success, false on failure.
  445. //-----------------------------------------------------------------------------
  446. bool CNPC_MetroPolice::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval )
  447. {
  448. // Don't do this if we're scripted
  449. if ( IsInAScript() )
  450. return BaseClass::OverrideMoveFacing( move, flInterval );
  451. // ROBIN: Disabled at request of mapmakers for now
  452. /*
  453. // If we're moving during a police sequence, always face our target
  454. if ( m_PolicingBehavior.IsEnabled() )
  455. {
  456. CBaseEntity *pTarget = m_PolicingBehavior.GetGoalTarget();
  457. if ( pTarget )
  458. {
  459. AddFacingTarget( pTarget, pTarget->WorldSpaceCenter(), 1.0f, 0.2f );
  460. }
  461. }
  462. */
  463. return BaseClass::OverrideMoveFacing( move, flInterval );
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose:
  467. //-----------------------------------------------------------------------------
  468. void CNPC_MetroPolice::Precache( void )
  469. {
  470. if ( HasSpawnFlags( SF_NPC_START_EFFICIENT ) )
  471. {
  472. SetModelName( AllocPooledString("models/police_cheaple.mdl" ) );
  473. }
  474. else
  475. {
  476. SetModelName( AllocPooledString("models/police.mdl") );
  477. }
  478. PrecacheModel( STRING( GetModelName() ) );
  479. UTIL_PrecacheOther( "npc_manhack" );
  480. PrecacheScriptSound( "NPC_Metropolice.Shove" );
  481. PrecacheScriptSound( "NPC_MetroPolice.WaterSpeech" );
  482. PrecacheScriptSound( "NPC_MetroPolice.HidingSpeech" );
  483. enginesound->PrecacheSentenceGroup( "METROPOLICE" );
  484. BaseClass::Precache();
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Create components
  488. //-----------------------------------------------------------------------------
  489. bool CNPC_MetroPolice::CreateComponents()
  490. {
  491. if ( !BaseClass::CreateComponents() )
  492. return false;
  493. m_Sentences.Init( this, "NPC_Metropolice.SentenceParameters" );
  494. return true;
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose:
  498. //
  499. //
  500. //-----------------------------------------------------------------------------
  501. void CNPC_MetroPolice::Spawn( void )
  502. {
  503. Precache();
  504. #ifdef _XBOX
  505. // Always fade the corpse
  506. AddSpawnFlags( SF_NPC_FADE_CORPSE );
  507. #endif // _XBOX
  508. SetModel( STRING( GetModelName() ) );
  509. SetHullType(HULL_HUMAN);
  510. SetHullSizeNormal();
  511. SetSolid( SOLID_BBOX );
  512. AddSolidFlags( FSOLID_NOT_STANDABLE );
  513. SetMoveType( MOVETYPE_STEP );
  514. SetBloodColor( BLOOD_COLOR_RED );
  515. m_nIdleChatterType = METROPOLICE_CHATTER_ASK_QUESTION;
  516. m_bSimpleCops = HasSpawnFlags( SF_METROPOLICE_SIMPLE_VERSION );
  517. if ( HasSpawnFlags( SF_METROPOLICE_NOCHATTER ) )
  518. {
  519. AddSpawnFlags( SF_NPC_GAG );
  520. }
  521. if (!m_bSimpleCops)
  522. {
  523. m_iHealth = sk_metropolice_health.GetFloat();
  524. }
  525. else
  526. {
  527. m_iHealth = sk_metropolice_simple_health.GetFloat();
  528. }
  529. m_flFieldOfView = -0.2;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
  530. m_NPCState = NPC_STATE_NONE;
  531. if ( !HasSpawnFlags( SF_NPC_START_EFFICIENT ) )
  532. {
  533. CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );
  534. CapabilitiesAdd( bits_CAP_AIM_GUN | bits_CAP_MOVE_SHOOT );
  535. }
  536. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  537. CapabilitiesAdd( bits_CAP_USE_WEAPONS | bits_CAP_NO_HIT_SQUADMATES );
  538. CapabilitiesAdd( bits_CAP_SQUAD );
  539. CapabilitiesAdd( bits_CAP_DUCK | bits_CAP_DOORS_GROUP );
  540. CapabilitiesAdd( bits_CAP_USE_SHOT_REGULATOR );
  541. m_nBurstHits = 0;
  542. m_HackedGunPos = Vector ( 0, 0, 55 );
  543. m_iPistolClips = METROPOLICE_NUM_CLIPS;
  544. NPCInit();
  545. // NOTE: This must occur *after* init, since init sets default dist look
  546. if ( HasSpawnFlags( SF_METROPOLICE_MID_RANGE_ATTACK ) )
  547. {
  548. m_flDistTooFar = METROPOLICE_MID_RANGE_ATTACK_RANGE;
  549. SetDistLook( METROPOLICE_MID_RANGE_ATTACK_RANGE );
  550. }
  551. m_hManhack = NULL;
  552. if ( GetActiveWeapon() )
  553. {
  554. CBaseCombatWeapon *pWeapon;
  555. pWeapon = GetActiveWeapon();
  556. if( !FClassnameIs( pWeapon, "weapon_pistol" ) )
  557. {
  558. m_fWeaponDrawn = true;
  559. }
  560. if( !m_fWeaponDrawn )
  561. {
  562. GetActiveWeapon()->AddEffects( EF_NODRAW );
  563. }
  564. }
  565. m_TimeYieldShootSlot.Set( 2, 6 );
  566. GetEnemies()->SetFreeKnowledgeDuration( 6.0 );
  567. m_bShouldActivateBaton = false;
  568. m_flValidStitchTime = -1.0f;
  569. m_flNextLedgeCheckTime = -1.0f;
  570. m_nBurstReloadCount = METROPOLICE_BURST_RELOAD_COUNT;
  571. SetBurstMode( false );
  572. // Clear out spawnflag if we're missing the smg1
  573. if( HasSpawnFlags( SF_METROPOLICE_ALWAYS_STITCH ) )
  574. {
  575. if ( !Weapon_OwnsThisType( "weapon_smg1" ) )
  576. {
  577. Warning( "Warning! Metrocop is trying to use the stitch behavior but he has no smg1!\n" );
  578. RemoveSpawnFlags( SF_METROPOLICE_ALWAYS_STITCH );
  579. }
  580. }
  581. m_nNumWarnings = 0;
  582. m_bPlayerTooClose = false;
  583. m_bKeepFacingPlayer = false;
  584. m_flChasePlayerTime = 0;
  585. m_vecPreChaseOrigin = vec3_origin;
  586. m_flPreChaseYaw = 0;
  587. SetUse( &CNPC_MetroPolice::PrecriminalUse );
  588. // Start us with a visible manhack if we have one
  589. if ( m_iManhacks )
  590. {
  591. SetBodygroup( METROPOLICE_BODYGROUP_MANHACK, true );
  592. }
  593. }
  594. //-----------------------------------------------------------------------------
  595. // Update weapon ranges
  596. //-----------------------------------------------------------------------------
  597. void CNPC_MetroPolice::Weapon_Equip( CBaseCombatWeapon *pWeapon )
  598. {
  599. BaseClass::Weapon_Equip( pWeapon );
  600. if ( HasSpawnFlags(SF_METROPOLICE_MID_RANGE_ATTACK) && GetActiveWeapon() )
  601. {
  602. GetActiveWeapon()->m_fMaxRange1 = METROPOLICE_MID_RANGE_ATTACK_RANGE;
  603. GetActiveWeapon()->m_fMaxRange2 = METROPOLICE_MID_RANGE_ATTACK_RANGE;
  604. }
  605. }
  606. //-----------------------------------------------------------------------------
  607. // FuncTankBehavior-related sentences
  608. //-----------------------------------------------------------------------------
  609. void CNPC_MetroPolice::SpeakFuncTankSentence( int nSentenceType )
  610. {
  611. switch ( nSentenceType )
  612. {
  613. case FUNCTANK_SENTENCE_MOVE_TO_MOUNT:
  614. m_Sentences.Speak( "METROPOLICE_FT_APPROACH", SENTENCE_PRIORITY_MEDIUM );
  615. break;
  616. case FUNCTANK_SENTENCE_JUST_MOUNTED:
  617. m_Sentences.Speak( "METROPOLICE_FT_MOUNT", SENTENCE_PRIORITY_HIGH );
  618. break;
  619. case FUNCTANK_SENTENCE_SCAN_FOR_ENEMIES:
  620. m_Sentences.Speak( "METROPOLICE_FT_SCAN", SENTENCE_PRIORITY_NORMAL );
  621. break;
  622. case FUNCTANK_SENTENCE_DISMOUNTING:
  623. m_Sentences.Speak( "METROPOLICE_FT_DISMOUNT", SENTENCE_PRIORITY_HIGH );
  624. break;
  625. }
  626. }
  627. //-----------------------------------------------------------------------------
  628. // Standoff Behavior-related sentences
  629. //-----------------------------------------------------------------------------
  630. void CNPC_MetroPolice::SpeakStandoffSentence( int nSentenceType )
  631. {
  632. switch ( nSentenceType )
  633. {
  634. case STANDOFF_SENTENCE_BEGIN_STANDOFF:
  635. m_Sentences.Speak( "METROPOLICE_SO_BEGIN", SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_SQUAD_LEADER );
  636. break;
  637. case STANDOFF_SENTENCE_END_STANDOFF:
  638. m_Sentences.Speak( "METROPOLICE_SO_END", SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_SQUAD_LEADER );
  639. break;
  640. case STANDOFF_SENTENCE_OUT_OF_AMMO:
  641. AnnounceOutOfAmmo( );
  642. break;
  643. case STANDOFF_SENTENCE_FORCED_TAKE_COVER:
  644. m_Sentences.Speak( "METROPOLICE_SO_FORCE_COVER" );
  645. break;
  646. case STANDOFF_SENTENCE_STAND_CHECK_TARGET:
  647. if ( gm_flTimeLastSpokePeek != 0 && gpGlobals->curtime - gm_flTimeLastSpokePeek > 20 )
  648. {
  649. m_Sentences.Speak( "METROPOLICE_SO_PEEK" );
  650. gm_flTimeLastSpokePeek = gpGlobals->curtime;
  651. }
  652. break;
  653. }
  654. }
  655. //-----------------------------------------------------------------------------
  656. // Assault Behavior-related sentences
  657. //-----------------------------------------------------------------------------
  658. void CNPC_MetroPolice::SpeakAssaultSentence( int nSentenceType )
  659. {
  660. switch ( nSentenceType )
  661. {
  662. case ASSAULT_SENTENCE_HIT_RALLY_POINT:
  663. m_Sentences.SpeakQueued( "METROPOLICE_AS_HIT_RALLY", SENTENCE_PRIORITY_NORMAL );
  664. break;
  665. case ASSAULT_SENTENCE_HIT_ASSAULT_POINT:
  666. m_Sentences.SpeakQueued( "METROPOLICE_AS_HIT_ASSAULT", SENTENCE_PRIORITY_NORMAL );
  667. break;
  668. case ASSAULT_SENTENCE_SQUAD_ADVANCE_TO_RALLY:
  669. if ( m_Sentences.Speak( "METROPOLICE_AS_ADV_RALLY", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_SQUAD_LEADER ) >= 0 )
  670. {
  671. GetSquad()->BroadcastInteraction( g_interactionMetrocopClearSentenceQueues, NULL );
  672. }
  673. break;
  674. case ASSAULT_SENTENCE_SQUAD_ADVANCE_TO_ASSAULT:
  675. if ( m_Sentences.Speak( "METROPOLICE_AS_ADV_ASSAULT", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_SQUAD_LEADER ) >= 0 )
  676. {
  677. GetSquad()->BroadcastInteraction( g_interactionMetrocopClearSentenceQueues, NULL );
  678. }
  679. break;
  680. case ASSAULT_SENTENCE_COVER_NO_AMMO:
  681. AnnounceOutOfAmmo( );
  682. break;
  683. case ASSAULT_SENTENCE_UNDER_ATTACK:
  684. m_Sentences.Speak( "METROPOLICE_GO_ALERT" );
  685. break;
  686. }
  687. }
  688. //-----------------------------------------------------------------------------
  689. // Speaking while using TASK_SPEAK_SENTENCE
  690. //-----------------------------------------------------------------------------
  691. void CNPC_MetroPolice::SpeakSentence( int nSentenceType )
  692. {
  693. if ( !PlayerIsCriminal() )
  694. return;
  695. if ( nSentenceType >= SENTENCE_BASE_BEHAVIOR_INDEX )
  696. {
  697. if ( GetRunningBehavior() == &m_FuncTankBehavior )
  698. {
  699. SpeakFuncTankSentence( nSentenceType );
  700. return;
  701. }
  702. if ( GetRunningBehavior() == &m_StandoffBehavior )
  703. {
  704. SpeakStandoffSentence( nSentenceType );
  705. return;
  706. }
  707. if ( GetRunningBehavior() == &m_AssaultBehavior )
  708. {
  709. SpeakAssaultSentence( nSentenceType );
  710. return;
  711. }
  712. }
  713. switch ( nSentenceType )
  714. {
  715. case METROPOLICE_SENTENCE_FREEZE:
  716. m_Sentences.Speak( "METROPOLICE_FREEZE", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
  717. break;
  718. case METROPOLICE_SENTENCE_HES_OVER_HERE:
  719. m_Sentences.Speak( "METROPOLICE_OVER_HERE", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
  720. break;
  721. case METROPOLICE_SENTENCE_HES_RUNNING:
  722. m_Sentences.Speak( "METROPOLICE_HES_RUNNING", SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL );
  723. break;
  724. case METROPOLICE_SENTENCE_TAKE_HIM_DOWN:
  725. m_Sentences.Speak( "METROPOLICE_TAKE_HIM_DOWN", SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL );
  726. break;
  727. case METROPOLICE_SENTENCE_ARREST_IN_POSITION:
  728. m_Sentences.Speak( "METROPOLICE_ARREST_IN_POS", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
  729. break;
  730. case METROPOLICE_SENTENCE_DEPLOY_MANHACK:
  731. m_Sentences.Speak( "METROPOLICE_DEPLOY_MANHACK" );
  732. break;
  733. case METROPOLICE_SENTENCE_MOVE_INTO_POSITION:
  734. {
  735. CBaseEntity *pEntity = GetEnemy();
  736. // NOTE: This is a good time to check to see if the player is hurt.
  737. // Have the cops notice this and call out
  738. if ( pEntity && !HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) )
  739. {
  740. if ( pEntity->IsPlayer() && (pEntity->GetHealth() <= 20) )
  741. {
  742. if ( !HasMemory(bits_MEMORY_PLAYER_HURT) )
  743. {
  744. if ( m_Sentences.Speak( "METROPOLICE_PLAYERHIT", SENTENCE_PRIORITY_HIGH ) >= 0 )
  745. {
  746. m_pSquad->SquadRemember(bits_MEMORY_PLAYER_HURT);
  747. }
  748. }
  749. }
  750. if ( GetNavigator()->GetPath()->GetPathLength() > 20 * 12.0f )
  751. {
  752. m_Sentences.Speak( "METROPOLICE_FLANK" );
  753. }
  754. }
  755. }
  756. break;
  757. case METROPOLICE_SENTENCE_HEARD_SOMETHING:
  758. if ( ( GetState() == NPC_STATE_ALERT ) || ( GetState() == NPC_STATE_IDLE ) )
  759. {
  760. m_Sentences.Speak( "METROPOLICE_HEARD_SOMETHING", SENTENCE_PRIORITY_MEDIUM );
  761. }
  762. break;
  763. }
  764. }
  765. //-----------------------------------------------------------------------------
  766. // Speaking
  767. //-----------------------------------------------------------------------------
  768. void CNPC_MetroPolice::AnnounceEnemyType( CBaseEntity *pEnemy )
  769. {
  770. if ( !pEnemy || !m_pSquad )
  771. return;
  772. // Don't announce enemies when the player isn't a criminal
  773. if ( !PlayerIsCriminal() )
  774. return;
  775. // Don't announce enemies when I'm in arrest behavior
  776. if ( HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) )
  777. return;
  778. if ( m_pSquad->IsLeader( this ) || ( m_pSquad->GetLeader() && m_pSquad->GetLeader()->GetEnemy() != GetEnemy() ) )
  779. {
  780. // First contact, and I'm the squad leader.
  781. const char *pSentenceName = "METROPOLICE_MONST";
  782. switch ( pEnemy->Classify() )
  783. {
  784. case CLASS_PLAYER:
  785. {
  786. CBasePlayer *pPlayer = assert_cast<CBasePlayer*>( pEnemy );
  787. if ( pPlayer && pPlayer->IsInAVehicle() )
  788. {
  789. pSentenceName = "METROPOLICE_MONST_PLAYER_VEHICLE";
  790. }
  791. else
  792. {
  793. pSentenceName = "METROPOLICE_MONST_PLAYER";
  794. }
  795. }
  796. break;
  797. case CLASS_PLAYER_ALLY:
  798. case CLASS_CITIZEN_REBEL:
  799. case CLASS_CITIZEN_PASSIVE:
  800. case CLASS_VORTIGAUNT:
  801. pSentenceName = "METROPOLICE_MONST_CITIZENS";
  802. break;
  803. case CLASS_PLAYER_ALLY_VITAL:
  804. pSentenceName = "METROPOLICE_MONST_CHARACTER";
  805. break;
  806. case CLASS_ANTLION:
  807. pSentenceName = "METROPOLICE_MONST_BUGS";
  808. break;
  809. case CLASS_ZOMBIE:
  810. pSentenceName = "METROPOLICE_MONST_ZOMBIES";
  811. break;
  812. case CLASS_HEADCRAB:
  813. case CLASS_BARNACLE:
  814. pSentenceName = "METROPOLICE_MONST_PARASITES";
  815. break;
  816. }
  817. m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH );
  818. }
  819. else
  820. {
  821. if ( m_pSquad->GetLeader() && FOkToMakeSound( SENTENCE_PRIORITY_MEDIUM ) )
  822. {
  823. // squelch anything that isn't high priority so the leader can speak
  824. JustMadeSound( SENTENCE_PRIORITY_MEDIUM );
  825. }
  826. }
  827. }
  828. //-----------------------------------------------------------------------------
  829. // Speaking
  830. //-----------------------------------------------------------------------------
  831. void CNPC_MetroPolice::AnnounceEnemyKill( CBaseEntity *pEnemy )
  832. {
  833. if ( !pEnemy )
  834. return;
  835. const char *pSentenceName = "METROPOLICE_KILL_MONST";
  836. switch ( pEnemy->Classify() )
  837. {
  838. case CLASS_PLAYER:
  839. pSentenceName = "METROPOLICE_KILL_PLAYER";
  840. break;
  841. // no sentences for these guys yet
  842. case CLASS_PLAYER_ALLY:
  843. case CLASS_CITIZEN_REBEL:
  844. case CLASS_CITIZEN_PASSIVE:
  845. case CLASS_VORTIGAUNT:
  846. pSentenceName = "METROPOLICE_KILL_CITIZENS";
  847. break;
  848. case CLASS_PLAYER_ALLY_VITAL:
  849. pSentenceName = "METROPOLICE_KILL_CHARACTER";
  850. break;
  851. case CLASS_ANTLION:
  852. pSentenceName = "METROPOLICE_KILL_BUGS";
  853. break;
  854. case CLASS_ZOMBIE:
  855. pSentenceName = "METROPOLICE_KILL_ZOMBIES";
  856. break;
  857. case CLASS_HEADCRAB:
  858. case CLASS_BARNACLE:
  859. pSentenceName = "METROPOLICE_KILL_PARASITES";
  860. break;
  861. }
  862. m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH );
  863. }
  864. //-----------------------------------------------------------------------------
  865. // Announce out of ammo
  866. //-----------------------------------------------------------------------------
  867. void CNPC_MetroPolice::AnnounceOutOfAmmo( )
  868. {
  869. if ( HasCondition( COND_NO_PRIMARY_AMMO ) )
  870. {
  871. m_Sentences.Speak( "METROPOLICE_COVER_NO_AMMO" );
  872. }
  873. else
  874. {
  875. m_Sentences.Speak( "METROPOLICE_COVER_LOW_AMMO" );
  876. }
  877. }
  878. //-----------------------------------------------------------------------------
  879. // We're taking cover from danger
  880. //-----------------------------------------------------------------------------
  881. void CNPC_MetroPolice::AnnounceTakeCoverFromDanger( CSound *pSound )
  882. {
  883. CBaseEntity *pSoundOwner = pSound->m_hOwner;
  884. if ( pSoundOwner )
  885. {
  886. CBaseGrenade *pGrenade = dynamic_cast<CBaseGrenade *>(pSoundOwner);
  887. if ( pGrenade )
  888. {
  889. if ( IRelationType( pGrenade->GetThrower() ) != D_LI )
  890. {
  891. // special case call out for enemy grenades
  892. m_Sentences.Speak( "METROPOLICE_DANGER_GREN", SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL );
  893. }
  894. return;
  895. }
  896. if ( pSoundOwner->GetServerVehicle() )
  897. {
  898. m_Sentences.Speak( "METROPOLICE_DANGER_VEHICLE", SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL );
  899. return;
  900. }
  901. if ( FClassnameIs( pSoundOwner, "npc_manhack" ) )
  902. {
  903. if ( pSoundOwner->HasPhysicsAttacker( 1.0f ) )
  904. {
  905. m_Sentences.Speak( "METROPOLICE_DANGER_MANHACK", SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL );
  906. }
  907. return;
  908. }
  909. }
  910. // I hear something dangerous, probably need to take cover.
  911. // dangerous sound nearby!, call it out
  912. const char *pSentenceName = "METROPOLICE_DANGER";
  913. m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH, SENTENCE_CRITERIA_NORMAL );
  914. }
  915. //-----------------------------------------------------------------------------
  916. // Are we currently firing a burst?
  917. //-----------------------------------------------------------------------------
  918. bool CNPC_MetroPolice::IsCurrentlyFiringBurst() const
  919. {
  920. return (m_nBurstMode != BURST_NOT_ACTIVE);
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Is my enemy currently in an airboat?
  924. //-----------------------------------------------------------------------------
  925. bool CNPC_MetroPolice::IsEnemyInAnAirboat() const
  926. {
  927. // Should this be a condition??
  928. if ( !GetEnemy() || !GetEnemy()->IsPlayer() )
  929. return false;
  930. CBaseEntity *pVehicle = static_cast<CBasePlayer*>( GetEnemy() )->GetVehicleEntity();
  931. if ( !pVehicle )
  932. return false;
  933. // NOTE: Could just return true if in a vehicle maybe
  934. return FClassnameIs( pVehicle, "prop_vehicle_airboat" );
  935. }
  936. //-----------------------------------------------------------------------------
  937. // Returns the airboat
  938. //-----------------------------------------------------------------------------
  939. CBaseEntity *CNPC_MetroPolice::GetEnemyAirboat() const
  940. {
  941. // Should this be a condition??
  942. if ( !GetEnemy() || !GetEnemy()->IsPlayer() )
  943. return NULL;
  944. return static_cast<CBasePlayer*>( GetEnemy() )->GetVehicleEntity();
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Which entity are we actually trying to shoot at?
  948. //-----------------------------------------------------------------------------
  949. CBaseEntity *CNPC_MetroPolice::GetShootTarget()
  950. {
  951. // Should this be a condition??
  952. CBaseEntity *pEnemy = GetEnemy();
  953. if ( !pEnemy || !pEnemy->IsPlayer() )
  954. return pEnemy;
  955. CBaseEntity *pVehicle = static_cast<CBasePlayer*>( pEnemy )->GetVehicleEntity();
  956. return pVehicle ? pVehicle : pEnemy;
  957. }
  958. //-----------------------------------------------------------------------------
  959. // Set up the shot regulator based on the equipped weapon
  960. //-----------------------------------------------------------------------------
  961. // Ranges across which to tune fire rates
  962. const float MIN_PISTOL_MODIFY_DIST = 15 * 12;
  963. const float MAX_PISTOL_MODIFY_DIST = 150 * 12;
  964. // Range for rest period minimums
  965. const float MIN_MIN_PISTOL_REST_INTERVAL = 0.6;
  966. const float MAX_MIN_PISTOL_REST_INTERVAL = 1.2;
  967. // Range for rest period maximums
  968. const float MIN_MAX_PISTOL_REST_INTERVAL = 1.2;
  969. const float MAX_MAX_PISTOL_REST_INTERVAL = 2.0;
  970. // Range for burst minimums
  971. const int MIN_MIN_PISTOL_BURST = 2;
  972. const int MAX_MIN_PISTOL_BURST = 4;
  973. // Range for burst maximums
  974. const int MIN_MAX_PISTOL_BURST = 5;
  975. const int MAX_MAX_PISTOL_BURST = 8;
  976. void CNPC_MetroPolice::OnUpdateShotRegulator( )
  977. {
  978. BaseClass::OnUpdateShotRegulator();
  979. // FIXME: This code (except the burst interval) could be used for all weapon types
  980. if( Weapon_OwnsThisType( "weapon_pistol" ) )
  981. {
  982. if ( m_nBurstMode == BURST_NOT_ACTIVE )
  983. {
  984. if ( GetEnemy() )
  985. {
  986. float dist = WorldSpaceCenter().DistTo( GetEnemy()->WorldSpaceCenter() );
  987. dist = clamp( dist, MIN_PISTOL_MODIFY_DIST, MAX_PISTOL_MODIFY_DIST );
  988. float factor = (dist - MIN_PISTOL_MODIFY_DIST) / (MAX_PISTOL_MODIFY_DIST - MIN_PISTOL_MODIFY_DIST);
  989. int nMinBurst = MIN_MIN_PISTOL_BURST + ( MAX_MIN_PISTOL_BURST - MIN_MIN_PISTOL_BURST ) * (1.0 - factor);
  990. int nMaxBurst = MIN_MAX_PISTOL_BURST + ( MAX_MAX_PISTOL_BURST - MIN_MAX_PISTOL_BURST ) * (1.0 - factor);
  991. float flMinRestInterval = MIN_MIN_PISTOL_REST_INTERVAL + ( MAX_MIN_PISTOL_REST_INTERVAL - MIN_MIN_PISTOL_REST_INTERVAL ) * factor;
  992. float flMaxRestInterval = MIN_MAX_PISTOL_REST_INTERVAL + ( MAX_MAX_PISTOL_REST_INTERVAL - MIN_MAX_PISTOL_REST_INTERVAL ) * factor;
  993. GetShotRegulator()->SetRestInterval( flMinRestInterval, flMaxRestInterval );
  994. GetShotRegulator()->SetBurstShotCountRange( nMinBurst, nMaxBurst );
  995. }
  996. else
  997. {
  998. GetShotRegulator()->SetBurstShotCountRange(GetActiveWeapon()->GetMinBurst(), GetActiveWeapon()->GetMaxBurst() );
  999. GetShotRegulator()->SetRestInterval( 0.6, 1.4 );
  1000. }
  1001. }
  1002. // Add some noise into the pistol
  1003. GetShotRegulator()->SetBurstInterval( 0.2f, 0.5f );
  1004. }
  1005. }
  1006. //-----------------------------------------------------------------------------
  1007. // Burst mode!
  1008. //-----------------------------------------------------------------------------
  1009. void CNPC_MetroPolice::SetBurstMode( bool bEnable )
  1010. {
  1011. int nOldBurstMode = m_nBurstMode;
  1012. m_nBurstSteerMode = BURST_STEER_NONE;
  1013. m_flBurstPredictTime = gpGlobals->curtime - 1.0f;
  1014. if ( GetActiveWeapon() )
  1015. {
  1016. m_nBurstMode = bEnable ? BURST_ACTIVE : BURST_NOT_ACTIVE;
  1017. if ( bEnable )
  1018. {
  1019. m_nBurstHits = 0;
  1020. }
  1021. }
  1022. else
  1023. {
  1024. m_nBurstMode = BURST_NOT_ACTIVE;
  1025. }
  1026. if ( m_nBurstMode != nOldBurstMode )
  1027. {
  1028. OnUpdateShotRegulator();
  1029. if ( m_nBurstMode == BURST_NOT_ACTIVE )
  1030. {
  1031. // Check for inconsistency...
  1032. int nMinBurstCount, nMaxBurstCount;
  1033. GetShotRegulator()->GetBurstShotCountRange( &nMinBurstCount, &nMaxBurstCount );
  1034. if ( GetShotRegulator()->GetBurstShotsRemaining() > nMaxBurstCount )
  1035. {
  1036. GetShotRegulator()->SetBurstShotsRemaining( nMaxBurstCount );
  1037. }
  1038. }
  1039. }
  1040. }
  1041. //-----------------------------------------------------------------------------
  1042. // Should we attempt to stitch?
  1043. //-----------------------------------------------------------------------------
  1044. bool CNPC_MetroPolice::ShouldAttemptToStitch()
  1045. {
  1046. if ( IsEnemyInAnAirboat() )
  1047. return true;
  1048. if ( !GetShootTarget() )
  1049. return false;
  1050. if ( HasSpawnFlags( SF_METROPOLICE_ALWAYS_STITCH ) )
  1051. {
  1052. // Don't stitch if the player is at the same level or higher
  1053. if ( GetEnemy()->GetAbsOrigin().z - GetAbsOrigin().z > -36 )
  1054. return false;
  1055. return true;
  1056. }
  1057. return false;
  1058. }
  1059. //-----------------------------------------------------------------------------
  1060. // position to shoot at
  1061. //-----------------------------------------------------------------------------
  1062. Vector CNPC_MetroPolice::StitchAimTarget( const Vector &posSrc, bool bNoisy )
  1063. {
  1064. // This will make us aim a stitch at the feet of the player so we can see it
  1065. if ( !GetEnemy()->IsPlayer() )
  1066. return GetShootTarget()->BodyTarget( posSrc, bNoisy );
  1067. if ( !IsEnemyInAnAirboat() )
  1068. {
  1069. Vector vecBodyTarget;
  1070. if ( ( GetEnemy()->GetWaterLevel() == 0 ) && ( GetEnemy()->GetFlags() & FL_ONGROUND ) )
  1071. {
  1072. GetEnemy()->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.08f ), &vecBodyTarget );
  1073. return vecBodyTarget;
  1074. }
  1075. // Underwater? Just use the normal thing
  1076. if ( GetEnemy()->GetWaterLevel() == 3 )
  1077. return GetShootTarget()->BodyTarget( posSrc, bNoisy );
  1078. // Trace down...
  1079. trace_t trace;
  1080. GetEnemy()->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &vecBodyTarget );
  1081. float flHeight = GetEnemy()->WorldAlignSize().z;
  1082. UTIL_TraceLine( vecBodyTarget, vecBodyTarget + Vector( 0, 0, -flHeight -80 ),
  1083. (MASK_SOLID_BRUSHONLY | MASK_WATER), NULL, COLLISION_GROUP_NONE, &trace );
  1084. return trace.endpos;
  1085. }
  1086. // NOTE: HACK! Ths 0.08 is where the water level happens to be.
  1087. // We probably want to find the exact water level and use that as the z position.
  1088. Vector vecBodyTarget;
  1089. if ( !bNoisy )
  1090. {
  1091. GetShootTarget()->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.08f ), &vecBodyTarget );
  1092. }
  1093. else
  1094. {
  1095. GetShootTarget()->CollisionProp()->RandomPointInBounds( Vector( 0.25f, 0.25f, 0.08f ), Vector( 0.75f, 0.75f, 0.08f ), &vecBodyTarget );
  1096. }
  1097. return vecBodyTarget;
  1098. }
  1099. //-----------------------------------------------------------------------------
  1100. // Burst mode!
  1101. //-----------------------------------------------------------------------------
  1102. void CNPC_MetroPolice::AimBurstRandomly( int nMinCount, int nMaxCount, float flMinDelay, float flMaxDelay )
  1103. {
  1104. if ( !IsCurrentlyFiringBurst() )
  1105. return;
  1106. GetShotRegulator()->SetParameters( nMinCount, nMaxCount, flMinDelay, flMaxDelay );
  1107. GetShotRegulator()->Reset( true );
  1108. int nShotCount = GetShotRegulator()->GetBurstShotsRemaining();
  1109. Vector vecDelta = StitchAimTarget( GetAbsOrigin(), true ) - Weapon_ShootPosition();
  1110. VectorNormalize( vecDelta );
  1111. // Choose a random direction vector perpendicular to the delta position
  1112. Vector vecRight, vecUp;
  1113. VectorVectors( vecDelta, vecRight, vecUp );
  1114. float flAngle = random->RandomFloat( 0.0f, 2 * M_PI );
  1115. VectorMultiply( vecRight, cos(flAngle), m_vecBurstDelta );
  1116. VectorMA( m_vecBurstDelta, sin(flAngle), vecUp, m_vecBurstDelta );
  1117. // The size of this determines the cone angle
  1118. m_vecBurstDelta *= 0.4f;
  1119. VectorMA( vecDelta, -0.5f, m_vecBurstDelta, m_vecBurstTargetPos );
  1120. m_vecBurstTargetPos += Weapon_ShootPosition();
  1121. m_vecBurstDelta /= (nShotCount - 1);
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. // Choose a random vector somewhere between the two specified vectors
  1125. //-----------------------------------------------------------------------------
  1126. void CNPC_MetroPolice::RandomDirectionBetweenVectors( const Vector &vecStart, const Vector &vecEnd, Vector *pResult )
  1127. {
  1128. Assert( fabs( vecStart.Length() - 1.0f ) < 1e-3 );
  1129. Assert( fabs( vecEnd.Length() - 1.0f ) < 1e-3 );
  1130. float flCosAngle = DotProduct( vecStart, vecEnd );
  1131. if ( fabs( flCosAngle - 1.0f ) < 1e-3 )
  1132. {
  1133. *pResult = vecStart;
  1134. return;
  1135. }
  1136. Vector vecNormal;
  1137. CrossProduct( vecStart, vecEnd, vecNormal );
  1138. float flLength = VectorNormalize( vecNormal );
  1139. if ( flLength < 1e-3 )
  1140. {
  1141. // This is wrong for anti-parallel vectors. so what?
  1142. *pResult = vecStart;
  1143. return;
  1144. }
  1145. // Rotate the starting angle the specified amount
  1146. float flAngle = acos(flCosAngle) * random->RandomFloat( 0.0f, 1.0f );
  1147. VMatrix rotationMatrix;
  1148. MatrixBuildRotationAboutAxis( rotationMatrix, vecNormal, flAngle );
  1149. Vector3DMultiply( rotationMatrix, vecStart, *pResult );
  1150. }
  1151. //-----------------------------------------------------------------------------
  1152. // Compute a predicted shoot target position n seconds into the future
  1153. //-----------------------------------------------------------------------------
  1154. void CNPC_MetroPolice::PredictShootTargetPosition( float flDeltaTime, float flMinLeadDist, float flAddVelocity, Vector *pVecTarget, Vector *pVecTargetVelocity )
  1155. {
  1156. CBaseEntity *pShootTarget = GetShootTarget();
  1157. *pVecTarget = StitchAimTarget( GetAbsOrigin(), true );
  1158. Vector vecSmoothedVel = pShootTarget->GetSmoothedVelocity();
  1159. // When we're in the air, don't predict vertical motion
  1160. if( (pShootTarget->GetFlags() & FL_ONGROUND) == 0 )
  1161. {
  1162. vecSmoothedVel.z = 0.0f;
  1163. }
  1164. Vector vecVelocity;
  1165. AngularImpulse angImpulse;
  1166. GetShootTarget()->GetVelocity( &vecVelocity, &angImpulse );
  1167. Vector vecLeadVector;
  1168. VMatrix rotationMatrix;
  1169. float flAngVel = VectorNormalize( angImpulse );
  1170. flAngVel -= 30.0f;
  1171. if ( flAngVel > 0.0f )
  1172. {
  1173. MatrixBuildRotationAboutAxis( rotationMatrix, angImpulse, flAngVel * flDeltaTime * 0.333f );
  1174. Vector3DMultiply( rotationMatrix, vecSmoothedVel, vecLeadVector );
  1175. }
  1176. else
  1177. {
  1178. vecLeadVector = vecSmoothedVel;
  1179. }
  1180. if ( flAddVelocity != 0.0f )
  1181. {
  1182. Vector vecForward;
  1183. pShootTarget->GetVectors( &vecForward, NULL, NULL );
  1184. VectorMA( vecLeadVector, flAddVelocity, vecForward, vecLeadVector );
  1185. }
  1186. *pVecTargetVelocity = vecLeadVector;
  1187. if ( (vecLeadVector.LengthSqr() * flDeltaTime * flDeltaTime) < flMinLeadDist * flMinLeadDist )
  1188. {
  1189. VectorNormalize( vecLeadVector );
  1190. vecLeadVector *= flMinLeadDist;
  1191. }
  1192. else
  1193. {
  1194. vecLeadVector *= flDeltaTime;
  1195. }
  1196. *pVecTarget += vecLeadVector;
  1197. }
  1198. //-----------------------------------------------------------------------------
  1199. // Compute a predicted velocity n seconds into the future (given a known acceleration rate)
  1200. //-----------------------------------------------------------------------------
  1201. void CNPC_MetroPolice::PredictShootTargetVelocity( float flDeltaTime, Vector *pVecTargetVel )
  1202. {
  1203. *pVecTargetVel = GetShootTarget()->GetSmoothedVelocity();
  1204. // Unless there's a big angular velocity, we can assume he accelerates
  1205. // along the forward direction. Predict acceleration for
  1206. Vector vecForward;
  1207. GetShootTarget()->GetVectors( &vecForward, NULL, NULL );
  1208. // float flBlendFactor = 1.0f;
  1209. // VectorMA( *pVecTargetVel, flBlendFactor * VEHICLE_PREDICT_ACCELERATION, vecForward, *pVecTargetVel );
  1210. // if ( pVecTargetVel->LengthSqr() > (VEHICLE_PREDICT_MAX_SPEED * VEHICLE_PREDICT_MAX_SPEED) )
  1211. // {
  1212. // VectorNormalize( *pVecTargetVel );
  1213. // *pVecTargetVel *= VEHICLE_PREDICT_MAX_SPEED;
  1214. // }
  1215. }
  1216. //-----------------------------------------------------------------------------
  1217. // How many shots will I fire in a particular amount of time?
  1218. //-----------------------------------------------------------------------------
  1219. int CNPC_MetroPolice::CountShotsInTime( float flDeltaTime ) const
  1220. {
  1221. return (int)(flDeltaTime / GetActiveWeapon()->GetFireRate() + 0.5f);
  1222. }
  1223. float CNPC_MetroPolice::GetTimeForShots( int nShotCount ) const
  1224. {
  1225. return nShotCount * GetActiveWeapon()->GetFireRate();
  1226. }
  1227. //-----------------------------------------------------------------------------
  1228. // Visualize stitch
  1229. //-----------------------------------------------------------------------------
  1230. void CNPC_MetroPolice::VisualizeStitch( const Vector &vecStart, const Vector &vecEnd )
  1231. {
  1232. NDebugOverlay::Cross3D( vecStart, -Vector(32,32,32), Vector(32,32,32), 255, 0, 0, false, 5.0f );
  1233. NDebugOverlay::Cross3D( vecEnd, -Vector(32,32,32), Vector(32,32,32), 0, 255, 0, false, 5.0f );
  1234. NDebugOverlay::Line( vecStart, vecEnd, 0, 255, 0, true, 5.0f );
  1235. }
  1236. //-----------------------------------------------------------------------------
  1237. // Visualize line of death
  1238. //-----------------------------------------------------------------------------
  1239. void CNPC_MetroPolice::VisualizeLineOfDeath( )
  1240. {
  1241. Vector vecAcross, vecStart;
  1242. CrossProduct( m_vecBurstLineOfDeathDelta, Vector( 0, 0, 1 ), vecAcross );
  1243. VectorNormalize( vecAcross );
  1244. NDebugOverlay::Line( m_vecBurstLineOfDeathOrigin, m_vecBurstLineOfDeathOrigin + m_vecBurstLineOfDeathDelta, 255, 255, 0, false, 5.0f );
  1245. VectorMA( m_vecBurstLineOfDeathOrigin, m_flBurstSteerDistance, vecAcross, vecStart );
  1246. NDebugOverlay::Line( vecStart, vecStart + m_vecBurstLineOfDeathDelta, 255, 0, 0, false, 5.0f );
  1247. VectorMA( m_vecBurstLineOfDeathOrigin, -m_flBurstSteerDistance, vecAcross, vecStart );
  1248. NDebugOverlay::Line( vecStart, vecStart + m_vecBurstLineOfDeathDelta, 255, 0, 0, false, 5.0f );
  1249. }
  1250. //-----------------------------------------------------------------------------
  1251. // Burst mode!
  1252. //-----------------------------------------------------------------------------
  1253. #define AIM_AT_NEAR_DISTANCE_MIN 400.0f
  1254. #define AIM_AT_NEAR_DISTANCE_MAX 1000.0f
  1255. #define AIM_AT_NEAR_DISTANCE_DELTA (AIM_AT_NEAR_DISTANCE_MAX - AIM_AT_NEAR_DISTANCE_MIN)
  1256. #define AIM_AT_NEAR_DISTANCE_BONUS -200.0f
  1257. #define AIM_AT_FAR_DISTANCE_MIN 2000.0f
  1258. #define AIM_AT_FAR_DISTANCE_BONUS_DISTANCE 500.0f
  1259. #define AIM_AT_FAR_DISTANCE_BONUS 200.0f // Add this much bonus after each BONUS_DISTANCE
  1260. //-----------------------------------------------------------------------------
  1261. // Modify the stitch length
  1262. //-----------------------------------------------------------------------------
  1263. float CNPC_MetroPolice::ComputeDistanceStitchModifier( float flDistanceToTarget ) const
  1264. {
  1265. if ( flDistanceToTarget < AIM_AT_NEAR_DISTANCE_MIN )
  1266. {
  1267. return AIM_AT_NEAR_DISTANCE_BONUS;
  1268. }
  1269. if ( flDistanceToTarget < AIM_AT_NEAR_DISTANCE_MAX )
  1270. {
  1271. float flFraction = 1.0f - ((flDistanceToTarget - AIM_AT_NEAR_DISTANCE_MIN) / AIM_AT_NEAR_DISTANCE_DELTA);
  1272. return flFraction * AIM_AT_NEAR_DISTANCE_BONUS;
  1273. }
  1274. if ( flDistanceToTarget > AIM_AT_FAR_DISTANCE_MIN )
  1275. {
  1276. float flFactor = (flDistanceToTarget - AIM_AT_FAR_DISTANCE_MIN) / AIM_AT_FAR_DISTANCE_BONUS_DISTANCE;
  1277. return flFactor * AIM_AT_FAR_DISTANCE_BONUS;
  1278. }
  1279. return 0.0f;
  1280. }
  1281. //-----------------------------------------------------------------------------
  1282. // Set up the shot regulator
  1283. //-----------------------------------------------------------------------------
  1284. int CNPC_MetroPolice::SetupBurstShotRegulator( float flReactionTime )
  1285. {
  1286. // We want a certain amount of reaction time before the shots hit the boat
  1287. int nDesiredShotCount = CountShotsInTime( flReactionTime );
  1288. GetShotRegulator()->SetBurstShotCountRange( nDesiredShotCount, nDesiredShotCount );
  1289. GetShotRegulator()->SetRestInterval( 0.7f, 0.9f );
  1290. GetShotRegulator()->Reset( true );
  1291. int nShots = GetShotRegulator()->GetBurstShotsRemaining();
  1292. OnRangeAttack1();
  1293. return nShots;
  1294. }
  1295. //-----------------------------------------------------------------------------
  1296. // Shoots a burst right at the player
  1297. //-----------------------------------------------------------------------------
  1298. #define TIGHT_GROUP_MIN_DIST 750.0f
  1299. #define TIGHT_GROUP_MIN_SPEED 400.0f
  1300. void CNPC_MetroPolice::AimBurstTightGrouping( float flShotTime )
  1301. {
  1302. if ( !IsCurrentlyFiringBurst() )
  1303. return;
  1304. // We want a certain amount of reaction time before the shots hit the boat
  1305. SetupBurstShotRegulator( flShotTime );
  1306. // Max number of times we can hit the enemy.
  1307. // Can hit more if we're slow + close
  1308. float flDistToTargetSqr = GetShootTarget()->WorldSpaceCenter().DistToSqr( Weapon_ShootPosition() );
  1309. int nHitCount = sk_metropolice_stitch_tight_hitcount.GetInt();
  1310. Vector vecTargetVel;
  1311. GetShootTarget()->GetVelocity( &vecTargetVel, NULL );
  1312. if (( flDistToTargetSqr > TIGHT_GROUP_MIN_DIST*TIGHT_GROUP_MIN_DIST ) ||
  1313. ( vecTargetVel.LengthSqr() > TIGHT_GROUP_MIN_SPEED * TIGHT_GROUP_MIN_SPEED ))
  1314. {
  1315. m_nMaxBurstHits = random->RandomInt( nHitCount, nHitCount + 1 );
  1316. }
  1317. else
  1318. {
  1319. m_nMaxBurstHits = random->RandomInt( 2 * nHitCount - 1, 2 * nHitCount + 1 );
  1320. }
  1321. m_nBurstMode = BURST_TIGHT_GROUPING;
  1322. // This helps the NPC model aim at the correct point
  1323. m_nBurstSteerMode = BURST_STEER_EXACTLY_TOWARD_TARGET;
  1324. m_vecBurstTargetPos = GetEnemy()->WorldSpaceCenter();
  1325. }
  1326. //-----------------------------------------------------------------------------
  1327. // Reaction time for stitch
  1328. //-----------------------------------------------------------------------------
  1329. #define AIM_AT_TIME_DELTA_SPEED 100.0f
  1330. #define AIM_AT_TIME_DELTA_DIST 500.0f
  1331. #define AIM_AT_TIME_SPEED_COUNT 6
  1332. #define AIM_AT_TIME_DIST_COUNT 7
  1333. static float s_pReactionFraction[AIM_AT_TIME_DIST_COUNT][AIM_AT_TIME_SPEED_COUNT] =
  1334. {
  1335. { 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 1.0f },
  1336. { 0.5f, 0.5f, 0.5f, 0.5f, 0.75f, 1.0f },
  1337. { 0.5f, 0.5f, 0.5f, 0.65f, 0.8f, 1.0f },
  1338. { 0.5f, 0.5f, 0.5f, 0.75f, 1.0f, 1.0f },
  1339. { 0.5f, 0.5f, 0.75f, 1.0f, 1.0f, 1.0f },
  1340. { 0.75f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
  1341. { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
  1342. };
  1343. float CNPC_MetroPolice::AimBurstAtReactionTime( float flReactionTime, float flDistToTarget, float flCurrentSpeed )
  1344. {
  1345. flReactionTime *= sk_metropolice_stitch_reaction.GetFloat();
  1346. if ( IsEnemyInAnAirboat() )
  1347. {
  1348. float u = flCurrentSpeed / AIM_AT_TIME_DELTA_SPEED;
  1349. float v = flDistToTarget / AIM_AT_TIME_DELTA_DIST;
  1350. int nu = (int)u;
  1351. int nv = (int)v;
  1352. if (( nu < AIM_AT_TIME_SPEED_COUNT - 1 ) && ( nv < AIM_AT_TIME_DIST_COUNT - 1 ))
  1353. {
  1354. float fu = u - nu;
  1355. float fv = v - nv;
  1356. float flReactionFactor = s_pReactionFraction[nv][nu] * (1.0f - fu) * (1.0f - fv);
  1357. flReactionFactor += s_pReactionFraction[nv+1][nu] * (1.0f - fu) * fv;
  1358. flReactionFactor += s_pReactionFraction[nv][nu+1] * fu * (1.0f - fv);
  1359. flReactionFactor += s_pReactionFraction[nv+1][nu+1] * fu * fv;
  1360. flReactionTime *= flReactionFactor;
  1361. }
  1362. }
  1363. return flReactionTime;
  1364. }
  1365. //-----------------------------------------------------------------------------
  1366. // Burst mode!
  1367. //-----------------------------------------------------------------------------
  1368. #define AIM_AT_SHOT_DELTA_SPEED 100.0f
  1369. #define AIM_AT_SHOT_DELTA_DIST 500.0f
  1370. #define AIM_AT_SHOT_SPEED_COUNT 6
  1371. #define AIM_AT_SHOT_DIST_COUNT 6
  1372. static float s_pShotCountFraction[AIM_AT_TIME_DIST_COUNT][AIM_AT_TIME_SPEED_COUNT] =
  1373. {
  1374. { 3.0f, 3.0f, 2.5f, 1.5f, 1.0f, 0.0f },
  1375. { 3.0f, 3.0f, 2.5f, 1.25f, 0.5f, 0.0f },
  1376. { 2.5f, 2.5f, 2.0f, 1.0f, 0.0f, 0.0f },
  1377. { 2.0f, 2.0f, 1.5f, 0.5f, 0.0f, 0.0f },
  1378. { 1.0f, 1.0f, 1.0f, 0.5f, 0.0f, 0.0f },
  1379. { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
  1380. };
  1381. int CNPC_MetroPolice::AimBurstAtSetupHitCount( float flDistToTarget, float flCurrentSpeed )
  1382. {
  1383. // Max number of times we can hit the enemy
  1384. int nHitCount = sk_metropolice_stitch_at_hitcount.GetInt();
  1385. m_nMaxBurstHits = random->RandomInt( nHitCount, nHitCount + 1 );
  1386. if ( IsEnemyInAnAirboat() )
  1387. {
  1388. float u = flCurrentSpeed / AIM_AT_SHOT_DELTA_SPEED;
  1389. float v = flDistToTarget / AIM_AT_SHOT_DELTA_DIST;
  1390. int nu = (int)u;
  1391. int nv = (int)v;
  1392. if (( nu < AIM_AT_SHOT_SPEED_COUNT - 1 ) && ( nv < AIM_AT_SHOT_DIST_COUNT - 1 ))
  1393. {
  1394. float fu = u - nu;
  1395. float fv = v - nv;
  1396. float flShotFactor = s_pShotCountFraction[nv][nu] * (1.0f - fu) * (1.0f - fv);
  1397. flShotFactor += s_pShotCountFraction[nv+1][nu] * (1.0f - fu) * fv;
  1398. flShotFactor += s_pShotCountFraction[nv][nu+1] * fu * (1.0f - fv);
  1399. flShotFactor += s_pShotCountFraction[nv+1][nu+1] * fu * fv;
  1400. int nExtraShots = nHitCount * flShotFactor;
  1401. m_nMaxBurstHits += random->RandomInt( nExtraShots, nExtraShots + 1 );
  1402. return nExtraShots;
  1403. }
  1404. }
  1405. return 0;
  1406. }
  1407. //-----------------------------------------------------------------------------
  1408. // Burst mode!
  1409. //-----------------------------------------------------------------------------
  1410. #define AIM_AT_DEFAULT_STITCH_SHOT_DIST 40.0f
  1411. #define AIM_AT_SPEED_BONUS 200.0f
  1412. #define AIM_AT_REACTION_TIME_FRACTION 0.8f
  1413. #define AIM_AT_NEAR_REACTION_TIME_FRACTION 0.3f
  1414. #define AIM_AT_STEER_DISTANCE 125.0f
  1415. void CNPC_MetroPolice::AimBurstAtEnemy( float flReactionTime )
  1416. {
  1417. if ( !IsCurrentlyFiringBurst() )
  1418. return;
  1419. Vector vecVelocity;
  1420. GetShootTarget()->GetVelocity( &vecVelocity, NULL );
  1421. float flCurrentSpeed = vecVelocity.Length();
  1422. float flDistToTargetSqr = GetShootTarget()->WorldSpaceCenter().AsVector2D().DistToSqr( Weapon_ShootPosition().AsVector2D() );
  1423. float flDistToTarget = sqrt(flDistToTargetSqr);
  1424. flReactionTime = AimBurstAtReactionTime( flReactionTime, flDistToTarget, flCurrentSpeed );
  1425. // We want a certain amount of reaction time before the shots hit the boat
  1426. int nShotCount = SetupBurstShotRegulator( flReactionTime );
  1427. bool bIsInVehicle = IsEnemyInAnAirboat();
  1428. if ( bIsInVehicle )
  1429. {
  1430. m_nBurstMode = BURST_LOCK_ON_AFTER_HIT;
  1431. m_flBurstSteerDistance = AIM_AT_STEER_DISTANCE;
  1432. }
  1433. else
  1434. {
  1435. m_nBurstMode = BURST_ACTIVE;
  1436. m_flBurstSteerDistance = 0;
  1437. }
  1438. m_nBurstSteerMode = BURST_STEER_WITHIN_LINE_OF_DEATH;
  1439. // Max number of times we can hit the enemy
  1440. int nExtraShots = AimBurstAtSetupHitCount( flDistToTarget, flCurrentSpeed );
  1441. float flExtraTime = GetTimeForShots( nExtraShots ) + (1.0f - AIM_AT_REACTION_TIME_FRACTION) * flReactionTime;
  1442. float flReactionFraction = 1.0f - flExtraTime / flReactionTime;
  1443. if ( flReactionFraction < 0.5f )
  1444. {
  1445. flReactionFraction = 0.5f;
  1446. }
  1447. float flFirstHitTime = flReactionTime * flReactionFraction;
  1448. Vector vecShootAt, vecShootAtVel;
  1449. PredictShootTargetPosition( flFirstHitTime, 0.0f, 0.0f, &vecShootAt, &vecShootAtVel );
  1450. Vector vecDelta;
  1451. VectorSubtract( vecShootAt, Weapon_ShootPosition(), vecDelta );
  1452. float flDistanceToTarget = vecDelta.Length();
  1453. // Always stitch horizontally...
  1454. vecDelta.z = 0.0f;
  1455. // The max stitch distance here is used to guarantee the cop doesn't try to lead
  1456. // the airboat so much that he ends up shooting behind himself
  1457. float flMaxStitchDistance = VectorNormalize( vecDelta );
  1458. flMaxStitchDistance -= 50.0f;
  1459. if ( flMaxStitchDistance < 0 )
  1460. {
  1461. flMaxStitchDistance = 0.0f;
  1462. }
  1463. float flStitchLength = nShotCount * AIM_AT_DEFAULT_STITCH_SHOT_DIST;
  1464. // Modify the stitch length based on distance from the shooter
  1465. flStitchLength += ComputeDistanceStitchModifier( flDistanceToTarget );
  1466. if ( bIsInVehicle )
  1467. {
  1468. // Make longer stitches if the enemy is going faster
  1469. Vector vecEnemyVelocity = GetShootTarget()->GetSmoothedVelocity();
  1470. if( (GetShootTarget()->GetFlags() & FL_ONGROUND) == 0 )
  1471. {
  1472. vecEnemyVelocity.z = 0.0f;
  1473. }
  1474. float flEnemySpeed = VectorNormalize( vecEnemyVelocity );
  1475. flStitchLength += AIM_AT_SPEED_BONUS * ( flEnemySpeed / 100.0f );
  1476. // Add in a little randomness across the direction of motion...
  1477. // Always put it on the side we're currently looking at
  1478. Vector vecAcross;
  1479. CrossProduct( vecEnemyVelocity, Vector( 0, 0, 1 ), vecAcross );
  1480. VectorNormalize( vecAcross );
  1481. Vector eyeForward;
  1482. AngleVectors( GetEnemy()->EyeAngles(), &eyeForward );
  1483. if ( DotProduct( vecAcross, eyeForward ) < 0.0f )
  1484. {
  1485. vecAcross *= -1.0f;
  1486. }
  1487. float flMinAdd = RemapVal( flEnemySpeed, 0.0f, 200.0f, 70.0f, 30.0f );
  1488. VectorMA( vecShootAt, random->RandomFloat( flMinAdd, 100.0f ), vecAcross, vecShootAt );
  1489. }
  1490. // Compute the distance along the stitch direction to the cop. we don't want to cross that line
  1491. Vector vecStitchStart, vecStitchEnd;
  1492. VectorMA( vecShootAt, -MIN( flStitchLength * flReactionFraction, flMaxStitchDistance ), vecDelta, vecStitchStart );
  1493. VectorMA( vecShootAt, flStitchLength * (1.0f - flReactionFraction), vecDelta, vecStitchEnd );
  1494. // Trace down a bit to hit the ground if we're above the ground...
  1495. trace_t trace;
  1496. UTIL_TraceLine( vecStitchStart, vecStitchStart + Vector( 0, 0, -512 ), (MASK_SOLID_BRUSHONLY | MASK_WATER), NULL, COLLISION_GROUP_NONE, &trace );
  1497. m_vecBurstTargetPos = trace.endpos;
  1498. VectorSubtract( vecStitchEnd, m_vecBurstTargetPos, m_vecBurstDelta );
  1499. m_vecBurstLineOfDeathOrigin = m_vecBurstTargetPos;
  1500. m_vecBurstLineOfDeathDelta = m_vecBurstDelta;
  1501. m_vecBurstDelta /= (nShotCount - 1);
  1502. // VisualizeStitch( m_vecBurstTargetPos, vecStitchEnd );
  1503. // VisualizeLineOfDeath();
  1504. }
  1505. //-----------------------------------------------------------------------------
  1506. // Burst mode!
  1507. //-----------------------------------------------------------------------------
  1508. #define AIM_IN_FRONT_OF_DEFAULT_STITCH_LENGTH 1000.0f
  1509. #define AIM_IN_FRONT_OF_MINIMUM_DISTANCE 500.0f
  1510. #define AIM_IN_FRONT_DRAW_LINE_OF_DEATH_FRACTION 0.5f
  1511. #define AIM_IN_FRONT_STEER_DISTANCE 150.0f
  1512. #define AIM_IN_FRONT_REACTION_FRACTION 0.8f
  1513. #define AIM_IN_FRONT_EXTRA_VEL 200.0f
  1514. void CNPC_MetroPolice::AimBurstInFrontOfEnemy( float flReactionTime )
  1515. {
  1516. if ( !IsCurrentlyFiringBurst() )
  1517. return;
  1518. flReactionTime *= sk_metropolice_stitch_reaction.GetFloat();
  1519. // We want a certain amount of reaction time before the shots hit the boat
  1520. int nShotCount = SetupBurstShotRegulator( flReactionTime );
  1521. // Max number of times we can hit the player in the airboat
  1522. m_nMaxBurstHits = random->RandomInt( 3, 4 );
  1523. m_nBurstMode = BURST_LOCK_ON_AFTER_HIT;
  1524. m_nBurstSteerMode = BURST_STEER_WITHIN_LINE_OF_DEATH;
  1525. // The goal here is to slow him down. Choose a target position such that we predict
  1526. // where he'd be in he accelerated by N over the reaction time. Prevent him from getting there.
  1527. Vector vecShootAt, vecShootAtVel, vecAcross;
  1528. PredictShootTargetPosition( flReactionTime * AIM_IN_FRONT_REACTION_FRACTION,
  1529. AIM_IN_FRONT_OF_MINIMUM_DISTANCE, 0.0f, &vecShootAt, &vecShootAtVel );
  1530. // Now add in some extra vel in a random direction + try to prevent that....
  1531. Vector vecTargetToGun, vecExtraDistance;
  1532. VectorSubtract( Weapon_ShootPosition(), vecShootAt, vecTargetToGun );
  1533. VectorNormalize( vecTargetToGun );
  1534. VectorNormalize( vecShootAtVel );
  1535. RandomDirectionBetweenVectors( vecShootAtVel, vecTargetToGun, &vecExtraDistance );
  1536. vecExtraDistance *= AIM_IN_FRONT_EXTRA_VEL;
  1537. vecShootAt += vecExtraDistance;
  1538. CrossProduct( vecExtraDistance, Vector( 0, 0, 1 ), vecAcross );
  1539. VectorNormalize( vecAcross );
  1540. float flStitchLength = AIM_IN_FRONT_OF_DEFAULT_STITCH_LENGTH;
  1541. Vector vecEndPoint1, vecEndPoint2;
  1542. VectorSubtract( Weapon_ShootPosition(), StitchAimTarget( GetAbsOrigin(), false ), vecTargetToGun );
  1543. float flSign = ( DotProduct( vecAcross, vecTargetToGun ) >= 0.0f ) ? 1.0f : -1.0f;
  1544. VectorMA( vecShootAt, flSign * flStitchLength * AIM_IN_FRONT_REACTION_FRACTION, vecAcross, vecEndPoint1 );
  1545. VectorMA( vecShootAt, -flSign * flStitchLength * (1.0f - AIM_IN_FRONT_REACTION_FRACTION), vecAcross, vecEndPoint2 );
  1546. m_vecBurstTargetPos = vecEndPoint1;
  1547. VectorSubtract( vecEndPoint2, vecEndPoint1, m_vecBurstDelta );
  1548. // This defines the line of death, which, when crossed, results in damage
  1549. m_vecBurstLineOfDeathOrigin = m_vecBurstTargetPos;
  1550. m_vecBurstLineOfDeathDelta = m_vecBurstDelta;
  1551. m_flBurstSteerDistance = AIM_IN_FRONT_STEER_DISTANCE;
  1552. // Make the visual representation of the line of death lie closest to the boat.
  1553. VectorMA( m_vecBurstTargetPos, -AIM_IN_FRONT_STEER_DISTANCE, vecShootAtVel, m_vecBurstTargetPos );
  1554. m_vecBurstDelta /= (nShotCount - 1);
  1555. // VisualizeStitch( m_vecBurstTargetPos, m_vecBurstTargetPos + m_vecBurstDelta * (nShotCount - 1) );
  1556. // VisualizeLineOfDeath();
  1557. }
  1558. //-----------------------------------------------------------------------------
  1559. // Aim burst behind enemy
  1560. //-----------------------------------------------------------------------------
  1561. void CNPC_MetroPolice::AimBurstBehindEnemy( float flShotTime )
  1562. {
  1563. if ( !IsCurrentlyFiringBurst() )
  1564. return;
  1565. flShotTime *= sk_metropolice_stitch_reaction.GetFloat();
  1566. // We want a certain amount of reaction time before the shots hit the boat
  1567. int nShotCount = SetupBurstShotRegulator( flShotTime );
  1568. // Max number of times we can hit the player in the airboat
  1569. int nHitCount = sk_metropolice_stitch_behind_hitcount.GetInt();
  1570. m_nMaxBurstHits = random->RandomInt( nHitCount, nHitCount + 1 );
  1571. m_nBurstMode = BURST_LOCK_ON_AFTER_HIT;
  1572. m_nBurstSteerMode = BURST_STEER_WITHIN_LINE_OF_DEATH;
  1573. // Shoot across the enemy in between the enemy and me
  1574. Vector vecShootAt, vecShootAtVel, vecAcross;
  1575. PredictShootTargetPosition( 0.0f, 0.0f, 0.0f, &vecShootAt, &vecShootAtVel );
  1576. // Choose a point in between the shooter + the target
  1577. Vector vecDelta;
  1578. VectorSubtract( Weapon_ShootPosition(), vecShootAt, vecDelta );
  1579. vecDelta.z = 0.0f;
  1580. float flDistTo = VectorNormalize( vecDelta );
  1581. if ( flDistTo > AIM_BEHIND_MINIMUM_DISTANCE )
  1582. {
  1583. flDistTo = AIM_BEHIND_MINIMUM_DISTANCE;
  1584. }
  1585. VectorMA( vecShootAt, flDistTo, vecDelta, vecShootAt );
  1586. CrossProduct( vecDelta, Vector( 0, 0, 1 ), vecAcross );
  1587. float flStitchLength = AIM_BEHIND_DEFAULT_STITCH_LENGTH;
  1588. Vector vecEndPoint1, vecEndPoint2;
  1589. VectorMA( vecShootAt, -flStitchLength * 0.5f, vecAcross, vecEndPoint1 );
  1590. VectorMA( vecShootAt, flStitchLength * 0.5f, vecAcross, vecEndPoint2 );
  1591. m_vecBurstTargetPos = vecEndPoint1;
  1592. VectorSubtract( vecEndPoint2, vecEndPoint1, m_vecBurstDelta );
  1593. // This defines the line of death, which, when crossed, results in damage
  1594. m_vecBurstLineOfDeathOrigin = m_vecBurstTargetPos;
  1595. m_vecBurstLineOfDeathDelta = m_vecBurstDelta;
  1596. m_flBurstSteerDistance = AIM_BEHIND_STEER_DISTANCE;
  1597. // Make the visual representation of the line of death lie closest to the boat.
  1598. VectorMA( m_vecBurstTargetPos, -AIM_BEHIND_STEER_DISTANCE, vecDelta, m_vecBurstTargetPos );
  1599. m_vecBurstDelta /= (nShotCount - 1);
  1600. // VisualizeStitch( m_vecBurstTargetPos, m_vecBurstTargetPos + m_vecBurstDelta * (nShotCount - 1) );
  1601. // VisualizeLineOfDeath();
  1602. }
  1603. //-----------------------------------------------------------------------------
  1604. // Burst mode!
  1605. //-----------------------------------------------------------------------------
  1606. void CNPC_MetroPolice::AimBurstAlongSideOfEnemy( float flFollowTime )
  1607. {
  1608. if ( !IsCurrentlyFiringBurst() )
  1609. return;
  1610. flFollowTime *= sk_metropolice_stitch_reaction.GetFloat();
  1611. // We want a certain amount of reaction time before the shots hit the boat
  1612. int nShotCount = SetupBurstShotRegulator( flFollowTime );
  1613. // Max number of times we can hit the player in the airboat
  1614. int nHitCount = sk_metropolice_stitch_along_hitcount.GetInt();
  1615. m_nMaxBurstHits = random->RandomInt( nHitCount, nHitCount + 1 );
  1616. m_nBurstMode = BURST_LOCK_ON_AFTER_HIT;
  1617. m_nBurstSteerMode = BURST_STEER_WITHIN_LINE_OF_DEATH;
  1618. Vector vecShootAt, vecShootAtVel, vecAcross;
  1619. PredictShootTargetPosition( AIM_ALONG_SIDE_LINE_OF_DEATH_LEAD_TIME, 225.0f, 0.0f, &vecShootAt, &vecShootAtVel );
  1620. CrossProduct( vecShootAtVel, Vector( 0, 0, 1 ), vecAcross );
  1621. VectorNormalize( vecAcross );
  1622. // Choose the side of the vehicle which is closer to the shooter
  1623. Vector vecSidePoint;
  1624. Vector vecTargetToGun;
  1625. VectorSubtract( Weapon_ShootPosition(), vecShootAt, vecTargetToGun );
  1626. float flSign = ( DotProduct( vecTargetToGun, vecAcross ) > 0.0f ) ? 1.0f : -1.0f;
  1627. float flDist = AIM_ALONG_SIDE_LINE_OF_DEATH_DISTANCE + random->RandomFloat( 0.0f, 50.0f );
  1628. VectorMA( vecShootAt, flSign * flDist, vecAcross, vecSidePoint );
  1629. vecShootAtVel.z = 0.0f;
  1630. float flTargetSpeed = VectorNormalize( vecShootAtVel );
  1631. float flStitchLength = MAX( AIM_IN_FRONT_OF_DEFAULT_STITCH_LENGTH, flTargetSpeed * flFollowTime * 0.9 );
  1632. // This defines the line of death, which, when crossed, results in damage
  1633. m_vecBurstLineOfDeathOrigin = vecSidePoint;
  1634. VectorMultiply( vecShootAtVel, flStitchLength, m_vecBurstLineOfDeathDelta );
  1635. // Pull the endpoint a little toward the NPC firing it...
  1636. float flExtraDist = random->RandomFloat( 25.0f, 50.0f );
  1637. VectorNormalize( vecTargetToGun );
  1638. if ( flSign * DotProduct( vecTargetToGun, vecShootAtVel ) < 0.1f )
  1639. {
  1640. flExtraDist += 100.0f;
  1641. }
  1642. VectorMA( m_vecBurstLineOfDeathDelta, flSign * flExtraDist, vecAcross, m_vecBurstLineOfDeathDelta );
  1643. m_flBurstSteerDistance = AIM_ALONG_SIDE_STEER_DISTANCE;
  1644. m_vecBurstDelta = m_vecBurstLineOfDeathDelta;
  1645. m_vecBurstTargetPos = m_vecBurstLineOfDeathOrigin;
  1646. // Make the visual representation of the line of death lie closest to the boat.
  1647. VectorMA( m_vecBurstTargetPos, -flSign * AIM_ALONG_SIDE_STEER_DISTANCE, vecAcross, m_vecBurstTargetPos );
  1648. m_vecBurstDelta /= (nShotCount - 1);
  1649. // VisualizeStitch( m_vecBurstTargetPos, m_vecBurstTargetPos + m_vecBurstDelta * (nShotCount - 1) );
  1650. // VisualizeLineOfDeath();
  1651. }
  1652. //-----------------------------------------------------------------------------
  1653. // Different burst steering modes
  1654. //-----------------------------------------------------------------------------
  1655. void CNPC_MetroPolice::SteerBurstTowardTargetUseSpeedOnly( const Vector &vecShootAt,
  1656. const Vector &vecShootAtVelocity, float flPredictTime, int nShotsTillPredict )
  1657. {
  1658. // Only account for changes in *speed*; ignore all changes in velocity direction, etc.
  1659. // This one only hits the player if there is *no* steering, just acceleration or decceleration
  1660. Vector vecBurstDir = m_vecBurstPredictedVelocityDir;
  1661. float flActualSpeed = DotProduct( vecShootAtVelocity, vecBurstDir );
  1662. vecBurstDir *= (flActualSpeed - m_vecBurstPredictedSpeed) * flPredictTime;
  1663. vecBurstDir /= (nShotsTillPredict - 1);
  1664. m_vecBurstPredictedSpeed = flActualSpeed;
  1665. m_vecBurstDelta += vecBurstDir;
  1666. }
  1667. void CNPC_MetroPolice::SteerBurstTowardTargetUseVelocity( const Vector &vecShootAt, const Vector &vecShootAtVelocity, int nShotsTillPredict )
  1668. {
  1669. // Only account for all velocity changes
  1670. // This one looks scary in that it always gets near to the player,
  1671. // but it never usually hits actually.
  1672. Vector vecBurstDir = m_vecBurstLineOfDeathDelta;
  1673. m_vecBurstLineOfDeathDelta = vecShootAtVelocity;
  1674. vecBurstDir = vecShootAtVelocity - vecBurstDir;
  1675. vecBurstDir /= (nShotsTillPredict - 1);
  1676. m_vecBurstDelta += vecBurstDir;
  1677. }
  1678. void CNPC_MetroPolice::SteerBurstTowardTargetUsePosition( const Vector &vecShootAt, const Vector &vecShootAtVelocity, int nShotsTillPredict )
  1679. {
  1680. // Account for velocity + position changes
  1681. // This method *always* hits
  1682. VectorSubtract( vecShootAt, m_vecBurstTargetPos, m_vecBurstDelta );
  1683. m_vecBurstDelta /= (nShotsTillPredict - 1);
  1684. }
  1685. void CNPC_MetroPolice::SteerBurstTowardPredictedPoint( const Vector &vecShootAt, const Vector &vecShootAtVelocity, int nShotsTillPredict )
  1686. {
  1687. // Account for velocity + position changes, but only within a constrained cylinder
  1688. Vector vecConstrainedShootPosition;
  1689. CalcClosestPointOnLine( vecShootAt, m_vecBurstLineOfDeathOrigin, m_vecBurstLineOfDeathOrigin + m_vecBurstLineOfDeathDelta, vecConstrainedShootPosition );
  1690. Vector vecDelta;
  1691. VectorSubtract( vecShootAt, vecConstrainedShootPosition, vecDelta );
  1692. if ( vecDelta.LengthSqr( ) <= m_flBurstSteerDistance * m_flBurstSteerDistance )
  1693. {
  1694. vecConstrainedShootPosition = vecShootAt;
  1695. }
  1696. else
  1697. {
  1698. VectorNormalize( vecDelta );
  1699. VectorMA( vecConstrainedShootPosition, m_flBurstSteerDistance, vecDelta, vecConstrainedShootPosition );
  1700. }
  1701. // This method *always* hits if the entity is within the cylinder
  1702. VectorSubtract( vecConstrainedShootPosition, m_vecBurstTargetPos, m_vecBurstDelta );
  1703. if ( nShotsTillPredict >= 2 )
  1704. {
  1705. m_vecBurstDelta /= (nShotsTillPredict - 1);
  1706. }
  1707. }
  1708. #define STEER_LINE_OF_DEATH_MAX_DISTANCE 250.0f
  1709. void CNPC_MetroPolice::SteerBurstWithinLineOfDeath( )
  1710. {
  1711. // Account for velocity + position changes, but only within a constrained cylinder
  1712. Vector vecShootAt;
  1713. vecShootAt = StitchAimTarget( GetAbsOrigin(), false );
  1714. // If the target close to the current point the shot is on,
  1715. // move the shot toward the point
  1716. Vector vecPointOnLineOfDeath;
  1717. CalcClosestPointOnLine( m_vecBurstTargetPos, m_vecBurstLineOfDeathOrigin, m_vecBurstLineOfDeathOrigin + m_vecBurstLineOfDeathDelta, vecPointOnLineOfDeath );
  1718. Vector vecDelta;
  1719. VectorSubtract( vecShootAt, vecPointOnLineOfDeath, vecDelta );
  1720. if ( vecDelta.LengthSqr( ) <= m_flBurstSteerDistance * m_flBurstSteerDistance )
  1721. {
  1722. VectorSubtract( vecShootAt, m_vecBurstTargetPos, m_vecBurstDelta );
  1723. if ( m_vecBurstDelta.LengthSqr() > (STEER_LINE_OF_DEATH_MAX_DISTANCE * STEER_LINE_OF_DEATH_MAX_DISTANCE) )
  1724. {
  1725. VectorNormalize( m_vecBurstDelta );
  1726. m_vecBurstDelta *= STEER_LINE_OF_DEATH_MAX_DISTANCE;
  1727. }
  1728. }
  1729. else
  1730. {
  1731. // Just make the burst go back and forth alont the line of death...
  1732. Vector vecNext = m_vecBurstTargetPos + m_vecBurstDelta;
  1733. float t;
  1734. CalcClosestPointOnLine( vecNext, m_vecBurstLineOfDeathOrigin, m_vecBurstLineOfDeathOrigin + m_vecBurstLineOfDeathDelta, vecPointOnLineOfDeath, &t );
  1735. if (( t < -0.1f ) || ( t > 1.1f ))
  1736. {
  1737. m_vecBurstDelta *= -1.0f;
  1738. // This is necessary to make it not look like a machine is firing the gun
  1739. Vector vecBurstDir = m_vecBurstDelta;
  1740. float flLength = VectorNormalize( vecBurstDir );
  1741. vecBurstDir *= random->RandomFloat( -flLength * 0.5f, flLength * 0.5f );
  1742. m_vecBurstTargetPos += vecBurstDir;
  1743. }
  1744. }
  1745. }
  1746. //-----------------------------------------------------------------------------
  1747. // Burst mode!
  1748. //-----------------------------------------------------------------------------
  1749. void CNPC_MetroPolice::SteerBurstTowardTarget( )
  1750. {
  1751. switch ( m_nBurstSteerMode )
  1752. {
  1753. case BURST_STEER_NONE:
  1754. return;
  1755. case BURST_STEER_EXACTLY_TOWARD_TARGET:
  1756. // Necessary to get the cop looking at the target
  1757. m_vecBurstTargetPos = GetEnemy()->WorldSpaceCenter();
  1758. return;
  1759. case BURST_STEER_ADJUST_FOR_SPEED_CHANGES:
  1760. {
  1761. // Predict the airboat position at the point where we were expecting to hit them
  1762. if ( m_flBurstPredictTime <= gpGlobals->curtime )
  1763. return;
  1764. float flPredictTime = m_flBurstPredictTime - gpGlobals->curtime;
  1765. int nShotsTillPredict = CountShotsInTime( flPredictTime );
  1766. if ( nShotsTillPredict <= 1 )
  1767. return;
  1768. Vector vecShootAt, vecShootAtVelocity;
  1769. PredictShootTargetPosition( flPredictTime, 0.0f, 0.0f, &vecShootAt, &vecShootAtVelocity );
  1770. SteerBurstTowardTargetUseSpeedOnly( vecShootAt, vecShootAtVelocity, flPredictTime, nShotsTillPredict );
  1771. }
  1772. break;
  1773. case BURST_STEER_TOWARD_PREDICTED_POINT:
  1774. // Don't course-correct until the predicted time
  1775. if ( m_flBurstPredictTime >= gpGlobals->curtime )
  1776. return;
  1777. // fall through!
  1778. case BURST_STEER_WITHIN_LINE_OF_DEATH:
  1779. break;
  1780. }
  1781. SteerBurstWithinLineOfDeath( );
  1782. }
  1783. //-----------------------------------------------------------------------------
  1784. // Various burst trajectory methods
  1785. //-----------------------------------------------------------------------------
  1786. Vector CNPC_MetroPolice::ComputeBurstLockOnTrajectory( const Vector &shootOrigin )
  1787. {
  1788. Vector vecTrajectory;
  1789. VectorSubtract( GetEnemy()->WorldSpaceCenter(), shootOrigin, vecTrajectory );
  1790. VectorNormalize( vecTrajectory );
  1791. return vecTrajectory;
  1792. }
  1793. Vector CNPC_MetroPolice::ComputeBurstDeliberatelyMissTrajectory( const Vector &shootOrigin )
  1794. {
  1795. m_vecBurstTargetPos.z += 8.0f;
  1796. Vector vecTrajectory;
  1797. VectorSubtract( m_vecBurstTargetPos, shootOrigin, vecTrajectory );
  1798. VectorNormalize( vecTrajectory );
  1799. return vecTrajectory;
  1800. }
  1801. Vector CNPC_MetroPolice::ComputeBurstTrajectory( const Vector &shootOrigin )
  1802. {
  1803. // Perform the stitch
  1804. Vector vecPos = m_vecBurstTargetPos;
  1805. // For players, don't let them jump over the burst.
  1806. CBaseEntity *pEnemy = GetEnemy();
  1807. bool bIsPlayerOnFoot = pEnemy && pEnemy->IsPlayer() && !IsEnemyInAnAirboat();
  1808. if ( bIsPlayerOnFoot )
  1809. {
  1810. Vector vecNormalizedPt;
  1811. pEnemy->CollisionProp()->WorldToNormalizedSpace( vecPos, &vecNormalizedPt );
  1812. if ( (vecNormalizedPt.x >= -0.1f) && (vecNormalizedPt.x <= 1.1f) &&
  1813. (vecNormalizedPt.y >= -0.1f) && (vecNormalizedPt.y <= 1.1f) &&
  1814. (vecNormalizedPt.z >= -0.7f) && (vecNormalizedPt.z < 1.1f) )
  1815. {
  1816. vecPos.z = pEnemy->WorldSpaceCenter().z;
  1817. }
  1818. }
  1819. vecPos -= shootOrigin;
  1820. // Add a little noise. Even though it's non-physical, it looks better
  1821. // to have the same amount of noise regardless of distance from the shooter
  1822. // Always make the noise perpendicular to the burst direction
  1823. float flNoise = bIsPlayerOnFoot ? 16.0f : 32.0f;
  1824. Vector vecNoise;
  1825. CrossProduct( m_vecBurstDelta, Vector( 0, 0, 1 ), vecNoise );
  1826. VectorNormalize( vecNoise );
  1827. vecNoise *= random->RandomFloat( -flNoise, flNoise );
  1828. vecPos += vecNoise;
  1829. VectorNormalize( vecPos );
  1830. // X360BUG: Was causing compiler crash in release, still?
  1831. // if ( IsPC() )
  1832. {
  1833. // Allow for steering towards the target.
  1834. SteerBurstTowardTarget();
  1835. }
  1836. // Update the burst target position
  1837. m_vecBurstTargetPos += m_vecBurstDelta;
  1838. // NDebugOverlay::Cross3D( m_vecBurstTargetPos, -Vector(32,32,32), Vector(32,32,32), 255, 0, 255, false, 1.0f );
  1839. return vecPos;
  1840. }
  1841. //-----------------------------------------------------------------------------
  1842. // Deliberately aims as close as possible w/o hitting
  1843. //-----------------------------------------------------------------------------
  1844. Vector CNPC_MetroPolice::AimCloseToTargetButMiss( CBaseEntity *pTarget, const Vector &shootOrigin )
  1845. {
  1846. Vector vecNormalizedSpace;
  1847. pTarget->CollisionProp()->WorldToNormalizedSpace( shootOrigin, &vecNormalizedSpace );
  1848. vecNormalizedSpace -= Vector( 0.5f, 0.5f, 0.5f );
  1849. float flDist = VectorNormalize( vecNormalizedSpace );
  1850. float flMinRadius = flDist * sqrt(3.0) / sqrt( flDist * flDist - 3 );
  1851. // Choose random points in a plane perpendicular to the shoot origin.
  1852. Vector vecRandomDir;
  1853. vecRandomDir.Random( -1.0f, 1.0f );
  1854. VectorMA( vecRandomDir, -DotProduct( vecNormalizedSpace, vecRandomDir ), vecNormalizedSpace, vecRandomDir );
  1855. VectorNormalize( vecRandomDir );
  1856. vecRandomDir *= flMinRadius;
  1857. vecRandomDir *= 0.5f;
  1858. vecRandomDir += Vector( 0.5f, 0.5f, 0.5f );
  1859. Vector vecBodyTarget;
  1860. pTarget->CollisionProp()->NormalizedToWorldSpace( vecRandomDir, &vecBodyTarget );
  1861. vecBodyTarget -= shootOrigin;
  1862. return vecBodyTarget;
  1863. }
  1864. //-----------------------------------------------------------------------------
  1865. // A burst that goes right at the enemy
  1866. //-----------------------------------------------------------------------------
  1867. #define MIN_TIGHT_BURST_DIST 1000.0f
  1868. #define MAX_TIGHT_BURST_DIST 2000.0f
  1869. Vector CNPC_MetroPolice::ComputeTightBurstTrajectory( const Vector &shootOrigin )
  1870. {
  1871. CBaseEntity *pEnemy = GetEnemy();
  1872. if ( !pEnemy )
  1873. {
  1874. return BaseClass::GetActualShootTrajectory( shootOrigin );
  1875. }
  1876. // Aim around the player...
  1877. if ( m_nBurstHits >= m_nMaxBurstHits )
  1878. {
  1879. return AimCloseToTargetButMiss( pEnemy, shootOrigin );
  1880. }
  1881. float flDist = shootOrigin.DistTo( pEnemy->WorldSpaceCenter() );
  1882. float flMin = -0.2f;
  1883. float flMax = 1.2f;
  1884. if ( flDist > MIN_TIGHT_BURST_DIST )
  1885. {
  1886. flDist = clamp( flDist, MIN_TIGHT_BURST_DIST, MAX_TIGHT_BURST_DIST );
  1887. flMin = SimpleSplineRemapVal( flDist, MIN_TIGHT_BURST_DIST, MAX_TIGHT_BURST_DIST, -0.2f, -0.7f );
  1888. flMax = SimpleSplineRemapVal( flDist, MIN_TIGHT_BURST_DIST, MAX_TIGHT_BURST_DIST, 1.2f, 1.7f );
  1889. }
  1890. // Aim randomly at the player. Since body target uses the vehicle body target,
  1891. // we instead are going to not use it
  1892. Vector vecBodyTarget;
  1893. pEnemy->CollisionProp()->RandomPointInBounds( Vector( flMin, flMin, flMin ), Vector( flMax, flMax, flMax * 0.75f ), &vecBodyTarget );
  1894. vecBodyTarget -= shootOrigin;
  1895. return vecBodyTarget;
  1896. }
  1897. //-----------------------------------------------------------------------------
  1898. // Burst mode!
  1899. //-----------------------------------------------------------------------------
  1900. Vector CNPC_MetroPolice::GetActualShootTrajectory( const Vector &shootOrigin )
  1901. {
  1902. switch ( m_nBurstMode )
  1903. {
  1904. case BURST_NOT_ACTIVE:
  1905. return BaseClass::GetActualShootTrajectory( shootOrigin );
  1906. case BURST_LOCKED_ON:
  1907. if ( m_nBurstHits < m_nMaxBurstHits )
  1908. {
  1909. return ComputeBurstLockOnTrajectory( shootOrigin );
  1910. }
  1911. // Start shooting over the head of the enemy
  1912. GetShootTarget()->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &m_vecBurstTargetPos );
  1913. m_nBurstMode = BURST_DELIBERATELY_MISS;
  1914. // NOTE: Fall through to BURST_DELIBERATELY_MISS!!
  1915. case BURST_DELIBERATELY_MISS:
  1916. return ComputeBurstDeliberatelyMissTrajectory( shootOrigin );
  1917. case BURST_LOCK_ON_AFTER_HIT:
  1918. // See if our target is within the bounds of the enemy
  1919. if ( GetShootTarget()->CollisionProp()->IsPointInBounds( m_vecBurstTargetPos ) )
  1920. {
  1921. // Now raytrace against only the world + (good for cops on bridges)
  1922. trace_t tr;
  1923. CTraceFilterWorldOnly traceFilter;
  1924. UTIL_TraceLine( Weapon_ShootPosition(), m_vecBurstTargetPos, MASK_SOLID, &traceFilter, &tr );
  1925. if ( tr.fraction == 1.0f )
  1926. {
  1927. m_nBurstMode = BURST_LOCKED_ON;
  1928. }
  1929. }
  1930. // NOTE: Fall through to BURST_ACTIVE!
  1931. case BURST_ACTIVE:
  1932. // Stitch toward the target, we haven't hit it yet
  1933. return ComputeBurstTrajectory( shootOrigin );
  1934. case BURST_TIGHT_GROUPING:
  1935. // This one goes right at the enemy
  1936. return ComputeTightBurstTrajectory( shootOrigin );
  1937. }
  1938. Assert(0);
  1939. return vec3_origin;
  1940. }
  1941. //-----------------------------------------------------------------------------
  1942. // Burst mode!
  1943. //-----------------------------------------------------------------------------
  1944. void CNPC_MetroPolice::FireBullets( const FireBulletsInfo_t &info )
  1945. {
  1946. CBaseEntity *pEnemy = GetEnemy();
  1947. bool bIsPlayer = pEnemy && pEnemy->IsPlayer();
  1948. if ( bIsPlayer && IsCurrentlyFiringBurst() )
  1949. {
  1950. FireBulletsInfo_t actualInfo = info;
  1951. if ( m_nBurstHits < m_nMaxBurstHits )
  1952. {
  1953. CBasePlayer *pPlayer = assert_cast<CBasePlayer*>(pEnemy);
  1954. // This makes it so that if the player gets hit underwater,
  1955. // he won't take damage if his viewpoint is above water.
  1956. if ( !IsEnemyInAnAirboat() && ( pPlayer->GetWaterLevel() != 3 ) )
  1957. {
  1958. actualInfo.m_nFlags |= FIRE_BULLETS_DONT_HIT_UNDERWATER;
  1959. }
  1960. // This test is here to see if we've damaged the player
  1961. int nPrevHealth = pPlayer->GetHealth();
  1962. int nPrevArmor = pPlayer->ArmorValue();
  1963. BaseClass::FireBullets( actualInfo );
  1964. if (( pPlayer->GetHealth() < nPrevHealth ) || ( pPlayer->ArmorValue() < nPrevArmor ))
  1965. {
  1966. ++m_nBurstHits;
  1967. }
  1968. }
  1969. else
  1970. {
  1971. actualInfo.m_pAdditionalIgnoreEnt = pEnemy;
  1972. BaseClass::FireBullets( actualInfo );
  1973. }
  1974. }
  1975. else
  1976. {
  1977. BaseClass::FireBullets( info );
  1978. }
  1979. }
  1980. //-----------------------------------------------------------------------------
  1981. // Behaviors! Lovely behaviors
  1982. //-----------------------------------------------------------------------------
  1983. bool CNPC_MetroPolice::CreateBehaviors()
  1984. {
  1985. AddBehavior( &m_RappelBehavior );
  1986. AddBehavior( &m_FollowBehavior );
  1987. AddBehavior( &m_PolicingBehavior );
  1988. AddBehavior( &m_ActBusyBehavior );
  1989. AddBehavior( &m_AssaultBehavior );
  1990. AddBehavior( &m_StandoffBehavior );
  1991. AddBehavior( &m_FuncTankBehavior );
  1992. return BaseClass::CreateBehaviors();
  1993. }
  1994. void CNPC_MetroPolice::InputEnableManhackToss( inputdata_t &inputdata )
  1995. {
  1996. if ( HasSpawnFlags( SF_METROPOLICE_NO_MANHACK_DEPLOY ) )
  1997. {
  1998. RemoveSpawnFlags( SF_METROPOLICE_NO_MANHACK_DEPLOY );
  1999. }
  2000. }
  2001. //-----------------------------------------------------------------------------
  2002. // Purpose:
  2003. // Input : &inputdata -
  2004. //-----------------------------------------------------------------------------
  2005. void CNPC_MetroPolice::InputSetPoliceGoal( inputdata_t &inputdata )
  2006. {
  2007. CBaseEntity *pGoal = gEntList.FindEntityByName( NULL, inputdata.value.String() );
  2008. if ( pGoal == NULL )
  2009. {
  2010. DevMsg( "SetPoliceGoal: %s (%s) unable to find ai_goal_police: %s\n", GetClassname(), GetDebugName(), inputdata.value.String() );
  2011. return;
  2012. }
  2013. CAI_PoliceGoal *pPoliceGoal = dynamic_cast<CAI_PoliceGoal *>(pGoal);
  2014. if ( pPoliceGoal == NULL )
  2015. {
  2016. DevMsg( "SetPoliceGoal: %s (%s)'s target %s is not an ai_goal_police entity!\n", GetClassname(), GetDebugName(), inputdata.value.String() );
  2017. return;
  2018. }
  2019. m_PolicingBehavior.Enable( pPoliceGoal );
  2020. }
  2021. //-----------------------------------------------------------------------------
  2022. // Purpose:
  2023. // Input : &inputdata -
  2024. //-----------------------------------------------------------------------------
  2025. void CNPC_MetroPolice::InputActivateBaton( inputdata_t &inputdata )
  2026. {
  2027. SetBatonState( inputdata.value.Bool() );
  2028. }
  2029. //-----------------------------------------------------------------------------
  2030. // Purpose:
  2031. //
  2032. //-----------------------------------------------------------------------------
  2033. void CNPC_MetroPolice::AlertSound( void )
  2034. {
  2035. m_Sentences.Speak( "METROPOLICE_GO_ALERT" );
  2036. }
  2037. //-----------------------------------------------------------------------------
  2038. // Purpose:
  2039. //
  2040. //-----------------------------------------------------------------------------
  2041. void CNPC_MetroPolice::DeathSound( const CTakeDamageInfo &info )
  2042. {
  2043. if ( IsOnFire() )
  2044. return;
  2045. m_Sentences.Speak( "METROPOLICE_DIE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
  2046. }
  2047. //-----------------------------------------------------------------------------
  2048. // Purpose: implemented by subclasses to give them an opportunity to make
  2049. // a sound when they lose their enemy
  2050. // Input :
  2051. // Output :
  2052. //-----------------------------------------------------------------------------
  2053. void CNPC_MetroPolice::LostEnemySound( void)
  2054. {
  2055. // Don't announce enemies when the player isn't a criminal
  2056. if ( !PlayerIsCriminal() )
  2057. return;
  2058. if ( gpGlobals->curtime <= m_flNextLostSoundTime )
  2059. return;
  2060. const char *pSentence;
  2061. if (!(CBaseEntity*)GetEnemy() || gpGlobals->curtime - GetEnemyLastTimeSeen() > 10)
  2062. {
  2063. pSentence = "METROPOLICE_LOST_LONG";
  2064. }
  2065. else
  2066. {
  2067. pSentence = "METROPOLICE_LOST_SHORT";
  2068. }
  2069. if ( m_Sentences.Speak( pSentence ) >= 0 )
  2070. {
  2071. m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0);
  2072. }
  2073. }
  2074. //-----------------------------------------------------------------------------
  2075. // Purpose: implemented by subclasses to give them an opportunity to make
  2076. // a sound when they lose their enemy
  2077. // Input :
  2078. // Output :
  2079. //-----------------------------------------------------------------------------
  2080. void CNPC_MetroPolice::FoundEnemySound( void)
  2081. {
  2082. // Don't announce enemies when I'm in arrest behavior
  2083. if ( HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) )
  2084. return;
  2085. m_Sentences.Speak( "METROPOLICE_REFIND_ENEMY", SENTENCE_PRIORITY_HIGH );
  2086. }
  2087. //-----------------------------------------------------------------------------
  2088. // Purpose: Indicates whether or not this npc should play an idle sound now.
  2089. //-----------------------------------------------------------------------------
  2090. bool CNPC_MetroPolice::ShouldPlayIdleSound( void )
  2091. {
  2092. // If someone is waiting for a response, then respond!
  2093. if ( ( m_NPCState == NPC_STATE_IDLE ) || ( m_NPCState == NPC_STATE_ALERT ) )
  2094. {
  2095. if ( m_nIdleChatterType >= METROPOLICE_CHATTER_RESPONSE )
  2096. return FOkToMakeSound();
  2097. }
  2098. return BaseClass::ShouldPlayIdleSound();
  2099. }
  2100. //-----------------------------------------------------------------------------
  2101. // IdleSound
  2102. //-----------------------------------------------------------------------------
  2103. void CNPC_MetroPolice::IdleSound( void )
  2104. {
  2105. bool bIsCriminal = PlayerIsCriminal();
  2106. // This happens when the NPC is waiting for his buddies to respond to him
  2107. switch( m_nIdleChatterType )
  2108. {
  2109. case METROPOLICE_CHATTER_WAIT_FOR_RESPONSE:
  2110. break;
  2111. case METROPOLICE_CHATTER_ASK_QUESTION:
  2112. {
  2113. if ( m_bPlayerIsNear && !HasMemory(bits_MEMORY_PLAYER_HARASSED) )
  2114. {
  2115. if ( m_Sentences.Speak( "METROPOLICE_IDLE_HARASS_PLAYER", SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ) >= 0 )
  2116. {
  2117. Remember( bits_MEMORY_PLAYER_HARASSED );
  2118. if ( GetSquad() )
  2119. {
  2120. GetSquad()->SquadRemember(bits_MEMORY_PLAYER_HARASSED);
  2121. }
  2122. }
  2123. return;
  2124. }
  2125. if ( !random->RandomInt(0,1) )
  2126. break;
  2127. int nQuestionType = random->RandomInt( 0, METROPOLICE_CHATTER_RESPONSE_TYPE_COUNT );
  2128. if ( !IsInSquad() || ( nQuestionType == METROPOLICE_CHATTER_RESPONSE_TYPE_COUNT ) )
  2129. {
  2130. m_Sentences.Speak( bIsCriminal ? "METROPOLICE_IDLE_CR" : "METROPOLICE_IDLE" );
  2131. break;
  2132. }
  2133. static const char *pQuestion[2][METROPOLICE_CHATTER_RESPONSE_TYPE_COUNT] =
  2134. {
  2135. { "METROPOLICE_IDLE_CHECK", "METROPOLICE_IDLE_QUEST" },
  2136. { "METROPOLICE_IDLE_CHECK_CR", "METROPOLICE_IDLE_QUEST_CR" },
  2137. };
  2138. if ( m_Sentences.Speak( pQuestion[bIsCriminal][nQuestionType] ) >= 0 )
  2139. {
  2140. GetSquad()->BroadcastInteraction( g_interactionMetrocopIdleChatter, (void*)(METROPOLICE_CHATTER_RESPONSE + nQuestionType), this );
  2141. m_nIdleChatterType = METROPOLICE_CHATTER_WAIT_FOR_RESPONSE;
  2142. }
  2143. }
  2144. break;
  2145. default:
  2146. {
  2147. int nResponseType = m_nIdleChatterType - METROPOLICE_CHATTER_RESPONSE;
  2148. static const char *pResponse[2][METROPOLICE_CHATTER_RESPONSE_TYPE_COUNT] =
  2149. {
  2150. { "METROPOLICE_IDLE_CLEAR", "METROPOLICE_IDLE_ANSWER" },
  2151. { "METROPOLICE_IDLE_CLEAR_CR", "METROPOLICE_IDLE_ANSWER_CR" },
  2152. };
  2153. if ( m_Sentences.Speak( pResponse[bIsCriminal][nResponseType] ) >= 0 )
  2154. {
  2155. GetSquad()->BroadcastInteraction( g_interactionMetrocopIdleChatter, (void*)(METROPOLICE_CHATTER_ASK_QUESTION), this );
  2156. m_nIdleChatterType = METROPOLICE_CHATTER_ASK_QUESTION;
  2157. }
  2158. }
  2159. break;
  2160. }
  2161. }
  2162. //-----------------------------------------------------------------------------
  2163. // Purpose:
  2164. //-----------------------------------------------------------------------------
  2165. void CNPC_MetroPolice::PainSound( const CTakeDamageInfo &info )
  2166. {
  2167. if ( gpGlobals->curtime < m_flNextPainSoundTime )
  2168. return;
  2169. // Don't make pain sounds if I'm on fire. The looping sound will take care of that for us.
  2170. if ( IsOnFire() )
  2171. return;
  2172. float healthRatio = (float)GetHealth() / (float)GetMaxHealth();
  2173. if ( healthRatio > 0.0f )
  2174. {
  2175. const char *pSentenceName = "METROPOLICE_PAIN";
  2176. if ( !HasMemory(bits_MEMORY_PAIN_HEAVY_SOUND) && (healthRatio < 0.25f) )
  2177. {
  2178. Remember( bits_MEMORY_PAIN_HEAVY_SOUND | bits_MEMORY_PAIN_LIGHT_SOUND );
  2179. pSentenceName = "METROPOLICE_PAIN_HEAVY";
  2180. }
  2181. else if ( !HasMemory(bits_MEMORY_PAIN_LIGHT_SOUND) && healthRatio > 0.8f )
  2182. {
  2183. Remember( bits_MEMORY_PAIN_LIGHT_SOUND );
  2184. pSentenceName = "METROPOLICE_PAIN_LIGHT";
  2185. }
  2186. // This causes it to speak it no matter what; doesn't bother with setting sounds.
  2187. m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
  2188. m_flNextPainSoundTime = gpGlobals->curtime + 1;
  2189. }
  2190. }
  2191. //-----------------------------------------------------------------------------
  2192. // Purpose:
  2193. //-----------------------------------------------------------------------------
  2194. int CNPC_MetroPolice::GetSoundInterests( void )
  2195. {
  2196. return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_PLAYER_VEHICLE | SOUND_DANGER |
  2197. SOUND_PHYSICS_DANGER | SOUND_BULLET_IMPACT | SOUND_MOVE_AWAY;
  2198. }
  2199. //-----------------------------------------------------------------------------
  2200. // Purpose:
  2201. //-----------------------------------------------------------------------------
  2202. float CNPC_MetroPolice::MaxYawSpeed( void )
  2203. {
  2204. switch( GetActivity() )
  2205. {
  2206. case ACT_TURN_LEFT:
  2207. case ACT_TURN_RIGHT:
  2208. return 120;
  2209. case ACT_RUN:
  2210. case ACT_RUN_HURT:
  2211. return 15;
  2212. case ACT_WALK:
  2213. case ACT_WALK_CROUCH:
  2214. case ACT_RUN_CROUCH:
  2215. return 25;
  2216. default:
  2217. return 45;
  2218. }
  2219. }
  2220. //-----------------------------------------------------------------------------
  2221. // Purpose:
  2222. //
  2223. //
  2224. //-----------------------------------------------------------------------------
  2225. Class_T CNPC_MetroPolice::Classify ( void )
  2226. {
  2227. return CLASS_METROPOLICE;
  2228. }
  2229. //-----------------------------------------------------------------------------
  2230. // Purpose:
  2231. // Output : Returns true on success, false on failure.
  2232. //-----------------------------------------------------------------------------
  2233. bool CNPC_MetroPolice::PlayerIsCriminal( void )
  2234. {
  2235. if ( m_PolicingBehavior.IsEnabled() && m_PolicingBehavior.TargetIsHostile() )
  2236. return true;
  2237. if ( GlobalEntity_GetState( "gordon_precriminal" ) == GLOBAL_ON )
  2238. return false;
  2239. return true;
  2240. }
  2241. //-----------------------------------------------------------------------------
  2242. // Purpose: Overridden because if the player is a criminal, we hate them.
  2243. // Input : pTarget - Entity with which to determine relationship.
  2244. // Output : Returns relationship value.
  2245. //-----------------------------------------------------------------------------
  2246. Disposition_t CNPC_MetroPolice::IRelationType(CBaseEntity *pTarget)
  2247. {
  2248. Disposition_t disp = BaseClass::IRelationType(pTarget);
  2249. if ( pTarget == NULL )
  2250. return disp;
  2251. // If the player's not a criminal, then we don't necessary hate him
  2252. if ( pTarget->Classify() == CLASS_PLAYER )
  2253. {
  2254. if ( !PlayerIsCriminal() && (disp == D_HT) )
  2255. {
  2256. // If we're pissed at the player, we're allowed to hate them.
  2257. if ( m_flChasePlayerTime && m_flChasePlayerTime > gpGlobals->curtime )
  2258. return D_HT;
  2259. return D_NU;
  2260. }
  2261. }
  2262. return disp;
  2263. }
  2264. //-----------------------------------------------------------------------------
  2265. // Purpose:
  2266. // Input : *pEvent -
  2267. //-----------------------------------------------------------------------------
  2268. void CNPC_MetroPolice::OnAnimEventStartDeployManhack( void )
  2269. {
  2270. Assert( m_iManhacks );
  2271. if ( m_iManhacks <= 0 )
  2272. {
  2273. DevMsg( "Error: Throwing manhack but out of manhacks!\n" );
  2274. return;
  2275. }
  2276. m_iManhacks--;
  2277. // Turn off the manhack on our body
  2278. if ( m_iManhacks <= 0 )
  2279. {
  2280. SetBodygroup( METROPOLICE_BODYGROUP_MANHACK, false );
  2281. }
  2282. // Create the manhack to throw
  2283. CNPC_Manhack *pManhack = (CNPC_Manhack *)CreateEntityByName( "npc_manhack" );
  2284. Vector vecOrigin;
  2285. QAngle vecAngles;
  2286. int handAttachment = LookupAttachment( "LHand" );
  2287. GetAttachment( handAttachment, vecOrigin, vecAngles );
  2288. pManhack->SetLocalOrigin( vecOrigin );
  2289. pManhack->SetLocalAngles( vecAngles );
  2290. pManhack->AddSpawnFlags( (SF_MANHACK_PACKED_UP|SF_MANHACK_CARRIED|SF_NPC_WAIT_FOR_SCRIPT) );
  2291. // Also fade if our parent is marked to do it
  2292. if ( HasSpawnFlags( SF_NPC_FADE_CORPSE ) )
  2293. {
  2294. pManhack->AddSpawnFlags( SF_NPC_FADE_CORPSE );
  2295. }
  2296. pManhack->Spawn();
  2297. // Make us move with his hand until we're deployed
  2298. pManhack->SetParent( this, handAttachment );
  2299. m_hManhack = pManhack;
  2300. }
  2301. //-----------------------------------------------------------------------------
  2302. // Anim event handlers
  2303. //-----------------------------------------------------------------------------
  2304. void CNPC_MetroPolice::OnAnimEventDeployManhack( animevent_t *pEvent )
  2305. {
  2306. // Let it go
  2307. ReleaseManhack();
  2308. Vector forward, right;
  2309. GetVectors( &forward, &right, NULL );
  2310. IPhysicsObject *pPhysObj = m_hManhack->VPhysicsGetObject();
  2311. if ( pPhysObj )
  2312. {
  2313. Vector yawOff = right * random->RandomFloat( -1.0f, 1.0f );
  2314. Vector forceVel = ( forward + yawOff * 16.0f ) + Vector( 0, 0, 250 );
  2315. Vector forceAng = vec3_origin;
  2316. // Give us velocity
  2317. pPhysObj->AddVelocity( &forceVel, &forceAng );
  2318. }
  2319. // Stop dealing with this manhack
  2320. m_hManhack = NULL;
  2321. }
  2322. //-----------------------------------------------------------------------------
  2323. // Purpose:
  2324. //-----------------------------------------------------------------------------
  2325. void CNPC_MetroPolice::OnAnimEventShove( void )
  2326. {
  2327. CBaseEntity *pHurt = CheckTraceHullAttack( 16, Vector(-16,-16,-16), Vector(16,16,16), 15, DMG_CLUB, 1.0f, false );
  2328. if ( pHurt )
  2329. {
  2330. Vector vecForceDir = ( pHurt->WorldSpaceCenter() - WorldSpaceCenter() );
  2331. CBasePlayer *pPlayer = ToBasePlayer( pHurt );
  2332. if ( pPlayer != NULL )
  2333. {
  2334. //Kick the player angles
  2335. pPlayer->ViewPunch( QAngle( 8, 14, 0 ) );
  2336. Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
  2337. VectorNormalize(dir);
  2338. QAngle angles;
  2339. VectorAngles( dir, angles );
  2340. Vector forward, right;
  2341. AngleVectors( angles, &forward, &right, NULL );
  2342. //If not on ground, then don't make them fly!
  2343. if ( !(pHurt->GetFlags() & FL_ONGROUND ) )
  2344. forward.z = 0.0f;
  2345. //Push the target back
  2346. pHurt->ApplyAbsVelocityImpulse( forward * 250.0f );
  2347. // Force the player to drop anyting they were holding
  2348. pPlayer->ForceDropOfCarriedPhysObjects();
  2349. }
  2350. // Play a random attack hit sound
  2351. EmitSound( "NPC_Metropolice.Shove" );
  2352. }
  2353. }
  2354. //-----------------------------------------------------------------------------
  2355. // Purpose:
  2356. //-----------------------------------------------------------------------------
  2357. void CNPC_MetroPolice::OnAnimEventBatonOn( void )
  2358. {
  2359. #ifndef HL2MP
  2360. CWeaponStunStick *pStick = dynamic_cast<CWeaponStunStick *>(GetActiveWeapon());
  2361. if ( pStick )
  2362. {
  2363. pStick->SetStunState( true );
  2364. }
  2365. #endif
  2366. }
  2367. //-----------------------------------------------------------------------------
  2368. // Purpose:
  2369. //-----------------------------------------------------------------------------
  2370. void CNPC_MetroPolice::OnAnimEventBatonOff( void )
  2371. {
  2372. #ifndef HL2MP
  2373. CWeaponStunStick *pStick = dynamic_cast<CWeaponStunStick *>(GetActiveWeapon());
  2374. if ( pStick )
  2375. {
  2376. pStick->SetStunState( false );
  2377. }
  2378. #endif
  2379. }
  2380. //-----------------------------------------------------------------------------
  2381. // Purpose:
  2382. //
  2383. // Input : *pEvent -
  2384. //
  2385. //-----------------------------------------------------------------------------
  2386. void CNPC_MetroPolice::HandleAnimEvent( animevent_t *pEvent )
  2387. {
  2388. // Shove!
  2389. if ( pEvent->event == AE_METROPOLICE_SHOVE )
  2390. {
  2391. OnAnimEventShove();
  2392. return;
  2393. }
  2394. if ( pEvent->event == AE_METROPOLICE_BATON_ON )
  2395. {
  2396. OnAnimEventBatonOn();
  2397. return;
  2398. }
  2399. if ( pEvent->event == AE_METROPOLICE_BATON_OFF )
  2400. {
  2401. OnAnimEventBatonOff();
  2402. return;
  2403. }
  2404. if ( pEvent->event == AE_METROPOLICE_START_DEPLOY )
  2405. {
  2406. OnAnimEventStartDeployManhack();
  2407. return;
  2408. }
  2409. if ( pEvent->event == AE_METROPOLICE_DRAW_PISTOL )
  2410. {
  2411. m_fWeaponDrawn = true;
  2412. if( GetActiveWeapon() )
  2413. {
  2414. GetActiveWeapon()->RemoveEffects( EF_NODRAW );
  2415. }
  2416. return;
  2417. }
  2418. if ( pEvent->event == AE_METROPOLICE_DEPLOY_MANHACK )
  2419. {
  2420. OnAnimEventDeployManhack( pEvent );
  2421. return;
  2422. }
  2423. BaseClass::HandleAnimEvent( pEvent );
  2424. }
  2425. //-----------------------------------------------------------------------------
  2426. // Purpose: This is a generic function (to be implemented by sub-classes) to
  2427. // handle specific interactions between different types of characters
  2428. // (For example the barnacle grabbing an NPC)
  2429. // Input : Constant for the type of interaction
  2430. // Output : true - if sub-class has a response for the interaction
  2431. // false - if sub-class has no response
  2432. //-----------------------------------------------------------------------------
  2433. bool CNPC_MetroPolice::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
  2434. {
  2435. if ( interactionType == g_interactionMetrocopStartedStitch )
  2436. {
  2437. // If anybody in our squad started a stitch, we can't for a little while
  2438. m_flValidStitchTime = gpGlobals->curtime + random->RandomFloat( METROPOLICE_SQUAD_STITCH_MIN_INTERVAL, METROPOLICE_SQUAD_STITCH_MAX_INTERVAL );
  2439. return true;
  2440. }
  2441. if ( interactionType == g_interactionMetrocopIdleChatter )
  2442. {
  2443. m_nIdleChatterType = (int)data;
  2444. return true;
  2445. }
  2446. if ( interactionType == g_interactionMetrocopClearSentenceQueues )
  2447. {
  2448. m_Sentences.ClearQueue();
  2449. return true;
  2450. }
  2451. // React to being hit by physics objects
  2452. if ( interactionType == g_interactionHitByPlayerThrownPhysObj )
  2453. {
  2454. // Ignore if I'm in scripted state
  2455. if ( !IsInAScript() && (m_NPCState != NPC_STATE_SCRIPT) )
  2456. {
  2457. SetCondition( COND_METROPOLICE_PHYSOBJECT_ASSAULT );
  2458. }
  2459. else
  2460. {
  2461. AdministerJustice();
  2462. }
  2463. // See if the object is the cupcop can. If so, fire the output (for x360 achievement)
  2464. CBaseProp *pProp = (CBaseProp*)data;
  2465. if( pProp != NULL )
  2466. {
  2467. if( pProp->NameMatches("cupcop_can") )
  2468. m_OnCupCopped.FireOutput( this, NULL );
  2469. }
  2470. return true;
  2471. }
  2472. return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
  2473. }
  2474. //-----------------------------------------------------------------------------
  2475. //-----------------------------------------------------------------------------
  2476. Activity CNPC_MetroPolice::NPC_TranslateActivity( Activity newActivity )
  2477. {
  2478. if( IsOnFire() && newActivity == ACT_RUN )
  2479. {
  2480. return ACT_RUN_ON_FIRE;
  2481. }
  2482. // If we're shoving, see if we should be more forceful in doing so
  2483. if ( newActivity == ACT_PUSH_PLAYER )
  2484. {
  2485. if ( m_nNumWarnings >= METROPOLICE_MAX_WARNINGS )
  2486. return ACT_MELEE_ATTACK1;
  2487. }
  2488. newActivity = BaseClass::NPC_TranslateActivity( newActivity );
  2489. // This will put him into an angry idle, which will then be translated
  2490. // by the weapon to the appropriate type.
  2491. if ( m_fWeaponDrawn && newActivity == ACT_IDLE && ( GetState() == NPC_STATE_COMBAT || BatonActive() ) )
  2492. {
  2493. newActivity = ACT_IDLE_ANGRY;
  2494. }
  2495. return newActivity;
  2496. }
  2497. //-----------------------------------------------------------------------------
  2498. // Purpose: Makes the held manhack solid
  2499. //-----------------------------------------------------------------------------
  2500. void CNPC_MetroPolice::ReleaseManhack( void )
  2501. {
  2502. Assert( m_hManhack );
  2503. // Make us physical
  2504. m_hManhack->RemoveSpawnFlags( SF_MANHACK_CARRIED );
  2505. m_hManhack->CreateVPhysics();
  2506. // Release us
  2507. m_hManhack->RemoveSolidFlags( FSOLID_NOT_SOLID );
  2508. m_hManhack->SetMoveType( MOVETYPE_VPHYSICS );
  2509. m_hManhack->SetParent( NULL );
  2510. // Make us active
  2511. m_hManhack->RemoveSpawnFlags( SF_NPC_WAIT_FOR_SCRIPT );
  2512. m_hManhack->ClearSchedule( "Manhack released by metropolice" );
  2513. // Start him with knowledge of our current enemy
  2514. if ( GetEnemy() )
  2515. {
  2516. m_hManhack->SetEnemy( GetEnemy() );
  2517. m_hManhack->SetState( NPC_STATE_COMBAT );
  2518. m_hManhack->UpdateEnemyMemory( GetEnemy(), GetEnemy()->GetAbsOrigin() );
  2519. }
  2520. // Place him into our squad so we can communicate
  2521. if ( m_pSquad )
  2522. {
  2523. m_pSquad->AddToSquad( m_hManhack );
  2524. }
  2525. }
  2526. //-----------------------------------------------------------------------------
  2527. //
  2528. //-----------------------------------------------------------------------------
  2529. void CNPC_MetroPolice::Event_Killed( const CTakeDamageInfo &info )
  2530. {
  2531. // Release the manhack if we're in the middle of deploying him
  2532. if ( m_hManhack && m_hManhack->IsAlive() )
  2533. {
  2534. ReleaseManhack();
  2535. m_hManhack = NULL;
  2536. }
  2537. CBasePlayer *pPlayer = ToBasePlayer( info.GetAttacker() );
  2538. if ( pPlayer != NULL )
  2539. {
  2540. CHalfLife2 *pHL2GameRules = static_cast<CHalfLife2 *>(g_pGameRules);
  2541. // Attempt to drop health
  2542. if ( pHL2GameRules->NPC_ShouldDropHealth( pPlayer ) )
  2543. {
  2544. DropItem( "item_healthvial", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
  2545. pHL2GameRules->NPC_DroppedHealth();
  2546. }
  2547. }
  2548. BaseClass::Event_Killed( info );
  2549. }
  2550. //-----------------------------------------------------------------------------
  2551. // Try to enter a slot where we shoot a pistol
  2552. //-----------------------------------------------------------------------------
  2553. bool CNPC_MetroPolice::TryToEnterPistolSlot( int nSquadSlot )
  2554. {
  2555. // This logic here will not allow us to occupy the a squad slot
  2556. // too soon after we already were in it.
  2557. if ( ( m_LastShootSlot != nSquadSlot || !m_TimeYieldShootSlot.Expired() ) &&
  2558. OccupyStrategySlot( nSquadSlot ) )
  2559. {
  2560. if ( m_LastShootSlot != nSquadSlot )
  2561. {
  2562. m_TimeYieldShootSlot.Reset();
  2563. m_LastShootSlot = nSquadSlot;
  2564. }
  2565. return true;
  2566. }
  2567. return false;
  2568. }
  2569. //-----------------------------------------------------------------------------
  2570. // Combat schedule selection
  2571. //-----------------------------------------------------------------------------
  2572. int CNPC_MetroPolice::SelectRangeAttackSchedule()
  2573. {
  2574. if ( HasSpawnFlags( SF_METROPOLICE_ALWAYS_STITCH ) )
  2575. {
  2576. int nSched = SelectMoveToLedgeSchedule();
  2577. if ( nSched != SCHED_NONE )
  2578. return nSched;
  2579. }
  2580. // Range attack if we're able
  2581. if( TryToEnterPistolSlot( SQUAD_SLOT_ATTACK1 ) || TryToEnterPistolSlot( SQUAD_SLOT_ATTACK2 ))
  2582. return SCHED_RANGE_ATTACK1;
  2583. // We're not in a shoot slot... so we've allowed someone else to grab it
  2584. m_LastShootSlot = SQUAD_SLOT_NONE;
  2585. if( CanDeployManhack() && OccupyStrategySlot( SQUAD_SLOT_POLICE_DEPLOY_MANHACK ) )
  2586. {
  2587. return SCHED_METROPOLICE_DEPLOY_MANHACK;
  2588. }
  2589. return SCHED_METROPOLICE_ADVANCE;
  2590. }
  2591. //-----------------------------------------------------------------------------
  2592. // How many squad members are trying to arrest the player?
  2593. //-----------------------------------------------------------------------------
  2594. int CNPC_MetroPolice::SquadArrestCount()
  2595. {
  2596. int nCount = 0;
  2597. AISquadIter_t iter;
  2598. CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter );
  2599. while ( pSquadmate )
  2600. {
  2601. if ( pSquadmate->IsCurSchedule( SCHED_METROPOLICE_ARREST_ENEMY ) ||
  2602. pSquadmate->IsCurSchedule( SCHED_METROPOLICE_WARN_AND_ARREST_ENEMY ) )
  2603. {
  2604. ++nCount;
  2605. }
  2606. pSquadmate = m_pSquad->GetNextMember( &iter );
  2607. }
  2608. return nCount;
  2609. }
  2610. //-----------------------------------------------------------------------------
  2611. // Arrest schedule selection
  2612. //-----------------------------------------------------------------------------
  2613. int CNPC_MetroPolice::SelectScheduleArrestEnemy()
  2614. {
  2615. if ( !HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) || !IsInSquad() )
  2616. return SCHED_NONE;
  2617. if ( !HasCondition( COND_SEE_ENEMY ) )
  2618. return SCHED_NONE;
  2619. if ( !m_fWeaponDrawn )
  2620. return SCHED_METROPOLICE_DRAW_PISTOL;
  2621. // First guy that sees the enemy will tell him to freeze
  2622. if ( OccupyStrategySlot( SQUAD_SLOT_POLICE_ARREST_ENEMY ) )
  2623. return SCHED_METROPOLICE_WARN_AND_ARREST_ENEMY;
  2624. // Squad members 1 -> n will simply gain a line of sight
  2625. return SCHED_METROPOLICE_ARREST_ENEMY;
  2626. }
  2627. //-----------------------------------------------------------------------------
  2628. // Combat schedule selection
  2629. //-----------------------------------------------------------------------------
  2630. int CNPC_MetroPolice::SelectScheduleNewEnemy()
  2631. {
  2632. int nSched = SelectScheduleArrestEnemy();
  2633. if ( nSched != SCHED_NONE )
  2634. return nSched;
  2635. if ( HasCondition( COND_NEW_ENEMY ) )
  2636. {
  2637. m_flNextLedgeCheckTime = gpGlobals->curtime;
  2638. if( CanDeployManhack() && OccupyStrategySlot( SQUAD_SLOT_POLICE_DEPLOY_MANHACK ) )
  2639. return SCHED_METROPOLICE_DEPLOY_MANHACK;
  2640. }
  2641. if ( !m_fWeaponDrawn )
  2642. return SCHED_METROPOLICE_DRAW_PISTOL;
  2643. // Switch our baton on, if it's not already
  2644. if ( HasBaton() && BatonActive() == false && IsCurSchedule( SCHED_METROPOLICE_ACTIVATE_BATON ) == false )
  2645. {
  2646. SetTarget( GetEnemy() );
  2647. SetBatonState( true );
  2648. m_flBatonDebounceTime = gpGlobals->curtime + random->RandomFloat( 2.5f, 4.0f );
  2649. return SCHED_METROPOLICE_ACTIVATE_BATON;
  2650. }
  2651. return SCHED_NONE;
  2652. }
  2653. //-----------------------------------------------------------------------------
  2654. // Sound investigation
  2655. //-----------------------------------------------------------------------------
  2656. int CNPC_MetroPolice::SelectScheduleInvestigateSound()
  2657. {
  2658. // SEE_ENEMY is set if LOS is available *and* we're looking the right way
  2659. // Don't investigate if the player's not a criminal.
  2660. if ( PlayerIsCriminal() && !HasCondition( COND_SEE_ENEMY ) )
  2661. {
  2662. if ( HasCondition( COND_HEAR_COMBAT ) || HasCondition( COND_HEAR_PLAYER ) )
  2663. {
  2664. if ( m_pSquad && OccupyStrategySlot( SQUAD_SLOT_INVESTIGATE_SOUND ) )
  2665. {
  2666. return SCHED_METROPOLICE_INVESTIGATE_SOUND;
  2667. }
  2668. }
  2669. }
  2670. return SCHED_NONE;
  2671. }
  2672. //-----------------------------------------------------------------------------
  2673. // Purpose:
  2674. //-----------------------------------------------------------------------------
  2675. bool CNPC_MetroPolice::OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  2676. {
  2677. if ( pMoveGoal->directTrace.pObstruction )
  2678. {
  2679. // Is it a physics prop? Store it off as the last thing to block me
  2680. CPhysicsProp *pProp = dynamic_cast<CPhysicsProp*>( pMoveGoal->directTrace.pObstruction );
  2681. if ( pProp && pProp->GetHealth() )
  2682. {
  2683. m_hBlockingProp = pProp;
  2684. }
  2685. else
  2686. {
  2687. m_hBlockingProp = NULL;
  2688. }
  2689. }
  2690. return BaseClass::OnObstructionPreSteer( pMoveGoal, distClear, pResult );
  2691. }
  2692. //-----------------------------------------------------------------------------
  2693. // Combat schedule selection
  2694. //-----------------------------------------------------------------------------
  2695. int CNPC_MetroPolice::SelectScheduleNoDirectEnemy()
  2696. {
  2697. // If you can't attack, but you can deploy a manhack, do it!
  2698. if( CanDeployManhack() && OccupyStrategySlot( SQUAD_SLOT_POLICE_DEPLOY_MANHACK ) )
  2699. return SCHED_METROPOLICE_DEPLOY_MANHACK;
  2700. // If you can't attack, but you have a baton & there's a physics object in front of you, swat it
  2701. if ( m_hBlockingProp && HasBaton() )
  2702. {
  2703. SetTarget( m_hBlockingProp );
  2704. m_hBlockingProp = NULL;
  2705. return SCHED_METROPOLICE_SMASH_PROP;
  2706. }
  2707. return SCHED_METROPOLICE_CHASE_ENEMY;
  2708. }
  2709. //-----------------------------------------------------------------------------
  2710. // Combat schedule selection
  2711. //-----------------------------------------------------------------------------
  2712. int CNPC_MetroPolice::SelectCombatSchedule()
  2713. {
  2714. // Announce a new enemy
  2715. if ( HasCondition( COND_NEW_ENEMY ) )
  2716. {
  2717. AnnounceEnemyType( GetEnemy() );
  2718. }
  2719. int nResult = SelectScheduleNewEnemy();
  2720. if ( nResult != SCHED_NONE )
  2721. return nResult;
  2722. if( !m_fWeaponDrawn )
  2723. {
  2724. return SCHED_METROPOLICE_DRAW_PISTOL;
  2725. }
  2726. if (!HasBaton() && ((float)m_nRecentDamage / (float)GetMaxHealth()) > RECENT_DAMAGE_THRESHOLD)
  2727. {
  2728. m_nRecentDamage = 0;
  2729. m_flRecentDamageTime = 0;
  2730. m_Sentences.Speak( "METROPOLICE_COVER_HEAVY_DAMAGE", SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
  2731. return SCHED_TAKE_COVER_FROM_ENEMY;
  2732. }
  2733. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  2734. {
  2735. if ( !GetShotRegulator()->IsInRestInterval() )
  2736. return SelectRangeAttackSchedule();
  2737. else
  2738. return SCHED_METROPOLICE_ADVANCE;
  2739. }
  2740. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  2741. {
  2742. if ( m_BatonSwingTimer.Expired() )
  2743. {
  2744. // Stop chasing the player now that we've taken a swing at them
  2745. m_flChasePlayerTime = 0;
  2746. m_BatonSwingTimer.Set( 1.0, 1.75 );
  2747. return SCHED_MELEE_ATTACK1;
  2748. }
  2749. else
  2750. return SCHED_COMBAT_FACE;
  2751. }
  2752. if ( HasCondition( COND_TOO_CLOSE_TO_ATTACK ) )
  2753. {
  2754. return SCHED_BACK_AWAY_FROM_ENEMY;
  2755. }
  2756. if ( HasCondition( COND_LOW_PRIMARY_AMMO ) || HasCondition( COND_NO_PRIMARY_AMMO ) )
  2757. {
  2758. AnnounceOutOfAmmo( );
  2759. return SCHED_HIDE_AND_RELOAD;
  2760. }
  2761. if ( HasCondition(COND_WEAPON_SIGHT_OCCLUDED) && !HasBaton() )
  2762. {
  2763. // If they are hiding behind something that we can destroy, start shooting at it.
  2764. CBaseEntity *pBlocker = GetEnemyOccluder();
  2765. if ( pBlocker && pBlocker->GetHealth() > 0 && OccupyStrategySlotRange( SQUAD_SLOT_POLICE_ATTACK_OCCLUDER1, SQUAD_SLOT_POLICE_ATTACK_OCCLUDER2 ) )
  2766. {
  2767. m_Sentences.Speak( "METROPOLICE_SHOOT_COVER" );
  2768. return SCHED_SHOOT_ENEMY_COVER;
  2769. }
  2770. }
  2771. if (HasCondition(COND_ENEMY_OCCLUDED))
  2772. {
  2773. if ( GetEnemy() && !(GetEnemy()->GetFlags() & FL_NOTARGET) )
  2774. {
  2775. // Charge in and break the enemy's cover!
  2776. return SCHED_ESTABLISH_LINE_OF_FIRE;
  2777. }
  2778. }
  2779. nResult = SelectScheduleNoDirectEnemy();
  2780. if ( nResult != SCHED_NONE )
  2781. return nResult;
  2782. return SCHED_NONE;
  2783. }
  2784. //-----------------------------------------------------------------------------
  2785. // Purpose: This is a bridge between stunstick, NPC and its behavior
  2786. // Output : Returns true on success, false on failure.
  2787. //-----------------------------------------------------------------------------
  2788. bool CNPC_MetroPolice::ShouldKnockOutTarget( CBaseEntity *pTarget )
  2789. {
  2790. if ( m_PolicingBehavior.IsEnabled() && m_PolicingBehavior.ShouldKnockOutTarget( pTarget ) )
  2791. return true;
  2792. return false;
  2793. }
  2794. //-----------------------------------------------------------------------------
  2795. // Purpose: This is a bridge between stunstick, NPC and its behavior
  2796. //-----------------------------------------------------------------------------
  2797. void CNPC_MetroPolice::KnockOutTarget( CBaseEntity *pTarget )
  2798. {
  2799. if ( m_PolicingBehavior.IsEnabled() )
  2800. {
  2801. m_PolicingBehavior.KnockOutTarget( pTarget );
  2802. }
  2803. }
  2804. //-----------------------------------------------------------------------------
  2805. // Can me enemy see me?
  2806. //-----------------------------------------------------------------------------
  2807. bool CNPC_MetroPolice::CanEnemySeeMe( )
  2808. {
  2809. if ( GetEnemy()->IsPlayer() )
  2810. {
  2811. if ( static_cast<CBasePlayer*>(GetEnemy())->FInViewCone( this ) )
  2812. {
  2813. return true;
  2814. }
  2815. }
  2816. return false;
  2817. }
  2818. //-----------------------------------------------------------------------------
  2819. // Choose weights about where we can use particular stitching behaviors
  2820. //-----------------------------------------------------------------------------
  2821. #define STITCH_MIN_DISTANCE 1000.0f
  2822. #define STITCH_MIN_DISTANCE_SLOW 1250.0f
  2823. #define STITCH_AT_CONE 0.866f // cos(30)
  2824. #define STITCH_AT_CONE_WHEN_VISIBLE_MAX 0.3f // cos(?)
  2825. #define STITCH_AT_CONE_WHEN_VISIBLE_MIN 0.707f // cos(45)
  2826. #define STITCH_AT_COS_MIN_SPIN_ANGLE 0.2f
  2827. float CNPC_MetroPolice::StitchAtWeight( float flDist, float flSpeed, float flDot, float flReactionTime, const Vector &vecTargetToGun )
  2828. {
  2829. // Can't do an 'attacking' stitch if it's too soon
  2830. if ( m_flValidStitchTime > gpGlobals->curtime )
  2831. return 0.0f;
  2832. // No squad slots? no way.
  2833. if( IsStrategySlotRangeOccupied( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  2834. return false;
  2835. // Don't do it if the player doesn't have enough time to react
  2836. if ( flDist < STITCH_MIN_DISTANCE )
  2837. return 0.0f;
  2838. // Don't do it if the player is farther but really slow
  2839. if ( ( flDist < STITCH_MIN_DISTANCE_SLOW ) && ( flSpeed < 150.0f ) )
  2840. return 0.0f;
  2841. // Does the predicted stitch position cross the plane from me to the target's initial position?
  2842. // If so, it'll look really dumb. Disallow that.
  2843. Vector vecGunToPredictedTarget, vecShootAtVel;
  2844. PredictShootTargetPosition( flReactionTime, 0.0f, 0.0f, &vecGunToPredictedTarget, &vecShootAtVel );
  2845. vecGunToPredictedTarget -= Weapon_ShootPosition();
  2846. vecGunToPredictedTarget.z = 0.0f;
  2847. VectorNormalize( vecGunToPredictedTarget );
  2848. Vector2D vecGunToTarget;
  2849. Vector2DMultiply( vecTargetToGun.AsVector2D(), -1.0f, vecGunToTarget );
  2850. Vector2DNormalize( vecGunToTarget );
  2851. if ( DotProduct2D( vecGunToTarget, vecGunToPredictedTarget.AsVector2D() ) <= STITCH_AT_COS_MIN_SPIN_ANGLE )
  2852. return 0.0f;
  2853. // If the cop is in the view cone, then up the cone in which the stitch will occur
  2854. float flConeAngle = STITCH_AT_CONE;
  2855. if ( CanEnemySeeMe() )
  2856. {
  2857. flDist = clamp( flDist, 1500.0f, 2500.0f );
  2858. flConeAngle = RemapVal( flDist, 1500.0f, 2500.0f, STITCH_AT_CONE_WHEN_VISIBLE_MIN, STITCH_AT_CONE_WHEN_VISIBLE_MAX );
  2859. }
  2860. flDot = clamp( flDot, -1.0f, flConeAngle );
  2861. return RemapVal( flDot, -1.0f, flConeAngle, 0.5f, 1.0f );
  2862. }
  2863. #define STITCH_ACROSS_CONE 0.5f // cos(60)
  2864. float CNPC_MetroPolice::StitchAcrossWeight( float flDist, float flSpeed, float flDot, float flReactionTime )
  2865. {
  2866. return 0.0f;
  2867. // Can't do an 'attacking' stitch if it's too soon
  2868. if ( m_flValidStitchTime > gpGlobals->curtime )
  2869. return 0.0f;
  2870. // No squad slots? no way.
  2871. if( IsStrategySlotRangeOccupied( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  2872. return 0.0f;
  2873. if ( flDist < STITCH_MIN_DISTANCE )
  2874. return 0.0f;
  2875. // Don't do it if the player doesn't have enough time to react
  2876. if ( flDist < flSpeed * flReactionTime )
  2877. return 0.0f;
  2878. // We want to stitch across if we're within the stitch across cone
  2879. if ( flDot < STITCH_ACROSS_CONE )
  2880. return 0.0f;
  2881. return 1.0f;
  2882. }
  2883. #define STITCH_ALONG_MIN_CONE 0.866f // cos(30)
  2884. #define STITCH_ALONG_MIN_CONE_WHEN_VISIBLE 0.707f // cos(45)
  2885. #define STITCH_ALONG_MAX_CONE -0.4f //
  2886. #define STITCH_ALONG_MIN_SPEED 300.0f
  2887. float CNPC_MetroPolice::StitchAlongSideWeight( float flDist, float flSpeed, float flDot )
  2888. {
  2889. // No squad slots? no way.
  2890. if( IsStrategySlotRangeOccupied( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) &&
  2891. IsStrategySlotRangeOccupied( SQUAD_SLOT_POLICE_COVERING_FIRE1, SQUAD_SLOT_POLICE_COVERING_FIRE2 ) )
  2892. return 0.0f;
  2893. if ( flDist < (AIM_ALONG_SIDE_LINE_OF_DEATH_DISTANCE + AIM_ALONG_SIDE_STEER_DISTANCE + 100.0f) )
  2894. return 0.0f;
  2895. if ( flSpeed < STITCH_ALONG_MIN_SPEED )
  2896. return 0.0f;
  2897. // We want to stitch across if we're within the stitch across cone
  2898. float flMinConeAngle = STITCH_ALONG_MIN_CONE;
  2899. bool bCanEnemySeeMe = CanEnemySeeMe( );
  2900. if ( bCanEnemySeeMe )
  2901. {
  2902. flMinConeAngle = STITCH_ALONG_MIN_CONE_WHEN_VISIBLE;
  2903. }
  2904. if (( flDot > flMinConeAngle ) || ( flDot < STITCH_ALONG_MAX_CONE ))
  2905. return 0.0f;
  2906. return bCanEnemySeeMe ? 1.0f : 2.0f;
  2907. }
  2908. #define STITCH_BEHIND_MIN_CONE 0.0f // cos(90)
  2909. float CNPC_MetroPolice::StitchBehindWeight( float flDist, float flSpeed, float flDot )
  2910. {
  2911. return 0.0f;
  2912. // No squad slots? no way.
  2913. if( IsStrategySlotRangeOccupied( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) &&
  2914. IsStrategySlotRangeOccupied( SQUAD_SLOT_POLICE_COVERING_FIRE1, SQUAD_SLOT_POLICE_COVERING_FIRE2 ) )
  2915. return 0.0f;
  2916. if ( flDist < AIM_BEHIND_MINIMUM_DISTANCE )
  2917. return 0.0f;
  2918. // We want to stitch across if we're within the stitch across cone
  2919. if ( flDot > STITCH_BEHIND_MIN_CONE )
  2920. return 0.0f;
  2921. // If we're close, reduce the chances of this if we're also slow
  2922. if ( flDist < STITCH_MIN_DISTANCE )
  2923. {
  2924. flSpeed = clamp( flSpeed, 300.0f, 450.0f );
  2925. float flWeight = RemapVal( flSpeed, 300.0f, 450.0f, 0.0f, 1.0f );
  2926. return flWeight;
  2927. }
  2928. return 1.0f;
  2929. }
  2930. float CNPC_MetroPolice::StitchTightWeight( float flDist, float flSpeed, const Vector &vecTargetToGun, const Vector &vecVelocity )
  2931. {
  2932. // No squad slots? no way.
  2933. if( IsStrategySlotRangeOccupied( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) &&
  2934. IsStrategySlotRangeOccupied( SQUAD_SLOT_POLICE_COVERING_FIRE1, SQUAD_SLOT_POLICE_COVERING_FIRE2 ) )
  2935. return 0.0f;
  2936. if ( flDist > STITCH_MIN_DISTANCE )
  2937. {
  2938. if ( flDist > 2000.0f )
  2939. return 0.0f;
  2940. // We can stitch tight if they are close and no other rules apply.
  2941. return 0.0001f;
  2942. }
  2943. // If we're heading right at him, them fire it!
  2944. Vector vecTargetToGunDir = vecTargetToGun;
  2945. Vector vecVelocityDir = vecVelocity;
  2946. VectorNormalize( vecTargetToGunDir );
  2947. VectorNormalize( vecVelocityDir );
  2948. if ( DotProduct( vecTargetToGunDir, vecVelocityDir ) > 0.95f )
  2949. return 8.0f;
  2950. // If we're on the same level, fire at him!
  2951. if ( ( fabs(vecTargetToGun.z) < 50.0f ) && ( flDist < STITCH_MIN_DISTANCE ) )
  2952. return 1.0f;
  2953. flSpeed = clamp( flSpeed, 300.0f, 450.0f );
  2954. float flWeight = RemapVal( flSpeed, 300.0f, 450.0f, 1.0f, 0.0f );
  2955. return flWeight;
  2956. }
  2957. //-----------------------------------------------------------------------------
  2958. // Combat schedule selection
  2959. //-----------------------------------------------------------------------------
  2960. #define STITCH_REACTION_TIME 2.0f
  2961. #define STITCH_SCHEDULE_COUNT 5
  2962. int CNPC_MetroPolice::SelectStitchSchedule()
  2963. {
  2964. // If the boat is very close to us, we're going to stitch at it
  2965. // even if the squad slot is full..
  2966. Vector vecTargetToGun;
  2967. Vector vecTarget = StitchAimTarget( GetAbsOrigin(), false );
  2968. VectorSubtract( Weapon_ShootPosition(), vecTarget, vecTargetToGun );
  2969. Vector2D vecTargetToGun2D = vecTargetToGun.AsVector2D();
  2970. float flDist = Vector2DNormalize( vecTargetToGun2D );
  2971. if ( HasSpawnFlags( SF_METROPOLICE_NO_FAR_STITCH ) )
  2972. {
  2973. if ( flDist > 6000.0f )
  2974. return SCHED_NONE;
  2975. }
  2976. float flReactionTime = STITCH_REACTION_TIME * sk_metropolice_stitch_reaction.GetFloat();
  2977. Vector vecVelocity;
  2978. PredictShootTargetVelocity( flReactionTime, &vecVelocity );
  2979. Vector2D vecVelocity2D = vecVelocity.AsVector2D();
  2980. float flSpeed = Vector2DNormalize( vecVelocity2D );
  2981. float flDot = DotProduct2D( vecTargetToGun2D, vecVelocity2D );
  2982. float flWeight[STITCH_SCHEDULE_COUNT];
  2983. flWeight[0] = StitchAtWeight( flDist, flSpeed, flDot, flReactionTime, vecTargetToGun );
  2984. flWeight[1] = flWeight[0] + StitchAcrossWeight( flDist, flSpeed, flDot, flReactionTime );
  2985. flWeight[2] = flWeight[1] + StitchTightWeight( flDist, flSpeed, vecTargetToGun, vecVelocity );
  2986. flWeight[3] = flWeight[2] + StitchAlongSideWeight( flDist, flSpeed, flDot );
  2987. flWeight[4] = flWeight[3] + StitchBehindWeight( flDist, flSpeed, flDot );
  2988. if ( flWeight[STITCH_SCHEDULE_COUNT - 1] == 0.0f )
  2989. return SCHED_NONE;
  2990. int pSched[STITCH_SCHEDULE_COUNT] =
  2991. {
  2992. SCHED_METROPOLICE_AIM_STITCH_AT_AIRBOAT,
  2993. SCHED_METROPOLICE_AIM_STITCH_IN_FRONT_OF_AIRBOAT,
  2994. SCHED_METROPOLICE_AIM_STITCH_TIGHTLY,
  2995. SCHED_METROPOLICE_AIM_STITCH_ALONG_SIDE_OF_AIRBOAT,
  2996. SCHED_METROPOLICE_AIM_STITCH_BEHIND_AIRBOAT,
  2997. };
  2998. int i;
  2999. float flRand = random->RandomFloat( 0.0f, flWeight[STITCH_SCHEDULE_COUNT - 1] );
  3000. for ( i = 0; i < STITCH_SCHEDULE_COUNT; ++i )
  3001. {
  3002. if ( flRand <= flWeight[i] )
  3003. break;
  3004. }
  3005. // If we're basically a covering activity, take up that slot
  3006. if ( i >= 3 )
  3007. {
  3008. if( OccupyStrategySlotRange( SQUAD_SLOT_POLICE_COVERING_FIRE1, SQUAD_SLOT_POLICE_COVERING_FIRE2 ) )
  3009. {
  3010. return pSched[i];
  3011. }
  3012. }
  3013. if( OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  3014. {
  3015. if ( IsInSquad() && (i < 2) )
  3016. {
  3017. GetSquad()->BroadcastInteraction( g_interactionMetrocopStartedStitch, NULL );
  3018. }
  3019. return pSched[i];
  3020. }
  3021. return SCHED_NONE;
  3022. }
  3023. //-----------------------------------------------------------------------------
  3024. // Combat schedule selection
  3025. //-----------------------------------------------------------------------------
  3026. int CNPC_MetroPolice::SelectMoveToLedgeSchedule()
  3027. {
  3028. // Prevent a bunch of unnecessary raycasts.
  3029. if ( m_flNextLedgeCheckTime > gpGlobals->curtime )
  3030. return SCHED_NONE;
  3031. // If the NPC is above the airboat (say, on a bridge), make sure he
  3032. // goes to the closest ledge. (may need a spawnflag for this)
  3033. if ( (GetAbsOrigin().z - GetShootTarget()->GetAbsOrigin().z) >= 150.0f )
  3034. {
  3035. m_flNextLedgeCheckTime = gpGlobals->curtime + 3.0f;
  3036. // We need to be able to shoot downward at a 60 degree angle.
  3037. Vector vecDelta;
  3038. VectorSubtract( GetShootTarget()->WorldSpaceCenter(), Weapon_ShootPosition(), vecDelta );
  3039. vecDelta.z = 0.0f;
  3040. VectorNormalize( vecDelta );
  3041. // At this point, vecDelta is 45 degrees below horizontal.
  3042. vecDelta.z = -1;
  3043. vecDelta *= 100.0f;
  3044. trace_t tr;
  3045. CTraceFilterWorldOnly traceFilter;
  3046. UTIL_TraceLine( Weapon_ShootPosition(), Weapon_ShootPosition() + vecDelta, MASK_SOLID, &traceFilter, &tr );
  3047. if (tr.endpos.z >= GetAbsOrigin().z - 25.0f )
  3048. return SCHED_METROPOLICE_ESTABLISH_STITCH_LINE_OF_FIRE;
  3049. }
  3050. return SCHED_NONE;
  3051. }
  3052. //-----------------------------------------------------------------------------
  3053. // Combat schedule selection
  3054. //-----------------------------------------------------------------------------
  3055. int CNPC_MetroPolice::SelectAirboatRangeAttackSchedule()
  3056. {
  3057. // Move to a ledge, if we need to.
  3058. int nSched = SelectMoveToLedgeSchedule();
  3059. if ( nSched != SCHED_NONE )
  3060. return nSched;
  3061. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  3062. {
  3063. nSched = SelectStitchSchedule();
  3064. if ( nSched != SCHED_NONE )
  3065. {
  3066. m_LastShootSlot = SQUAD_SLOT_NONE;
  3067. return nSched;
  3068. }
  3069. }
  3070. if( CanDeployManhack() && OccupyStrategySlot( SQUAD_SLOT_POLICE_DEPLOY_MANHACK ) )
  3071. {
  3072. return SCHED_METROPOLICE_DEPLOY_MANHACK;
  3073. }
  3074. return SCHED_METROPOLICE_ESTABLISH_LINE_OF_FIRE;
  3075. }
  3076. //-----------------------------------------------------------------------------
  3077. // Combat schedule selection for when the enemy is in an airboat
  3078. //-----------------------------------------------------------------------------
  3079. int CNPC_MetroPolice::SelectAirboatCombatSchedule()
  3080. {
  3081. int nResult = SelectScheduleNewEnemy();
  3082. if ( nResult != SCHED_NONE )
  3083. return nResult;
  3084. // We're assuming here that the cops who attack airboats have SMGs
  3085. // Assert( Weapon_OwnsThisType( "weapon_smg1" ) );
  3086. if ( HasCondition( COND_SEE_ENEMY ) )
  3087. {
  3088. return SelectAirboatRangeAttackSchedule();
  3089. }
  3090. if ( HasCondition( COND_WEAPON_SIGHT_OCCLUDED ) )
  3091. {
  3092. // If they are hiding behind something also attack. Don't bother
  3093. // shooting the destroyable thing; it'll happen anyways with the SMG
  3094. CBaseEntity *pBlocker = GetEnemyOccluder();
  3095. if ( pBlocker && pBlocker->GetHealth() > 0 && OccupyStrategySlotRange( SQUAD_SLOT_POLICE_ATTACK_OCCLUDER1, SQUAD_SLOT_POLICE_ATTACK_OCCLUDER2 ) )
  3096. {
  3097. return SelectAirboatRangeAttackSchedule();
  3098. }
  3099. }
  3100. nResult = SelectScheduleNoDirectEnemy();
  3101. if ( nResult != SCHED_NONE )
  3102. return nResult;
  3103. return SCHED_NONE;
  3104. }
  3105. //-----------------------------------------------------------------------------
  3106. // Purpose:
  3107. // Input : &info -
  3108. // Output : Returns true on success, false on failure.
  3109. //-----------------------------------------------------------------------------
  3110. bool CNPC_MetroPolice::IsHeavyDamage( const CTakeDamageInfo &info )
  3111. {
  3112. // Metropolice considers bullet fire heavy damage
  3113. if ( info.GetDamageType() & DMG_BULLET )
  3114. return true;
  3115. return BaseClass::IsHeavyDamage( info );
  3116. }
  3117. //-----------------------------------------------------------------------------
  3118. // TraceAttack
  3119. //-----------------------------------------------------------------------------
  3120. void CNPC_MetroPolice::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  3121. {
  3122. // This is needed so we can keep track of the direction of the shot
  3123. // because we're going to use it to choose the flinch animation
  3124. if ( m_bSimpleCops )
  3125. {
  3126. if ( m_takedamage == DAMAGE_YES )
  3127. {
  3128. Vector vecLastHitDirection;
  3129. VectorIRotate( vecDir, EntityToWorldTransform(), vecLastHitDirection );
  3130. // Point *at* the shooter
  3131. vecLastHitDirection *= -1.0f;
  3132. QAngle lastHitAngles;
  3133. VectorAngles( vecLastHitDirection, lastHitAngles );
  3134. m_flLastHitYaw = lastHitAngles.y;
  3135. }
  3136. }
  3137. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  3138. }
  3139. //-----------------------------------------------------------------------------
  3140. // Determines the best type of flinch anim to play.
  3141. //-----------------------------------------------------------------------------
  3142. Activity CNPC_MetroPolice::GetFlinchActivity( bool bHeavyDamage, bool bGesture )
  3143. {
  3144. if ( !bGesture && m_bSimpleCops )
  3145. {
  3146. // Version for getting shot from behind
  3147. if ( ( m_flLastHitYaw > 90 ) && ( m_flLastHitYaw < 270 ) )
  3148. {
  3149. Activity flinchActivity = (Activity)ACT_METROPOLICE_FLINCH_BEHIND;
  3150. if ( SelectWeightedSequence ( flinchActivity ) != ACTIVITY_NOT_AVAILABLE )
  3151. return flinchActivity;
  3152. }
  3153. if ( ( LastHitGroup() == HITGROUP_CHEST ) ||
  3154. ( LastHitGroup() == HITGROUP_LEFTLEG ) ||
  3155. ( LastHitGroup() == HITGROUP_RIGHTLEG ) )
  3156. {
  3157. Activity flinchActivity = ACT_FLINCH_STOMACH;
  3158. if ( SelectWeightedSequence ( ACT_FLINCH_STOMACH ) != ACTIVITY_NOT_AVAILABLE )
  3159. return flinchActivity;
  3160. }
  3161. }
  3162. return BaseClass::GetFlinchActivity( bHeavyDamage, bGesture );
  3163. }
  3164. //-----------------------------------------------------------------------------
  3165. // Purpose:
  3166. //-----------------------------------------------------------------------------
  3167. void CNPC_MetroPolice::PlayFlinchGesture( void )
  3168. {
  3169. BaseClass::PlayFlinchGesture();
  3170. // To ensure old playtested difficulty stays the same, stop cops shooting for a bit after gesture flinches
  3171. GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + 0.5 );
  3172. }
  3173. //-----------------------------------------------------------------------------
  3174. // We're taking cover from danger
  3175. //-----------------------------------------------------------------------------
  3176. void CNPC_MetroPolice::AnnounceHarrassment( void )
  3177. {
  3178. static const char *pWarnings[3] =
  3179. {
  3180. "METROPOLICE_BACK_UP_A",
  3181. "METROPOLICE_BACK_UP_B",
  3182. "METROPOLICE_BACK_UP_C",
  3183. };
  3184. m_Sentences.Speak( pWarnings[ random->RandomInt( 0, ARRAYSIZE(pWarnings)-1 ) ], SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
  3185. }
  3186. //-----------------------------------------------------------------------------
  3187. // Purpose:
  3188. //-----------------------------------------------------------------------------
  3189. void CNPC_MetroPolice::IncrementPlayerCriminalStatus( void )
  3190. {
  3191. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  3192. if ( pPlayer )
  3193. {
  3194. AddLookTarget( pPlayer, 0.8f, 5.0f );
  3195. if ( m_nNumWarnings < METROPOLICE_MAX_WARNINGS )
  3196. {
  3197. m_nNumWarnings++;
  3198. }
  3199. if ( m_nNumWarnings >= (METROPOLICE_MAX_WARNINGS-1) )
  3200. {
  3201. SetTarget( pPlayer );
  3202. SetBatonState( true );
  3203. }
  3204. }
  3205. m_flBatonDebounceTime = gpGlobals->curtime + random->RandomFloat( 2.0f, 4.0f );
  3206. AnnounceHarrassment();
  3207. m_bKeepFacingPlayer = true;
  3208. }
  3209. //-----------------------------------------------------------------------------
  3210. // Purpose:
  3211. // Output : int
  3212. //-----------------------------------------------------------------------------
  3213. int CNPC_MetroPolice::SelectShoveSchedule( void )
  3214. {
  3215. IncrementPlayerCriminalStatus();
  3216. // Stop chasing the player now that we've taken a swing at them
  3217. m_flChasePlayerTime = 0;
  3218. return SCHED_METROPOLICE_SHOVE;
  3219. }
  3220. //-----------------------------------------------------------------------------
  3221. // Purpose:
  3222. // Output : float
  3223. //-----------------------------------------------------------------------------
  3224. float CNPC_MetroPolice::GetIdealAccel( void ) const
  3225. {
  3226. return GetIdealSpeed() * 2.0f;
  3227. }
  3228. //-----------------------------------------------------------------------------
  3229. // Purpose: Chase after a player who's just pissed us off, and hit him
  3230. //-----------------------------------------------------------------------------
  3231. void CNPC_MetroPolice::AdministerJustice( void )
  3232. {
  3233. if ( !AI_IsSinglePlayer() )
  3234. return;
  3235. // If we're allowed to chase the player, do so. Otherwise, just threaten.
  3236. if ( !IsInAScript() && (m_NPCState != NPC_STATE_SCRIPT) && HasSpawnFlags( SF_METROPOLICE_ALLOWED_TO_RESPOND ) )
  3237. {
  3238. if ( m_vecPreChaseOrigin == vec3_origin )
  3239. {
  3240. m_vecPreChaseOrigin = GetAbsOrigin();
  3241. m_flPreChaseYaw = GetAbsAngles().y;
  3242. }
  3243. m_flChasePlayerTime = gpGlobals->curtime + RandomFloat( 3, 7 );
  3244. // Attack the target
  3245. CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
  3246. SetEnemy( pPlayer );
  3247. SetState( NPC_STATE_COMBAT );
  3248. UpdateEnemyMemory( pPlayer, pPlayer->GetAbsOrigin() );
  3249. }
  3250. else
  3251. {
  3252. // Watch the player for a time.
  3253. m_bKeepFacingPlayer = true;
  3254. // Try and find a nearby cop to administer justice
  3255. CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  3256. int nAIs = g_AI_Manager.NumAIs();
  3257. for ( int i = 0; i < nAIs; i++ )
  3258. {
  3259. if ( ppAIs[i] == this )
  3260. continue;
  3261. if ( ppAIs[i]->Classify() == CLASS_METROPOLICE && FClassnameIs( ppAIs[i], "npc_metropolice" ) )
  3262. {
  3263. CNPC_MetroPolice *pNPC = assert_cast<CNPC_MetroPolice*>(ppAIs[i]);
  3264. if ( pNPC->HasSpawnFlags( SF_METROPOLICE_ALLOWED_TO_RESPOND ) )
  3265. {
  3266. // Is he within site & range?
  3267. if ( FVisible(pNPC) && pNPC->FVisible( UTIL_PlayerByIndex(1) ) &&
  3268. UTIL_DistApprox( WorldSpaceCenter(), pNPC->WorldSpaceCenter() ) < 512 )
  3269. {
  3270. pNPC->AdministerJustice();
  3271. break;
  3272. }
  3273. }
  3274. }
  3275. }
  3276. }
  3277. }
  3278. //-----------------------------------------------------------------------------
  3279. // Schedule selection
  3280. //-----------------------------------------------------------------------------
  3281. int CNPC_MetroPolice::SelectSchedule( void )
  3282. {
  3283. if ( !GetEnemy() && HasCondition( COND_IN_PVS ) && AI_GetSinglePlayer() && !AI_GetSinglePlayer()->IsAlive() )
  3284. {
  3285. return SCHED_PATROL_WALK;
  3286. }
  3287. if ( HasCondition(COND_METROPOLICE_ON_FIRE) )
  3288. {
  3289. m_Sentences.Speak( "METROPOLICE_ON_FIRE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
  3290. return SCHED_METROPOLICE_BURNING_STAND;
  3291. }
  3292. // React to being struck by a physics object
  3293. if ( HasCondition( COND_METROPOLICE_PHYSOBJECT_ASSAULT ) )
  3294. {
  3295. ClearCondition( COND_METROPOLICE_PHYSOBJECT_ASSAULT );
  3296. // See which state our player relationship is in
  3297. if ( PlayerIsCriminal() == false )
  3298. {
  3299. m_Sentences.Speak( "METROPOLICE_HIT_BY_PHYSOBJECT", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
  3300. m_nNumWarnings = METROPOLICE_MAX_WARNINGS;
  3301. AdministerJustice();
  3302. }
  3303. else if ( GlobalEntity_GetState( "gordon_precriminal" ) == GLOBAL_ON )
  3304. {
  3305. // We're not allowed to respond, but warn them
  3306. m_Sentences.Speak( "METROPOLICE_IDLE_HARASS_PLAYER", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
  3307. }
  3308. }
  3309. int nSched = SelectFlinchSchedule();
  3310. if ( nSched != SCHED_NONE )
  3311. return nSched;
  3312. if ( HasBaton() )
  3313. {
  3314. // See if we're being told to activate our baton
  3315. if ( m_bShouldActivateBaton && BatonActive() == false && IsCurSchedule( SCHED_METROPOLICE_ACTIVATE_BATON ) == false )
  3316. return SCHED_METROPOLICE_ACTIVATE_BATON;
  3317. if ( m_bShouldActivateBaton == false && BatonActive() && IsCurSchedule( SCHED_METROPOLICE_DEACTIVATE_BATON ) == false )
  3318. return SCHED_METROPOLICE_DEACTIVATE_BATON;
  3319. if( metropolice_chase_use_follow.GetBool() )
  3320. {
  3321. if( GetEnemy() )
  3322. {
  3323. AI_FollowParams_t params;
  3324. params.formation = AIF_TIGHT;
  3325. m_FollowBehavior.SetParameters( params );
  3326. m_FollowBehavior.SetFollowTarget( GetEnemy() );
  3327. }
  3328. }
  3329. }
  3330. // See if the player is in our face (unless we're scripting)
  3331. if ( PlayerIsCriminal() == false )
  3332. {
  3333. if ( !IsInAScript() && (HasCondition( COND_METROPOLICE_PLAYER_TOO_CLOSE ) || m_bPlayerTooClose) )
  3334. {
  3335. // Don't hit the player too many times in a row, unless he's trying to push a cop who hasn't moved
  3336. if ( m_iNumPlayerHits < 3 || m_vecPreChaseOrigin == vec3_origin )
  3337. {
  3338. ClearCondition( COND_METROPOLICE_PLAYER_TOO_CLOSE );
  3339. m_bPlayerTooClose = false;
  3340. return SelectShoveSchedule();
  3341. }
  3342. }
  3343. else if ( m_iNumPlayerHits )
  3344. {
  3345. // If we're not in combat, and we've got a pre-chase origin, move back to it
  3346. if ( ( m_NPCState != NPC_STATE_COMBAT ) &&
  3347. ( m_vecPreChaseOrigin != vec3_origin ) &&
  3348. ( m_flChasePlayerTime < gpGlobals->curtime ) )
  3349. {
  3350. return SCHED_METROPOLICE_RETURN_TO_PRECHASE;
  3351. }
  3352. }
  3353. }
  3354. // Cower when physics objects are thrown at me
  3355. if ( HasCondition( COND_HEAR_PHYSICS_DANGER ) )
  3356. {
  3357. if ( m_flLastPhysicsFlinchTime + 4.0f <= gpGlobals->curtime )
  3358. {
  3359. m_flLastPhysicsFlinchTime = gpGlobals->curtime;
  3360. return SCHED_FLINCH_PHYSICS;
  3361. }
  3362. }
  3363. // Always run for cover from danger sounds
  3364. if ( HasCondition(COND_HEAR_DANGER) )
  3365. {
  3366. CSound *pSound;
  3367. pSound = GetBestSound();
  3368. Assert( pSound != NULL );
  3369. if ( pSound )
  3370. {
  3371. if (pSound->m_iType & SOUND_DANGER)
  3372. {
  3373. AnnounceTakeCoverFromDanger( pSound );
  3374. return SCHED_TAKE_COVER_FROM_BEST_SOUND;
  3375. }
  3376. if (!HasCondition( COND_SEE_ENEMY ) && ( pSound->m_iType & (SOUND_PLAYER | SOUND_PLAYER_VEHICLE | SOUND_COMBAT) ))
  3377. {
  3378. GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() );
  3379. }
  3380. }
  3381. }
  3382. bool bHighHealth = ((float)GetHealth() / (float)GetMaxHealth() > 0.75f);
  3383. // This will cause the cops to run backwards + shoot at the same time
  3384. if ( !bHighHealth && !HasBaton() )
  3385. {
  3386. if ( GetActiveWeapon() && (GetActiveWeapon()->m_iClip1 <= 5) )
  3387. {
  3388. m_Sentences.Speak( "METROPOLICE_COVER_LOW_AMMO" );
  3389. return SCHED_HIDE_AND_RELOAD;
  3390. }
  3391. }
  3392. if( HasCondition( COND_NO_PRIMARY_AMMO ) )
  3393. {
  3394. if ( bHighHealth )
  3395. return SCHED_RELOAD;
  3396. AnnounceOutOfAmmo( );
  3397. return SCHED_HIDE_AND_RELOAD;
  3398. }
  3399. // If we're clubbing someone who threw something at us. chase them
  3400. if ( m_NPCState == NPC_STATE_COMBAT && m_flChasePlayerTime > gpGlobals->curtime )
  3401. return SCHED_CHASE_ENEMY;
  3402. if ( !BehaviorSelectSchedule() )
  3403. {
  3404. // If we've warned the player at all, watch him like a hawk
  3405. if ( m_bKeepFacingPlayer && !PlayerIsCriminal() )
  3406. return SCHED_TARGET_FACE;
  3407. switch( m_NPCState )
  3408. {
  3409. case NPC_STATE_IDLE:
  3410. {
  3411. nSched = SelectScheduleInvestigateSound();
  3412. if ( nSched != SCHED_NONE )
  3413. return nSched;
  3414. break;
  3415. }
  3416. case NPC_STATE_ALERT:
  3417. {
  3418. nSched = SelectScheduleInvestigateSound();
  3419. if ( nSched != SCHED_NONE )
  3420. return nSched;
  3421. }
  3422. break;
  3423. case NPC_STATE_COMBAT:
  3424. if (!IsEnemyInAnAirboat() || !Weapon_OwnsThisType( "weapon_smg1" ) )
  3425. {
  3426. int nResult = SelectCombatSchedule();
  3427. if ( nResult != SCHED_NONE )
  3428. return nResult;
  3429. }
  3430. else
  3431. {
  3432. int nResult = SelectAirboatCombatSchedule();
  3433. if ( nResult != SCHED_NONE )
  3434. return nResult;
  3435. }
  3436. break;
  3437. }
  3438. }
  3439. // If we're not in combat, and we've got a pre-chase origin, move back to it
  3440. if ( ( m_NPCState != NPC_STATE_COMBAT ) &&
  3441. ( m_vecPreChaseOrigin != vec3_origin ) &&
  3442. ( m_flChasePlayerTime < gpGlobals->curtime ) )
  3443. {
  3444. return SCHED_METROPOLICE_RETURN_TO_PRECHASE;
  3445. }
  3446. return BaseClass::SelectSchedule();
  3447. }
  3448. //-----------------------------------------------------------------------------
  3449. // Purpose:
  3450. // Input : failedSchedule -
  3451. // failedTask -
  3452. // taskFailCode -
  3453. // Output : int
  3454. //-----------------------------------------------------------------------------
  3455. int CNPC_MetroPolice::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  3456. {
  3457. if ( failedSchedule == SCHED_METROPOLICE_CHASE_ENEMY )
  3458. {
  3459. return SCHED_METROPOLICE_ESTABLISH_LINE_OF_FIRE;
  3460. }
  3461. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  3462. }
  3463. //-----------------------------------------------------------------------------
  3464. //-----------------------------------------------------------------------------
  3465. int CNPC_MetroPolice::TranslateSchedule( int scheduleType )
  3466. {
  3467. switch( scheduleType )
  3468. {
  3469. case SCHED_ALERT_FACE_BESTSOUND:
  3470. if ( !IsCurSchedule( SCHED_METROPOLICE_ALERT_FACE_BESTSOUND, false ) )
  3471. {
  3472. return SCHED_METROPOLICE_ALERT_FACE_BESTSOUND;
  3473. }
  3474. return SCHED_ALERT_FACE_BESTSOUND;
  3475. case SCHED_CHASE_ENEMY:
  3476. if ( !IsRunningBehavior() )
  3477. {
  3478. return SCHED_METROPOLICE_CHASE_ENEMY;
  3479. }
  3480. break;
  3481. case SCHED_ESTABLISH_LINE_OF_FIRE:
  3482. case SCHED_METROPOLICE_ESTABLISH_LINE_OF_FIRE:
  3483. if ( IsEnemyInAnAirboat() )
  3484. {
  3485. int nSched = SelectMoveToLedgeSchedule();
  3486. if ( nSched != SCHED_NONE )
  3487. return nSched;
  3488. }
  3489. return SCHED_METROPOLICE_ESTABLISH_LINE_OF_FIRE;
  3490. case SCHED_WAKE_ANGRY:
  3491. return SCHED_METROPOLICE_WAKE_ANGRY;
  3492. case SCHED_FAIL_TAKE_COVER:
  3493. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  3494. {
  3495. // Must be able to shoot now
  3496. if( TryToEnterPistolSlot( SQUAD_SLOT_ATTACK1 ) || TryToEnterPistolSlot( SQUAD_SLOT_ATTACK2 ) )
  3497. return SCHED_RANGE_ATTACK1;
  3498. }
  3499. if ( HasCondition( COND_NO_PRIMARY_AMMO ) )
  3500. return SCHED_RELOAD;
  3501. return SCHED_RUN_RANDOM;
  3502. case SCHED_RANGE_ATTACK1:
  3503. Assert( !HasCondition( COND_NO_PRIMARY_AMMO ) );
  3504. if( !m_fWeaponDrawn )
  3505. {
  3506. return SCHED_METROPOLICE_DRAW_PISTOL;
  3507. }
  3508. if( Weapon_OwnsThisType( "weapon_smg1" ) )
  3509. {
  3510. if ( IsEnemyInAnAirboat() )
  3511. {
  3512. int nSched = SelectStitchSchedule();
  3513. if ( nSched != SCHED_NONE )
  3514. return nSched;
  3515. }
  3516. if ( ShouldAttemptToStitch() )
  3517. {
  3518. return SCHED_METROPOLICE_SMG_BURST_ATTACK;
  3519. }
  3520. else
  3521. {
  3522. return SCHED_METROPOLICE_SMG_NORMAL_ATTACK;
  3523. }
  3524. }
  3525. break;
  3526. case SCHED_METROPOLICE_ADVANCE:
  3527. if ( m_NextChargeTimer.Expired() && metropolice_charge.GetBool() )
  3528. {
  3529. if ( Weapon_OwnsThisType( "weapon_pistol" ) )
  3530. {
  3531. if ( GetEnemy() && GetEnemy()->GetAbsOrigin().DistToSqr( GetAbsOrigin() ) > 300*300 )
  3532. {
  3533. if ( OccupyStrategySlot( SQUAD_SLOT_POLICE_CHARGE_ENEMY ) )
  3534. {
  3535. m_NextChargeTimer.Set( 3, 7 );
  3536. return SCHED_METROPOLICE_CHARGE;
  3537. }
  3538. }
  3539. }
  3540. else
  3541. {
  3542. m_NextChargeTimer.Set( 99999 );
  3543. }
  3544. }
  3545. break;
  3546. }
  3547. return BaseClass::TranslateSchedule( scheduleType );
  3548. }
  3549. //-----------------------------------------------------------------------------
  3550. // Can't move and shoot when the enemy is an airboat
  3551. //-----------------------------------------------------------------------------
  3552. bool CNPC_MetroPolice::ShouldMoveAndShoot()
  3553. {
  3554. if ( HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) )
  3555. return false;
  3556. if ( ShouldAttemptToStitch() )
  3557. return false;
  3558. return BaseClass::ShouldMoveAndShoot();
  3559. }
  3560. //-----------------------------------------------------------------------------
  3561. // Only move and shoot when attacking
  3562. //-----------------------------------------------------------------------------
  3563. bool CNPC_MetroPolice::OnBeginMoveAndShoot()
  3564. {
  3565. if ( BaseClass::OnBeginMoveAndShoot() )
  3566. {
  3567. if( HasStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  3568. return true; // already have the slot I need
  3569. if( OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  3570. return true;
  3571. }
  3572. return false;
  3573. }
  3574. //-----------------------------------------------------------------------------
  3575. // Only move and shoot when attacking
  3576. //-----------------------------------------------------------------------------
  3577. void CNPC_MetroPolice::OnEndMoveAndShoot()
  3578. {
  3579. VacateStrategySlot();
  3580. }
  3581. //-----------------------------------------------------------------------------
  3582. // Purpose:
  3583. // Input : pTask -
  3584. //-----------------------------------------------------------------------------
  3585. void CNPC_MetroPolice::StartTask( const Task_t *pTask )
  3586. {
  3587. switch (pTask->iTask)
  3588. {
  3589. case TASK_METROPOLICE_WAIT_FOR_SENTENCE:
  3590. {
  3591. if ( FOkToMakeSound( pTask->flTaskData ) )
  3592. {
  3593. TaskComplete();
  3594. }
  3595. }
  3596. break;
  3597. case TASK_METROPOLICE_GET_PATH_TO_PRECHASE:
  3598. {
  3599. Assert( m_vecPreChaseOrigin != vec3_origin );
  3600. if ( GetNavigator()->SetGoal( m_vecPreChaseOrigin ) )
  3601. {
  3602. QAngle vecAngles( 0, m_flPreChaseYaw, 0 );
  3603. GetNavigator()->SetArrivalDirection( vecAngles );
  3604. TaskComplete();
  3605. }
  3606. else
  3607. {
  3608. TaskFail( FAIL_NO_ROUTE );
  3609. }
  3610. break;
  3611. }
  3612. case TASK_METROPOLICE_CLEAR_PRECHASE:
  3613. {
  3614. m_vecPreChaseOrigin = vec3_origin;
  3615. m_flPreChaseYaw = 0;
  3616. TaskComplete();
  3617. break;
  3618. }
  3619. case TASK_METROPOLICE_ACTIVATE_BATON:
  3620. {
  3621. // Simply early out if we're in here without a baton
  3622. if ( HasBaton() == false )
  3623. {
  3624. TaskComplete();
  3625. break;
  3626. }
  3627. bool activate = ( pTask->flTaskData != 0 );
  3628. if ( activate )
  3629. {
  3630. if ( BatonActive() || m_bShouldActivateBaton == false )
  3631. {
  3632. TaskComplete();
  3633. break;
  3634. }
  3635. m_Sentences.Speak( "METROPOLICE_ACTIVATE_BATON", SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL );
  3636. SetIdealActivity( (Activity) ACT_ACTIVATE_BATON );
  3637. }
  3638. else
  3639. {
  3640. if ( BatonActive() == false || m_bShouldActivateBaton )
  3641. {
  3642. TaskComplete();
  3643. break;
  3644. }
  3645. m_Sentences.Speak( "METROPOLICE_DEACTIVATE_BATON", SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL );
  3646. SetIdealActivity( (Activity) ACT_DEACTIVATE_BATON );
  3647. }
  3648. }
  3649. break;
  3650. case TASK_METROPOLICE_DIE_INSTANTLY:
  3651. {
  3652. CTakeDamageInfo info;
  3653. info.SetAttacker( this );
  3654. info.SetInflictor( this );
  3655. info.SetDamage( m_iHealth );
  3656. info.SetDamageType( pTask->flTaskData );
  3657. info.SetDamageForce( Vector( 0.1, 0.1, 0.1 ) );
  3658. TakeDamage( info );
  3659. TaskComplete();
  3660. }
  3661. break;
  3662. case TASK_METROPOLICE_RESET_LEDGE_CHECK_TIME:
  3663. m_flNextLedgeCheckTime = gpGlobals->curtime;
  3664. TaskComplete();
  3665. break;
  3666. case TASK_METROPOLICE_LEAD_ARREST_ENEMY:
  3667. case TASK_METROPOLICE_ARREST_ENEMY:
  3668. m_flTaskCompletionTime = gpGlobals->curtime + pTask->flTaskData;
  3669. break;
  3670. case TASK_METROPOLICE_SIGNAL_FIRING_TIME:
  3671. EnemyResistingArrest();
  3672. TaskComplete();
  3673. break;
  3674. case TASK_METROPOLICE_GET_PATH_TO_STITCH:
  3675. {
  3676. if ( !ShouldAttemptToStitch() )
  3677. {
  3678. TaskFail( FAIL_NO_ROUTE );
  3679. break;
  3680. }
  3681. Vector vecTarget, vecTargetVel;
  3682. PredictShootTargetPosition( 0.5f, 0.0f, 0.0f, &vecTarget, &vecTargetVel );
  3683. vecTarget -= GetAbsOrigin();
  3684. vecTarget.z = 0.0f;
  3685. float flDist = VectorNormalize( vecTarget );
  3686. if ( GetNavigator()->SetVectorGoal( vecTarget, flDist ) )
  3687. {
  3688. TaskComplete();
  3689. }
  3690. else
  3691. {
  3692. TaskFail( FAIL_NO_ROUTE );
  3693. }
  3694. }
  3695. break;
  3696. // Stitching aiming
  3697. case TASK_METROPOLICE_AIM_STITCH_TIGHTLY:
  3698. SetBurstMode( true );
  3699. AimBurstTightGrouping( pTask->flTaskData );
  3700. TaskComplete();
  3701. break;
  3702. case TASK_METROPOLICE_AIM_STITCH_AT_PLAYER:
  3703. SetBurstMode( true );
  3704. AimBurstAtEnemy( pTask->flTaskData );
  3705. TaskComplete();
  3706. break;
  3707. case TASK_METROPOLICE_AIM_STITCH_AT_AIRBOAT:
  3708. if ( IsEnemyInAnAirboat() )
  3709. {
  3710. SetBurstMode( true );
  3711. AimBurstAtEnemy( pTask->flTaskData );
  3712. TaskComplete();
  3713. }
  3714. else
  3715. {
  3716. TaskFail(FAIL_NO_TARGET);
  3717. }
  3718. break;
  3719. case TASK_METROPOLICE_AIM_STITCH_IN_FRONT_OF_AIRBOAT:
  3720. if ( IsEnemyInAnAirboat() )
  3721. {
  3722. SetBurstMode( true );
  3723. AimBurstInFrontOfEnemy( pTask->flTaskData );
  3724. TaskComplete();
  3725. }
  3726. else
  3727. {
  3728. TaskFail(FAIL_NO_TARGET);
  3729. }
  3730. break;
  3731. case TASK_METROPOLICE_AIM_STITCH_ALONG_SIDE_OF_AIRBOAT:
  3732. if ( IsEnemyInAnAirboat() )
  3733. {
  3734. SetBurstMode( true );
  3735. AimBurstAlongSideOfEnemy( pTask->flTaskData );
  3736. TaskComplete();
  3737. }
  3738. else
  3739. {
  3740. TaskFail(FAIL_NO_TARGET);
  3741. }
  3742. break;
  3743. case TASK_METROPOLICE_AIM_STITCH_BEHIND_AIRBOAT:
  3744. if ( IsEnemyInAnAirboat() )
  3745. {
  3746. SetBurstMode( true );
  3747. AimBurstBehindEnemy( pTask->flTaskData );
  3748. TaskComplete();
  3749. }
  3750. else
  3751. {
  3752. TaskFail(FAIL_NO_TARGET);
  3753. }
  3754. break;
  3755. case TASK_METROPOLICE_BURST_ATTACK:
  3756. ResetIdealActivity( ACT_RANGE_ATTACK1 );
  3757. break;
  3758. case TASK_METROPOLICE_STOP_FIRE_BURST:
  3759. {
  3760. SetBurstMode( false );
  3761. TaskComplete();
  3762. }
  3763. break;
  3764. case TASK_METROPOLICE_HARASS:
  3765. {
  3766. if( !( m_spawnflags & SF_METROPOLICE_NOCHATTER ) )
  3767. {
  3768. if( GetEnemy() && GetEnemy()->GetWaterLevel() > 0 )
  3769. {
  3770. EmitSound( "NPC_MetroPolice.WaterSpeech" );
  3771. }
  3772. else
  3773. {
  3774. EmitSound( "NPC_MetroPolice.HidingSpeech" );
  3775. }
  3776. }
  3777. TaskComplete();
  3778. }
  3779. break;
  3780. case TASK_METROPOLICE_RELOAD_FOR_BURST:
  3781. {
  3782. if (GetActiveWeapon())
  3783. {
  3784. int nDesiredShotCount = CountShotsInTime( pTask->flTaskData );
  3785. // Do our fake reload to simulate a bigger clip without having to change the SMG1
  3786. int nAddCount = nDesiredShotCount - GetActiveWeapon()->Clip1();
  3787. if ( nAddCount > 0 )
  3788. {
  3789. if ( m_nBurstReloadCount >= nAddCount )
  3790. {
  3791. GetActiveWeapon()->m_iClip1 += nAddCount;
  3792. m_nBurstReloadCount -= nAddCount;
  3793. }
  3794. }
  3795. if ( nDesiredShotCount <= GetActiveWeapon()->Clip1() )
  3796. {
  3797. TaskComplete();
  3798. break;
  3799. }
  3800. }
  3801. // Fake a TASK_RELOAD to make sure we've got a full clip...
  3802. Task_t reloadTask;
  3803. reloadTask.iTask = TASK_RELOAD;
  3804. reloadTask.flTaskData = 0.0f;
  3805. StartTask( &reloadTask );
  3806. }
  3807. break;
  3808. case TASK_RELOAD:
  3809. m_nBurstReloadCount = METROPOLICE_BURST_RELOAD_COUNT;
  3810. BaseClass::StartTask( pTask );
  3811. break;
  3812. case TASK_METROPOLICE_GET_PATH_TO_BESTSOUND_LOS:
  3813. {
  3814. }
  3815. break;
  3816. default:
  3817. BaseClass::StartTask( pTask );
  3818. break;
  3819. }
  3820. }
  3821. //-----------------------------------------------------------------------------
  3822. //
  3823. // Run tasks!
  3824. //
  3825. //-----------------------------------------------------------------------------
  3826. //-----------------------------------------------------------------------------
  3827. // He's resisting arrest!
  3828. //-----------------------------------------------------------------------------
  3829. void CNPC_MetroPolice::EnemyResistingArrest()
  3830. {
  3831. // Prevent any other arrest from being made in this squad
  3832. // and tell them all that the player is resisting arrest!
  3833. if ( m_pSquad != NULL )
  3834. {
  3835. AISquadIter_t iter;
  3836. CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter );
  3837. while ( pSquadmate )
  3838. {
  3839. pSquadmate->RemoveSpawnFlags( SF_METROPOLICE_ARREST_ENEMY );
  3840. pSquadmate->SetCondition( COND_METROPOLICE_ENEMY_RESISTING_ARREST );
  3841. pSquadmate = m_pSquad->GetNextMember( &iter );
  3842. }
  3843. }
  3844. }
  3845. //-----------------------------------------------------------------------------
  3846. // Purpose:
  3847. // Input : pTask -
  3848. //-----------------------------------------------------------------------------
  3849. #define FLEEING_DISTANCE_SQR (100 * 100)
  3850. void CNPC_MetroPolice::RunTask( const Task_t *pTask )
  3851. {
  3852. switch( pTask->iTask )
  3853. {
  3854. case TASK_WAIT_FOR_MOVEMENT:
  3855. BaseClass::RunTask( pTask );
  3856. break;
  3857. case TASK_METROPOLICE_WAIT_FOR_SENTENCE:
  3858. {
  3859. if ( FOkToMakeSound( pTask->flTaskData ) )
  3860. {
  3861. TaskComplete();
  3862. }
  3863. }
  3864. break;
  3865. case TASK_METROPOLICE_ACTIVATE_BATON:
  3866. AutoMovement();
  3867. if ( IsActivityFinished() )
  3868. {
  3869. TaskComplete();
  3870. }
  3871. break;
  3872. case TASK_METROPOLICE_BURST_ATTACK:
  3873. {
  3874. AutoMovement( );
  3875. Vector vecAimPoint;
  3876. GetMotor()->SetIdealYawToTargetAndUpdate( m_vecBurstTargetPos, AI_KEEP_YAW_SPEED );
  3877. if ( IsActivityFinished() )
  3878. {
  3879. if ( GetShotRegulator()->IsInRestInterval() )
  3880. {
  3881. TaskComplete();
  3882. }
  3883. else
  3884. {
  3885. OnRangeAttack1();
  3886. ResetIdealActivity( ACT_RANGE_ATTACK1 );
  3887. }
  3888. }
  3889. }
  3890. break;
  3891. case TASK_METROPOLICE_RELOAD_FOR_BURST:
  3892. {
  3893. // Fake a TASK_RELOAD
  3894. Task_t reloadTask;
  3895. reloadTask.iTask = TASK_RELOAD;
  3896. reloadTask.flTaskData = 0.0f;
  3897. RunTask( &reloadTask );
  3898. }
  3899. break;
  3900. case TASK_METROPOLICE_LEAD_ARREST_ENEMY:
  3901. case TASK_METROPOLICE_ARREST_ENEMY:
  3902. {
  3903. if ( !GetEnemy() )
  3904. {
  3905. TaskComplete();
  3906. break;
  3907. }
  3908. if ( gpGlobals->curtime >= m_flTaskCompletionTime )
  3909. {
  3910. TaskComplete();
  3911. break;
  3912. }
  3913. // Complete the arrest after the last squad member has a bead on the enemy
  3914. // But only if you're the first guy who saw him
  3915. if ( pTask->iTask == TASK_METROPOLICE_LEAD_ARREST_ENEMY )
  3916. {
  3917. int nArrestCount = SquadArrestCount();
  3918. if ( nArrestCount == m_pSquad->NumMembers() )
  3919. {
  3920. TaskComplete();
  3921. break;
  3922. }
  3923. // Do a distance check of the enemy from his initial position.
  3924. // Shoot if he gets too far.
  3925. if ( m_vSavePosition.DistToSqr( GetEnemy()->GetAbsOrigin() ) > FLEEING_DISTANCE_SQR )
  3926. {
  3927. SpeakSentence( METROPOLICE_SENTENCE_HES_RUNNING );
  3928. EnemyResistingArrest();
  3929. break;
  3930. }
  3931. }
  3932. // Keep aiming at the enemy
  3933. if ( GetEnemy() && FacingIdeal() )
  3934. {
  3935. float flNewIdealYaw = CalcIdealYaw( GetEnemy()->EyePosition() );
  3936. if ( fabs(UTIL_AngleDiff( GetMotor()->GetIdealYaw(), flNewIdealYaw )) >= 45.0f )
  3937. {
  3938. GetMotor()->SetIdealYawToTarget( GetEnemy()->EyePosition() );
  3939. SetTurnActivity();
  3940. }
  3941. }
  3942. GetMotor()->UpdateYaw();
  3943. }
  3944. break;
  3945. case TASK_METROPOLICE_GET_PATH_TO_BESTSOUND_LOS:
  3946. {
  3947. switch( GetTaskInterrupt() )
  3948. {
  3949. case 0:
  3950. {
  3951. CSound *pSound = GetBestSound();
  3952. if (!pSound)
  3953. {
  3954. TaskFail(FAIL_NO_SOUND);
  3955. }
  3956. else
  3957. {
  3958. float flMaxRange = 2000;
  3959. float flMinRange = 0;
  3960. if ( GetActiveWeapon() )
  3961. {
  3962. flMaxRange = MAX( GetActiveWeapon()->m_fMaxRange1, GetActiveWeapon()->m_fMaxRange2 );
  3963. flMinRange = MIN( GetActiveWeapon()->m_fMinRange1, GetActiveWeapon()->m_fMinRange2 );
  3964. }
  3965. // Check against NPC's max range
  3966. if (flMaxRange > m_flDistTooFar)
  3967. {
  3968. flMaxRange = m_flDistTooFar;
  3969. }
  3970. // Why not doing lateral LOS first?
  3971. Vector losTarget = pSound->GetSoundReactOrigin();
  3972. if ( GetTacticalServices()->FindLos( pSound->GetSoundReactOrigin(), losTarget, flMinRange, flMaxRange, 1.0, &m_vInterruptSavePosition ) )
  3973. {
  3974. TaskInterrupt();
  3975. }
  3976. else
  3977. {
  3978. TaskFail(FAIL_NO_SHOOT);
  3979. }
  3980. }
  3981. }
  3982. break;
  3983. case 1:
  3984. {
  3985. AI_NavGoal_t goal( m_vInterruptSavePosition, ACT_RUN, AIN_HULL_TOLERANCE );
  3986. GetNavigator()->SetGoal( goal );
  3987. }
  3988. break;
  3989. }
  3990. }
  3991. break;
  3992. default:
  3993. BaseClass::RunTask( pTask );
  3994. break;
  3995. }
  3996. }
  3997. //-----------------------------------------------------------------------------
  3998. // Purpose:
  3999. // Input : pevInflictor -
  4000. // pAttacker -
  4001. // flDamage -
  4002. // bitsDamageType -
  4003. // Output : int
  4004. //-----------------------------------------------------------------------------
  4005. int CNPC_MetroPolice::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  4006. {
  4007. CTakeDamageInfo info = inputInfo;
  4008. if ( HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) )
  4009. {
  4010. EnemyResistingArrest();
  4011. }
  4012. #if 0
  4013. // Die instantly from a hit in idle/alert states
  4014. if( m_NPCState == NPC_STATE_IDLE || m_NPCState == NPC_STATE_ALERT )
  4015. {
  4016. info.SetDamage( m_iHealth );
  4017. }
  4018. #endif //0
  4019. if (info.GetAttacker() == GetEnemy())
  4020. {
  4021. // Keep track of recent damage by my attacker. If it seems like we're
  4022. // being killed, consider running off and hiding.
  4023. m_nRecentDamage += info.GetDamage();
  4024. m_flRecentDamageTime = gpGlobals->curtime;
  4025. }
  4026. return BaseClass::OnTakeDamage_Alive( info );
  4027. }
  4028. //-----------------------------------------------------------------------------
  4029. // Purpose: I want to deploy a manhack. Can I?
  4030. //-----------------------------------------------------------------------------
  4031. bool CNPC_MetroPolice::CanDeployManhack( void )
  4032. {
  4033. if ( HasSpawnFlags( SF_METROPOLICE_NO_MANHACK_DEPLOY ) )
  4034. return false;
  4035. // Nope, already have one out.
  4036. if( m_hManhack != NULL )
  4037. return false;
  4038. // Nope, don't have any!
  4039. if( m_iManhacks < 1 )
  4040. return false;
  4041. return true;
  4042. }
  4043. //-----------------------------------------------------------------------------
  4044. // Purpose: Allows for modification of the interrupt mask for the current schedule.
  4045. // In the most cases the base implementation should be called first.
  4046. //-----------------------------------------------------------------------------
  4047. void CNPC_MetroPolice::BuildScheduleTestBits( void )
  4048. {
  4049. BaseClass::BuildScheduleTestBits();
  4050. if ( PlayerIsCriminal() == false )
  4051. {
  4052. SetCustomInterruptCondition( COND_METROPOLICE_PHYSOBJECT_ASSAULT );
  4053. }
  4054. //FIXME: Always interrupt for now
  4055. if ( !IsInAScript() &&
  4056. !IsCurSchedule( SCHED_METROPOLICE_SHOVE ) &&
  4057. !IsCurSchedule( SCHED_MELEE_ATTACK1 ) &&
  4058. !IsCurSchedule( SCHED_RELOAD ) &&
  4059. !IsCurSchedule( SCHED_METROPOLICE_ACTIVATE_BATON ) )
  4060. {
  4061. SetCustomInterruptCondition( COND_METROPOLICE_PLAYER_TOO_CLOSE );
  4062. }
  4063. if ( !IsCurSchedule( SCHED_METROPOLICE_BURNING_RUN ) && !IsCurSchedule( SCHED_METROPOLICE_BURNING_STAND ) && !IsMoving() )
  4064. {
  4065. SetCustomInterruptCondition( COND_METROPOLICE_ON_FIRE );
  4066. }
  4067. if (IsCurSchedule(SCHED_TAKE_COVER_FROM_ENEMY))
  4068. {
  4069. ClearCustomInterruptCondition( COND_LIGHT_DAMAGE );
  4070. ClearCustomInterruptCondition( COND_HEAVY_DAMAGE );
  4071. }
  4072. if ( !IsCurSchedule( SCHED_CHASE_ENEMY ) &&
  4073. !IsCurSchedule( SCHED_METROPOLICE_ACTIVATE_BATON ) &&
  4074. !IsCurSchedule( SCHED_METROPOLICE_DEACTIVATE_BATON ) &&
  4075. !IsCurSchedule( SCHED_METROPOLICE_SHOVE ) &&
  4076. !IsCurSchedule( SCHED_METROPOLICE_RETURN_TO_PRECHASE ) )
  4077. {
  4078. SetCustomInterruptCondition( COND_METROPOLICE_CHANGE_BATON_STATE );
  4079. }
  4080. if ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) )
  4081. {
  4082. if ( gpGlobals->curtime - m_flLastDamageFlinchTime < 10.0 )
  4083. {
  4084. ClearCustomInterruptCondition( COND_LIGHT_DAMAGE );
  4085. ClearCustomInterruptCondition( COND_HEAVY_DAMAGE );
  4086. }
  4087. }
  4088. else if ( HasBaton() && IsCurSchedule( SCHED_COMBAT_FACE ) && !m_BatonSwingTimer.Expired() )
  4089. {
  4090. ClearCustomInterruptCondition( COND_CAN_MELEE_ATTACK1 );
  4091. }
  4092. }
  4093. //-----------------------------------------------------------------------------
  4094. //-----------------------------------------------------------------------------
  4095. WeaponProficiency_t CNPC_MetroPolice::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon )
  4096. {
  4097. if( FClassnameIs( pWeapon, "weapon_pistol" ) )
  4098. {
  4099. return WEAPON_PROFICIENCY_POOR;
  4100. }
  4101. if( FClassnameIs( pWeapon, "weapon_smg1" ) )
  4102. {
  4103. return WEAPON_PROFICIENCY_VERY_GOOD;
  4104. }
  4105. return BaseClass::CalcWeaponProficiency( pWeapon );
  4106. }
  4107. //-----------------------------------------------------------------------------
  4108. // Purpose:
  4109. //-----------------------------------------------------------------------------
  4110. void CNPC_MetroPolice::GatherConditions( void )
  4111. {
  4112. BaseClass::GatherConditions();
  4113. if ( m_bPlayerTooClose == false )
  4114. {
  4115. ClearCondition( COND_METROPOLICE_PLAYER_TOO_CLOSE );
  4116. }
  4117. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  4118. // FIXME: Player can be NULL here during level transitions.
  4119. if ( !pPlayer )
  4120. return;
  4121. float distToPlayerSqr = ( pPlayer->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
  4122. // See if we're too close
  4123. if ( pPlayer->GetGroundEntity() == this )
  4124. {
  4125. // Always beat a player on our head
  4126. m_iNumPlayerHits = 0;
  4127. SetCondition( COND_METROPOLICE_PLAYER_TOO_CLOSE );
  4128. }
  4129. else if ( (distToPlayerSqr < (42.0f*42.0f) && FVisible(pPlayer)) )
  4130. {
  4131. // Ignore the player if we've been beating him, but not if we haven't moved
  4132. if ( m_iNumPlayerHits < 3 || m_vecPreChaseOrigin == vec3_origin )
  4133. {
  4134. SetCondition( COND_METROPOLICE_PLAYER_TOO_CLOSE );
  4135. }
  4136. }
  4137. else
  4138. {
  4139. ClearCondition( COND_METROPOLICE_PLAYER_TOO_CLOSE );
  4140. // Don't clear out the player hit count for a few seconds after we last hit him
  4141. // This avoids states where two metropolice have the player pinned between them.
  4142. if ( (gpGlobals->curtime - GetLastAttackTime()) > 3 )
  4143. {
  4144. m_iNumPlayerHits = 0;
  4145. }
  4146. m_bPlayerTooClose = false;
  4147. }
  4148. if( metropolice_move_and_melee.GetBool() )
  4149. {
  4150. if( IsMoving() && HasCondition(COND_CAN_MELEE_ATTACK1) && HasBaton() )
  4151. {
  4152. if ( m_BatonSwingTimer.Expired() )
  4153. {
  4154. m_BatonSwingTimer.Set( 1.0, 1.75 );
  4155. Activity activity = TranslateActivity( ACT_MELEE_ATTACK_SWING_GESTURE );
  4156. Assert( activity != ACT_INVALID );
  4157. AddGesture( activity );
  4158. }
  4159. }
  4160. }
  4161. }
  4162. //-----------------------------------------------------------------------------
  4163. // Purpose:
  4164. // Output : Returns true on success, false on failure.
  4165. //-----------------------------------------------------------------------------
  4166. bool CNPC_MetroPolice::HasBaton( void )
  4167. {
  4168. CBaseCombatWeapon *pWeapon = GetActiveWeapon();
  4169. if ( pWeapon )
  4170. return FClassnameIs( pWeapon, "weapon_stunstick" );
  4171. return false;
  4172. }
  4173. //-----------------------------------------------------------------------------
  4174. // Purpose:
  4175. // Output : Returns true on success, false on failure.
  4176. //-----------------------------------------------------------------------------
  4177. bool CNPC_MetroPolice::BatonActive( void )
  4178. {
  4179. #ifndef HL2MP
  4180. CWeaponStunStick *pStick = dynamic_cast<CWeaponStunStick *>(GetActiveWeapon());
  4181. if ( pStick )
  4182. return pStick->GetStunState();
  4183. #endif
  4184. return false;
  4185. }
  4186. //-----------------------------------------------------------------------------
  4187. // Purpose:
  4188. // Input : state -
  4189. //-----------------------------------------------------------------------------
  4190. void CNPC_MetroPolice::SetBatonState( bool state )
  4191. {
  4192. if ( !HasBaton() )
  4193. return;
  4194. if ( m_bShouldActivateBaton != state )
  4195. {
  4196. m_bShouldActivateBaton = state;
  4197. SetCondition( COND_METROPOLICE_CHANGE_BATON_STATE );
  4198. }
  4199. }
  4200. //-----------------------------------------------------------------------------
  4201. // Purpose:
  4202. // Input : *pSound -
  4203. // Output : Returns true on success, false on failure.
  4204. //-----------------------------------------------------------------------------
  4205. bool CNPC_MetroPolice::QueryHearSound( CSound *pSound )
  4206. {
  4207. // Only behave differently if the player is pre-criminal
  4208. if ( PlayerIsCriminal() == false )
  4209. {
  4210. // If the person making the sound was a friend, don't respond
  4211. if ( pSound->IsSoundType( SOUND_DANGER ) && pSound->m_hOwner && IRelationType( pSound->m_hOwner ) == D_NU )
  4212. return false;
  4213. }
  4214. return BaseClass::QueryHearSound( pSound );
  4215. }
  4216. //-----------------------------------------------------------------------------
  4217. // Purpose:
  4218. // Input : index -
  4219. // *pEvent -
  4220. //-----------------------------------------------------------------------------
  4221. void CNPC_MetroPolice::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  4222. {
  4223. BaseClass::VPhysicsCollision( index, pEvent );
  4224. int otherIndex = !index;
  4225. CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
  4226. if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  4227. {
  4228. CHL2_Player *pPlayer = dynamic_cast<CHL2_Player *>(UTIL_PlayerByIndex( 1 ));
  4229. // See if it's being held by the player
  4230. if ( pPlayer != NULL && pPlayer->IsHoldingEntity( pHitEntity ) )
  4231. {
  4232. //TODO: Play an angry sentence, "Get that outta here!"
  4233. if ( IsCurSchedule( SCHED_METROPOLICE_SHOVE ) == false )
  4234. {
  4235. SetCondition( COND_METROPOLICE_PLAYER_TOO_CLOSE );
  4236. m_bPlayerTooClose = true;
  4237. }
  4238. }
  4239. }
  4240. }
  4241. //-----------------------------------------------------------------------------
  4242. // Purpose:
  4243. // Input : *pTarget -
  4244. //-----------------------------------------------------------------------------
  4245. void CNPC_MetroPolice::StunnedTarget( CBaseEntity *pTarget )
  4246. {
  4247. SetLastAttackTime( gpGlobals->curtime );
  4248. if ( pTarget && pTarget->IsPlayer() )
  4249. {
  4250. m_OnStunnedPlayer.FireOutput( this, this );
  4251. m_iNumPlayerHits++;
  4252. }
  4253. }
  4254. //-----------------------------------------------------------------------------
  4255. // Purpose: Use response for when the player is pre-criminal
  4256. //-----------------------------------------------------------------------------
  4257. void CNPC_MetroPolice::PrecriminalUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  4258. {
  4259. if ( IsInAScript() )
  4260. return;
  4261. // Don't respond if I'm busy hating the player
  4262. if ( IRelationType( pActivator ) == D_HT || ((GetState() != NPC_STATE_ALERT) && (GetState() != NPC_STATE_IDLE)) )
  4263. return;
  4264. if ( PlayerIsCriminal() )
  4265. return;
  4266. // Treat it like the player's bothered the cop
  4267. IncrementPlayerCriminalStatus();
  4268. // If we've hit max warnings, and we're allowed to chase, go for it
  4269. if ( m_nNumWarnings == METROPOLICE_MAX_WARNINGS )
  4270. {
  4271. AdministerJustice();
  4272. }
  4273. }
  4274. //-----------------------------------------------------------------------------
  4275. //
  4276. // Schedules
  4277. //
  4278. //-----------------------------------------------------------------------------
  4279. AI_BEGIN_CUSTOM_NPC( npc_metropolice, CNPC_MetroPolice )
  4280. gm_flTimeLastSpokePeek = 0;
  4281. DECLARE_ANIMEVENT( AE_METROPOLICE_BATON_ON );
  4282. DECLARE_ANIMEVENT( AE_METROPOLICE_BATON_OFF );
  4283. DECLARE_ANIMEVENT( AE_METROPOLICE_SHOVE );
  4284. DECLARE_ANIMEVENT( AE_METROPOLICE_START_DEPLOY );
  4285. DECLARE_ANIMEVENT( AE_METROPOLICE_DRAW_PISTOL );
  4286. DECLARE_ANIMEVENT( AE_METROPOLICE_DEPLOY_MANHACK );
  4287. DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_CHARGE_ENEMY );
  4288. DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_HARASS );
  4289. DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_DEPLOY_MANHACK );
  4290. DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_ATTACK_OCCLUDER1 );
  4291. DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_ATTACK_OCCLUDER2 );
  4292. DECLARE_SQUADSLOT( SQUAD_SLOT_POLICE_ARREST_ENEMY );
  4293. DECLARE_ACTIVITY( ACT_METROPOLICE_DRAW_PISTOL );
  4294. DECLARE_ACTIVITY( ACT_METROPOLICE_DEPLOY_MANHACK );
  4295. DECLARE_ACTIVITY( ACT_METROPOLICE_FLINCH_BEHIND );
  4296. DECLARE_ACTIVITY( ACT_PUSH_PLAYER );
  4297. DECLARE_ACTIVITY( ACT_MELEE_ATTACK_THRUST );
  4298. DECLARE_ACTIVITY( ACT_ACTIVATE_BATON );
  4299. DECLARE_ACTIVITY( ACT_DEACTIVATE_BATON );
  4300. DECLARE_ACTIVITY( ACT_WALK_BATON );
  4301. DECLARE_ACTIVITY( ACT_IDLE_ANGRY_BATON );
  4302. DECLARE_INTERACTION( g_interactionMetrocopStartedStitch );
  4303. DECLARE_INTERACTION( g_interactionMetrocopIdleChatter );
  4304. DECLARE_INTERACTION( g_interactionMetrocopClearSentenceQueues );
  4305. DECLARE_TASK( TASK_METROPOLICE_HARASS );
  4306. DECLARE_TASK( TASK_METROPOLICE_DIE_INSTANTLY );
  4307. DECLARE_TASK( TASK_METROPOLICE_BURST_ATTACK );
  4308. DECLARE_TASK( TASK_METROPOLICE_STOP_FIRE_BURST );
  4309. DECLARE_TASK( TASK_METROPOLICE_AIM_STITCH_AT_PLAYER );
  4310. DECLARE_TASK( TASK_METROPOLICE_AIM_STITCH_AT_AIRBOAT );
  4311. DECLARE_TASK( TASK_METROPOLICE_AIM_STITCH_IN_FRONT_OF_AIRBOAT );
  4312. DECLARE_TASK( TASK_METROPOLICE_AIM_STITCH_TIGHTLY );
  4313. DECLARE_TASK( TASK_METROPOLICE_AIM_STITCH_ALONG_SIDE_OF_AIRBOAT );
  4314. DECLARE_TASK( TASK_METROPOLICE_AIM_STITCH_BEHIND_AIRBOAT );
  4315. DECLARE_TASK( TASK_METROPOLICE_RELOAD_FOR_BURST );
  4316. DECLARE_TASK( TASK_METROPOLICE_GET_PATH_TO_STITCH );
  4317. DECLARE_TASK( TASK_METROPOLICE_RESET_LEDGE_CHECK_TIME );
  4318. DECLARE_TASK( TASK_METROPOLICE_GET_PATH_TO_BESTSOUND_LOS );
  4319. DECLARE_TASK( TASK_METROPOLICE_ARREST_ENEMY );
  4320. DECLARE_TASK( TASK_METROPOLICE_LEAD_ARREST_ENEMY );
  4321. DECLARE_TASK( TASK_METROPOLICE_SIGNAL_FIRING_TIME );
  4322. DECLARE_TASK( TASK_METROPOLICE_ACTIVATE_BATON );
  4323. DECLARE_TASK( TASK_METROPOLICE_WAIT_FOR_SENTENCE );
  4324. DECLARE_TASK( TASK_METROPOLICE_GET_PATH_TO_PRECHASE );
  4325. DECLARE_TASK( TASK_METROPOLICE_CLEAR_PRECHASE );
  4326. DECLARE_CONDITION( COND_METROPOLICE_ON_FIRE );
  4327. DECLARE_CONDITION( COND_METROPOLICE_ENEMY_RESISTING_ARREST );
  4328. // DECLARE_CONDITION( COND_METROPOLICE_START_POLICING );
  4329. DECLARE_CONDITION( COND_METROPOLICE_PLAYER_TOO_CLOSE );
  4330. DECLARE_CONDITION( COND_METROPOLICE_CHANGE_BATON_STATE );
  4331. DECLARE_CONDITION( COND_METROPOLICE_PHYSOBJECT_ASSAULT );
  4332. //=========================================================
  4333. //=========================================================
  4334. DEFINE_SCHEDULE
  4335. (
  4336. SCHED_METROPOLICE_WAKE_ANGRY,
  4337. " Tasks"
  4338. " TASK_STOP_MOVING 0"
  4339. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  4340. " TASK_FACE_ENEMY 0"
  4341. " "
  4342. " Interrupts"
  4343. );
  4344. //=========================================================
  4345. // > InvestigateSound
  4346. //
  4347. // sends a monster to the location of the
  4348. // sound that was just heard to check things out.
  4349. //=========================================================
  4350. DEFINE_SCHEDULE
  4351. (
  4352. SCHED_METROPOLICE_INVESTIGATE_SOUND,
  4353. " Tasks"
  4354. " TASK_STOP_MOVING 0"
  4355. " TASK_STORE_LASTPOSITION 0"
  4356. " TASK_METROPOLICE_GET_PATH_TO_BESTSOUND_LOS 0"
  4357. " TASK_FACE_IDEAL 0"
  4358. // " TASK_SET_TOLERANCE_DISTANCE 32"
  4359. " TASK_RUN_PATH 0"
  4360. " TASK_WAIT_FOR_MOVEMENT 0"
  4361. " TASK_STOP_MOVING 0"
  4362. " TASK_WAIT 5"
  4363. " TASK_GET_PATH_TO_LASTPOSITION 0"
  4364. " TASK_WALK_PATH 0"
  4365. " TASK_WAIT_FOR_MOVEMENT 0"
  4366. " TASK_STOP_MOVING 0"
  4367. " TASK_CLEAR_LASTPOSITION 0"
  4368. " TASK_FACE_REASONABLE 0"
  4369. ""
  4370. " Interrupts"
  4371. " COND_NEW_ENEMY"
  4372. " COND_SEE_FEAR"
  4373. " COND_SEE_ENEMY"
  4374. " COND_LIGHT_DAMAGE"
  4375. " COND_HEAVY_DAMAGE"
  4376. " COND_HEAR_DANGER"
  4377. );
  4378. //=========================================================
  4379. //=========================================================
  4380. DEFINE_SCHEDULE
  4381. (
  4382. SCHED_METROPOLICE_HARASS,
  4383. " Tasks"
  4384. " TASK_STOP_MOVING 0"
  4385. " TASK_FACE_ENEMY 0"
  4386. " TASK_WAIT_FACE_ENEMY 6"
  4387. " TASK_METROPOLICE_HARASS 0"
  4388. " TASK_WAIT_PVS 0"
  4389. " "
  4390. " Interrupts"
  4391. " "
  4392. " COND_CAN_RANGE_ATTACK1"
  4393. " COND_NEW_ENEMY"
  4394. );
  4395. //=========================================================
  4396. //=========================================================
  4397. DEFINE_SCHEDULE
  4398. (
  4399. SCHED_METROPOLICE_DRAW_PISTOL,
  4400. " Tasks"
  4401. " TASK_STOP_MOVING 0"
  4402. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_METROPOLICE_DRAW_PISTOL"
  4403. " TASK_WAIT_FACE_ENEMY 0.1"
  4404. " "
  4405. " Interrupts"
  4406. " "
  4407. );
  4408. //=========================================================
  4409. // > ChaseEnemy
  4410. //=========================================================
  4411. DEFINE_SCHEDULE
  4412. (
  4413. SCHED_METROPOLICE_CHASE_ENEMY,
  4414. " Tasks"
  4415. " TASK_STOP_MOVING 0"
  4416. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_METROPOLICE_ESTABLISH_LINE_OF_FIRE"
  4417. " TASK_SET_TOLERANCE_DISTANCE 24"
  4418. " TASK_GET_CHASE_PATH_TO_ENEMY 300"
  4419. " TASK_SPEAK_SENTENCE 6" // METROPOLICE_SENTENCE_MOVE_INTO_POSITION
  4420. " TASK_RUN_PATH 0"
  4421. " TASK_METROPOLICE_RESET_LEDGE_CHECK_TIME 0"
  4422. " TASK_WAIT_FOR_MOVEMENT 0"
  4423. " TASK_FACE_ENEMY 0"
  4424. " "
  4425. " Interrupts"
  4426. " COND_NEW_ENEMY"
  4427. " COND_ENEMY_DEAD"
  4428. " COND_ENEMY_UNREACHABLE"
  4429. " COND_CAN_RANGE_ATTACK1"
  4430. " COND_CAN_MELEE_ATTACK1"
  4431. " COND_CAN_RANGE_ATTACK2"
  4432. " COND_CAN_MELEE_ATTACK2"
  4433. " COND_TOO_CLOSE_TO_ATTACK"
  4434. " COND_TASK_FAILED"
  4435. " COND_LOST_ENEMY"
  4436. " COND_BETTER_WEAPON_AVAILABLE"
  4437. " COND_HEAR_DANGER"
  4438. );
  4439. DEFINE_SCHEDULE
  4440. (
  4441. SCHED_METROPOLICE_ESTABLISH_LINE_OF_FIRE,
  4442. " Tasks "
  4443. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_FAIL_ESTABLISH_LINE_OF_FIRE"
  4444. " TASK_FACE_ENEMY 0"
  4445. " TASK_SET_TOLERANCE_DISTANCE 48"
  4446. " TASK_GET_PATH_TO_ENEMY_LKP_LOS 0"
  4447. " TASK_SPEAK_SENTENCE 6" // METROPOLICE_SENTENCE_MOVE_INTO_POSITION
  4448. " TASK_RUN_PATH 0"
  4449. " TASK_METROPOLICE_RESET_LEDGE_CHECK_TIME 0"
  4450. " TASK_WAIT_FOR_MOVEMENT 0"
  4451. " TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
  4452. " "
  4453. " Interrupts "
  4454. " COND_NEW_ENEMY"
  4455. " COND_ENEMY_DEAD"
  4456. " COND_CAN_RANGE_ATTACK1"
  4457. " COND_CAN_RANGE_ATTACK2"
  4458. " COND_CAN_MELEE_ATTACK1"
  4459. " COND_CAN_MELEE_ATTACK2"
  4460. " COND_HEAR_DANGER"
  4461. " COND_HEAVY_DAMAGE"
  4462. );
  4463. DEFINE_SCHEDULE
  4464. (
  4465. SCHED_METROPOLICE_ESTABLISH_STITCH_LINE_OF_FIRE,
  4466. " Tasks "
  4467. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_FAIL_ESTABLISH_LINE_OF_FIRE"
  4468. " TASK_FACE_ENEMY 0"
  4469. " TASK_SET_TOLERANCE_DISTANCE 48"
  4470. " TASK_METROPOLICE_GET_PATH_TO_STITCH 0"
  4471. " TASK_RUN_PATH 0"
  4472. " TASK_WAIT_FOR_MOVEMENT 0"
  4473. " TASK_SET_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
  4474. " "
  4475. " Interrupts "
  4476. " COND_NEW_ENEMY"
  4477. " COND_ENEMY_DEAD"
  4478. " COND_HEAR_DANGER"
  4479. " COND_HEAVY_DAMAGE"
  4480. );
  4481. //=========================================================
  4482. // The uninterruptible portion of this behavior, whereupon
  4483. // the police actually releases the manhack.
  4484. //=========================================================
  4485. DEFINE_SCHEDULE
  4486. (
  4487. SCHED_METROPOLICE_DEPLOY_MANHACK,
  4488. " Tasks"
  4489. " TASK_SPEAK_SENTENCE 5" // METROPOLICE_SENTENCE_DEPLOY_MANHACK
  4490. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_METROPOLICE_DEPLOY_MANHACK"
  4491. " "
  4492. " Interrupts"
  4493. " "
  4494. );
  4495. //===============================================
  4496. //===============================================
  4497. DEFINE_SCHEDULE
  4498. (
  4499. SCHED_METROPOLICE_ADVANCE,
  4500. " Tasks"
  4501. " TASK_STOP_MOVING 0"
  4502. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ANGRY"
  4503. " TASK_FACE_ENEMY 0"
  4504. " TASK_WAIT_FACE_ENEMY 1" // give the guy some time to come out on his own
  4505. " TASK_WAIT_FACE_ENEMY_RANDOM 3"
  4506. " TASK_GET_PATH_TO_ENEMY_LOS 0"
  4507. " TASK_RUN_PATH 0"
  4508. " TASK_WAIT_FOR_MOVEMENT 0"
  4509. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ANGRY"
  4510. " TASK_FACE_ENEMY 0"
  4511. ""
  4512. " Interrupts"
  4513. " COND_CAN_RANGE_ATTACK1"
  4514. " COND_ENEMY_DEAD"
  4515. ""
  4516. );
  4517. //===============================================
  4518. //===============================================
  4519. DEFINE_SCHEDULE
  4520. (
  4521. SCHED_METROPOLICE_CHARGE,
  4522. " Tasks"
  4523. " TASK_STOP_MOVING 0"
  4524. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_METROPOLICE_ADVANCE"
  4525. // " TASK_SET_TOLERANCE_DISTANCE 24"
  4526. " TASK_STORE_LASTPOSITION 0"
  4527. " TASK_GET_CHASE_PATH_TO_ENEMY 300"
  4528. " TASK_RUN_PATH_FOR_UNITS 150"
  4529. " TASK_STOP_MOVING 1"
  4530. " TASK_FACE_ENEMY 0"
  4531. ""
  4532. " Interrupts"
  4533. " COND_NEW_ENEMY"
  4534. " COND_ENEMY_DEAD"
  4535. " COND_LOST_ENEMY"
  4536. " COND_CAN_MELEE_ATTACK1"
  4537. " COND_CAN_MELEE_ATTACK2"
  4538. " COND_HEAR_DANGER"
  4539. " COND_METROPOLICE_PLAYER_TOO_CLOSE"
  4540. );
  4541. //=========================================================
  4542. //=========================================================
  4543. DEFINE_SCHEDULE
  4544. (
  4545. SCHED_METROPOLICE_BURNING_RUN,
  4546. " Tasks"
  4547. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_METROPOLICE_BURNING_STAND"
  4548. " TASK_SET_TOLERANCE_DISTANCE 24"
  4549. " TASK_GET_PATH_TO_ENEMY 0"
  4550. " TASK_RUN_PATH_TIMED 10"
  4551. " TASK_METROPOLICE_DIE_INSTANTLY 0"
  4552. " "
  4553. " Interrupts"
  4554. );
  4555. //=========================================================
  4556. //=========================================================
  4557. DEFINE_SCHEDULE
  4558. (
  4559. SCHED_METROPOLICE_BURNING_STAND,
  4560. " Tasks"
  4561. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ON_FIRE"
  4562. " TASK_WAIT 1.5"
  4563. " TASK_METROPOLICE_DIE_INSTANTLY DMG_BURN"
  4564. " TASK_WAIT 1.0"
  4565. " "
  4566. " Interrupts"
  4567. );
  4568. //=========================================================
  4569. //=========================================================
  4570. DEFINE_SCHEDULE
  4571. (
  4572. SCHED_METROPOLICE_RETURN_TO_PRECHASE,
  4573. " Tasks"
  4574. " TASK_WAIT_RANDOM 1"
  4575. " TASK_METROPOLICE_GET_PATH_TO_PRECHASE 0"
  4576. " TASK_WALK_PATH 0"
  4577. " TASK_WAIT_FOR_MOVEMENT 0"
  4578. " TASK_STOP_MOVING 0"
  4579. " TASK_METROPOLICE_CLEAR_PRECHASE 0"
  4580. " "
  4581. " Interrupts"
  4582. " COND_NEW_ENEMY"
  4583. " COND_CAN_MELEE_ATTACK1"
  4584. " COND_CAN_MELEE_ATTACK2"
  4585. " COND_TASK_FAILED"
  4586. " COND_LOST_ENEMY"
  4587. " COND_HEAR_DANGER"
  4588. );
  4589. //===============================================
  4590. //===============================================
  4591. DEFINE_SCHEDULE
  4592. (
  4593. SCHED_METROPOLICE_ALERT_FACE_BESTSOUND,
  4594. " Tasks"
  4595. " TASK_SPEAK_SENTENCE 7" // METROPOLICE_SENTENCE_HEARD_SOMETHING
  4596. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ALERT_FACE_BESTSOUND"
  4597. ""
  4598. " Interrupts"
  4599. ""
  4600. )
  4601. //===============================================
  4602. //===============================================
  4603. DEFINE_SCHEDULE
  4604. (
  4605. SCHED_METROPOLICE_ENEMY_RESISTING_ARREST,
  4606. " Tasks"
  4607. " TASK_METROPOLICE_SIGNAL_FIRING_TIME 0"
  4608. ""
  4609. " Interrupts"
  4610. ""
  4611. )
  4612. //===============================================
  4613. //===============================================
  4614. DEFINE_SCHEDULE
  4615. (
  4616. SCHED_METROPOLICE_WARN_AND_ARREST_ENEMY,
  4617. " Tasks"
  4618. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_METROPOLICE_ENEMY_RESISTING_ARREST"
  4619. " TASK_STOP_MOVING 0"
  4620. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_IDLE_ANGRY"
  4621. " TASK_SPEAK_SENTENCE 0" // "Freeze!"
  4622. " TASK_METROPOLICE_ARREST_ENEMY 0.5"
  4623. " TASK_STORE_ENEMY_POSITION_IN_SAVEPOSITION 0"
  4624. " TASK_METROPOLICE_ARREST_ENEMY 1"
  4625. " TASK_METROPOLICE_WAIT_FOR_SENTENCE 1"
  4626. " TASK_SPEAK_SENTENCE 1" // "He's over here!"
  4627. " TASK_METROPOLICE_LEAD_ARREST_ENEMY 5"
  4628. " TASK_METROPOLICE_ARREST_ENEMY 2"
  4629. " TASK_METROPOLICE_WAIT_FOR_SENTENCE 1"
  4630. " TASK_SPEAK_SENTENCE 3" // "Take him down!"
  4631. " TASK_METROPOLICE_ARREST_ENEMY 1.5"
  4632. " TASK_METROPOLICE_WAIT_FOR_SENTENCE 2"
  4633. " TASK_METROPOLICE_SIGNAL_FIRING_TIME 0"
  4634. ""
  4635. " Interrupts"
  4636. " COND_NEW_ENEMY"
  4637. " COND_LIGHT_DAMAGE"
  4638. " COND_HEAVY_DAMAGE"
  4639. " COND_HEAR_DANGER"
  4640. " COND_ENEMY_DEAD"
  4641. " COND_METROPOLICE_ENEMY_RESISTING_ARREST"
  4642. ""
  4643. );
  4644. //===============================================
  4645. //===============================================
  4646. DEFINE_SCHEDULE
  4647. (
  4648. SCHED_METROPOLICE_ARREST_ENEMY,
  4649. " Tasks"
  4650. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_METROPOLICE_ENEMY_RESISTING_ARREST"
  4651. " TASK_GET_PATH_TO_ENEMY_LOS 0"
  4652. " TASK_RUN_PATH 0"
  4653. " TASK_WAIT_FOR_MOVEMENT 0"
  4654. " TASK_STOP_MOVING 0"
  4655. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_IDLE_ANGRY"
  4656. " TASK_METROPOLICE_WAIT_FOR_SENTENCE 0"
  4657. " TASK_SPEAK_SENTENCE 4"
  4658. " TASK_METROPOLICE_ARREST_ENEMY 30"
  4659. ""
  4660. " Interrupts"
  4661. " COND_NEW_ENEMY"
  4662. " COND_LIGHT_DAMAGE"
  4663. " COND_HEAVY_DAMAGE"
  4664. " COND_HEAR_DANGER"
  4665. " COND_ENEMY_DEAD"
  4666. " COND_METROPOLICE_ENEMY_RESISTING_ARREST"
  4667. " COND_WEAPON_BLOCKED_BY_FRIEND"
  4668. " COND_WEAPON_SIGHT_OCCLUDED"
  4669. ""
  4670. );
  4671. //===============================================
  4672. //===============================================
  4673. DEFINE_SCHEDULE
  4674. (
  4675. SCHED_METROPOLICE_SMG_NORMAL_ATTACK,
  4676. " Tasks"
  4677. " TASK_STOP_MOVING 0"
  4678. " TASK_FACE_ENEMY 0"
  4679. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4680. " TASK_METROPOLICE_STOP_FIRE_BURST 0"
  4681. " TASK_RANGE_ATTACK1 0"
  4682. ""
  4683. " Interrupts"
  4684. " COND_NEW_ENEMY"
  4685. " COND_ENEMY_DEAD"
  4686. " COND_LIGHT_DAMAGE"
  4687. " COND_HEAVY_DAMAGE"
  4688. " COND_ENEMY_OCCLUDED"
  4689. " COND_NO_PRIMARY_AMMO"
  4690. " COND_HEAR_DANGER"
  4691. " COND_WEAPON_BLOCKED_BY_FRIEND"
  4692. " COND_WEAPON_SIGHT_OCCLUDED"
  4693. );
  4694. //===============================================
  4695. //===============================================
  4696. DEFINE_SCHEDULE
  4697. (
  4698. SCHED_METROPOLICE_SMG_BURST_ATTACK,
  4699. " Tasks"
  4700. " TASK_STOP_MOVING 0"
  4701. " TASK_FACE_ENEMY 0"
  4702. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4703. " TASK_METROPOLICE_RELOAD_FOR_BURST 1.4"
  4704. " TASK_METROPOLICE_AIM_STITCH_AT_PLAYER 1.4"
  4705. " TASK_METROPOLICE_BURST_ATTACK 0"
  4706. " TASK_FACE_ENEMY 0"
  4707. ""
  4708. " Interrupts"
  4709. " COND_LIGHT_DAMAGE"
  4710. " COND_HEAVY_DAMAGE"
  4711. " COND_NO_PRIMARY_AMMO"
  4712. " COND_HEAR_DANGER"
  4713. " COND_WEAPON_BLOCKED_BY_FRIEND"
  4714. );
  4715. //===============================================
  4716. //===============================================
  4717. DEFINE_SCHEDULE
  4718. (
  4719. SCHED_METROPOLICE_AIM_STITCH_TIGHTLY,
  4720. " Tasks"
  4721. " TASK_STOP_MOVING 0"
  4722. " TASK_FACE_ENEMY 0"
  4723. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4724. " TASK_METROPOLICE_RELOAD_FOR_BURST 1.0"
  4725. " TASK_METROPOLICE_AIM_STITCH_TIGHTLY 1.0"
  4726. " TASK_METROPOLICE_BURST_ATTACK 0"
  4727. " TASK_FACE_ENEMY 0"
  4728. ""
  4729. " Interrupts"
  4730. " COND_LIGHT_DAMAGE"
  4731. " COND_HEAVY_DAMAGE"
  4732. " COND_NO_PRIMARY_AMMO"
  4733. " COND_HEAR_DANGER"
  4734. " COND_WEAPON_BLOCKED_BY_FRIEND"
  4735. );
  4736. //===============================================
  4737. //===============================================
  4738. DEFINE_SCHEDULE
  4739. (
  4740. SCHED_METROPOLICE_AIM_STITCH_AT_AIRBOAT,
  4741. " Tasks"
  4742. " TASK_STOP_MOVING 0"
  4743. " TASK_FACE_ENEMY 0"
  4744. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4745. " TASK_METROPOLICE_RELOAD_FOR_BURST 2.5"
  4746. " TASK_METROPOLICE_AIM_STITCH_AT_AIRBOAT 2.5"
  4747. " TASK_METROPOLICE_BURST_ATTACK 0"
  4748. " TASK_FACE_ENEMY 0"
  4749. ""
  4750. " Interrupts"
  4751. " COND_LIGHT_DAMAGE"
  4752. " COND_HEAVY_DAMAGE"
  4753. " COND_NO_PRIMARY_AMMO"
  4754. " COND_HEAR_DANGER"
  4755. " COND_WEAPON_BLOCKED_BY_FRIEND"
  4756. );
  4757. //===============================================
  4758. //===============================================
  4759. DEFINE_SCHEDULE
  4760. (
  4761. SCHED_METROPOLICE_AIM_STITCH_IN_FRONT_OF_AIRBOAT,
  4762. " Tasks"
  4763. " TASK_STOP_MOVING 0"
  4764. " TASK_FACE_ENEMY 0"
  4765. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4766. " TASK_METROPOLICE_RELOAD_FOR_BURST 2.5"
  4767. " TASK_METROPOLICE_AIM_STITCH_IN_FRONT_OF_AIRBOAT 2.5"
  4768. " TASK_METROPOLICE_BURST_ATTACK 0"
  4769. " TASK_FACE_ENEMY 0"
  4770. ""
  4771. " Interrupts"
  4772. " COND_LIGHT_DAMAGE"
  4773. " COND_HEAVY_DAMAGE"
  4774. " COND_NO_PRIMARY_AMMO"
  4775. " COND_HEAR_DANGER"
  4776. " COND_WEAPON_BLOCKED_BY_FRIEND"
  4777. );
  4778. //===============================================
  4779. //===============================================
  4780. DEFINE_SCHEDULE
  4781. (
  4782. SCHED_METROPOLICE_AIM_STITCH_ALONG_SIDE_OF_AIRBOAT,
  4783. " Tasks"
  4784. " TASK_STOP_MOVING 0"
  4785. " TASK_FACE_ENEMY 0"
  4786. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4787. " TASK_METROPOLICE_RELOAD_FOR_BURST 2.5"
  4788. " TASK_METROPOLICE_AIM_STITCH_ALONG_SIDE_OF_AIRBOAT 2.5"
  4789. " TASK_METROPOLICE_BURST_ATTACK 0"
  4790. " TASK_FACE_ENEMY 0"
  4791. ""
  4792. " Interrupts"
  4793. " COND_LIGHT_DAMAGE"
  4794. " COND_HEAVY_DAMAGE"
  4795. " COND_NO_PRIMARY_AMMO"
  4796. " COND_HEAR_DANGER"
  4797. " COND_WEAPON_BLOCKED_BY_FRIEND"
  4798. );
  4799. //===============================================
  4800. //===============================================
  4801. DEFINE_SCHEDULE
  4802. (
  4803. SCHED_METROPOLICE_AIM_STITCH_BEHIND_AIRBOAT,
  4804. " Tasks"
  4805. " TASK_STOP_MOVING 0"
  4806. " TASK_FACE_ENEMY 0"
  4807. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4808. " TASK_METROPOLICE_RELOAD_FOR_BURST 2.5"
  4809. " TASK_METROPOLICE_AIM_STITCH_BEHIND_AIRBOAT 2.5"
  4810. " TASK_METROPOLICE_BURST_ATTACK 0"
  4811. " TASK_FACE_ENEMY 0"
  4812. ""
  4813. " Interrupts"
  4814. " COND_LIGHT_DAMAGE"
  4815. " COND_HEAVY_DAMAGE"
  4816. " COND_NO_PRIMARY_AMMO"
  4817. " COND_HEAR_DANGER"
  4818. " COND_WEAPON_BLOCKED_BY_FRIEND"
  4819. );
  4820. DEFINE_SCHEDULE
  4821. (
  4822. SCHED_METROPOLICE_SHOVE,
  4823. " Tasks"
  4824. " TASK_STOP_MOVING 0"
  4825. " TASK_FACE_PLAYER 0.1" //FIXME: This needs to be the target or enemy
  4826. " TASK_METROPOLICE_ACTIVATE_BATON 1"
  4827. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_PUSH_PLAYER"
  4828. ""
  4829. " Interrupts"
  4830. );
  4831. DEFINE_SCHEDULE
  4832. (
  4833. SCHED_METROPOLICE_ACTIVATE_BATON,
  4834. " Tasks"
  4835. " TASK_STOP_MOVING 0"
  4836. " TASK_FACE_TARGET 0"
  4837. " TASK_METROPOLICE_ACTIVATE_BATON 1"
  4838. ""
  4839. " Interrupts"
  4840. );
  4841. DEFINE_SCHEDULE
  4842. (
  4843. SCHED_METROPOLICE_DEACTIVATE_BATON,
  4844. " Tasks"
  4845. " TASK_STOP_MOVING 0"
  4846. " TASK_METROPOLICE_ACTIVATE_BATON 0"
  4847. ""
  4848. " Interrupts"
  4849. );
  4850. DEFINE_SCHEDULE
  4851. (
  4852. SCHED_METROPOLICE_SMASH_PROP,
  4853. " Tasks"
  4854. " TASK_GET_PATH_TO_TARGET 0"
  4855. " TASK_MOVE_TO_TARGET_RANGE 50"
  4856. " TASK_STOP_MOVING 0"
  4857. " TASK_FACE_TARGET 0"
  4858. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  4859. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_MELEE_ATTACK1"
  4860. ""
  4861. " Interrupts"
  4862. " COND_NEW_ENEMY"
  4863. " COND_ENEMY_DEAD"
  4864. );
  4865. AI_END_CUSTOM_NPC()