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.

3029 lines
85 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "npcevent.h"
  8. #include "soundenvelope.h"
  9. #include "ai_hint.h"
  10. #include "ai_moveprobe.h"
  11. #include "ai_squad.h"
  12. #include "beam_shared.h"
  13. #include "globalstate.h"
  14. #include "soundent.h"
  15. #include "npc_citizen17.h"
  16. #include "gib.h"
  17. #include "spotlightend.h"
  18. #include "IEffects.h"
  19. #include "items.h"
  20. #include "ai_route.h"
  21. #include "player_pickup.h"
  22. #include "weapon_physcannon.h"
  23. #include "hl2_player.h"
  24. #include "npc_scanner.h"
  25. #include "materialsystem/imaterialsystemhardwareconfig.h"
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. //-----------------------------------------------------------------------------
  29. // Singleton interfaces
  30. //-----------------------------------------------------------------------------
  31. extern IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig;
  32. //-----------------------------------------------------------------------------
  33. // Parameters for how the scanner relates to citizens.
  34. //-----------------------------------------------------------------------------
  35. #define SCANNER_CIT_INSPECT_DELAY 10 // Check for citizens this often
  36. #define SCANNER_CIT_INSPECT_GROUND_DIST 500 // How far to look for citizens to inspect
  37. #define SCANNER_CIT_INSPECT_FLY_DIST 1500 // How far to look for citizens to inspect
  38. #define SCANNER_CIT_INSPECT_LENGTH 5 // How long does the inspection last
  39. #define SCANNER_HINT_INSPECT_LENGTH 5 // How long does the inspection last
  40. #define SCANNER_SOUND_INSPECT_LENGTH 5 // How long does the inspection last
  41. #define SCANNER_HINT_INSPECT_DELAY 15 // Check for hint nodes this often
  42. #define SPOTLIGHT_WIDTH 32
  43. #define SCANNER_SPOTLIGHT_NEAR_DIST 64
  44. #define SCANNER_SPOTLIGHT_FAR_DIST 256
  45. #define SCANNER_SPOTLIGHT_FLY_HEIGHT 72
  46. #define SCANNER_NOSPOTLIGHT_FLY_HEIGHT 72
  47. #define SCANNER_FLASH_MIN_DIST 900 // How far does flash effect enemy
  48. #define SCANNER_FLASH_MAX_DIST 1200 // How far does flash effect enemy
  49. #define SCANNER_FLASH_MAX_VALUE 240 // How bright is maximum flash
  50. #define SCANNER_PHOTO_NEAR_DIST 64
  51. #define SCANNER_PHOTO_FAR_DIST 128
  52. #define SCANNER_FOLLOW_DIST 128
  53. #define SCANNER_NUM_GIBS 6 // Number of gibs in gib file
  54. // Strider Scout Scanners
  55. #define SCANNER_SCOUT_MAX_SPEED 150
  56. ConVar sk_scanner_health( "sk_scanner_health","0");
  57. ConVar g_debug_cscanner( "g_debug_cscanner", "0" );
  58. //-----------------------------------------------------------------------------
  59. // Private activities.
  60. //-----------------------------------------------------------------------------
  61. static int ACT_SCANNER_SMALL_FLINCH_ALERT = 0;
  62. static int ACT_SCANNER_SMALL_FLINCH_COMBAT = 0;
  63. static int ACT_SCANNER_INSPECT = 0;
  64. static int ACT_SCANNER_WALK_ALERT = 0;
  65. static int ACT_SCANNER_WALK_COMBAT = 0;
  66. static int ACT_SCANNER_FLARE = 0;
  67. static int ACT_SCANNER_RETRACT = 0;
  68. static int ACT_SCANNER_FLARE_PRONGS = 0;
  69. static int ACT_SCANNER_RETRACT_PRONGS = 0;
  70. static int ACT_SCANNER_FLARE_START = 0;
  71. //-----------------------------------------------------------------------------
  72. // Interactions
  73. //-----------------------------------------------------------------------------
  74. int g_interactionScannerInspect = 0;
  75. int g_interactionScannerInspectBegin = 0;
  76. int g_interactionScannerInspectHandsUp = 0;
  77. int g_interactionScannerInspectShowArmband = 0;//<<TEMP>>still to be completed
  78. int g_interactionScannerInspectDone = 0;
  79. int g_interactionScannerSupportEntity = 0;
  80. int g_interactionScannerSupportPosition = 0;
  81. //-----------------------------------------------------------------------------
  82. // Animation events
  83. //------------------------------------------------------------------------
  84. int AE_SCANNER_CLOSED;
  85. //-----------------------------------------------------------------------------
  86. // Attachment points
  87. //-----------------------------------------------------------------------------
  88. #define SCANNER_ATTACHMENT_LIGHT "light"
  89. #define SCANNER_ATTACHMENT_FLASH 1
  90. #define SCANNER_ATTACHMENT_LPRONG 2
  91. #define SCANNER_ATTACHMENT_RPRONG 3
  92. //-----------------------------------------------------------------------------
  93. // Other defines.
  94. //-----------------------------------------------------------------------------
  95. #define SCANNER_MAX_BEAMS 4
  96. BEGIN_DATADESC( CNPC_CScanner )
  97. DEFINE_SOUNDPATCH( m_pEngineSound ),
  98. DEFINE_EMBEDDED( m_KilledInfo ),
  99. DEFINE_FIELD( m_flGoalOverrideDistance, FIELD_FLOAT ),
  100. DEFINE_FIELD( m_bPhotoTaken, FIELD_BOOLEAN ),
  101. DEFINE_FIELD( m_vInspectPos, FIELD_VECTOR ),
  102. DEFINE_FIELD( m_fInspectEndTime, FIELD_TIME ),
  103. DEFINE_FIELD( m_fCheckCitizenTime, FIELD_TIME ),
  104. DEFINE_FIELD( m_fCheckHintTime, FIELD_TIME ),
  105. DEFINE_KEYFIELD( m_bShouldInspect, FIELD_BOOLEAN, "ShouldInspect" ),
  106. DEFINE_KEYFIELD( m_bOnlyInspectPlayers, FIELD_BOOLEAN, "OnlyInspectPlayers" ),
  107. DEFINE_KEYFIELD( m_bNeverInspectPlayers,FIELD_BOOLEAN, "NeverInspectPlayers" ),
  108. DEFINE_FIELD( m_fNextPhotographTime, FIELD_TIME ),
  109. // DEFINE_FIELD( m_pEyeFlash, FIELD_CLASSPTR ),
  110. DEFINE_FIELD( m_vSpotlightTargetPos, FIELD_POSITION_VECTOR ),
  111. DEFINE_FIELD( m_vSpotlightCurrentPos, FIELD_POSITION_VECTOR ),
  112. // don't save (recreated after restore/transition)
  113. // DEFINE_FIELD( m_hSpotlight, FIELD_EHANDLE ),
  114. // DEFINE_FIELD( m_hSpotlightTarget, FIELD_EHANDLE ),
  115. DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ),
  116. DEFINE_FIELD( m_vSpotlightAngVelocity, FIELD_VECTOR ),
  117. DEFINE_FIELD( m_flSpotlightCurLength, FIELD_FLOAT ),
  118. DEFINE_FIELD( m_fNextSpotlightTime, FIELD_TIME ),
  119. DEFINE_FIELD( m_nHaloSprite, FIELD_INTEGER ),
  120. DEFINE_FIELD( m_fNextFlySoundTime, FIELD_TIME ),
  121. DEFINE_FIELD( m_nFlyMode, FIELD_INTEGER ),
  122. DEFINE_FIELD( m_nPoseTail, FIELD_INTEGER ),
  123. DEFINE_FIELD( m_nPoseDynamo, FIELD_INTEGER ),
  124. DEFINE_FIELD( m_nPoseFlare, FIELD_INTEGER ),
  125. DEFINE_FIELD( m_nPoseFaceVert, FIELD_INTEGER ),
  126. DEFINE_FIELD( m_nPoseFaceHoriz, FIELD_INTEGER ),
  127. DEFINE_FIELD( m_bIsClawScanner, FIELD_BOOLEAN ),
  128. DEFINE_FIELD( m_bIsOpen, FIELD_BOOLEAN ),
  129. // DEFINE_FIELD( m_bHasSpoken, FIELD_BOOLEAN ),
  130. DEFINE_FIELD( m_pSmokeTrail, FIELD_CLASSPTR ),
  131. DEFINE_FIELD( m_flFlyNoiseBase, FIELD_FLOAT ),
  132. DEFINE_FIELD( m_flEngineStallTime, FIELD_TIME ),
  133. DEFINE_FIELD( m_vecDiveBombDirection, FIELD_VECTOR ),
  134. DEFINE_FIELD( m_flDiveBombRollForce, FIELD_FLOAT ),
  135. DEFINE_KEYFIELD( m_flSpotlightMaxLength, FIELD_FLOAT, "SpotlightLength"),
  136. DEFINE_KEYFIELD( m_flSpotlightGoalWidth, FIELD_FLOAT, "SpotlightWidth"),
  137. // Physics Influence
  138. DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
  139. DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
  140. DEFINE_KEYFIELD( m_bNoLight, FIELD_BOOLEAN, "SpotlightDisabled" ),
  141. DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpotlight", InputDisableSpotlight ),
  142. DEFINE_INPUTFUNC( FIELD_STRING, "InspectTargetPhoto", InputInspectTargetPhoto ),
  143. DEFINE_INPUTFUNC( FIELD_STRING, "InspectTargetSpotlight", InputInspectTargetSpotlight ),
  144. DEFINE_INPUTFUNC( FIELD_INTEGER, "InputShouldInspect", InputShouldInspect ),
  145. DEFINE_INPUTFUNC( FIELD_STRING, "SetFollowTarget", InputSetFollowTarget ),
  146. DEFINE_INPUTFUNC( FIELD_VOID, "ClearFollowTarget", InputClearFollowTarget ),
  147. DEFINE_INPUTFUNC( FIELD_STRING, "DeployMine", InputDeployMine ),
  148. DEFINE_INPUTFUNC( FIELD_STRING, "EquipMine", InputEquipMine ),
  149. DEFINE_OUTPUT( m_OnPhotographPlayer, "OnPhotographPlayer" ),
  150. DEFINE_OUTPUT( m_OnPhotographNPC, "OnPhotographNPC" ),
  151. END_DATADESC()
  152. LINK_ENTITY_TO_CLASS(npc_cscanner, CNPC_CScanner);
  153. //-----------------------------------------------------------------------------
  154. // Purpose:
  155. //-----------------------------------------------------------------------------
  156. CNPC_CScanner::CNPC_CScanner()
  157. {
  158. #ifdef _DEBUG
  159. m_vInspectPos.Init();
  160. m_vSpotlightTargetPos.Init();
  161. m_vSpotlightCurrentPos.Init();
  162. m_vSpotlightDir.Init();
  163. m_vSpotlightAngVelocity.Init();
  164. #endif
  165. m_bShouldInspect = true;
  166. m_bOnlyInspectPlayers = false;
  167. m_bNeverInspectPlayers = false;
  168. char szMapName[256];
  169. Q_strncpy(szMapName, STRING(gpGlobals->mapname), sizeof(szMapName) );
  170. Q_strlower(szMapName);
  171. if( !Q_strnicmp( szMapName, "d3_c17", 6 ) )
  172. {
  173. // Streetwar scanners are claw scanners
  174. m_bIsClawScanner = true;
  175. }
  176. else
  177. {
  178. m_bIsClawScanner = false;
  179. }
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. //-----------------------------------------------------------------------------
  184. void CNPC_CScanner::Spawn(void)
  185. {
  186. // Check for user error
  187. if (m_flSpotlightMaxLength <= 0)
  188. {
  189. DevMsg("CNPC_CScanner::Spawn: Invalid spotlight length <= 0, setting to 500\n");
  190. m_flSpotlightMaxLength = 500;
  191. }
  192. if (m_flSpotlightGoalWidth <= 0)
  193. {
  194. DevMsg("CNPC_CScanner::Spawn: Invalid spotlight width <= 0, setting to 100\n");
  195. m_flSpotlightGoalWidth = 100;
  196. }
  197. if (m_flSpotlightGoalWidth > MAX_BEAM_WIDTH )
  198. {
  199. DevMsg("CNPC_CScanner::Spawn: Invalid spotlight width %.1f (max %.1f).\n", m_flSpotlightGoalWidth, MAX_BEAM_WIDTH );
  200. m_flSpotlightGoalWidth = MAX_BEAM_WIDTH;
  201. }
  202. Precache();
  203. if( m_bIsClawScanner )
  204. {
  205. SetModel( "models/shield_scanner.mdl");
  206. }
  207. else
  208. {
  209. SetModel( "models/combine_scanner.mdl");
  210. }
  211. m_iHealth = sk_scanner_health.GetFloat();
  212. m_iMaxHealth = m_iHealth;
  213. // ------------------------------------
  214. // Init all class vars
  215. // ------------------------------------
  216. m_vInspectPos = vec3_origin;
  217. m_fInspectEndTime = 0;
  218. m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
  219. m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;
  220. m_fNextPhotographTime = 0;
  221. m_vSpotlightTargetPos = vec3_origin;
  222. m_vSpotlightCurrentPos = vec3_origin;
  223. m_hSpotlight = NULL;
  224. m_hSpotlightTarget = NULL;
  225. AngleVectors( GetLocalAngles(), &m_vSpotlightDir );
  226. m_vSpotlightAngVelocity = vec3_origin;
  227. m_pEyeFlash = 0;
  228. m_fNextSpotlightTime = 0;
  229. m_nFlyMode = SCANNER_FLY_PATROL;
  230. m_vCurrentBanking = m_vSpotlightDir;
  231. m_flSpotlightCurLength = m_flSpotlightMaxLength;
  232. m_nPoseTail = LookupPoseParameter( "tail_control" );
  233. m_nPoseDynamo = LookupPoseParameter( "dynamo_wheel" );
  234. m_nPoseFlare = LookupPoseParameter( "alert_control" );
  235. m_nPoseFaceVert = LookupPoseParameter( "flex_vert" );
  236. m_nPoseFaceHoriz = LookupPoseParameter( "flex_horz" );
  237. // --------------------------------------------
  238. CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 );
  239. m_bPhotoTaken = false;
  240. BaseClass::Spawn();
  241. // Watch for this error state
  242. if ( m_bOnlyInspectPlayers && m_bNeverInspectPlayers )
  243. {
  244. Assert( 0 );
  245. Warning( "ERROR: Scanner set to never and always inspect players!\n" );
  246. }
  247. }
  248. //-----------------------------------------------------------------------------
  249. //
  250. //-----------------------------------------------------------------------------
  251. void CNPC_CScanner::Activate()
  252. {
  253. BaseClass::Activate();
  254. // Have to do this here because sprites do not go across level transitions
  255. m_pEyeFlash = CSprite::SpriteCreate( "sprites/blueflare1.vmt", GetLocalOrigin(), FALSE );
  256. m_pEyeFlash->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation );
  257. m_pEyeFlash->SetAttachment( this, LookupAttachment( SCANNER_ATTACHMENT_LIGHT ) );
  258. m_pEyeFlash->SetBrightness( 0 );
  259. m_pEyeFlash->SetScale( 1.4 );
  260. }
  261. //------------------------------------------------------------------------------
  262. // Purpose: Override to split in two when attacked
  263. //------------------------------------------------------------------------------
  264. int CNPC_CScanner::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  265. {
  266. // Turn off my spotlight when shot
  267. SpotlightDestroy();
  268. m_fNextSpotlightTime = gpGlobals->curtime + 2.0f;
  269. return (BaseClass::OnTakeDamage_Alive( info ));
  270. }
  271. //------------------------------------------------------------------------------
  272. // Purpose:
  273. //------------------------------------------------------------------------------
  274. void CNPC_CScanner::Gib( void )
  275. {
  276. if ( IsMarkedForDeletion() )
  277. return;
  278. // Spawn all gibs
  279. if( m_bIsClawScanner )
  280. {
  281. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib1.mdl");
  282. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib2.mdl");
  283. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib3.mdl");
  284. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib4.mdl");
  285. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib5.mdl");
  286. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib6.mdl");
  287. }
  288. else
  289. {
  290. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib01.mdl" );
  291. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib02.mdl" );
  292. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib04.mdl" );
  293. CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib05.mdl" );
  294. }
  295. // Add a random chance of spawning a battery...
  296. if ( !HasSpawnFlags(SF_NPC_NO_WEAPON_DROP) && random->RandomFloat( 0.0f, 1.0f) < 0.3f )
  297. {
  298. CItem *pBattery = (CItem*)CreateEntityByName("item_battery");
  299. if ( pBattery )
  300. {
  301. pBattery->SetAbsOrigin( GetAbsOrigin() );
  302. pBattery->SetAbsVelocity( GetAbsVelocity() );
  303. pBattery->SetLocalAngularVelocity( GetLocalAngularVelocity() );
  304. pBattery->ActivateWhenAtRest();
  305. pBattery->Spawn();
  306. }
  307. }
  308. DeployMine();
  309. BaseClass::Gib();
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose:
  313. // Input : pInflictor -
  314. // pAttacker -
  315. // flDamage -
  316. // bitsDamageType -
  317. //-----------------------------------------------------------------------------
  318. void CNPC_CScanner::Event_Killed( const CTakeDamageInfo &info )
  319. {
  320. // Copy off the takedamage info that killed me, since we're not going to call
  321. // up into the base class's Event_Killed() until we gib. (gibbing is ultimate death)
  322. m_KilledInfo = info;
  323. DeployMine();
  324. ClearInspectTarget();
  325. // Interrupt whatever schedule I'm on
  326. SetCondition(COND_SCHEDULE_DONE);
  327. // Remove spotlight
  328. SpotlightDestroy();
  329. // Remove sprite
  330. UTIL_Remove(m_pEyeFlash);
  331. m_pEyeFlash = NULL;
  332. // If I have an enemy and I'm up high, do a dive bomb (unless dissolved)
  333. if ( !m_bIsClawScanner && GetEnemy() != NULL && (info.GetDamageType() & DMG_DISSOLVE) == false )
  334. {
  335. Vector vecDelta = GetLocalOrigin() - GetEnemy()->GetLocalOrigin();
  336. if ( ( vecDelta.z > 120 ) && ( vecDelta.Length() > 360 ) )
  337. {
  338. // If I'm divebombing, don't take any more damage. It will make Event_Killed() be called again.
  339. // This is especially bad if someone machineguns the divebombing scanner.
  340. AttackDivebomb();
  341. return;
  342. }
  343. }
  344. Gib();
  345. }
  346. //-----------------------------------------------------------------------------
  347. // Purpose: Tells use whether or not the NPC cares about a given type of hint node.
  348. // Input : sHint -
  349. // Output : TRUE if the NPC is interested in this hint type, FALSE if not.
  350. //-----------------------------------------------------------------------------
  351. bool CNPC_CScanner::FValidateHintType(CAI_Hint *pHint)
  352. {
  353. return( pHint->HintType() == HINT_WORLD_WINDOW );
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Purpose:
  357. // Input : Type -
  358. //-----------------------------------------------------------------------------
  359. int CNPC_CScanner::TranslateSchedule( int scheduleType )
  360. {
  361. switch ( scheduleType )
  362. {
  363. case SCHED_IDLE_STAND:
  364. {
  365. return SCHED_SCANNER_PATROL;
  366. }
  367. case SCHED_SCANNER_PATROL:
  368. return SCHED_CSCANNER_PATROL;
  369. }
  370. return BaseClass::TranslateSchedule(scheduleType);
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. // Input : idealActivity -
  375. // *pIdealWeaponActivity -
  376. // Output : int
  377. //-----------------------------------------------------------------------------
  378. Activity CNPC_CScanner::NPC_TranslateActivity( Activity eNewActivity )
  379. {
  380. if( !m_bIsClawScanner )
  381. {
  382. return BaseClass::NPC_TranslateActivity( eNewActivity );
  383. }
  384. // The claw scanner came along a little late and doesn't have the activities
  385. // of the city scanner. So Just pick between these three
  386. if( eNewActivity == ACT_DISARM )
  387. {
  388. // Closing up.
  389. return eNewActivity;
  390. }
  391. if( m_bIsOpen )
  392. {
  393. return ACT_IDLE_ANGRY;
  394. }
  395. else
  396. {
  397. return ACT_IDLE;
  398. }
  399. }
  400. //-----------------------------------------------------------------------------
  401. //-----------------------------------------------------------------------------
  402. void CNPC_CScanner::HandleAnimEvent( animevent_t *pEvent )
  403. {
  404. if( pEvent->event == AE_SCANNER_CLOSED )
  405. {
  406. m_bIsOpen = false;
  407. SetActivity( ACT_IDLE );
  408. return;
  409. }
  410. BaseClass::HandleAnimEvent( pEvent );
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Purpose:
  414. //-----------------------------------------------------------------------------
  415. char *CNPC_CScanner::GetEngineSound( void )
  416. {
  417. if( m_bIsClawScanner )
  418. return "NPC_SScanner.FlyLoop";
  419. return "NPC_CScanner.FlyLoop";
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose: Plays the engine sound.
  423. //-----------------------------------------------------------------------------
  424. void CNPC_CScanner::NPCThink(void)
  425. {
  426. if (!IsAlive())
  427. {
  428. SetActivity((Activity)ACT_SCANNER_RETRACT_PRONGS);
  429. StudioFrameAdvance( );
  430. SetNextThink( gpGlobals->curtime + 0.1f );
  431. }
  432. else
  433. {
  434. BaseClass::NPCThink();
  435. SpotlightUpdate();
  436. }
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Purpose:
  440. //-----------------------------------------------------------------------------
  441. void CNPC_CScanner::Precache(void)
  442. {
  443. // Model
  444. if( m_bIsClawScanner )
  445. {
  446. PrecacheModel("models/shield_scanner.mdl");
  447. PrecacheModel("models/gibs/Shield_Scanner_Gib1.mdl");
  448. PrecacheModel("models/gibs/Shield_Scanner_Gib2.mdl");
  449. PrecacheModel("models/gibs/Shield_Scanner_Gib3.mdl");
  450. PrecacheModel("models/gibs/Shield_Scanner_Gib4.mdl");
  451. PrecacheModel("models/gibs/Shield_Scanner_Gib5.mdl");
  452. PrecacheModel("models/gibs/Shield_Scanner_Gib6.mdl");
  453. PrecacheScriptSound( "NPC_SScanner.Shoot");
  454. PrecacheScriptSound( "NPC_SScanner.Alert" );
  455. PrecacheScriptSound( "NPC_SScanner.Die" );
  456. PrecacheScriptSound( "NPC_SScanner.Combat" );
  457. PrecacheScriptSound( "NPC_SScanner.Idle" );
  458. PrecacheScriptSound( "NPC_SScanner.Pain" );
  459. PrecacheScriptSound( "NPC_SScanner.TakePhoto" );
  460. PrecacheScriptSound( "NPC_SScanner.AttackFlash" );
  461. PrecacheScriptSound( "NPC_SScanner.DiveBombFlyby" );
  462. PrecacheScriptSound( "NPC_SScanner.DiveBomb" );
  463. PrecacheScriptSound( "NPC_SScanner.DeployMine" );
  464. PrecacheScriptSound( "NPC_SScanner.FlyLoop" );
  465. UTIL_PrecacheOther( "combine_mine" );
  466. }
  467. else
  468. {
  469. PrecacheModel("models/combine_scanner.mdl");
  470. PrecacheModel("models/gibs/scanner_gib01.mdl" );
  471. PrecacheModel("models/gibs/scanner_gib02.mdl" );
  472. PrecacheModel("models/gibs/scanner_gib02.mdl" );
  473. PrecacheModel("models/gibs/scanner_gib04.mdl" );
  474. PrecacheModel("models/gibs/scanner_gib05.mdl" );
  475. PrecacheScriptSound( "NPC_CScanner.Shoot");
  476. PrecacheScriptSound( "NPC_CScanner.Alert" );
  477. PrecacheScriptSound( "NPC_CScanner.Die" );
  478. PrecacheScriptSound( "NPC_CScanner.Combat" );
  479. PrecacheScriptSound( "NPC_CScanner.Idle" );
  480. PrecacheScriptSound( "NPC_CScanner.Pain" );
  481. PrecacheScriptSound( "NPC_CScanner.TakePhoto" );
  482. PrecacheScriptSound( "NPC_CScanner.AttackFlash" );
  483. PrecacheScriptSound( "NPC_CScanner.DiveBombFlyby" );
  484. PrecacheScriptSound( "NPC_CScanner.DiveBomb" );
  485. PrecacheScriptSound( "NPC_CScanner.DeployMine" );
  486. PrecacheScriptSound( "NPC_CScanner.FlyLoop" );
  487. }
  488. // Sprites
  489. m_nHaloSprite = PrecacheModel("sprites/light_glow03.vmt");
  490. PrecacheModel( "sprites/glow_test02.vmt" );
  491. BaseClass::Precache();
  492. }
  493. //------------------------------------------------------------------------------
  494. // Purpose: Request help inspecting from other squad members
  495. //------------------------------------------------------------------------------
  496. void CNPC_CScanner::RequestInspectSupport(void)
  497. {
  498. if (m_pSquad)
  499. {
  500. AISquadIter_t iter;
  501. for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
  502. {
  503. if (pSquadMember != this)
  504. {
  505. if (GetTarget())
  506. {
  507. pSquadMember->DispatchInteraction(g_interactionScannerSupportEntity,((void *)((CBaseEntity*)GetTarget())),this);
  508. }
  509. else
  510. {
  511. pSquadMember->DispatchInteraction(g_interactionScannerSupportPosition,((void *)m_vInspectPos.Base()),this);
  512. }
  513. }
  514. }
  515. }
  516. }
  517. //------------------------------------------------------------------------------
  518. // Purpose:
  519. //------------------------------------------------------------------------------
  520. bool CNPC_CScanner::IsValidInspectTarget(CBaseEntity *pEntity)
  521. {
  522. // If a citizen, make sure he can be inspected again
  523. if (pEntity->Classify() == CLASS_CITIZEN_PASSIVE)
  524. {
  525. if (((CNPC_Citizen*)pEntity)->GetNextScannerInspectTime() > gpGlobals->curtime)
  526. {
  527. return false;
  528. }
  529. }
  530. // Make sure no other squad member has already chosen to
  531. // inspect this entity
  532. if (m_pSquad)
  533. {
  534. AISquadIter_t iter;
  535. for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
  536. {
  537. if (pSquadMember->GetTarget() == pEntity)
  538. {
  539. return false;
  540. }
  541. }
  542. }
  543. // Do not inspect friendly targets
  544. if ( IRelationType( pEntity ) == D_LI )
  545. return false;
  546. return true;
  547. }
  548. //------------------------------------------------------------------------------
  549. // Purpose:
  550. //------------------------------------------------------------------------------
  551. CBaseEntity* CNPC_CScanner::BestInspectTarget(void)
  552. {
  553. if ( !m_bShouldInspect )
  554. return NULL;
  555. CBaseEntity* pBestEntity = NULL;
  556. float fBestDist = MAX_COORD_RANGE;
  557. float fTestDist;
  558. CBaseEntity *pEntity = NULL;
  559. // If I have a spotlight, search from the spotlight position
  560. // otherwise search from my position
  561. Vector vSearchOrigin;
  562. float fSearchDist;
  563. if (m_hSpotlightTarget != NULL)
  564. {
  565. vSearchOrigin = m_hSpotlightTarget->GetAbsOrigin();
  566. fSearchDist = SCANNER_CIT_INSPECT_GROUND_DIST;
  567. }
  568. else
  569. {
  570. vSearchOrigin = WorldSpaceCenter();
  571. fSearchDist = SCANNER_CIT_INSPECT_FLY_DIST;
  572. }
  573. if ( m_bOnlyInspectPlayers )
  574. {
  575. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  576. if ( !pPlayer )
  577. return NULL;
  578. if ( !pPlayer->IsAlive() || (pPlayer->GetFlags() & FL_NOTARGET) )
  579. return NULL;
  580. return WorldSpaceCenter().DistToSqr( pPlayer->EyePosition() ) <= (fSearchDist * fSearchDist) ? pPlayer : NULL;
  581. }
  582. CUtlVector<CBaseEntity *> candidates;
  583. float fSearchDistSq = fSearchDist * fSearchDist;
  584. int i;
  585. // Inspect players unless told otherwise
  586. if ( m_bNeverInspectPlayers == false )
  587. {
  588. // Players
  589. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  590. {
  591. CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
  592. if ( pPlayer )
  593. {
  594. if ( vSearchOrigin.DistToSqr(pPlayer->GetAbsOrigin()) < fSearchDistSq )
  595. {
  596. candidates.AddToTail( pPlayer );
  597. }
  598. }
  599. }
  600. }
  601. // NPCs
  602. CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  603. for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
  604. {
  605. if ( ppAIs[i] != this && vSearchOrigin.DistToSqr(ppAIs[i]->GetAbsOrigin()) < fSearchDistSq )
  606. {
  607. candidates.AddToTail( ppAIs[i] );
  608. }
  609. }
  610. for ( i = 0; i < candidates.Count(); i++ )
  611. {
  612. pEntity = candidates[i];
  613. Assert( pEntity != this && (pEntity->MyNPCPointer() || pEntity->IsPlayer() ) );
  614. CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
  615. if ( ( pNPC && pNPC->Classify() == CLASS_CITIZEN_PASSIVE ) || pEntity->IsPlayer() )
  616. {
  617. if ( pEntity->GetFlags() & FL_NOTARGET )
  618. continue;
  619. if ( pEntity->IsAlive() == false )
  620. continue;
  621. // Ensure it's within line of sight
  622. if ( !FVisible( pEntity ) )
  623. continue;
  624. fTestDist = ( GetAbsOrigin() - pEntity->EyePosition() ).Length();
  625. if ( fTestDist < fBestDist )
  626. {
  627. if ( IsValidInspectTarget( pEntity ) )
  628. {
  629. fBestDist = fTestDist;
  630. pBestEntity = pEntity;
  631. }
  632. }
  633. }
  634. }
  635. return pBestEntity;
  636. }
  637. //------------------------------------------------------------------------------
  638. // Purpose: Clears any previous inspect target and set inspect target to
  639. // the given entity and set the durection of the inspection
  640. //------------------------------------------------------------------------------
  641. void CNPC_CScanner::SetInspectTargetToEnt(CBaseEntity *pEntity, float fInspectDuration)
  642. {
  643. ClearInspectTarget();
  644. SetTarget(pEntity);
  645. m_fInspectEndTime = gpGlobals->curtime + fInspectDuration;
  646. }
  647. //------------------------------------------------------------------------------
  648. // Purpose: Clears any previous inspect target and set inspect target to
  649. // the given hint node and set the durection of the inspection
  650. //------------------------------------------------------------------------------
  651. void CNPC_CScanner::SetInspectTargetToHint(CAI_Hint *pHint, float fInspectDuration)
  652. {
  653. ClearInspectTarget();
  654. float yaw = pHint->Yaw();
  655. // --------------------------------------------
  656. // Figure out the location that the hint hits
  657. // --------------------------------------------
  658. Vector vHintDir = UTIL_YawToVector( yaw );
  659. Vector vHintOrigin;
  660. pHint->GetPosition( this, &vHintOrigin );
  661. Vector vHintEnd = vHintOrigin + (vHintDir * 512);
  662. trace_t tr;
  663. AI_TraceLine ( vHintOrigin, vHintEnd, MASK_BLOCKLOS, this, COLLISION_GROUP_NONE, &tr);
  664. if ( g_debug_cscanner.GetBool() )
  665. {
  666. NDebugOverlay::Line( vHintOrigin, tr.endpos, 255, 0, 0, true, 4.0f );
  667. NDebugOverlay::Cross3D( tr.endpos, -Vector(8,8,8), Vector(8,8,8), 255, 0, 0, true, 4.0f );
  668. }
  669. if (tr.fraction == 1.0f )
  670. {
  671. DevMsg("ERROR: Scanner hint node not facing a surface!\n");
  672. }
  673. else
  674. {
  675. SetHintNode( pHint );
  676. m_vInspectPos = tr.endpos;
  677. pHint->Lock( this );
  678. m_fInspectEndTime = gpGlobals->curtime + fInspectDuration;
  679. }
  680. }
  681. //------------------------------------------------------------------------------
  682. // Purpose: Clears any previous inspect target and set inspect target to
  683. // the given position and set the durection of the inspection
  684. // Input :
  685. // Output :
  686. //------------------------------------------------------------------------------
  687. void CNPC_CScanner::SetInspectTargetToPos(const Vector &vInspectPos, float fInspectDuration)
  688. {
  689. ClearInspectTarget();
  690. m_vInspectPos = vInspectPos;
  691. m_fInspectEndTime = gpGlobals->curtime + fInspectDuration;
  692. }
  693. //------------------------------------------------------------------------------
  694. // Purpose: Clears out any previous inspection targets
  695. //------------------------------------------------------------------------------
  696. void CNPC_CScanner::ClearInspectTarget(void)
  697. {
  698. if ( GetIdealState() != NPC_STATE_SCRIPT )
  699. {
  700. SetTarget( NULL );
  701. }
  702. ClearHintNode( SCANNER_HINT_INSPECT_LENGTH );
  703. m_vInspectPos = vec3_origin;
  704. }
  705. //------------------------------------------------------------------------------
  706. // Purpose: Returns true if there is a position to be inspected.
  707. //------------------------------------------------------------------------------
  708. bool CNPC_CScanner::HaveInspectTarget( void )
  709. {
  710. if ( GetTarget() != NULL )
  711. return true;
  712. if ( m_vInspectPos != vec3_origin )
  713. return true;
  714. return false;
  715. }
  716. //------------------------------------------------------------------------------
  717. // Purpose:
  718. //------------------------------------------------------------------------------
  719. Vector CNPC_CScanner::InspectTargetPosition(void)
  720. {
  721. // If we have a target, return an adjust position
  722. if ( GetTarget() != NULL )
  723. {
  724. Vector vEyePos = GetTarget()->EyePosition();
  725. // If in spotlight mode, aim for ground below target unless is client
  726. if ( m_nFlyMode == SCANNER_FLY_SPOT && !(GetTarget()->GetFlags() & FL_CLIENT) )
  727. {
  728. Vector vInspectPos;
  729. vInspectPos.x = vEyePos.x;
  730. vInspectPos.y = vEyePos.y;
  731. vInspectPos.z = GetFloorZ( vEyePos );
  732. // Let's take three-quarters between eyes and ground
  733. vInspectPos.z += ( vEyePos.z - vInspectPos.z ) * 0.75f;
  734. return vInspectPos;
  735. }
  736. else
  737. {
  738. // Otherwise aim for eyes
  739. return vEyePos;
  740. }
  741. }
  742. else if ( m_vInspectPos != vec3_origin )
  743. {
  744. return m_vInspectPos;
  745. }
  746. else
  747. {
  748. DevMsg("InspectTargetPosition called with no target!\n");
  749. return m_vInspectPos;
  750. }
  751. }
  752. //-----------------------------------------------------------------------------
  753. // Purpose:
  754. //-----------------------------------------------------------------------------
  755. void CNPC_CScanner::InputShouldInspect( inputdata_t &inputdata )
  756. {
  757. m_bShouldInspect = ( inputdata.value.Int() != 0 );
  758. if ( !m_bShouldInspect )
  759. {
  760. if ( GetEnemy() == GetTarget() )
  761. SetEnemy(NULL);
  762. ClearInspectTarget();
  763. SetTarget(NULL);
  764. SpotlightDestroy();
  765. }
  766. }
  767. //-----------------------------------------------------------------------------
  768. //-----------------------------------------------------------------------------
  769. void CNPC_CScanner::DeployMine()
  770. {
  771. CBaseEntity *child;
  772. // iterate through all children
  773. for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() )
  774. {
  775. if( FClassnameIs( child, "combine_mine" ) )
  776. {
  777. child->SetParent( NULL );
  778. child->SetAbsVelocity( GetAbsVelocity() );
  779. child->SetOwnerEntity( this );
  780. ScannerEmitSound( "DeployMine" );
  781. IPhysicsObject *pPhysObj = child->VPhysicsGetObject();
  782. if( pPhysObj )
  783. {
  784. // Make sure the mine's awake
  785. pPhysObj->Wake();
  786. }
  787. if( m_bIsClawScanner )
  788. {
  789. // Fold up.
  790. SetActivity( ACT_DISARM );
  791. }
  792. return;
  793. }
  794. }
  795. }
  796. //-----------------------------------------------------------------------------
  797. // Purpose:
  798. //-----------------------------------------------------------------------------
  799. float CNPC_CScanner::GetMaxSpeed()
  800. {
  801. if( IsStriderScout() )
  802. {
  803. return SCANNER_SCOUT_MAX_SPEED;
  804. }
  805. return BaseClass::GetMaxSpeed();
  806. }
  807. //-----------------------------------------------------------------------------
  808. // Purpose:
  809. //-----------------------------------------------------------------------------
  810. void CNPC_CScanner::InputDeployMine(inputdata_t &inputdata)
  811. {
  812. DeployMine();
  813. }
  814. //-----------------------------------------------------------------------------
  815. // Purpose:
  816. //-----------------------------------------------------------------------------
  817. void CNPC_CScanner::InputEquipMine(inputdata_t &inputdata)
  818. {
  819. CBaseEntity *child;
  820. // iterate through all children
  821. for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() )
  822. {
  823. if( FClassnameIs( child, "combine_mine" ) )
  824. {
  825. // Already have a mine!
  826. return;
  827. }
  828. }
  829. CBaseEntity *pEnt;
  830. pEnt = CreateEntityByName( "combine_mine" );
  831. bool bPlacedMine = false;
  832. if( m_bIsClawScanner )
  833. {
  834. Vector vecOrigin;
  835. QAngle angles;
  836. int attachment;
  837. attachment = LookupAttachment( "claw" );
  838. if( attachment > -1 )
  839. {
  840. GetAttachment( attachment, vecOrigin, angles );
  841. pEnt->SetAbsOrigin( vecOrigin );
  842. pEnt->SetAbsAngles( angles );
  843. pEnt->SetOwnerEntity( this );
  844. pEnt->SetParent( this, attachment );
  845. m_bIsOpen = true;
  846. SetActivity( ACT_IDLE_ANGRY );
  847. bPlacedMine = true;
  848. }
  849. }
  850. if( !bPlacedMine )
  851. {
  852. Vector vecMineLocation = GetAbsOrigin();
  853. vecMineLocation.z -= 32.0;
  854. pEnt->SetAbsOrigin( vecMineLocation );
  855. pEnt->SetAbsAngles( GetAbsAngles() );
  856. pEnt->SetOwnerEntity( this );
  857. pEnt->SetParent( this );
  858. }
  859. pEnt->Spawn();
  860. }
  861. //-----------------------------------------------------------------------------
  862. // Purpose: Tells the scanner to go photograph an entity.
  863. // Input : String name or classname of the entity to inspect.
  864. //-----------------------------------------------------------------------------
  865. void CNPC_CScanner::InputInspectTargetPhoto(inputdata_t &inputdata)
  866. {
  867. m_vLastPatrolDir = vec3_origin;
  868. m_bPhotoTaken = false;
  869. InspectTarget( inputdata, SCANNER_FLY_PHOTO );
  870. }
  871. //-----------------------------------------------------------------------------
  872. // Purpose: Tells the scanner to go spotlight an entity.
  873. // Input : String name or classname of the entity to inspect.
  874. //-----------------------------------------------------------------------------
  875. void CNPC_CScanner::InputInspectTargetSpotlight(inputdata_t &inputdata)
  876. {
  877. InspectTarget( inputdata, SCANNER_FLY_SPOT );
  878. }
  879. //-----------------------------------------------------------------------------
  880. // Purpose: Tells the scanner to go photo or spotlight an entity.
  881. // Input : String name or classname of the entity to inspect.
  882. //-----------------------------------------------------------------------------
  883. void CNPC_CScanner::InspectTarget( inputdata_t &inputdata, ScannerFlyMode_t eFlyMode )
  884. {
  885. CBaseEntity *pEnt = gEntList.FindEntityGeneric( NULL, inputdata.value.String(), this, inputdata.pActivator );
  886. if ( pEnt != NULL )
  887. {
  888. // Set and begin to inspect our target
  889. SetInspectTargetToEnt( pEnt, SCANNER_CIT_INSPECT_LENGTH );
  890. m_nFlyMode = eFlyMode;
  891. SetCondition( COND_CSCANNER_HAVE_INSPECT_TARGET );
  892. // Stop us from any other navigation we were doing
  893. GetNavigator()->ClearGoal();
  894. }
  895. else
  896. {
  897. DevMsg( "InspectTarget: target %s not found!\n", inputdata.value.String() );
  898. }
  899. }
  900. //-----------------------------------------------------------------------------
  901. // Purpose:
  902. // Output : Returns true on success, false on failure.
  903. //-----------------------------------------------------------------------------
  904. bool CNPC_CScanner::MovingToInspectTarget( void )
  905. {
  906. // If we're flying to a photograph target and the photo isn't yet taken, we're still moving to it
  907. if ( m_nFlyMode == SCANNER_FLY_PHOTO && m_bPhotoTaken == false )
  908. return true;
  909. // If we're still on a path, then we're still moving
  910. if ( HaveInspectTarget() && GetNavigator()->IsGoalActive() )
  911. return true;
  912. return false;
  913. }
  914. //-----------------------------------------------------------------------------
  915. // Purpose:
  916. //-----------------------------------------------------------------------------
  917. void CNPC_CScanner::GatherConditions( void )
  918. {
  919. BaseClass::GatherConditions();
  920. // Clear out our old conditions
  921. ClearCondition( COND_CSCANNER_INSPECT_DONE );
  922. ClearCondition( COND_CSCANNER_HAVE_INSPECT_TARGET );
  923. ClearCondition( COND_CSCANNER_SPOT_ON_TARGET );
  924. ClearCondition( COND_CSCANNER_CAN_PHOTOGRAPH );
  925. // We don't do any of these checks if we have an enemy
  926. if ( GetEnemy() )
  927. return;
  928. // --------------------------------------
  929. // COND_CSCANNER_INSPECT_DONE
  930. //
  931. // If my inspection over
  932. // ---------------------------------------------------------
  933. // Refresh our timing if we're still moving to our inspection target
  934. if ( MovingToInspectTarget() )
  935. {
  936. m_fInspectEndTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_LENGTH;
  937. }
  938. // Update our follow times
  939. if ( HaveInspectTarget() && gpGlobals->curtime > m_fInspectEndTime && m_nFlyMode != SCANNER_FLY_FOLLOW )
  940. {
  941. SetCondition ( COND_CSCANNER_INSPECT_DONE );
  942. m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
  943. m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;
  944. ClearInspectTarget();
  945. }
  946. // ----------------------------------------------------------
  947. // If I heard a sound and I don't have an enemy, inspect it
  948. // ----------------------------------------------------------
  949. if ( ( HasCondition( COND_HEAR_COMBAT ) || HasCondition( COND_HEAR_DANGER ) ) && m_nFlyMode != SCANNER_FLY_FOLLOW )
  950. {
  951. CSound *pSound = GetBestSound();
  952. if ( pSound )
  953. {
  954. // Chase an owner if we can
  955. if ( pSound->m_hOwner != NULL )
  956. {
  957. // Don't inspect sounds of things we like
  958. if ( IRelationType( pSound->m_hOwner ) != D_LI )
  959. {
  960. // Only bother if we can see it
  961. if ( FVisible( pSound->m_hOwner ) )
  962. {
  963. SetInspectTargetToEnt( pSound->m_hOwner, SCANNER_SOUND_INSPECT_LENGTH );
  964. }
  965. }
  966. }
  967. else
  968. {
  969. // Otherwise chase the specific sound
  970. Vector vSoundPos = pSound->GetSoundOrigin();
  971. SetInspectTargetToPos( vSoundPos, SCANNER_SOUND_INSPECT_LENGTH );
  972. }
  973. m_nFlyMode = (random->RandomInt(0,2)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
  974. }
  975. }
  976. // --------------------------------------
  977. // COND_CSCANNER_HAVE_INSPECT_TARGET
  978. //
  979. // Look for a nearby citizen or player to hassle.
  980. // ---------------------------------------------------------
  981. // Check for citizens to inspect
  982. if ( gpGlobals->curtime > m_fCheckCitizenTime && HaveInspectTarget() == false )
  983. {
  984. CBaseEntity *pBestEntity = BestInspectTarget();
  985. if ( pBestEntity != NULL )
  986. {
  987. SetInspectTargetToEnt( pBestEntity, SCANNER_CIT_INSPECT_LENGTH );
  988. m_nFlyMode = (random->RandomInt(0,3)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
  989. SetCondition ( COND_CSCANNER_HAVE_INSPECT_TARGET );
  990. }
  991. }
  992. // Check for hints to inspect
  993. if ( gpGlobals->curtime > m_fCheckHintTime && HaveInspectTarget() == false )
  994. {
  995. SetHintNode( CAI_HintManager::FindHint( this, HINT_WORLD_WINDOW, 0, SCANNER_CIT_INSPECT_FLY_DIST ) );
  996. if ( GetHintNode() )
  997. {
  998. m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;
  999. m_nFlyMode = (random->RandomInt(0,2)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
  1000. SetInspectTargetToHint( GetHintNode(), SCANNER_HINT_INSPECT_LENGTH );
  1001. SetCondition ( COND_CSCANNER_HAVE_INSPECT_TARGET );
  1002. }
  1003. }
  1004. // --------------------------------------
  1005. // COND_CSCANNER_SPOT_ON_TARGET
  1006. //
  1007. // True when spotlight is on target ent
  1008. // --------------------------------------
  1009. if ( m_hSpotlightTarget != NULL && HaveInspectTarget() && m_hSpotlightTarget->GetSmoothedVelocity().Length() < 25 )
  1010. {
  1011. // If I have a target entity, check my spotlight against the
  1012. // actual position of the entity
  1013. if (GetTarget())
  1014. {
  1015. float fInspectDist = (m_vSpotlightTargetPos - m_vSpotlightCurrentPos).Length();
  1016. if ( fInspectDist < 100 )
  1017. {
  1018. SetCondition( COND_CSCANNER_SPOT_ON_TARGET );
  1019. }
  1020. }
  1021. // Otherwise just check by beam direction
  1022. else
  1023. {
  1024. Vector vTargetDir = SpotlightTargetPos() - GetLocalOrigin();
  1025. VectorNormalize(vTargetDir);
  1026. float dotpr = DotProduct(vTargetDir, m_vSpotlightDir);
  1027. if (dotpr > 0.95)
  1028. {
  1029. SetCondition( COND_CSCANNER_SPOT_ON_TARGET );
  1030. }
  1031. }
  1032. }
  1033. // --------------------------------------------
  1034. // COND_CSCANNER_CAN_PHOTOGRAPH
  1035. //
  1036. // True when can photograph target ent
  1037. // --------------------------------------------
  1038. ClearCondition( COND_CSCANNER_CAN_PHOTOGRAPH );
  1039. if ( m_nFlyMode == SCANNER_FLY_PHOTO )
  1040. {
  1041. // Make sure I have something to photograph and I'm ready to photograph and I'm not moving to fast
  1042. if ( gpGlobals->curtime > m_fNextPhotographTime && HaveInspectTarget() && GetCurrentVelocity().LengthSqr() < (64*64) )
  1043. {
  1044. // Check that I'm in the right distance range
  1045. float fInspectDist = (InspectTargetPosition() - GetAbsOrigin()).Length2D();
  1046. // See if we're within range
  1047. if ( fInspectDist > SCANNER_PHOTO_NEAR_DIST && fInspectDist < SCANNER_PHOTO_FAR_DIST )
  1048. {
  1049. // Make sure we're looking at the target
  1050. if ( UTIL_AngleDiff( GetAbsAngles().y, VecToYaw( InspectTargetPosition() - GetAbsOrigin() ) ) < 4.0f )
  1051. {
  1052. trace_t tr;
  1053. AI_TraceLine ( GetAbsOrigin(), InspectTargetPosition(), MASK_BLOCKLOS, GetTarget(), COLLISION_GROUP_NONE, &tr);
  1054. if ( tr.fraction == 1.0f )
  1055. {
  1056. SetCondition( COND_CSCANNER_CAN_PHOTOGRAPH );
  1057. }
  1058. }
  1059. }
  1060. }
  1061. }
  1062. }
  1063. //------------------------------------------------------------------------------
  1064. // Purpose:
  1065. //------------------------------------------------------------------------------
  1066. void CNPC_CScanner::PrescheduleThink(void)
  1067. {
  1068. BaseClass::PrescheduleThink();
  1069. // Go back to idling if we're done
  1070. if ( GetIdealActivity() == ACT_SCANNER_FLARE_START )
  1071. {
  1072. if ( IsSequenceFinished() )
  1073. {
  1074. SetIdealActivity( (Activity) ACT_IDLE );
  1075. }
  1076. }
  1077. }
  1078. //-----------------------------------------------------------------------------
  1079. // Purpose: Overridden because if the player is a criminal, we hate them.
  1080. // Input : pTarget - Entity with which to determine relationship.
  1081. // Output : Returns relationship value.
  1082. //-----------------------------------------------------------------------------
  1083. Disposition_t CNPC_CScanner::IRelationType(CBaseEntity *pTarget)
  1084. {
  1085. // If it's the player and they are a criminal, we hates them
  1086. if ( pTarget && pTarget->Classify() == CLASS_PLAYER )
  1087. {
  1088. if ( GlobalEntity_GetState("gordon_precriminal") == GLOBAL_ON )
  1089. return D_NU;
  1090. }
  1091. return BaseClass::IRelationType( pTarget );
  1092. }
  1093. //-----------------------------------------------------------------------------
  1094. // Purpose:
  1095. // Input : *pTask -
  1096. //-----------------------------------------------------------------------------
  1097. void CNPC_CScanner::RunTask( const Task_t *pTask )
  1098. {
  1099. switch ( pTask->iTask )
  1100. {
  1101. case TASK_CSCANNER_PHOTOGRAPH:
  1102. {
  1103. if ( IsWaitFinished() )
  1104. {
  1105. // If light was on turn it off
  1106. if ( m_pEyeFlash->GetBrightness() > 0 )
  1107. {
  1108. m_pEyeFlash->SetBrightness( 0 );
  1109. // I'm done with this target
  1110. if ( gpGlobals->curtime > m_fInspectEndTime )
  1111. {
  1112. ClearInspectTarget();
  1113. TaskComplete();
  1114. }
  1115. // Otherwise take another picture
  1116. else
  1117. {
  1118. SetWait( 5.0f, 10.0f );
  1119. }
  1120. }
  1121. // If light was off, take another picture
  1122. else
  1123. {
  1124. TakePhoto();
  1125. SetWait( 0.1f );
  1126. }
  1127. }
  1128. break;
  1129. }
  1130. case TASK_CSCANNER_ATTACK_PRE_FLASH:
  1131. {
  1132. AttackPreFlash();
  1133. if ( IsWaitFinished() )
  1134. {
  1135. TaskComplete();
  1136. }
  1137. break;
  1138. }
  1139. case TASK_CSCANNER_ATTACK_FLASH:
  1140. {
  1141. if (IsWaitFinished())
  1142. {
  1143. AttackFlashBlind();
  1144. TaskComplete();
  1145. }
  1146. break;
  1147. }
  1148. default:
  1149. {
  1150. BaseClass::RunTask(pTask);
  1151. }
  1152. }
  1153. }
  1154. //-----------------------------------------------------------------------------
  1155. // Purpose: Gets the appropriate next schedule based on current condition
  1156. // bits.
  1157. //-----------------------------------------------------------------------------
  1158. int CNPC_CScanner::SelectSchedule(void)
  1159. {
  1160. // Turn our flash off in case we were interrupted while it was on.
  1161. if ( m_pEyeFlash )
  1162. {
  1163. m_pEyeFlash->SetBrightness( 0 );
  1164. }
  1165. // ----------------------------------------------------
  1166. // If I'm dead, go into a dive bomb
  1167. // ----------------------------------------------------
  1168. if ( m_iHealth <= 0 )
  1169. {
  1170. m_flSpeed = SCANNER_MAX_DIVE_BOMB_SPEED;
  1171. return SCHED_SCANNER_ATTACK_DIVEBOMB;
  1172. }
  1173. // -------------------------------
  1174. // If I'm in a script sequence
  1175. // -------------------------------
  1176. if ( m_NPCState == NPC_STATE_SCRIPT )
  1177. return(BaseClass::SelectSchedule());
  1178. // -------------------------------
  1179. // Flinch
  1180. // -------------------------------
  1181. if ( HasCondition(COND_LIGHT_DAMAGE) || HasCondition(COND_HEAVY_DAMAGE) )
  1182. {
  1183. if ( IsHeldByPhyscannon( ) )
  1184. return SCHED_SMALL_FLINCH;
  1185. if ( m_NPCState == NPC_STATE_IDLE )
  1186. return SCHED_SMALL_FLINCH;
  1187. if ( m_NPCState == NPC_STATE_ALERT )
  1188. {
  1189. if ( m_iHealth < ( 3 * sk_scanner_health.GetFloat() / 4 ))
  1190. return SCHED_TAKE_COVER_FROM_ORIGIN;
  1191. if ( SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 )
  1192. return SCHED_SMALL_FLINCH;
  1193. }
  1194. else
  1195. {
  1196. if ( random->RandomInt( 0, 10 ) < 4 )
  1197. return SCHED_SMALL_FLINCH;
  1198. }
  1199. }
  1200. // I'm being held by the physcannon... struggle!
  1201. if ( IsHeldByPhyscannon( ) )
  1202. return SCHED_SCANNER_HELD_BY_PHYSCANNON;
  1203. // ----------------------------------------------------------
  1204. // If I have an enemy
  1205. // ----------------------------------------------------------
  1206. if ( GetEnemy() != NULL && GetEnemy()->IsAlive() && m_bShouldInspect )
  1207. {
  1208. // Always chase the enemy
  1209. SetInspectTargetToEnt( GetEnemy(), 9999 );
  1210. // Patrol if the enemy has vanished
  1211. if ( HasCondition( COND_LOST_ENEMY ) )
  1212. return SCHED_SCANNER_PATROL;
  1213. // Chase via route if we're directly blocked
  1214. if ( HasCondition( COND_SCANNER_FLY_BLOCKED ) )
  1215. return SCHED_SCANNER_CHASE_ENEMY;
  1216. // Attack if it's time
  1217. if ( gpGlobals->curtime < m_flNextAttack )
  1218. return SCHED_CSCANNER_SPOTLIGHT_HOVER;
  1219. // Melee attack if possible
  1220. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  1221. {
  1222. if ( random->RandomInt(0,1) )
  1223. return SCHED_CSCANNER_ATTACK_FLASH;
  1224. // TODO: a schedule where he makes an alarm sound?
  1225. return SCHED_SCANNER_CHASE_ENEMY;
  1226. }
  1227. // If I'm far from the enemy, stay up high and approach in spotlight mode
  1228. float fAttack2DDist = ( GetEnemyLKP() - GetAbsOrigin() ).Length2D();
  1229. if ( fAttack2DDist > SCANNER_ATTACK_FAR_DIST )
  1230. return SCHED_CSCANNER_SPOTLIGHT_HOVER;
  1231. // Otherwise fly in low for attack
  1232. return SCHED_SCANNER_ATTACK_HOVER;
  1233. }
  1234. // ----------------------------------------------------------
  1235. // If I have something to inspect
  1236. // ----------------------------------------------------------
  1237. if ( HaveInspectTarget() )
  1238. {
  1239. // Pathfind to our goal
  1240. if ( HasCondition( COND_SCANNER_FLY_BLOCKED ) )
  1241. return SCHED_CSCANNER_MOVE_TO_INSPECT;
  1242. // If I was chasing, pick with photographing or spotlighting
  1243. if ( m_nFlyMode == SCANNER_FLY_CHASE )
  1244. {
  1245. m_nFlyMode = (random->RandomInt(0,1)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
  1246. }
  1247. // Handle spotlight
  1248. if ( m_nFlyMode == SCANNER_FLY_SPOT )
  1249. {
  1250. if (HasCondition( COND_CSCANNER_SPOT_ON_TARGET ))
  1251. {
  1252. if (GetTarget())
  1253. {
  1254. RequestInspectSupport();
  1255. CAI_BaseNPC *pNPC = GetTarget()->MyNPCPointer();
  1256. // If I'm leading the inspection, so verbal inspection
  1257. if (pNPC && pNPC->GetTarget() == this)
  1258. {
  1259. return SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT;
  1260. }
  1261. return SCHED_CSCANNER_SPOTLIGHT_HOVER;
  1262. }
  1263. return SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS;
  1264. }
  1265. return SCHED_CSCANNER_SPOTLIGHT_HOVER;
  1266. }
  1267. // Handle photographing
  1268. if ( m_nFlyMode == SCANNER_FLY_PHOTO )
  1269. {
  1270. if ( HasCondition( COND_CSCANNER_CAN_PHOTOGRAPH ))
  1271. return SCHED_CSCANNER_PHOTOGRAPH;
  1272. return SCHED_CSCANNER_PHOTOGRAPH_HOVER;
  1273. }
  1274. // Handle following after a target
  1275. if ( m_nFlyMode == SCANNER_FLY_FOLLOW )
  1276. {
  1277. //TODO: Randomly make noise, photograph, etc
  1278. return SCHED_SCANNER_FOLLOW_HOVER;
  1279. }
  1280. // Handle patrolling
  1281. if ( ( m_nFlyMode == SCANNER_FLY_PATROL ) || ( m_nFlyMode == SCANNER_FLY_FAST ) )
  1282. return SCHED_SCANNER_PATROL;
  1283. }
  1284. // Default to patrolling around
  1285. return SCHED_SCANNER_PATROL;
  1286. }
  1287. //------------------------------------------------------------------------------
  1288. // Purpose:
  1289. //------------------------------------------------------------------------------
  1290. void CNPC_CScanner::SpotlightDestroy(void)
  1291. {
  1292. if ( m_hSpotlight )
  1293. {
  1294. UTIL_Remove(m_hSpotlight);
  1295. m_hSpotlight = NULL;
  1296. UTIL_Remove(m_hSpotlightTarget);
  1297. m_hSpotlightTarget = NULL;
  1298. }
  1299. }
  1300. //------------------------------------------------------------------------------
  1301. // Purpose:
  1302. //------------------------------------------------------------------------------
  1303. void CNPC_CScanner::SpotlightCreate(void)
  1304. {
  1305. // Make sure we don't already have one
  1306. if ( m_hSpotlight != NULL )
  1307. return;
  1308. // Can we create a spotlight yet?
  1309. if ( gpGlobals->curtime < m_fNextSpotlightTime )
  1310. return;
  1311. // If I have an enemy, start spotlight on my enemy
  1312. if (GetEnemy() != NULL)
  1313. {
  1314. Vector vEnemyPos = GetEnemyLKP();
  1315. Vector vTargetPos = vEnemyPos;
  1316. vTargetPos.z = GetFloorZ(vEnemyPos);
  1317. m_vSpotlightDir = vTargetPos - GetLocalOrigin();
  1318. VectorNormalize(m_vSpotlightDir);
  1319. }
  1320. // If I have an target, start spotlight on my target
  1321. else if (GetTarget() != NULL)
  1322. {
  1323. Vector vTargetPos = GetTarget()->GetLocalOrigin();
  1324. vTargetPos.z = GetFloorZ(GetTarget()->GetLocalOrigin());
  1325. m_vSpotlightDir = vTargetPos - GetLocalOrigin();
  1326. VectorNormalize(m_vSpotlightDir);
  1327. }
  1328. // Other wise just start looking down
  1329. else
  1330. {
  1331. m_vSpotlightDir = Vector(0,0,-1);
  1332. }
  1333. trace_t tr;
  1334. AI_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * 2024, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr );
  1335. m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" );
  1336. m_hSpotlightTarget->Spawn();
  1337. m_hSpotlightTarget->SetLocalOrigin( tr.endpos );
  1338. m_hSpotlightTarget->SetOwnerEntity( this );
  1339. // YWB: Because the scanner only moves the target during think, make sure we interpolate over 0.1 sec instead of every tick!!!
  1340. m_hSpotlightTarget->SetSimulatedEveryTick( false );
  1341. // Using the same color as the beam...
  1342. m_hSpotlightTarget->SetRenderColor( 255, 255, 255 );
  1343. m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength;
  1344. m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", SPOTLIGHT_WIDTH );
  1345. // Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore)
  1346. m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY );
  1347. m_hSpotlight->SetColor( 255, 255, 255 );
  1348. m_hSpotlight->SetHaloTexture( m_nHaloSprite );
  1349. m_hSpotlight->SetHaloScale( 32 );
  1350. m_hSpotlight->SetEndWidth( m_hSpotlight->GetWidth() );
  1351. m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) );
  1352. m_hSpotlight->SetBrightness( 32 );
  1353. m_hSpotlight->SetNoise( 0 );
  1354. m_hSpotlight->EntsInit( this, m_hSpotlightTarget );
  1355. m_hSpotlight->SetHDRColorScale( 0.75f ); // Scale this back a bit on HDR maps
  1356. // attach to light
  1357. m_hSpotlight->SetStartAttachment( LookupAttachment( SCANNER_ATTACHMENT_LIGHT ) );
  1358. m_vSpotlightAngVelocity = vec3_origin;
  1359. }
  1360. //------------------------------------------------------------------------------
  1361. // Purpose:
  1362. //------------------------------------------------------------------------------
  1363. Vector CNPC_CScanner::SpotlightTargetPos(void)
  1364. {
  1365. // ----------------------------------------------
  1366. // If I have an enemy
  1367. // ----------------------------------------------
  1368. if (GetEnemy() != NULL)
  1369. {
  1370. // If I can see my enemy aim for him
  1371. if (HasCondition(COND_SEE_ENEMY))
  1372. {
  1373. // If its client aim for his eyes
  1374. if (GetEnemy()->GetFlags() & FL_CLIENT)
  1375. {
  1376. m_vSpotlightTargetPos = GetEnemy()->EyePosition();
  1377. }
  1378. // Otherwise same for his feet
  1379. else
  1380. {
  1381. m_vSpotlightTargetPos = GetEnemy()->GetLocalOrigin();
  1382. m_vSpotlightTargetPos.z = GetFloorZ(GetEnemy()->GetLocalOrigin());
  1383. }
  1384. }
  1385. // Otherwise aim for last known position if I can see LKP
  1386. else
  1387. {
  1388. Vector vLKP = GetEnemyLKP();
  1389. m_vSpotlightTargetPos.x = vLKP.x;
  1390. m_vSpotlightTargetPos.y = vLKP.y;
  1391. m_vSpotlightTargetPos.z = GetFloorZ(vLKP);
  1392. }
  1393. }
  1394. // ----------------------------------------------
  1395. // If I have an inspect target
  1396. // ----------------------------------------------
  1397. else if (HaveInspectTarget())
  1398. {
  1399. m_vSpotlightTargetPos = InspectTargetPosition();
  1400. }
  1401. else
  1402. {
  1403. // This creates a nice patrol spotlight sweep
  1404. // in the direction that I'm travelling
  1405. m_vSpotlightTargetPos = GetCurrentVelocity();
  1406. m_vSpotlightTargetPos.z = 0;
  1407. VectorNormalize( m_vSpotlightTargetPos );
  1408. m_vSpotlightTargetPos *= 5;
  1409. float noiseScale = 2.5;
  1410. const Vector &noiseMod = GetNoiseMod();
  1411. m_vSpotlightTargetPos.x += noiseScale*sin(noiseMod.x * gpGlobals->curtime + noiseMod.x);
  1412. m_vSpotlightTargetPos.y += noiseScale*cos(noiseMod.y* gpGlobals->curtime + noiseMod.y);
  1413. m_vSpotlightTargetPos.z -= fabs(noiseScale*cos(noiseMod.z* gpGlobals->curtime + noiseMod.z) );
  1414. m_vSpotlightTargetPos = GetLocalOrigin()+m_vSpotlightTargetPos * 2024;
  1415. }
  1416. return m_vSpotlightTargetPos;
  1417. }
  1418. //------------------------------------------------------------------------------
  1419. // Purpose:
  1420. //------------------------------------------------------------------------------
  1421. Vector CNPC_CScanner::SpotlightCurrentPos(void)
  1422. {
  1423. Vector vTargetDir = SpotlightTargetPos() - GetLocalOrigin();
  1424. VectorNormalize(vTargetDir);
  1425. if (!m_hSpotlight)
  1426. {
  1427. DevMsg("Spotlight pos. called w/o spotlight!\n");
  1428. return vec3_origin;
  1429. }
  1430. // -------------------------------------------------
  1431. // Beam has momentum relative to it's ground speed
  1432. // so sclae the turn rate based on its distance
  1433. // from the beam source
  1434. // -------------------------------------------------
  1435. float fBeamDist = (m_hSpotlightTarget->GetLocalOrigin() - GetLocalOrigin()).Length();
  1436. float fBeamTurnRate = atan(50/fBeamDist);
  1437. Vector vNewAngVelocity = fBeamTurnRate * (vTargetDir - m_vSpotlightDir);
  1438. float myDecay = 0.4;
  1439. m_vSpotlightAngVelocity = (myDecay * m_vSpotlightAngVelocity + (1-myDecay) * vNewAngVelocity);
  1440. // ------------------------------
  1441. // Limit overall angular speed
  1442. // -----------------------------
  1443. if (m_vSpotlightAngVelocity.Length() > 1)
  1444. {
  1445. Vector velDir = m_vSpotlightAngVelocity;
  1446. VectorNormalize(velDir);
  1447. m_vSpotlightAngVelocity = velDir * 1;
  1448. }
  1449. // ------------------------------
  1450. // Calculate new beam direction
  1451. // ------------------------------
  1452. m_vSpotlightDir = m_vSpotlightDir + m_vSpotlightAngVelocity;
  1453. m_vSpotlightDir = m_vSpotlightDir;
  1454. VectorNormalize(m_vSpotlightDir);
  1455. // ---------------------------------------------
  1456. // Get beam end point. Only collide with
  1457. // solid objects, not npcs
  1458. // ---------------------------------------------
  1459. trace_t tr;
  1460. Vector vTraceEnd = GetAbsOrigin() + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength);
  1461. AI_TraceLine ( GetAbsOrigin(), vTraceEnd, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr);
  1462. return (tr.endpos);
  1463. }
  1464. //------------------------------------------------------------------------------
  1465. // Purpose: Update the direction and position of my spotlight
  1466. //------------------------------------------------------------------------------
  1467. void CNPC_CScanner::SpotlightUpdate(void)
  1468. {
  1469. //FIXME: JDW - E3 Hack
  1470. if ( m_bNoLight )
  1471. {
  1472. if ( m_hSpotlight )
  1473. {
  1474. SpotlightDestroy();
  1475. }
  1476. return;
  1477. }
  1478. if ((m_nFlyMode != SCANNER_FLY_SPOT) &&
  1479. (m_nFlyMode != SCANNER_FLY_PATROL) &&
  1480. (m_nFlyMode != SCANNER_FLY_FAST))
  1481. {
  1482. if ( m_hSpotlight )
  1483. {
  1484. SpotlightDestroy();
  1485. }
  1486. return;
  1487. }
  1488. // If I don't have a spotlight attempt to create one
  1489. if ( m_hSpotlight == NULL )
  1490. {
  1491. SpotlightCreate();
  1492. if ( m_hSpotlight== NULL )
  1493. return;
  1494. }
  1495. // Calculate the new homing target position
  1496. m_vSpotlightCurrentPos = SpotlightCurrentPos();
  1497. // ------------------------------------------------------------------
  1498. // If I'm not facing the spotlight turn it off
  1499. // ------------------------------------------------------------------
  1500. Vector vSpotDir = m_vSpotlightCurrentPos - GetAbsOrigin();
  1501. VectorNormalize(vSpotDir);
  1502. Vector vForward;
  1503. AngleVectors( GetAbsAngles(), &vForward );
  1504. float dotpr = DotProduct( vForward, vSpotDir );
  1505. if ( dotpr < 0.0 )
  1506. {
  1507. // Leave spotlight off for a while
  1508. m_fNextSpotlightTime = gpGlobals->curtime + 3.0f;
  1509. SpotlightDestroy();
  1510. return;
  1511. }
  1512. // --------------------------------------------------------------
  1513. // Update spotlight target velocity
  1514. // --------------------------------------------------------------
  1515. Vector vTargetDir = (m_vSpotlightCurrentPos - m_hSpotlightTarget->GetLocalOrigin());
  1516. float vTargetDist = vTargetDir.Length();
  1517. Vector vecNewVelocity = vTargetDir;
  1518. VectorNormalize(vecNewVelocity);
  1519. vecNewVelocity *= (10 * vTargetDist);
  1520. // If a large move is requested, just jump to final spot as we
  1521. // probably hit a discontinuity
  1522. if (vecNewVelocity.Length() > 200)
  1523. {
  1524. VectorNormalize(vecNewVelocity);
  1525. vecNewVelocity *= 200;
  1526. m_hSpotlightTarget->SetLocalOrigin( m_vSpotlightCurrentPos );
  1527. }
  1528. m_hSpotlightTarget->SetAbsVelocity( vecNewVelocity );
  1529. m_hSpotlightTarget->m_vSpotlightOrg = GetAbsOrigin();
  1530. // Avoid sudden change in where beam fades out when cross disconinuities
  1531. m_hSpotlightTarget->m_vSpotlightDir = m_hSpotlightTarget->GetLocalOrigin() - m_hSpotlightTarget->m_vSpotlightOrg;
  1532. float flBeamLength = VectorNormalize( m_hSpotlightTarget->m_vSpotlightDir );
  1533. m_flSpotlightCurLength = (0.80*m_flSpotlightCurLength) + (0.2*flBeamLength);
  1534. // Fade out spotlight end if past max length.
  1535. if (m_flSpotlightCurLength > 2*m_flSpotlightMaxLength)
  1536. {
  1537. m_hSpotlightTarget->SetRenderColorA( 0 );
  1538. m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
  1539. }
  1540. else if (m_flSpotlightCurLength > m_flSpotlightMaxLength)
  1541. {
  1542. m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) );
  1543. m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
  1544. }
  1545. else
  1546. {
  1547. m_hSpotlightTarget->SetRenderColorA( 1.0 );
  1548. m_hSpotlight->SetFadeLength(m_flSpotlightCurLength);
  1549. }
  1550. // Adjust end width to keep beam width constant
  1551. float flNewWidth = SPOTLIGHT_WIDTH * ( flBeamLength/m_flSpotlightMaxLength);
  1552. m_hSpotlight->SetWidth(flNewWidth);
  1553. m_hSpotlight->SetEndWidth(flNewWidth);
  1554. m_hSpotlightTarget->m_flLightScale = 0.0;
  1555. }
  1556. //-----------------------------------------------------------------------------
  1557. // Purpose: Called just before we are deleted.
  1558. //-----------------------------------------------------------------------------
  1559. void CNPC_CScanner::UpdateOnRemove( void )
  1560. {
  1561. SpotlightDestroy();
  1562. BaseClass::UpdateOnRemove();
  1563. }
  1564. //------------------------------------------------------------------------------
  1565. // Purpose:
  1566. //------------------------------------------------------------------------------
  1567. void CNPC_CScanner::TakePhoto(void)
  1568. {
  1569. ScannerEmitSound( "TakePhoto" );
  1570. m_pEyeFlash->SetScale( 1.4 );
  1571. m_pEyeFlash->SetBrightness( 255 );
  1572. m_pEyeFlash->SetColor(255,255,255);
  1573. Vector vRawPos = InspectTargetPosition();
  1574. Vector vLightPos = vRawPos;
  1575. // If taking picture of entity, aim at feet
  1576. if ( GetTarget() )
  1577. {
  1578. if ( GetTarget()->IsPlayer() )
  1579. {
  1580. m_OnPhotographPlayer.FireOutput( GetTarget(), this );
  1581. BlindFlashTarget( GetTarget() );
  1582. }
  1583. if ( GetTarget()->MyNPCPointer() != NULL )
  1584. {
  1585. m_OnPhotographNPC.FireOutput( GetTarget(), this );
  1586. GetTarget()->MyNPCPointer()->DispatchInteraction( g_interactionScannerInspectBegin, NULL, this );
  1587. }
  1588. }
  1589. SetIdealActivity( (Activity) ACT_SCANNER_FLARE_START );
  1590. m_bPhotoTaken = true;
  1591. }
  1592. //------------------------------------------------------------------------------
  1593. // Purpose:
  1594. //------------------------------------------------------------------------------
  1595. void CNPC_CScanner::AttackPreFlash(void)
  1596. {
  1597. ScannerEmitSound( "TakePhoto" );
  1598. // If off turn on, if on turn off
  1599. if (m_pEyeFlash->GetBrightness() == 0)
  1600. {
  1601. m_pEyeFlash->SetScale( 0.5 );
  1602. m_pEyeFlash->SetBrightness( 255 );
  1603. m_pEyeFlash->SetColor(255,0,0);
  1604. }
  1605. else
  1606. {
  1607. m_pEyeFlash->SetBrightness( 0 );
  1608. }
  1609. }
  1610. //------------------------------------------------------------------------------
  1611. // Purpose:
  1612. //------------------------------------------------------------------------------
  1613. void CNPC_CScanner::AttackFlash(void)
  1614. {
  1615. ScannerEmitSound( "AttackFlash" );
  1616. m_pEyeFlash->SetScale( 1.8 );
  1617. m_pEyeFlash->SetBrightness( 255 );
  1618. m_pEyeFlash->SetColor(255,255,255);
  1619. if (GetEnemy() != NULL)
  1620. {
  1621. Vector pos = GetEnemyLKP();
  1622. CBroadcastRecipientFilter filter;
  1623. te->DynamicLight( filter, 0.0, &pos, 200, 200, 255, 0, 300, 0.2, 50 );
  1624. if (GetEnemy()->IsPlayer())
  1625. {
  1626. m_OnPhotographPlayer.FireOutput(GetTarget(), this);
  1627. }
  1628. else if( GetEnemy()->MyNPCPointer() )
  1629. {
  1630. m_OnPhotographNPC.FireOutput(GetTarget(), this);
  1631. }
  1632. }
  1633. }
  1634. //-----------------------------------------------------------------------------
  1635. // Purpose:
  1636. // Input : *pTarget -
  1637. //-----------------------------------------------------------------------------
  1638. void CNPC_CScanner::BlindFlashTarget( CBaseEntity *pTarget )
  1639. {
  1640. // Tell all the striders this person is here!
  1641. CAI_BaseNPC ** ppAIs = g_AI_Manager.AccessAIs();
  1642. int nAIs = g_AI_Manager.NumAIs();
  1643. if( IsStriderScout() )
  1644. {
  1645. for ( int i = 0; i < nAIs; i++ )
  1646. {
  1647. if( FClassnameIs( ppAIs[ i ], "npc_strider" ) )
  1648. {
  1649. ppAIs[ i ]->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin(), this );
  1650. }
  1651. }
  1652. }
  1653. // Only bother with player
  1654. if ( pTarget->IsPlayer() == false )
  1655. return;
  1656. // Scale the flash value by how closely the player is looking at me
  1657. Vector vFlashDir = GetAbsOrigin() - pTarget->EyePosition();
  1658. VectorNormalize(vFlashDir);
  1659. Vector vFacing;
  1660. AngleVectors( pTarget->EyeAngles(), &vFacing );
  1661. float dotPr = DotProduct( vFlashDir, vFacing );
  1662. // Not if behind us
  1663. if ( dotPr > 0.5f )
  1664. {
  1665. // Make sure nothing in the way
  1666. trace_t tr;
  1667. AI_TraceLine ( GetAbsOrigin(), pTarget->EyePosition(), MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr );
  1668. if ( tr.startsolid == false && tr.fraction == 1.0)
  1669. {
  1670. color32 white = { 255, 255, 255, (byte)(SCANNER_FLASH_MAX_VALUE * dotPr) };
  1671. if ( ( g_pMaterialSystemHardwareConfig != NULL ) && ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) )
  1672. {
  1673. white.a = ( byte )( ( float )white.a * 0.9f );
  1674. }
  1675. float flFadeTime = ( IsX360() ) ? 0.5f : 3.0f;
  1676. UTIL_ScreenFade( pTarget, white, flFadeTime, 0.5, FFADE_IN );
  1677. }
  1678. }
  1679. }
  1680. //------------------------------------------------------------------------------
  1681. // Purpose:
  1682. //------------------------------------------------------------------------------
  1683. void CNPC_CScanner::AttackFlashBlind(void)
  1684. {
  1685. if( GetEnemy() )
  1686. {
  1687. BlindFlashTarget( GetEnemy() );
  1688. }
  1689. m_pEyeFlash->SetBrightness( 0 );
  1690. float fAttackDelay = random->RandomFloat(SCANNER_ATTACK_MIN_DELAY,SCANNER_ATTACK_MAX_DELAY);
  1691. if( IsStriderScout() )
  1692. {
  1693. // Make strider scouts more snappy.
  1694. fAttackDelay *= 0.5;
  1695. }
  1696. m_flNextAttack = gpGlobals->curtime + fAttackDelay;
  1697. m_fNextSpotlightTime = gpGlobals->curtime + 1.0f;
  1698. }
  1699. //------------------------------------------------------------------------------
  1700. // Purpose:
  1701. //------------------------------------------------------------------------------
  1702. void CNPC_CScanner::AttackDivebomb( void )
  1703. {
  1704. if (m_hSpotlight)
  1705. {
  1706. SpotlightDestroy();
  1707. }
  1708. BaseClass::AttackDivebomb();
  1709. }
  1710. //-----------------------------------------------------------------------------
  1711. // Purpose:
  1712. // Input : pTask -
  1713. //-----------------------------------------------------------------------------
  1714. void CNPC_CScanner::StartTask( const Task_t *pTask )
  1715. {
  1716. switch (pTask->iTask)
  1717. {
  1718. case TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET:
  1719. {
  1720. // Must have somewhere to fly to
  1721. if ( HaveInspectTarget() == false )
  1722. {
  1723. TaskFail( "No inspection target to fly to!\n" );
  1724. return;
  1725. }
  1726. if ( GetTarget() )
  1727. {
  1728. //FIXME: Tweak
  1729. //Vector idealPos = IdealGoalForMovement( InspectTargetPosition(), GetAbsOrigin(), 128.0f, 128.0f );
  1730. AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin );
  1731. if ( GetNavigator()->SetGoal( goal ) )
  1732. {
  1733. TaskComplete();
  1734. return;
  1735. }
  1736. }
  1737. else
  1738. {
  1739. AI_NavGoal_t goal( GOALTYPE_LOCATION, InspectTargetPosition() );
  1740. if ( GetNavigator()->SetGoal( goal ) )
  1741. {
  1742. TaskComplete();
  1743. return;
  1744. }
  1745. }
  1746. // Don't try and inspect this target again for a few seconds
  1747. CNPC_Citizen *pCitizen = dynamic_cast<CNPC_Citizen *>( GetTarget() );
  1748. if ( pCitizen )
  1749. {
  1750. pCitizen->SetNextScannerInspectTime( gpGlobals->curtime + 5.0 );
  1751. }
  1752. TaskFail("No route to inspection target!\n");
  1753. }
  1754. break;
  1755. case TASK_CSCANNER_SPOT_INSPECT_ON:
  1756. {
  1757. if (GetTarget() == NULL)
  1758. {
  1759. TaskFail(FAIL_NO_TARGET);
  1760. }
  1761. else
  1762. {
  1763. CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
  1764. if (!pNPC)
  1765. {
  1766. TaskFail(FAIL_NO_TARGET);
  1767. }
  1768. else
  1769. {
  1770. pNPC->DispatchInteraction(g_interactionScannerInspectBegin,NULL,this);
  1771. // Now we need some time to inspect
  1772. m_fInspectEndTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_LENGTH;
  1773. TaskComplete();
  1774. }
  1775. }
  1776. break;
  1777. }
  1778. case TASK_CSCANNER_SPOT_INSPECT_WAIT:
  1779. {
  1780. if (GetTarget() == NULL)
  1781. {
  1782. TaskFail(FAIL_NO_TARGET);
  1783. }
  1784. else
  1785. {
  1786. CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
  1787. if (!pNPC)
  1788. {
  1789. SetTarget( NULL );
  1790. TaskFail(FAIL_NO_TARGET);
  1791. }
  1792. else
  1793. {
  1794. //<<TEMP>>//<<TEMP>> armband too!
  1795. pNPC->DispatchInteraction(g_interactionScannerInspectHandsUp,NULL,this);
  1796. }
  1797. TaskComplete();
  1798. }
  1799. break;
  1800. }
  1801. case TASK_CSCANNER_SPOT_INSPECT_OFF:
  1802. {
  1803. if (GetTarget() == NULL)
  1804. {
  1805. TaskFail(FAIL_NO_TARGET);
  1806. }
  1807. else
  1808. {
  1809. CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
  1810. if (!pNPC)
  1811. {
  1812. TaskFail(FAIL_NO_TARGET);
  1813. }
  1814. else
  1815. {
  1816. pNPC->DispatchInteraction(g_interactionScannerInspectDone,NULL,this);
  1817. // Clear target entity and don't inspect again for a while
  1818. SetTarget( NULL );
  1819. m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
  1820. TaskComplete();
  1821. }
  1822. }
  1823. break;
  1824. }
  1825. case TASK_CSCANNER_CLEAR_INSPECT_TARGET:
  1826. {
  1827. ClearInspectTarget();
  1828. TaskComplete();
  1829. break;
  1830. }
  1831. case TASK_CSCANNER_SET_FLY_SPOT:
  1832. {
  1833. m_nFlyMode = SCANNER_FLY_SPOT;
  1834. TaskComplete();
  1835. break;
  1836. }
  1837. case TASK_CSCANNER_SET_FLY_PHOTO:
  1838. {
  1839. m_nFlyMode = SCANNER_FLY_PHOTO;
  1840. m_bPhotoTaken = false;
  1841. // Leave spotlight off for a while
  1842. m_fNextSpotlightTime = gpGlobals->curtime + 2.0;
  1843. TaskComplete();
  1844. break;
  1845. }
  1846. case TASK_CSCANNER_PHOTOGRAPH:
  1847. {
  1848. TakePhoto();
  1849. SetWait( 0.1 );
  1850. break;
  1851. }
  1852. case TASK_CSCANNER_ATTACK_PRE_FLASH:
  1853. {
  1854. if( IsStriderScout() )
  1855. {
  1856. Vector vecScare = GetEnemy()->EarPosition();
  1857. Vector vecDir = WorldSpaceCenter() - vecScare;
  1858. VectorNormalize( vecDir );
  1859. vecScare += vecDir * 64.0f;
  1860. CSoundEnt::InsertSound( SOUND_DANGER, vecScare, 256, 1.0, this );
  1861. }
  1862. if (m_pEyeFlash)
  1863. {
  1864. AttackPreFlash();
  1865. // Flash red for a while
  1866. SetWait( 1.0f );
  1867. }
  1868. else
  1869. {
  1870. TaskFail("No Flash");
  1871. }
  1872. break;
  1873. }
  1874. case TASK_CSCANNER_ATTACK_FLASH:
  1875. {
  1876. AttackFlash();
  1877. // Blinding occurs slightly later
  1878. SetWait( 0.05 );
  1879. break;
  1880. }
  1881. // Override to go to inspect target position whether or not is an entity
  1882. case TASK_GET_PATH_TO_TARGET:
  1883. {
  1884. if (!HaveInspectTarget())
  1885. {
  1886. TaskFail(FAIL_NO_TARGET);
  1887. }
  1888. else if (GetHintNode())
  1889. {
  1890. Vector vNodePos;
  1891. GetHintNode()->GetPosition(this,&vNodePos);
  1892. GetNavigator()->SetGoal( vNodePos );
  1893. }
  1894. else
  1895. {
  1896. AI_NavGoal_t goal( (const Vector &)InspectTargetPosition() );
  1897. goal.pTarget = GetTarget();
  1898. GetNavigator()->SetGoal( goal );
  1899. }
  1900. break;
  1901. }
  1902. default:
  1903. BaseClass::StartTask(pTask);
  1904. break;
  1905. }
  1906. }
  1907. //-----------------------------------------------------------------------------
  1908. // Purpose:
  1909. //-----------------------------------------------------------------------------
  1910. char *CNPC_CScanner::GetScannerSoundPrefix( void )
  1911. {
  1912. if( m_bIsClawScanner )
  1913. return "NPC_SScanner";
  1914. return "NPC_CScanner";
  1915. }
  1916. //------------------------------------------------------------------------------
  1917. // Purpose:
  1918. //------------------------------------------------------------------------------
  1919. float CNPC_CScanner::MinGroundDist( void )
  1920. {
  1921. if ( m_nFlyMode == SCANNER_FLY_SPOT && !GetHintNode() )
  1922. {
  1923. return SCANNER_SPOTLIGHT_FLY_HEIGHT;
  1924. }
  1925. return SCANNER_NOSPOTLIGHT_FLY_HEIGHT;
  1926. }
  1927. //-----------------------------------------------------------------------------
  1928. // Purpose:
  1929. //-----------------------------------------------------------------------------
  1930. void CNPC_CScanner::AdjustScannerVelocity( void )
  1931. {
  1932. if ( m_bIsClawScanner )
  1933. {
  1934. m_vCurrentVelocity *= ( 1 + sin( ( gpGlobals->curtime + m_flFlyNoiseBase ) * 2.5f ) * .1 );
  1935. }
  1936. }
  1937. //-----------------------------------------------------------------------------
  1938. // Purpose:
  1939. // Input : flInterval -
  1940. // Output : Returns true on success, false on failure.
  1941. //-----------------------------------------------------------------------------
  1942. bool CNPC_CScanner::OverrideMove( float flInterval )
  1943. {
  1944. // ----------------------------------------------
  1945. // If dive bombing
  1946. // ----------------------------------------------
  1947. if (m_nFlyMode == SCANNER_FLY_DIVE)
  1948. {
  1949. MoveToDivebomb( flInterval );
  1950. }
  1951. else
  1952. {
  1953. Vector vMoveTargetPos(0,0,0);
  1954. CBaseEntity *pMoveTarget = NULL;
  1955. // The original line of code was, due to the accidental use of '|' instead of
  1956. // '&', always true. Replacing with 'true' to suppress the warning without changing
  1957. // the (long-standing) behavior.
  1958. if ( true ) //!GetNavigator()->IsGoalActive() || ( GetNavigator()->GetCurWaypointFlags() | bits_WP_TO_PATHCORNER ) )
  1959. {
  1960. // Select move target
  1961. if ( GetTarget() != NULL )
  1962. {
  1963. pMoveTarget = GetTarget();
  1964. }
  1965. else if ( GetEnemy() != NULL )
  1966. {
  1967. pMoveTarget = GetEnemy();
  1968. }
  1969. // Select move target position
  1970. if ( HaveInspectTarget() )
  1971. {
  1972. vMoveTargetPos = InspectTargetPosition();
  1973. }
  1974. else if ( GetEnemy() != NULL )
  1975. {
  1976. vMoveTargetPos = GetEnemy()->GetAbsOrigin();
  1977. }
  1978. }
  1979. else
  1980. {
  1981. vMoveTargetPos = GetNavigator()->GetCurWaypointPos();
  1982. }
  1983. ClearCondition( COND_SCANNER_FLY_CLEAR );
  1984. ClearCondition( COND_SCANNER_FLY_BLOCKED );
  1985. // See if we can fly there directly
  1986. if ( pMoveTarget || HaveInspectTarget() )
  1987. {
  1988. trace_t tr;
  1989. AI_TraceHull( GetAbsOrigin(), vMoveTargetPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  1990. float fTargetDist = (1.0f-tr.fraction)*(GetAbsOrigin() - vMoveTargetPos).Length();
  1991. if ( ( tr.m_pEnt == pMoveTarget ) || ( fTargetDist < 50 ) )
  1992. {
  1993. if ( g_debug_cscanner.GetBool() )
  1994. {
  1995. NDebugOverlay::Line(GetLocalOrigin(), vMoveTargetPos, 0,255,0, true, 0);
  1996. NDebugOverlay::Cross3D(tr.endpos,Vector(-5,-5,-5),Vector(5,5,5),0,255,0,true,0.1);
  1997. }
  1998. SetCondition( COND_SCANNER_FLY_CLEAR );
  1999. }
  2000. else
  2001. {
  2002. //HANDY DEBUG TOOL
  2003. if ( g_debug_cscanner.GetBool() )
  2004. {
  2005. NDebugOverlay::Line(GetLocalOrigin(), vMoveTargetPos, 255,0,0, true, 0);
  2006. NDebugOverlay::Cross3D(tr.endpos,Vector(-5,-5,-5),Vector(5,5,5),255,0,0,true,0.1);
  2007. }
  2008. SetCondition( COND_SCANNER_FLY_BLOCKED );
  2009. }
  2010. }
  2011. // If I have a route, keep it updated and move toward target
  2012. if ( GetNavigator()->IsGoalActive() )
  2013. {
  2014. if ( OverridePathMove( pMoveTarget, flInterval ) )
  2015. {
  2016. BlendPhyscannonLaunchSpeed();
  2017. return true;
  2018. }
  2019. }
  2020. else if (m_nFlyMode == SCANNER_FLY_SPOT)
  2021. {
  2022. MoveToSpotlight( flInterval );
  2023. }
  2024. // If photographing
  2025. else if ( m_nFlyMode == SCANNER_FLY_PHOTO )
  2026. {
  2027. MoveToPhotograph( flInterval );
  2028. }
  2029. else if ( m_nFlyMode == SCANNER_FLY_FOLLOW )
  2030. {
  2031. MoveToSpotlight( flInterval );
  2032. }
  2033. // ----------------------------------------------
  2034. // If attacking
  2035. // ----------------------------------------------
  2036. else if (m_nFlyMode == SCANNER_FLY_ATTACK)
  2037. {
  2038. if ( m_hSpotlight )
  2039. {
  2040. SpotlightDestroy();
  2041. }
  2042. MoveToAttack( flInterval );
  2043. }
  2044. // -----------------------------------------------------------------
  2045. // If I don't have a route, just decelerate
  2046. // -----------------------------------------------------------------
  2047. else if (!GetNavigator()->IsGoalActive())
  2048. {
  2049. float myDecay = 9.5;
  2050. Decelerate( flInterval, myDecay);
  2051. }
  2052. }
  2053. MoveExecute_Alive( flInterval );
  2054. return true;
  2055. }
  2056. //-----------------------------------------------------------------------------
  2057. // Purpose: Accelerates toward a given position.
  2058. // Input : flInterval - Time interval over which to move.
  2059. // vecMoveTarget - Position to move toward.
  2060. //-----------------------------------------------------------------------------
  2061. void CNPC_CScanner::MoveToTarget( float flInterval, const Vector &vecMoveTarget )
  2062. {
  2063. // Don't move if stalling
  2064. if ( m_flEngineStallTime > gpGlobals->curtime )
  2065. return;
  2066. // Look at our inspection target if we have one
  2067. if ( GetEnemy() != NULL )
  2068. {
  2069. // Otherwise at our enemy
  2070. TurnHeadToTarget( flInterval, GetEnemy()->EyePosition() );
  2071. }
  2072. else if ( HaveInspectTarget() )
  2073. {
  2074. TurnHeadToTarget( flInterval, InspectTargetPosition() );
  2075. }
  2076. else
  2077. {
  2078. // Otherwise face our motion direction
  2079. TurnHeadToTarget( flInterval, vecMoveTarget );
  2080. }
  2081. // -------------------------------------
  2082. // Move towards our target
  2083. // -------------------------------------
  2084. float myAccel;
  2085. float myZAccel = 400.0f;
  2086. float myDecay = 0.15f;
  2087. Vector vecCurrentDir;
  2088. // Get the relationship between my current velocity and the way I want to be going.
  2089. vecCurrentDir = GetCurrentVelocity();
  2090. VectorNormalize( vecCurrentDir );
  2091. Vector targetDir = vecMoveTarget - GetAbsOrigin();
  2092. float flDist = VectorNormalize(targetDir);
  2093. float flDot;
  2094. flDot = DotProduct( targetDir, vecCurrentDir );
  2095. if( flDot > 0.25 )
  2096. {
  2097. // If my target is in front of me, my flight model is a bit more accurate.
  2098. myAccel = 250;
  2099. }
  2100. else
  2101. {
  2102. // Have a harder time correcting my course if I'm currently flying away from my target.
  2103. myAccel = 128;
  2104. }
  2105. if ( myAccel > flDist / flInterval )
  2106. {
  2107. myAccel = flDist / flInterval;
  2108. }
  2109. if ( myZAccel > flDist / flInterval )
  2110. {
  2111. myZAccel = flDist / flInterval;
  2112. }
  2113. MoveInDirection( flInterval, targetDir, myAccel, myZAccel, myDecay );
  2114. // calc relative banking targets
  2115. Vector forward, right, up;
  2116. GetVectors( &forward, &right, &up );
  2117. m_vCurrentBanking.x = targetDir.x;
  2118. m_vCurrentBanking.z = 120.0f * DotProduct( right, targetDir );
  2119. m_vCurrentBanking.y = 0;
  2120. float speedPerc = SimpleSplineRemapVal( GetCurrentVelocity().Length(), 0.0f, GetMaxSpeed(), 0.0f, 1.0f );
  2121. speedPerc = clamp( speedPerc, 0.0f, 1.0f );
  2122. m_vCurrentBanking *= speedPerc;
  2123. }
  2124. //-----------------------------------------------------------------------------
  2125. // Purpose:
  2126. // Input : flInterval -
  2127. //-----------------------------------------------------------------------------
  2128. void CNPC_CScanner::MoveToSpotlight( float flInterval )
  2129. {
  2130. if ( flInterval <= 0 )
  2131. return;
  2132. Vector vTargetPos;
  2133. if ( HaveInspectTarget() )
  2134. {
  2135. vTargetPos = InspectTargetPosition();
  2136. }
  2137. else if ( GetEnemy() != NULL )
  2138. {
  2139. vTargetPos = GetEnemyLKP();
  2140. }
  2141. else
  2142. {
  2143. return;
  2144. }
  2145. //float flDesiredDist = SCANNER_SPOTLIGHT_NEAR_DIST + ( ( SCANNER_SPOTLIGHT_FAR_DIST - SCANNER_SPOTLIGHT_NEAR_DIST ) / 2 );
  2146. float flIdealHeightDiff = SCANNER_SPOTLIGHT_NEAR_DIST;
  2147. if( IsEnemyPlayerInSuit() )
  2148. {
  2149. flIdealHeightDiff *= 0.5;
  2150. }
  2151. Vector idealPos = IdealGoalForMovement( vTargetPos, GetAbsOrigin(), GetGoalDistance(), flIdealHeightDiff );
  2152. MoveToTarget( flInterval, idealPos );
  2153. //TODO: Re-implement?
  2154. /*
  2155. // ------------------------------------------------
  2156. // Also keep my distance from other squad members
  2157. // unless I'm inspecting
  2158. // ------------------------------------------------
  2159. if (m_pSquad &&
  2160. gpGlobals->curtime > m_fInspectEndTime)
  2161. {
  2162. CBaseEntity* pNearest = m_pSquad->NearestSquadMember(this);
  2163. if (pNearest)
  2164. {
  2165. Vector vNearestDir = (pNearest->GetLocalOrigin() - GetLocalOrigin());
  2166. if (vNearestDir.Length() < SCANNER_SQUAD_FLY_DIST)
  2167. {
  2168. vNearestDir = pNearest->GetLocalOrigin() - GetLocalOrigin();
  2169. VectorNormalize(vNearestDir);
  2170. vFlyDirection -= 0.5*vNearestDir;
  2171. }
  2172. }
  2173. }
  2174. // ---------------------------------------------------------
  2175. // Add evasion if I have taken damage recently
  2176. // ---------------------------------------------------------
  2177. if ((m_flLastDamageTime + SCANNER_EVADE_TIME) > gpGlobals->curtime)
  2178. {
  2179. vFlyDirection = vFlyDirection + VelocityToEvade(GetEnemyCombatCharacterPointer());
  2180. }
  2181. */
  2182. }
  2183. //-----------------------------------------------------------------------------
  2184. // Purpose:
  2185. // Output : float
  2186. //-----------------------------------------------------------------------------
  2187. float CNPC_CScanner::GetGoalDistance( void )
  2188. {
  2189. if ( m_flGoalOverrideDistance != 0.0f )
  2190. return m_flGoalOverrideDistance;
  2191. switch ( m_nFlyMode )
  2192. {
  2193. case SCANNER_FLY_PHOTO:
  2194. return ( SCANNER_PHOTO_NEAR_DIST + ( ( SCANNER_PHOTO_FAR_DIST - SCANNER_PHOTO_NEAR_DIST ) / 2 ) );
  2195. break;
  2196. case SCANNER_FLY_SPOT:
  2197. {
  2198. float goalDist = ( SCANNER_SPOTLIGHT_NEAR_DIST + ( ( SCANNER_SPOTLIGHT_FAR_DIST - SCANNER_SPOTLIGHT_NEAR_DIST ) / 2 ) );
  2199. if( IsEnemyPlayerInSuit() )
  2200. {
  2201. goalDist *= 0.5;
  2202. }
  2203. return goalDist;
  2204. }
  2205. break;
  2206. case SCANNER_FLY_FOLLOW:
  2207. return ( SCANNER_FOLLOW_DIST );
  2208. break;
  2209. }
  2210. return BaseClass::GetGoalDistance();
  2211. }
  2212. //-----------------------------------------------------------------------------
  2213. // Purpose:
  2214. //-----------------------------------------------------------------------------
  2215. void CNPC_CScanner::MoveToPhotograph(float flInterval)
  2216. {
  2217. if ( HaveInspectTarget() == false )
  2218. return;
  2219. //float flDesiredDist = SCANNER_PHOTO_NEAR_DIST + ( ( SCANNER_PHOTO_FAR_DIST - SCANNER_PHOTO_NEAR_DIST ) / 2 );
  2220. Vector idealPos = IdealGoalForMovement( InspectTargetPosition(), GetAbsOrigin(), GetGoalDistance(), 32.0f );
  2221. MoveToTarget( flInterval, idealPos );
  2222. //FIXME: Re-implement?
  2223. /*
  2224. // ------------------------------------------------
  2225. // Also keep my distance from other squad members
  2226. // unless I'm inspecting
  2227. // ------------------------------------------------
  2228. if (m_pSquad &&
  2229. gpGlobals->curtime > m_fInspectEndTime)
  2230. {
  2231. CBaseEntity* pNearest = m_pSquad->NearestSquadMember(this);
  2232. if (pNearest)
  2233. {
  2234. Vector vNearestDir = (pNearest->GetLocalOrigin() - GetLocalOrigin());
  2235. if (vNearestDir.Length() < SCANNER_SQUAD_FLY_DIST)
  2236. {
  2237. vNearestDir = pNearest->GetLocalOrigin() - GetLocalOrigin();
  2238. VectorNormalize(vNearestDir);
  2239. vFlyDirection -= 0.5*vNearestDir;
  2240. }
  2241. }
  2242. }
  2243. */
  2244. }
  2245. //-----------------------------------------------------------------------------
  2246. // Purpose: This is a generic function (to be implemented by sub-classes) to
  2247. // handle specific interactions between different types of characters
  2248. // (For example the barnacle grabbing an NPC)
  2249. // Input : Constant for the type of interaction
  2250. // Output : true - if sub-class has a response for the interaction
  2251. // false - if sub-class has no response
  2252. //-----------------------------------------------------------------------------
  2253. bool CNPC_CScanner::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* pSourceEnt)
  2254. {
  2255. // TODO:: - doing this by just an interrupt contition would be a lot better!
  2256. if (interactionType == g_interactionScannerSupportEntity)
  2257. {
  2258. // Only accept help request if I'm not already busy
  2259. if (GetEnemy() == NULL && !HaveInspectTarget())
  2260. {
  2261. // Only accept if target is a reasonable distance away
  2262. CBaseEntity* pTarget = (CBaseEntity*)data;
  2263. float fTargetDist = (pTarget->GetLocalOrigin() - GetLocalOrigin()).Length();
  2264. if (fTargetDist < SCANNER_SQUAD_HELP_DIST)
  2265. {
  2266. float fInspectTime = (((CNPC_CScanner*)pSourceEnt)->m_fInspectEndTime - gpGlobals->curtime);
  2267. SetInspectTargetToEnt(pTarget,fInspectTime);
  2268. if (random->RandomInt(0,2)==0)
  2269. {
  2270. SetSchedule(SCHED_CSCANNER_PHOTOGRAPH_HOVER);
  2271. }
  2272. else
  2273. {
  2274. SetSchedule(SCHED_CSCANNER_SPOTLIGHT_HOVER);
  2275. }
  2276. return true;
  2277. }
  2278. }
  2279. }
  2280. else if (interactionType == g_interactionScannerSupportPosition)
  2281. {
  2282. // Only accept help request if I'm not already busy
  2283. if (GetEnemy() == NULL && !HaveInspectTarget())
  2284. {
  2285. // Only accept if target is a reasonable distance away
  2286. Vector vInspectPos;
  2287. vInspectPos.x = ((Vector *)data)->x;
  2288. vInspectPos.y = ((Vector *)data)->y;
  2289. vInspectPos.z = ((Vector *)data)->z;
  2290. float fTargetDist = (vInspectPos - GetLocalOrigin()).Length();
  2291. if (fTargetDist < SCANNER_SQUAD_HELP_DIST)
  2292. {
  2293. float fInspectTime = (((CNPC_CScanner*)pSourceEnt)->m_fInspectEndTime - gpGlobals->curtime);
  2294. SetInspectTargetToPos(vInspectPos,fInspectTime);
  2295. if (random->RandomInt(0,2)==0)
  2296. {
  2297. SetSchedule(SCHED_CSCANNER_PHOTOGRAPH_HOVER);
  2298. }
  2299. else
  2300. {
  2301. SetSchedule(SCHED_CSCANNER_SPOTLIGHT_HOVER);
  2302. }
  2303. return true;
  2304. }
  2305. }
  2306. }
  2307. return false;
  2308. }
  2309. //-----------------------------------------------------------------------------
  2310. // Purpose:
  2311. // Input : &inputdata -
  2312. //-----------------------------------------------------------------------------
  2313. void CNPC_CScanner::InputDisableSpotlight( inputdata_t &inputdata )
  2314. {
  2315. m_bNoLight = true;
  2316. }
  2317. //-----------------------------------------------------------------------------
  2318. // Purpose:
  2319. // Output : float
  2320. //-----------------------------------------------------------------------------
  2321. float CNPC_CScanner::GetHeadTurnRate( void )
  2322. {
  2323. if ( GetEnemy() )
  2324. return 800.0f;
  2325. if ( HaveInspectTarget() )
  2326. return 500.0f;
  2327. return BaseClass::GetHeadTurnRate();
  2328. }
  2329. //-----------------------------------------------------------------------------
  2330. // Purpose:
  2331. // Input : &inputdata -
  2332. //-----------------------------------------------------------------------------
  2333. void CNPC_CScanner::InputSetFollowTarget( inputdata_t &inputdata )
  2334. {
  2335. InspectTarget( inputdata, SCANNER_FLY_FOLLOW );
  2336. }
  2337. //-----------------------------------------------------------------------------
  2338. // Purpose:
  2339. // Input : &inputdata -
  2340. //-----------------------------------------------------------------------------
  2341. void CNPC_CScanner::InputClearFollowTarget( inputdata_t &inputdata )
  2342. {
  2343. SetInspectTargetToEnt( NULL, 0 );
  2344. m_nFlyMode = SCANNER_FLY_PATROL;
  2345. }
  2346. //-----------------------------------------------------------------------------
  2347. //
  2348. // Schedules
  2349. //
  2350. //-----------------------------------------------------------------------------
  2351. AI_BEGIN_CUSTOM_NPC( npc_cscanner, CNPC_CScanner )
  2352. DECLARE_TASK(TASK_CSCANNER_SET_FLY_PHOTO)
  2353. DECLARE_TASK(TASK_CSCANNER_SET_FLY_SPOT)
  2354. DECLARE_TASK(TASK_CSCANNER_PHOTOGRAPH)
  2355. DECLARE_TASK(TASK_CSCANNER_ATTACK_PRE_FLASH)
  2356. DECLARE_TASK(TASK_CSCANNER_ATTACK_FLASH)
  2357. DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_ON)
  2358. DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_WAIT)
  2359. DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_OFF)
  2360. DECLARE_TASK(TASK_CSCANNER_CLEAR_INSPECT_TARGET)
  2361. DECLARE_TASK(TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET)
  2362. DECLARE_CONDITION(COND_CSCANNER_HAVE_INSPECT_TARGET)
  2363. DECLARE_CONDITION(COND_CSCANNER_INSPECT_DONE)
  2364. DECLARE_CONDITION(COND_CSCANNER_CAN_PHOTOGRAPH)
  2365. DECLARE_CONDITION(COND_CSCANNER_SPOT_ON_TARGET)
  2366. DECLARE_ACTIVITY(ACT_SCANNER_SMALL_FLINCH_ALERT)
  2367. DECLARE_ACTIVITY(ACT_SCANNER_SMALL_FLINCH_COMBAT)
  2368. DECLARE_ACTIVITY(ACT_SCANNER_INSPECT)
  2369. DECLARE_ACTIVITY(ACT_SCANNER_WALK_ALERT)
  2370. DECLARE_ACTIVITY(ACT_SCANNER_WALK_COMBAT)
  2371. DECLARE_ACTIVITY(ACT_SCANNER_FLARE)
  2372. DECLARE_ACTIVITY(ACT_SCANNER_RETRACT)
  2373. DECLARE_ACTIVITY(ACT_SCANNER_FLARE_PRONGS)
  2374. DECLARE_ACTIVITY(ACT_SCANNER_RETRACT_PRONGS)
  2375. DECLARE_ACTIVITY(ACT_SCANNER_FLARE_START)
  2376. DECLARE_ANIMEVENT( AE_SCANNER_CLOSED )
  2377. DECLARE_INTERACTION(g_interactionScannerInspect)
  2378. DECLARE_INTERACTION(g_interactionScannerInspectBegin)
  2379. DECLARE_INTERACTION(g_interactionScannerInspectDone)
  2380. DECLARE_INTERACTION(g_interactionScannerInspectHandsUp)
  2381. DECLARE_INTERACTION(g_interactionScannerInspectShowArmband)
  2382. DECLARE_INTERACTION(g_interactionScannerSupportEntity)
  2383. DECLARE_INTERACTION(g_interactionScannerSupportPosition)
  2384. //=========================================================
  2385. // > SCHED_CSCANNER_PATROL
  2386. //=========================================================
  2387. DEFINE_SCHEDULE
  2388. (
  2389. SCHED_CSCANNER_PATROL,
  2390. " Tasks"
  2391. " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0"
  2392. " TASK_SCANNER_SET_FLY_PATROL 0"
  2393. " TASK_SET_TOLERANCE_DISTANCE 32"
  2394. " TASK_SET_ROUTE_SEARCH_TIME 5" // Spend 5 seconds trying to build a path if stuck
  2395. " TASK_GET_PATH_TO_RANDOM_NODE 2000"
  2396. " TASK_RUN_PATH 0"
  2397. " TASK_WAIT_FOR_MOVEMENT 0"
  2398. ""
  2399. " Interrupts"
  2400. " COND_GIVE_WAY"
  2401. " COND_NEW_ENEMY"
  2402. " COND_SEE_ENEMY"
  2403. " COND_SEE_FEAR"
  2404. " COND_HEAR_COMBAT"
  2405. " COND_HEAR_DANGER"
  2406. " COND_HEAR_PLAYER"
  2407. " COND_LIGHT_DAMAGE"
  2408. " COND_HEAVY_DAMAGE"
  2409. " COND_PROVOKED"
  2410. " COND_CSCANNER_HAVE_INSPECT_TARGET"
  2411. " COND_SCANNER_GRABBED_BY_PHYSCANNON"
  2412. )
  2413. //=========================================================
  2414. // > SCHED_CSCANNER_SPOTLIGHT_HOVER
  2415. //
  2416. // Hover above target entity, trying to get spotlight
  2417. // on my target
  2418. //=========================================================
  2419. DEFINE_SCHEDULE
  2420. (
  2421. SCHED_CSCANNER_SPOTLIGHT_HOVER,
  2422. " Tasks"
  2423. " TASK_CSCANNER_SET_FLY_SPOT 0"
  2424. " TASK_SET_ACTIVITY ACTIVITY:ACT_WALK "
  2425. " TASK_WAIT 1"
  2426. ""
  2427. " Interrupts"
  2428. " COND_CSCANNER_SPOT_ON_TARGET"
  2429. " COND_CSCANNER_INSPECT_DONE"
  2430. " COND_SCANNER_FLY_BLOCKED"
  2431. " COND_NEW_ENEMY"
  2432. " COND_SCANNER_GRABBED_BY_PHYSCANNON"
  2433. )
  2434. //=========================================================
  2435. // > SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS
  2436. //
  2437. // Inspect a position once spotlight is on it
  2438. //=========================================================
  2439. DEFINE_SCHEDULE
  2440. (
  2441. SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS,
  2442. " Tasks"
  2443. " TASK_CSCANNER_SET_FLY_SPOT 0"
  2444. " TASK_SET_ACTIVITY ACTIVITY:ACT_SCANNER_INSPECT"
  2445. " TASK_SPEAK_SENTENCE 3" // Curious sound
  2446. " TASK_WAIT 5"
  2447. " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0"
  2448. ""
  2449. " Interrupts"
  2450. " COND_CSCANNER_INSPECT_DONE"
  2451. " COND_HEAR_DANGER"
  2452. " COND_HEAR_COMBAT"
  2453. " COND_NEW_ENEMY"
  2454. " COND_SCANNER_GRABBED_BY_PHYSCANNON"
  2455. )
  2456. //=========================================================
  2457. // > SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT
  2458. //
  2459. // Inspect a citizen once spotlight is on it
  2460. //=========================================================
  2461. DEFINE_SCHEDULE
  2462. (
  2463. SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT,
  2464. " Tasks"
  2465. " TASK_CSCANNER_SET_FLY_SPOT 0"
  2466. " TASK_SET_ACTIVITY ACTIVITY:ACT_SCANNER_INSPECT"
  2467. " TASK_SPEAK_SENTENCE 0" // Stop!
  2468. " TASK_WAIT 1"
  2469. " TASK_CSCANNER_SPOT_INSPECT_ON 0"
  2470. " TASK_WAIT 2"
  2471. " TASK_SPEAK_SENTENCE 1" // Hands on head or Show Armband!
  2472. " TASK_WAIT 1"
  2473. " TASK_CSCANNER_SPOT_INSPECT_WAIT 0"
  2474. " TASK_WAIT 5"
  2475. " TASK_SPEAK_SENTENCE 2" // Free to go!
  2476. " TASK_WAIT 1"
  2477. " TASK_CSCANNER_SPOT_INSPECT_OFF 0"
  2478. " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0"
  2479. ""
  2480. " Interrupts"
  2481. " COND_NEW_ENEMY"
  2482. " COND_SCANNER_GRABBED_BY_PHYSCANNON"
  2483. )
  2484. //=========================================================
  2485. // > SCHED_CSCANNER_PHOTOGRAPH_HOVER
  2486. //=========================================================
  2487. DEFINE_SCHEDULE
  2488. (
  2489. SCHED_CSCANNER_PHOTOGRAPH_HOVER,
  2490. " Tasks"
  2491. " TASK_CSCANNER_SET_FLY_PHOTO 0"
  2492. " TASK_WAIT 2"
  2493. ""
  2494. " Interrupts"
  2495. " COND_CSCANNER_INSPECT_DONE"
  2496. " COND_CSCANNER_CAN_PHOTOGRAPH"
  2497. " COND_SCANNER_FLY_BLOCKED"
  2498. " COND_NEW_ENEMY"
  2499. " COND_SCANNER_GRABBED_BY_PHYSCANNON"
  2500. )
  2501. //=========================================================
  2502. // > SCHED_CSCANNER_PHOTOGRAPH
  2503. //=========================================================
  2504. DEFINE_SCHEDULE
  2505. (
  2506. SCHED_CSCANNER_PHOTOGRAPH,
  2507. " Tasks"
  2508. " TASK_CSCANNER_SET_FLY_PHOTO 0"
  2509. " TASK_CSCANNER_PHOTOGRAPH 0"
  2510. ""
  2511. " Interrupts"
  2512. " COND_CSCANNER_INSPECT_DONE"
  2513. " COND_NEW_ENEMY"
  2514. " COND_ENEMY_DEAD"
  2515. " COND_SCANNER_GRABBED_BY_PHYSCANNON"
  2516. )
  2517. //=========================================================
  2518. // > SCHED_CSCANNER_ATTACK_FLASH
  2519. //=========================================================
  2520. DEFINE_SCHEDULE
  2521. (
  2522. SCHED_CSCANNER_ATTACK_FLASH,
  2523. " Tasks"
  2524. " TASK_SCANNER_SET_FLY_ATTACK 0"
  2525. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2526. " TASK_CSCANNER_ATTACK_PRE_FLASH 0 "
  2527. " TASK_CSCANNER_ATTACK_FLASH 0"
  2528. " TASK_WAIT 0.5"
  2529. ""
  2530. " Interrupts"
  2531. " COND_NEW_ENEMY"
  2532. " COND_ENEMY_DEAD"
  2533. " COND_SCANNER_GRABBED_BY_PHYSCANNON"
  2534. )
  2535. //=========================================================
  2536. // > SCHED_CSCANNER_MOVE_TO_INSPECT
  2537. //=========================================================
  2538. DEFINE_SCHEDULE
  2539. (
  2540. SCHED_CSCANNER_MOVE_TO_INSPECT,
  2541. " Tasks"
  2542. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCANNER_PATROL"
  2543. " TASK_SET_TOLERANCE_DISTANCE 128"
  2544. " TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET 0"
  2545. " TASK_RUN_PATH 0"
  2546. " TASK_WAIT_FOR_MOVEMENT 0"
  2547. ""
  2548. " Interrupts"
  2549. " COND_SCANNER_FLY_CLEAR"
  2550. " COND_NEW_ENEMY"
  2551. " COND_SCANNER_GRABBED_BY_PHYSCANNON"
  2552. )
  2553. AI_END_CUSTOM_NPC()
  2554. //-----------------------------------------------------------------------------
  2555. // Claw Scanner
  2556. //
  2557. // Scanner that always spawns as a claw scanner
  2558. //-----------------------------------------------------------------------------
  2559. class CNPC_ClawScanner : public CNPC_CScanner
  2560. {
  2561. DECLARE_CLASS( CNPC_ClawScanner, CNPC_CScanner );
  2562. public:
  2563. CNPC_ClawScanner();
  2564. DECLARE_DATADESC();
  2565. };
  2566. BEGIN_DATADESC( CNPC_ClawScanner )
  2567. END_DATADESC()
  2568. LINK_ENTITY_TO_CLASS(npc_clawscanner, CNPC_ClawScanner);
  2569. //-----------------------------------------------------------------------------
  2570. // Purpose:
  2571. //-----------------------------------------------------------------------------
  2572. CNPC_ClawScanner::CNPC_ClawScanner()
  2573. {
  2574. // override our superclass's setting
  2575. BecomeClawScanner();
  2576. }