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.

1126 lines
30 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_basenpc.h"
  9. #include "ai_senses.h"
  10. #include "ai_memory.h"
  11. #include "engine/IEngineSound.h"
  12. #include "ammodef.h"
  13. #include "Sprite.h"
  14. #include "hl2/hl2_player.h"
  15. #include "soundenvelope.h"
  16. #include "explode.h"
  17. #include "IEffects.h"
  18. #include "animation.h"
  19. #include "basehlcombatweapon_shared.h"
  20. #include "iservervehicle.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. //Debug visualization
  24. ConVar g_debug_turret_ceiling( "g_debug_turret_ceiling", "0" );
  25. #define CEILING_TURRET_MODEL "models/combine_turrets/ceiling_turret.mdl"
  26. #define CEILING_TURRET_GLOW_SPRITE "sprites/glow1.vmt"
  27. /* // we now inherit these from the ai_basenpc baseclass
  28. #define CEILING_TURRET_BC_YAW "aim_yaw"
  29. #define CEILING_TURRET_BC_PITCH "aim_pitch"
  30. */
  31. #define CEILING_TURRET_RANGE 1500
  32. #define CEILING_TURRET_SPREAD VECTOR_CONE_2DEGREES
  33. #define CEILING_TURRET_MAX_WAIT 5
  34. #define CEILING_TURRET_PING_TIME 1.0f //LPB!!
  35. #define CEILING_TURRET_VOICE_PITCH_LOW 45
  36. #define CEILING_TURRET_VOICE_PITCH_HIGH 100
  37. //Aiming variables
  38. #define CEILING_TURRET_MAX_NOHARM_PERIOD 0.0f
  39. #define CEILING_TURRET_MAX_GRACE_PERIOD 3.0f
  40. //Spawnflags
  41. #define SF_CEILING_TURRET_AUTOACTIVATE 0x00000020
  42. #define SF_CEILING_TURRET_STARTINACTIVE 0x00000040
  43. #define SF_CEILING_TURRET_NEVERRETIRE 0x00000080
  44. #define SF_CEILING_TURRET_OUT_OF_AMMO 0x00000100
  45. //Heights
  46. #define CEILING_TURRET_RETRACT_HEIGHT 24
  47. #define CEILING_TURRET_DEPLOY_HEIGHT 64
  48. //Activities
  49. int ACT_CEILING_TURRET_OPEN;
  50. int ACT_CEILING_TURRET_CLOSE;
  51. int ACT_CEILING_TURRET_OPEN_IDLE;
  52. int ACT_CEILING_TURRET_CLOSED_IDLE;
  53. int ACT_CEILING_TURRET_FIRE;
  54. int ACT_CEILING_TURRET_DRYFIRE;
  55. //Turret states
  56. enum turretState_e
  57. {
  58. TURRET_SEARCHING,
  59. TURRET_AUTO_SEARCHING,
  60. TURRET_ACTIVE,
  61. TURRET_DEPLOYING,
  62. TURRET_RETIRING,
  63. TURRET_DEAD,
  64. };
  65. //Eye states
  66. enum eyeState_t
  67. {
  68. TURRET_EYE_SEE_TARGET, //Sees the target, bright and big
  69. TURRET_EYE_SEEKING_TARGET, //Looking for a target, blinking (bright)
  70. TURRET_EYE_DORMANT, //Not active
  71. TURRET_EYE_DEAD, //Completely invisible
  72. TURRET_EYE_DISABLED, //Turned off, must be reactivated before it'll deploy again (completely invisible)
  73. };
  74. //
  75. // Ceiling Turret
  76. //
  77. class CNPC_CeilingTurret : public CAI_BaseNPC
  78. {
  79. DECLARE_CLASS( CNPC_CeilingTurret, CAI_BaseNPC );
  80. public:
  81. CNPC_CeilingTurret( void );
  82. ~CNPC_CeilingTurret( void );
  83. void Precache( void );
  84. void Spawn( void );
  85. // Think functions
  86. void Retire( void );
  87. void Deploy( void );
  88. void ActiveThink( void );
  89. void SearchThink( void );
  90. void AutoSearchThink( void );
  91. void DeathThink( void );
  92. // Inputs
  93. void InputToggle( inputdata_t &inputdata );
  94. void InputEnable( inputdata_t &inputdata );
  95. void InputDisable( inputdata_t &inputdata );
  96. void SetLastSightTime();
  97. float MaxYawSpeed( void );
  98. int OnTakeDamage( const CTakeDamageInfo &inputInfo );
  99. virtual bool CanBeAnEnemyOf( CBaseEntity *pEnemy );
  100. Class_T Classify( void )
  101. {
  102. if( m_bEnabled )
  103. return CLASS_COMBINE;
  104. return CLASS_NONE;
  105. }
  106. bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
  107. Vector EyeOffset( Activity nActivity )
  108. {
  109. Vector vecEyeOffset(0,0,-64);
  110. GetEyePosition( GetModelPtr(), vecEyeOffset );
  111. return vecEyeOffset;
  112. }
  113. Vector EyePosition( void )
  114. {
  115. return GetAbsOrigin() + EyeOffset(GetActivity());
  116. }
  117. Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget )
  118. {
  119. return VECTOR_CONE_5DEGREES * ((CBaseHLCombatWeapon::GetDefaultProficiencyValues())[ WEAPON_PROFICIENCY_PERFECT ].spreadscale);
  120. }
  121. protected:
  122. bool PreThink( turretState_e state );
  123. void Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy );
  124. void SetEyeState( eyeState_t state );
  125. void Ping( void );
  126. void Toggle( void );
  127. void Enable( void );
  128. void Disable( void );
  129. void SpinUp( void );
  130. void SpinDown( void );
  131. void SetHeight( float height );
  132. bool UpdateFacing( void );
  133. int m_iAmmoType;
  134. int m_iMinHealthDmg;
  135. bool m_bAutoStart;
  136. bool m_bActive; //Denotes the turret is deployed and looking for targets
  137. bool m_bBlinkState;
  138. bool m_bEnabled; //Denotes whether the turret is able to deploy or not
  139. float m_flShotTime;
  140. float m_flLastSight;
  141. float m_flPingTime;
  142. QAngle m_vecGoalAngles;
  143. CSprite *m_pEyeGlow;
  144. COutputEvent m_OnDeploy;
  145. COutputEvent m_OnRetire;
  146. COutputEvent m_OnTipped;
  147. DECLARE_DATADESC();
  148. };
  149. //Datatable
  150. BEGIN_DATADESC( CNPC_CeilingTurret )
  151. DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
  152. DEFINE_KEYFIELD( m_iMinHealthDmg, FIELD_INTEGER, "minhealthdmg" ),
  153. DEFINE_FIELD( m_bAutoStart, FIELD_BOOLEAN ),
  154. DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
  155. DEFINE_FIELD( m_bBlinkState, FIELD_BOOLEAN ),
  156. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  157. DEFINE_FIELD( m_flShotTime, FIELD_TIME ),
  158. DEFINE_FIELD( m_flLastSight, FIELD_TIME ),
  159. DEFINE_FIELD( m_flPingTime, FIELD_TIME ),
  160. DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ),
  161. DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ),
  162. DEFINE_THINKFUNC( Retire ),
  163. DEFINE_THINKFUNC( Deploy ),
  164. DEFINE_THINKFUNC( ActiveThink ),
  165. DEFINE_THINKFUNC( SearchThink ),
  166. DEFINE_THINKFUNC( AutoSearchThink ),
  167. DEFINE_THINKFUNC( DeathThink ),
  168. // Inputs
  169. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  170. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  171. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  172. DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ),
  173. DEFINE_OUTPUT( m_OnRetire, "OnRetire" ),
  174. DEFINE_OUTPUT( m_OnTipped, "OnTipped" ),
  175. END_DATADESC()
  176. LINK_ENTITY_TO_CLASS( npc_turret_ceiling, CNPC_CeilingTurret );
  177. //-----------------------------------------------------------------------------
  178. // Constructor
  179. //-----------------------------------------------------------------------------
  180. CNPC_CeilingTurret::CNPC_CeilingTurret( void )
  181. {
  182. m_bActive = false;
  183. m_pEyeGlow = NULL;
  184. m_iAmmoType = -1;
  185. m_iMinHealthDmg = 0;
  186. m_bAutoStart = false;
  187. m_flPingTime = 0;
  188. m_flShotTime = 0;
  189. m_flLastSight = 0;
  190. m_bBlinkState = false;
  191. m_bEnabled = false;
  192. m_vecGoalAngles.Init();
  193. }
  194. CNPC_CeilingTurret::~CNPC_CeilingTurret( void )
  195. {
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose: Precache
  199. //-----------------------------------------------------------------------------
  200. void CNPC_CeilingTurret::Precache( void )
  201. {
  202. PrecacheModel( CEILING_TURRET_MODEL );
  203. PrecacheModel( CEILING_TURRET_GLOW_SPRITE );
  204. // Activities
  205. ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN );
  206. ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSE );
  207. ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSED_IDLE );
  208. ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN_IDLE );
  209. ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_FIRE );
  210. ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_DRYFIRE );
  211. PrecacheScriptSound( "NPC_CeilingTurret.Retire" );
  212. PrecacheScriptSound( "NPC_CeilingTurret.Deploy" );
  213. PrecacheScriptSound( "NPC_CeilingTurret.Move" );
  214. PrecacheScriptSound( "NPC_CeilingTurret.Active" );
  215. PrecacheScriptSound( "NPC_CeilingTurret.Alert" );
  216. PrecacheScriptSound( "NPC_CeilingTurret.ShotSounds" );
  217. PrecacheScriptSound( "NPC_CeilingTurret.Ping" );
  218. PrecacheScriptSound( "NPC_CeilingTurret.Die" );
  219. PrecacheScriptSound( "NPC_FloorTurret.DryFire" );
  220. BaseClass::Precache();
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose: Spawn the entity
  224. //-----------------------------------------------------------------------------
  225. void CNPC_CeilingTurret::Spawn( void )
  226. {
  227. Precache();
  228. SetModel( CEILING_TURRET_MODEL );
  229. BaseClass::Spawn();
  230. m_HackedGunPos = Vector( 0, 0, 12.75 );
  231. SetViewOffset( EyeOffset( ACT_IDLE ) );
  232. m_flFieldOfView = 0.0f;
  233. m_takedamage = DAMAGE_YES;
  234. m_iHealth = 1000;
  235. m_bloodColor = BLOOD_COLOR_MECH;
  236. SetSolid( SOLID_BBOX );
  237. AddSolidFlags( FSOLID_NOT_STANDABLE );
  238. SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
  239. AddFlag( FL_AIMTARGET );
  240. AddEFlags( EFL_NO_DISSOLVE );
  241. SetPoseParameter( m_poseAim_Yaw, 0 );
  242. SetPoseParameter( m_poseAim_Pitch, 0 );
  243. m_iAmmoType = GetAmmoDef()->Index( "AR2" );
  244. //Create our eye sprite
  245. m_pEyeGlow = CSprite::SpriteCreate( CEILING_TURRET_GLOW_SPRITE, GetLocalOrigin(), false );
  246. m_pEyeGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation );
  247. m_pEyeGlow->SetAttachment( this, 2 );
  248. //Set our autostart state
  249. m_bAutoStart = !!( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE );
  250. m_bEnabled = ( ( m_spawnflags & SF_CEILING_TURRET_STARTINACTIVE ) == false );
  251. //Do we start active?
  252. if ( m_bAutoStart && m_bEnabled )
  253. {
  254. SetThink( &CNPC_CeilingTurret::AutoSearchThink );
  255. SetEyeState( TURRET_EYE_DORMANT );
  256. }
  257. else
  258. {
  259. SetEyeState( TURRET_EYE_DISABLED );
  260. }
  261. //Stagger our starting times
  262. SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ) );
  263. // Don't allow us to skip animation setup because our attachments are critical to us!
  264. SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose:
  268. //-----------------------------------------------------------------------------
  269. int CNPC_CeilingTurret::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  270. {
  271. if ( !m_takedamage )
  272. return 0;
  273. CTakeDamageInfo info = inputInfo;
  274. if ( m_bActive == false )
  275. info.ScaleDamage( 0.1f );
  276. // If attacker can't do at least the min required damage to us, don't take any damage from them
  277. if ( info.GetDamage() < m_iMinHealthDmg )
  278. return 0;
  279. m_iHealth -= info.GetDamage();
  280. if ( m_iHealth <= 0 )
  281. {
  282. m_iHealth = 0;
  283. m_takedamage = DAMAGE_NO;
  284. RemoveFlag( FL_NPC ); // why are they set in the first place???
  285. //FIXME: This needs to throw a ragdoll gib or something other than animating the retraction -- jdw
  286. ExplosionCreate( GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false );
  287. SetThink( &CNPC_CeilingTurret::DeathThink );
  288. StopSound( "NPC_CeilingTurret.Alert" );
  289. m_OnDamaged.FireOutput( info.GetInflictor(), this );
  290. SetNextThink( gpGlobals->curtime + 0.1f );
  291. return 0;
  292. }
  293. return 1;
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose: Retract and stop attacking
  297. //-----------------------------------------------------------------------------
  298. void CNPC_CeilingTurret::Retire( void )
  299. {
  300. if ( PreThink( TURRET_RETIRING ) )
  301. return;
  302. //Level out the turret
  303. m_vecGoalAngles = GetAbsAngles();
  304. SetNextThink( gpGlobals->curtime );
  305. //Set ourselves to close
  306. if ( GetActivity() != ACT_CEILING_TURRET_CLOSE )
  307. {
  308. //Set our visible state to dormant
  309. SetEyeState( TURRET_EYE_DORMANT );
  310. SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
  311. //If we're done moving to our desired facing, close up
  312. if ( UpdateFacing() == false )
  313. {
  314. SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE );
  315. EmitSound( "NPC_CeilingTurret.Retire" );
  316. //Notify of the retraction
  317. m_OnRetire.FireOutput( NULL, this );
  318. }
  319. }
  320. else if ( IsActivityFinished() )
  321. {
  322. SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
  323. m_bActive = false;
  324. m_flLastSight = 0;
  325. SetActivity( (Activity) ACT_CEILING_TURRET_CLOSED_IDLE );
  326. //Go back to auto searching
  327. if ( m_bAutoStart )
  328. {
  329. SetThink( &CNPC_CeilingTurret::AutoSearchThink );
  330. SetNextThink( gpGlobals->curtime + 0.05f );
  331. }
  332. else
  333. {
  334. //Set our visible state to dormant
  335. SetEyeState( TURRET_EYE_DISABLED );
  336. SetThink( &CNPC_CeilingTurret::SUB_DoNothing );
  337. }
  338. }
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Purpose: Deploy and start attacking
  342. //-----------------------------------------------------------------------------
  343. void CNPC_CeilingTurret::Deploy( void )
  344. {
  345. if ( PreThink( TURRET_DEPLOYING ) )
  346. return;
  347. m_vecGoalAngles = GetAbsAngles();
  348. SetNextThink( gpGlobals->curtime );
  349. //Show we've seen a target
  350. SetEyeState( TURRET_EYE_SEE_TARGET );
  351. //Open if we're not already
  352. if ( GetActivity() != ACT_CEILING_TURRET_OPEN )
  353. {
  354. m_bActive = true;
  355. SetActivity( (Activity) ACT_CEILING_TURRET_OPEN );
  356. EmitSound( "NPC_CeilingTurret.Deploy" );
  357. //Notify we're deploying
  358. m_OnDeploy.FireOutput( NULL, this );
  359. }
  360. //If we're done, then start searching
  361. if ( IsActivityFinished() )
  362. {
  363. SetHeight( CEILING_TURRET_DEPLOY_HEIGHT );
  364. SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
  365. m_flShotTime = gpGlobals->curtime + 1.0f;
  366. m_flPlaybackRate = 0;
  367. SetThink( &CNPC_CeilingTurret::SearchThink );
  368. EmitSound( "NPC_CeilingTurret.Move" );
  369. }
  370. SetLastSightTime();
  371. }
  372. //-----------------------------------------------------------------------------
  373. //-----------------------------------------------------------------------------
  374. void CNPC_CeilingTurret::SetLastSightTime()
  375. {
  376. if( HasSpawnFlags( SF_CEILING_TURRET_NEVERRETIRE ) )
  377. {
  378. m_flLastSight = FLT_MAX;
  379. }
  380. else
  381. {
  382. m_flLastSight = gpGlobals->curtime + CEILING_TURRET_MAX_WAIT;
  383. }
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: Returns the speed at which the turret can face a target
  387. //-----------------------------------------------------------------------------
  388. float CNPC_CeilingTurret::MaxYawSpeed( void )
  389. {
  390. //TODO: Scale by difficulty?
  391. return 360.0f;
  392. }
  393. //-----------------------------------------------------------------------------
  394. // Purpose: Causes the turret to face its desired angles
  395. //-----------------------------------------------------------------------------
  396. bool CNPC_CeilingTurret::UpdateFacing( void )
  397. {
  398. bool bMoved = false;
  399. matrix3x4_t localToWorld;
  400. GetAttachment( LookupAttachment( "eyes" ), localToWorld );
  401. Vector vecGoalDir;
  402. AngleVectors( m_vecGoalAngles, &vecGoalDir );
  403. Vector vecGoalLocalDir;
  404. VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir );
  405. if ( g_debug_turret_ceiling.GetBool() )
  406. {
  407. Vector vecMuzzle, vecMuzzleDir;
  408. QAngle vecMuzzleAng;
  409. GetAttachment( "eyes", vecMuzzle, vecMuzzleAng );
  410. AngleVectors( vecMuzzleAng, &vecMuzzleDir );
  411. NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 );
  412. NDebugOverlay::Cross3D( vecMuzzle+(vecMuzzleDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 );
  413. NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecMuzzleDir*256), 255, 255, 0, false, 0.05 );
  414. NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 );
  415. NDebugOverlay::Cross3D( vecMuzzle+(vecGoalDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 );
  416. NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecGoalDir*256), 255, 0, 0, false, 0.05 );
  417. }
  418. QAngle vecGoalLocalAngles;
  419. VectorAngles( vecGoalLocalDir, vecGoalLocalAngles );
  420. // Update pitch
  421. float flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed() ) );
  422. SetPoseParameter( m_poseAim_Pitch, GetPoseParameter( m_poseAim_Pitch ) + ( flDiff / 1.5f ) );
  423. if ( fabs( flDiff ) > 0.1f )
  424. {
  425. bMoved = true;
  426. }
  427. // Update yaw
  428. flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed() ) );
  429. SetPoseParameter( m_poseAim_Yaw, GetPoseParameter( m_poseAim_Yaw ) + ( flDiff / 1.5f ) );
  430. if ( fabs( flDiff ) > 0.1f )
  431. {
  432. bMoved = true;
  433. }
  434. InvalidateBoneCache();
  435. return bMoved;
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose:
  439. // Input : *pEntity -
  440. // Output : Returns true on success, false on failure.
  441. //-----------------------------------------------------------------------------
  442. bool CNPC_CeilingTurret::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
  443. {
  444. CBaseEntity *pHitEntity = NULL;
  445. if ( BaseClass::FVisible( pEntity, traceMask, &pHitEntity ) )
  446. return true;
  447. // If we hit something that's okay to hit anyway, still fire
  448. if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() )
  449. {
  450. if (IRelationType(pHitEntity) == D_HT)
  451. return true;
  452. }
  453. if (ppBlocker)
  454. {
  455. *ppBlocker = pHitEntity;
  456. }
  457. return false;
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose: Allows the turret to fire on targets if they're visible
  461. //-----------------------------------------------------------------------------
  462. void CNPC_CeilingTurret::ActiveThink( void )
  463. {
  464. //Allow descended classes a chance to do something before the think function
  465. if ( PreThink( TURRET_ACTIVE ) )
  466. return;
  467. //Update our think time
  468. SetNextThink( gpGlobals->curtime + 0.1f );
  469. //If we've become inactive, go back to searching
  470. if ( ( m_bActive == false ) || ( GetEnemy() == NULL ) )
  471. {
  472. SetEnemy( NULL );
  473. SetLastSightTime();
  474. SetThink( &CNPC_CeilingTurret::SearchThink );
  475. m_vecGoalAngles = GetAbsAngles();
  476. return;
  477. }
  478. //Get our shot positions
  479. Vector vecMid = EyePosition();
  480. Vector vecMidEnemy = GetEnemy()->GetAbsOrigin();
  481. //Store off our last seen location
  482. UpdateEnemyMemory( GetEnemy(), vecMidEnemy );
  483. //Look for our current enemy
  484. bool bEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() ) && GetEnemy()->IsAlive();
  485. //Calculate dir and dist to enemy
  486. Vector vecDirToEnemy = vecMidEnemy - vecMid;
  487. float flDistToEnemy = VectorNormalize( vecDirToEnemy );
  488. //We want to look at the enemy's eyes so we don't jitter
  489. Vector vecDirToEnemyEyes = GetEnemy()->WorldSpaceCenter() - vecMid;
  490. VectorNormalize( vecDirToEnemyEyes );
  491. QAngle vecAnglesToEnemy;
  492. VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy );
  493. //Draw debug info
  494. if ( g_debug_turret_ceiling.GetBool() )
  495. {
  496. NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  497. NDebugOverlay::Cross3D( GetEnemy()->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  498. NDebugOverlay::Line( vecMid, GetEnemy()->WorldSpaceCenter(), 0, 255, 0, false, 0.05 );
  499. NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  500. NDebugOverlay::Cross3D( vecMidEnemy, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  501. NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.05f );
  502. }
  503. //Current enemy is not visible
  504. if ( ( bEnemyVisible == false ) || ( flDistToEnemy > CEILING_TURRET_RANGE ))
  505. {
  506. if ( m_flLastSight )
  507. {
  508. m_flLastSight = gpGlobals->curtime + 0.5f;
  509. }
  510. else if ( gpGlobals->curtime > m_flLastSight )
  511. {
  512. // Should we look for a new target?
  513. ClearEnemyMemory();
  514. SetEnemy( NULL );
  515. SetLastSightTime();
  516. SetThink( &CNPC_CeilingTurret::SearchThink );
  517. m_vecGoalAngles = GetAbsAngles();
  518. SpinDown();
  519. return;
  520. }
  521. bEnemyVisible = false;
  522. }
  523. Vector vecMuzzle, vecMuzzleDir;
  524. QAngle vecMuzzleAng;
  525. GetAttachment( "eyes", vecMuzzle, vecMuzzleAng );
  526. AngleVectors( vecMuzzleAng, &vecMuzzleDir );
  527. if ( m_flShotTime < gpGlobals->curtime )
  528. {
  529. //Fire the gun
  530. if ( DotProduct( vecDirToEnemy, vecMuzzleDir ) >= 0.9848 ) // 10 degree slop
  531. {
  532. if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
  533. {
  534. SetActivity( (Activity) ACT_CEILING_TURRET_DRYFIRE );
  535. }
  536. else
  537. {
  538. SetActivity( (Activity) ACT_CEILING_TURRET_FIRE );
  539. }
  540. //Fire the weapon
  541. Shoot( vecMuzzle, vecMuzzleDir );
  542. }
  543. }
  544. else
  545. {
  546. SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
  547. }
  548. //If we can see our enemy, face it
  549. if ( bEnemyVisible )
  550. {
  551. m_vecGoalAngles.y = vecAnglesToEnemy.y;
  552. m_vecGoalAngles.x = vecAnglesToEnemy.x;
  553. }
  554. //Turn to face
  555. UpdateFacing();
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Purpose: Target doesn't exist or has eluded us, so search for one
  559. //-----------------------------------------------------------------------------
  560. void CNPC_CeilingTurret::SearchThink( void )
  561. {
  562. //Allow descended classes a chance to do something before the think function
  563. if ( PreThink( TURRET_SEARCHING ) )
  564. return;
  565. SetNextThink( gpGlobals->curtime + 0.05f );
  566. SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
  567. //If our enemy has died, pick a new enemy
  568. if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
  569. {
  570. SetEnemy( NULL );
  571. }
  572. //Acquire the target
  573. if ( GetEnemy() == NULL )
  574. {
  575. GetSenses()->Look( CEILING_TURRET_RANGE );
  576. CBaseEntity *pEnemy = BestEnemy();
  577. if ( pEnemy )
  578. {
  579. SetEnemy( pEnemy );
  580. }
  581. }
  582. //If we've found a target, spin up the barrel and start to attack
  583. if ( GetEnemy() != NULL )
  584. {
  585. //Give players a grace period
  586. if ( GetEnemy()->IsPlayer() )
  587. {
  588. m_flShotTime = gpGlobals->curtime + 0.5f;
  589. }
  590. else
  591. {
  592. m_flShotTime = gpGlobals->curtime + 0.1f;
  593. }
  594. m_flLastSight = 0;
  595. SetThink( &CNPC_CeilingTurret::ActiveThink );
  596. SetEyeState( TURRET_EYE_SEE_TARGET );
  597. SpinUp();
  598. EmitSound( "NPC_CeilingTurret.Active" );
  599. return;
  600. }
  601. //Are we out of time and need to retract?
  602. if ( gpGlobals->curtime > m_flLastSight )
  603. {
  604. //Before we retrace, make sure that we are spun down.
  605. m_flLastSight = 0;
  606. SetThink( &CNPC_CeilingTurret::Retire );
  607. return;
  608. }
  609. //Display that we're scanning
  610. m_vecGoalAngles.x = 15.0f;
  611. m_vecGoalAngles.y = GetAbsAngles().y + ( sin( gpGlobals->curtime * 2.0f ) * 45.0f );
  612. //Turn and ping
  613. UpdateFacing();
  614. Ping();
  615. }
  616. //-----------------------------------------------------------------------------
  617. // Purpose: Watch for a target to wander into our view
  618. //-----------------------------------------------------------------------------
  619. void CNPC_CeilingTurret::AutoSearchThink( void )
  620. {
  621. //Allow descended classes a chance to do something before the think function
  622. if ( PreThink( TURRET_AUTO_SEARCHING ) )
  623. return;
  624. //Spread out our thinking
  625. SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2f, 0.4f ) );
  626. //If the enemy is dead, find a new one
  627. if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
  628. {
  629. SetEnemy( NULL );
  630. }
  631. //Acquire Target
  632. if ( GetEnemy() == NULL )
  633. {
  634. GetSenses()->Look( CEILING_TURRET_RANGE );
  635. SetEnemy( BestEnemy() );
  636. }
  637. //Deploy if we've got an active target
  638. if ( GetEnemy() != NULL )
  639. {
  640. SetThink( &CNPC_CeilingTurret::Deploy );
  641. EmitSound( "NPC_CeilingTurret.Alert" );
  642. }
  643. }
  644. //-----------------------------------------------------------------------------
  645. // Purpose: Fire!
  646. //-----------------------------------------------------------------------------
  647. void CNPC_CeilingTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy )
  648. {
  649. if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
  650. {
  651. EmitSound( "NPC_FloorTurret.DryFire");
  652. EmitSound( "NPC_CeilingTurret.Activate" );
  653. if ( RandomFloat( 0, 1 ) > 0.7 )
  654. {
  655. m_flShotTime = gpGlobals->curtime + random->RandomFloat( 0.5, 1.5 );
  656. }
  657. else
  658. {
  659. m_flShotTime = gpGlobals->curtime;
  660. }
  661. return;
  662. }
  663. FireBulletsInfo_t info;
  664. if ( GetEnemy() != NULL )
  665. {
  666. Vector vecDir = GetActualShootTrajectory( vecSrc );
  667. info.m_vecSrc = vecSrc;
  668. info.m_vecDirShooting = vecDir;
  669. info.m_iTracerFreq = 1;
  670. info.m_iShots = 1;
  671. info.m_pAttacker = this;
  672. info.m_vecSpread = VECTOR_CONE_PRECALCULATED;
  673. info.m_flDistance = MAX_COORD_RANGE;
  674. info.m_iAmmoType = m_iAmmoType;
  675. }
  676. else
  677. {
  678. // Just shoot where you're facing!
  679. Vector vecMuzzle, vecMuzzleDir;
  680. QAngle vecMuzzleAng;
  681. info.m_vecSrc = vecSrc;
  682. info.m_vecDirShooting = vecDirToEnemy;
  683. info.m_iTracerFreq = 1;
  684. info.m_iShots = 1;
  685. info.m_pAttacker = this;
  686. info.m_vecSpread = GetAttackSpread( NULL, NULL );
  687. info.m_flDistance = MAX_COORD_RANGE;
  688. info.m_iAmmoType = m_iAmmoType;
  689. }
  690. FireBullets( info );
  691. EmitSound( "NPC_CeilingTurret.ShotSounds" );
  692. DoMuzzleFlash();
  693. }
  694. //-----------------------------------------------------------------------------
  695. // Purpose: Allows a generic think function before the others are called
  696. // Input : state - which state the turret is currently in
  697. //-----------------------------------------------------------------------------
  698. bool CNPC_CeilingTurret::PreThink( turretState_e state )
  699. {
  700. CheckPVSCondition();
  701. //Animate
  702. StudioFrameAdvance();
  703. //Do not interrupt current think function
  704. return false;
  705. }
  706. //-----------------------------------------------------------------------------
  707. // Purpose: Sets the state of the glowing eye attached to the turret
  708. // Input : state - state the eye should be in
  709. //-----------------------------------------------------------------------------
  710. void CNPC_CeilingTurret::SetEyeState( eyeState_t state )
  711. {
  712. //Must have a valid eye to affect
  713. if ( m_pEyeGlow == NULL )
  714. return;
  715. //Set the state
  716. switch( state )
  717. {
  718. default:
  719. case TURRET_EYE_SEE_TARGET: //Fade in and scale up
  720. m_pEyeGlow->SetColor( 255, 0, 0 );
  721. m_pEyeGlow->SetBrightness( 164, 0.1f );
  722. m_pEyeGlow->SetScale( 0.4f, 0.1f );
  723. break;
  724. case TURRET_EYE_SEEKING_TARGET: //Ping-pongs
  725. //Toggle our state
  726. m_bBlinkState = !m_bBlinkState;
  727. m_pEyeGlow->SetColor( 255, 128, 0 );
  728. if ( m_bBlinkState )
  729. {
  730. //Fade up and scale up
  731. m_pEyeGlow->SetScale( 0.25f, 0.1f );
  732. m_pEyeGlow->SetBrightness( 164, 0.1f );
  733. }
  734. else
  735. {
  736. //Fade down and scale down
  737. m_pEyeGlow->SetScale( 0.2f, 0.1f );
  738. m_pEyeGlow->SetBrightness( 64, 0.1f );
  739. }
  740. break;
  741. case TURRET_EYE_DORMANT: //Fade out and scale down
  742. m_pEyeGlow->SetColor( 0, 255, 0 );
  743. m_pEyeGlow->SetScale( 0.1f, 0.5f );
  744. m_pEyeGlow->SetBrightness( 64, 0.5f );
  745. break;
  746. case TURRET_EYE_DEAD: //Fade out slowly
  747. m_pEyeGlow->SetColor( 255, 0, 0 );
  748. m_pEyeGlow->SetScale( 0.1f, 3.0f );
  749. m_pEyeGlow->SetBrightness( 0, 3.0f );
  750. break;
  751. case TURRET_EYE_DISABLED:
  752. m_pEyeGlow->SetColor( 0, 255, 0 );
  753. m_pEyeGlow->SetScale( 0.1f, 1.0f );
  754. m_pEyeGlow->SetBrightness( 0, 1.0f );
  755. break;
  756. }
  757. }
  758. //-----------------------------------------------------------------------------
  759. // Purpose: Make a pinging noise so the player knows where we are
  760. //-----------------------------------------------------------------------------
  761. void CNPC_CeilingTurret::Ping( void )
  762. {
  763. //See if it's time to ping again
  764. if ( m_flPingTime > gpGlobals->curtime )
  765. return;
  766. //Ping!
  767. EmitSound( "NPC_CeilingTurret.Ping" );
  768. SetEyeState( TURRET_EYE_SEEKING_TARGET );
  769. m_flPingTime = gpGlobals->curtime + CEILING_TURRET_PING_TIME;
  770. }
  771. //-----------------------------------------------------------------------------
  772. // Purpose: Toggle the turret's state
  773. //-----------------------------------------------------------------------------
  774. void CNPC_CeilingTurret::Toggle( void )
  775. {
  776. //Toggle the state
  777. if ( m_bEnabled )
  778. {
  779. Disable();
  780. }
  781. else
  782. {
  783. Enable();
  784. }
  785. }
  786. //-----------------------------------------------------------------------------
  787. // Purpose: Enable the turret and deploy
  788. //-----------------------------------------------------------------------------
  789. void CNPC_CeilingTurret::Enable( void )
  790. {
  791. m_bEnabled = true;
  792. // if the turret is flagged as an autoactivate turret, re-enable its ability open self.
  793. if ( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE )
  794. {
  795. m_bAutoStart = true;
  796. }
  797. SetThink( &CNPC_CeilingTurret::Deploy );
  798. SetNextThink( gpGlobals->curtime + 0.05f );
  799. }
  800. //-----------------------------------------------------------------------------
  801. // Purpose: Retire the turret until enabled again
  802. //-----------------------------------------------------------------------------
  803. void CNPC_CeilingTurret::Disable( void )
  804. {
  805. m_bEnabled = false;
  806. m_bAutoStart = false;
  807. SetEnemy( NULL );
  808. SetThink( &CNPC_CeilingTurret::Retire );
  809. SetNextThink( gpGlobals->curtime + 0.1f );
  810. }
  811. //-----------------------------------------------------------------------------
  812. // Purpose: Toggle the turret's state via input function
  813. //-----------------------------------------------------------------------------
  814. void CNPC_CeilingTurret::InputToggle( inputdata_t &inputdata )
  815. {
  816. Toggle();
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Purpose:
  820. //-----------------------------------------------------------------------------
  821. void CNPC_CeilingTurret::InputEnable( inputdata_t &inputdata )
  822. {
  823. Enable();
  824. }
  825. //-----------------------------------------------------------------------------
  826. // Purpose:
  827. //-----------------------------------------------------------------------------
  828. void CNPC_CeilingTurret::InputDisable( inputdata_t &inputdata )
  829. {
  830. Disable();
  831. }
  832. //-----------------------------------------------------------------------------
  833. // Purpose:
  834. //-----------------------------------------------------------------------------
  835. void CNPC_CeilingTurret::SpinUp( void )
  836. {
  837. }
  838. #define CEILING_TURRET_MIN_SPIN_DOWN 1.0f
  839. //-----------------------------------------------------------------------------
  840. // Purpose:
  841. //-----------------------------------------------------------------------------
  842. void CNPC_CeilingTurret::SpinDown( void )
  843. {
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose:
  847. //-----------------------------------------------------------------------------
  848. void CNPC_CeilingTurret::DeathThink( void )
  849. {
  850. if ( PreThink( TURRET_DEAD ) )
  851. return;
  852. //Level out our angles
  853. m_vecGoalAngles = GetAbsAngles();
  854. SetNextThink( gpGlobals->curtime );
  855. if ( m_lifeState != LIFE_DEAD )
  856. {
  857. m_lifeState = LIFE_DEAD;
  858. EmitSound( "NPC_CeilingTurret.Die" );
  859. SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE );
  860. }
  861. // lots of smoke
  862. Vector pos;
  863. CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos );
  864. CBroadcastRecipientFilter filter;
  865. te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, 2.5, 10 );
  866. g_pEffects->Sparks( pos );
  867. if ( IsActivityFinished() && ( UpdateFacing() == false ) )
  868. {
  869. SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
  870. m_flPlaybackRate = 0;
  871. SetThink( NULL );
  872. }
  873. }
  874. //-----------------------------------------------------------------------------
  875. // Purpose:
  876. // Input : height -
  877. //-----------------------------------------------------------------------------
  878. void CNPC_CeilingTurret::SetHeight( float height )
  879. {
  880. Vector forward, right, up;
  881. AngleVectors( GetLocalAngles(), &forward, &right, &up );
  882. Vector mins = ( forward * -16.0f ) + ( right * -16.0f );
  883. Vector maxs = ( forward * 16.0f ) + ( right * 16.0f ) + ( up * -height );
  884. if ( mins.x > maxs.x )
  885. {
  886. V_swap( mins.x, maxs.x );
  887. }
  888. if ( mins.y > maxs.y )
  889. {
  890. V_swap( mins.y, maxs.y );
  891. }
  892. if ( mins.z > maxs.z )
  893. {
  894. V_swap( mins.z, maxs.z );
  895. }
  896. SetCollisionBounds( mins, maxs );
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Purpose:
  900. // Input : *pEnemy -
  901. // Output : Returns true on success, false on failure.
  902. //-----------------------------------------------------------------------------
  903. bool CNPC_CeilingTurret::CanBeAnEnemyOf( CBaseEntity *pEnemy )
  904. {
  905. // If we're out of ammo, make friendly companions ignore us
  906. if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
  907. {
  908. if ( pEnemy->Classify() == CLASS_PLAYER_ALLY_VITAL )
  909. return false;
  910. }
  911. return BaseClass::CanBeAnEnemyOf( pEnemy );
  912. }