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.

1509 lines
34 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "ai_default.h"
  10. #include "ai_task.h"
  11. #include "ai_schedule.h"
  12. #include "ai_node.h"
  13. #include "ai_hull.h"
  14. #include "ai_hint.h"
  15. #include "ai_memory.h"
  16. #include "ai_route.h"
  17. #include "ai_motor.h"
  18. #include "ai_senses.h"
  19. #include "soundent.h"
  20. #include "game.h"
  21. #include "npcevent.h"
  22. #include "entitylist.h"
  23. #include "activitylist.h"
  24. #include "animation.h"
  25. #include "basecombatweapon.h"
  26. #include "IEffects.h"
  27. #include "vstdlib/random.h"
  28. #include "engine/IEngineSound.h"
  29. #include "ammodef.h"
  30. #include "Sprite.h"
  31. #define TURRET_SHOTS 2
  32. #define TURRET_RANGE (100 * 12)
  33. #define TURRET_SPREAD Vector( 0, 0, 0 )
  34. #define TURRET_TURNRATE 30 //angles per 0.1 second
  35. #define TURRET_MAXWAIT 15 // seconds turret will stay active w/o a target
  36. #define TURRET_MAXSPIN 5 // seconds turret barrel will spin w/o a target
  37. typedef enum
  38. {
  39. // TURRET_ANIM_NONE = 0,
  40. TURRET_ANIM_FIRE = 0,
  41. TURRET_ANIM_SPIN,
  42. TURRET_ANIM_DEPLOY,
  43. TURRET_ANIM_RETIRE,
  44. TURRET_ANIM_DIE,
  45. } TURRET_ANIM;
  46. #define SF_MONSTER_TURRET_AUTOACTIVATE 32
  47. #define SF_MONSTER_TURRET_STARTINACTIVE 64
  48. #define TURRET_GLOW_SPRITE "sprites/flare3.vmt"
  49. #define TURRET_ORIENTATION_FLOOR 0
  50. #define TURRET_ORIENTATION_CEILING 1
  51. class CNPC_BaseTurret : public CAI_BaseNPC
  52. {
  53. DECLARE_CLASS( CNPC_BaseTurret, CAI_BaseNPC );
  54. public:
  55. void Spawn(void);
  56. virtual void Precache(void);
  57. void EXPORT TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  58. virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
  59. virtual int OnTakeDamage( const CTakeDamageInfo &info );
  60. virtual int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
  61. Class_T Classify( void );
  62. // Think functions
  63. void EXPORT ActiveThink(void);
  64. void EXPORT SearchThink(void);
  65. void EXPORT AutoSearchThink(void);
  66. void EXPORT TurretDeath(void);
  67. virtual void EXPORT SpinDownCall(void) { m_iSpin = 0; }
  68. virtual void EXPORT SpinUpCall(void) { m_iSpin = 1; }
  69. void EXPORT Deploy(void);
  70. void EXPORT Retire(void);
  71. void EXPORT Initialize(void);
  72. virtual void Ping(void);
  73. virtual void EyeOn(void);
  74. virtual void EyeOff(void);
  75. void InputActivate( inputdata_t &inputdata );
  76. void InputDeactivate( inputdata_t &inputdata );
  77. void Event_Killed( const CTakeDamageInfo &info );
  78. virtual bool ShouldFadeOnDeath( void ) { return false; }
  79. bool ShouldGib( const CTakeDamageInfo &info ) { return false; }
  80. // other functions
  81. void SetTurretAnim(TURRET_ANIM anim);
  82. int MoveTurret(void);
  83. virtual void Shoot(Vector &vecSrc, Vector &vecDirToEnemy) { };
  84. float m_flMaxSpin; // Max time to spin the barrel w/o a target
  85. int m_iSpin;
  86. CSprite *m_pEyeGlow;
  87. int m_eyeBrightness;
  88. int m_iDeployHeight;
  89. int m_iRetractHeight;
  90. int m_iMinPitch;
  91. int m_iBaseTurnRate; // angles per second
  92. float m_fTurnRate; // actual turn rate
  93. int m_iOrientation; // 0 = floor, 1 = Ceiling
  94. int m_iOn;
  95. int m_fBeserk; // Sometimes this bitch will just freak out
  96. int m_iAutoStart; // true if the turret auto deploys when a target
  97. // enters its range
  98. Vector m_vecLastSight;
  99. float m_flLastSight; // Last time we saw a target
  100. float m_flMaxWait; // Max time to seach w/o a target
  101. int m_iSearchSpeed; // Not Used!
  102. // movement
  103. float m_flStartYaw;
  104. QAngle m_vecCurAngles;
  105. Vector m_vecGoalAngles;
  106. float m_flPingTime; // Time until the next ping, used when searching
  107. float m_flSpinUpTime; // Amount of time until the barrel should spin down when searching
  108. float m_flDamageTime;
  109. int m_iAmmoType;
  110. COutputEvent m_OnActivate;
  111. COutputEvent m_OnDeactivate;
  112. //DEFINE_CUSTOM_AI;
  113. DECLARE_DATADESC();
  114. };
  115. BEGIN_DATADESC( CNPC_BaseTurret )
  116. //FIELDS
  117. DEFINE_FIELD( m_flMaxSpin, FIELD_FLOAT ),
  118. DEFINE_FIELD( m_iSpin, FIELD_INTEGER ),
  119. DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ),
  120. DEFINE_FIELD( m_eyeBrightness, FIELD_INTEGER ),
  121. DEFINE_FIELD( m_iDeployHeight, FIELD_INTEGER ),
  122. DEFINE_FIELD( m_iRetractHeight, FIELD_INTEGER ),
  123. DEFINE_FIELD( m_iMinPitch, FIELD_INTEGER ),
  124. DEFINE_FIELD( m_fTurnRate, FIELD_FLOAT ),
  125. DEFINE_FIELD( m_iOn, FIELD_INTEGER ),
  126. DEFINE_FIELD( m_fBeserk, FIELD_INTEGER ),
  127. DEFINE_FIELD( m_iAutoStart, FIELD_INTEGER ),
  128. DEFINE_FIELD( m_vecLastSight, FIELD_POSITION_VECTOR ),
  129. DEFINE_FIELD( m_flLastSight, FIELD_TIME ),
  130. DEFINE_FIELD( m_flStartYaw, FIELD_FLOAT ),
  131. DEFINE_FIELD( m_vecCurAngles, FIELD_VECTOR ),
  132. DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ),
  133. DEFINE_FIELD( m_flPingTime, FIELD_TIME ),
  134. DEFINE_FIELD( m_flSpinUpTime, FIELD_TIME ),
  135. DEFINE_FIELD( m_flDamageTime, FIELD_TIME ),
  136. //DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
  137. //KEYFIELDS
  138. DEFINE_KEYFIELD( m_flMaxWait, FIELD_FLOAT, "maxsleep" ),
  139. DEFINE_KEYFIELD( m_iOrientation, FIELD_INTEGER, "orientation" ),
  140. DEFINE_KEYFIELD( m_iSearchSpeed, FIELD_INTEGER, "searchspeed" ),
  141. DEFINE_KEYFIELD( m_iBaseTurnRate, FIELD_INTEGER, "turnrate" ),
  142. //Use
  143. DEFINE_USEFUNC( TurretUse ),
  144. //Thinks
  145. DEFINE_THINKFUNC( ActiveThink ),
  146. DEFINE_THINKFUNC( SearchThink ),
  147. DEFINE_THINKFUNC( AutoSearchThink ),
  148. DEFINE_THINKFUNC( TurretDeath ),
  149. DEFINE_THINKFUNC( SpinDownCall ),
  150. DEFINE_THINKFUNC( SpinUpCall ),
  151. DEFINE_THINKFUNC( Deploy ),
  152. DEFINE_THINKFUNC( Retire ),
  153. DEFINE_THINKFUNC( Initialize ),
  154. //Inputs
  155. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  156. DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
  157. //Outputs
  158. DEFINE_OUTPUT( m_OnActivate, "OnActivate"),
  159. DEFINE_OUTPUT( m_OnDeactivate, "OnDeactivate"),
  160. END_DATADESC()
  161. void CNPC_BaseTurret::Spawn()
  162. {
  163. Precache( );
  164. SetNextThink( gpGlobals->curtime + 1 );
  165. SetMoveType( MOVETYPE_FLY );
  166. SetSequence( 0 );
  167. SetCycle( 0 );
  168. SetSolid( SOLID_BBOX );
  169. AddSolidFlags( FSOLID_NOT_STANDABLE );
  170. m_takedamage = DAMAGE_YES;
  171. AddFlag( FL_AIMTARGET );
  172. AddFlag( FL_NPC );
  173. SetUse( &CNPC_BaseTurret::TurretUse );
  174. if (( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE )
  175. && !( m_spawnflags & SF_MONSTER_TURRET_STARTINACTIVE ))
  176. {
  177. m_iAutoStart = true;
  178. }
  179. ResetSequenceInfo( );
  180. SetBoneController(0, 0);
  181. SetBoneController(1, 0);
  182. m_flFieldOfView = VIEW_FIELD_FULL;
  183. m_bloodColor = DONT_BLEED;
  184. m_flDamageTime = 0;
  185. if ( GetSpawnFlags() & SF_MONSTER_TURRET_STARTINACTIVE )
  186. {
  187. SetTurretAnim( TURRET_ANIM_RETIRE );
  188. SetCycle( 0.0f );
  189. m_flPlaybackRate = 0.0f;
  190. }
  191. }
  192. void CNPC_BaseTurret::Precache()
  193. {
  194. m_iAmmoType = GetAmmoDef()->Index("12mmRound");
  195. PrecacheScriptSound( "Turret.Alert" );
  196. PrecacheScriptSound( "Turret.Die" );
  197. PrecacheScriptSound( "Turret.Deploy" );
  198. PrecacheScriptSound( "Turret.Undeploy" );
  199. PrecacheScriptSound( "Turret.Ping" );
  200. PrecacheScriptSound( "Turret.Shoot" );
  201. }
  202. Class_T CNPC_BaseTurret::Classify( void )
  203. {
  204. if (m_iOn || m_iAutoStart)
  205. return CLASS_MACHINE;
  206. return CLASS_NONE;
  207. }
  208. //=========================================================
  209. // TraceAttack - being attacked
  210. //=========================================================
  211. void CNPC_BaseTurret::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  212. {
  213. CTakeDamageInfo ainfo = info;
  214. if ( ptr->hitgroup == 10 )
  215. {
  216. // hit armor
  217. if ( m_flDamageTime != gpGlobals->curtime || (random->RandomInt(0,10) < 1) )
  218. {
  219. g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
  220. m_flDamageTime = gpGlobals->curtime;
  221. }
  222. ainfo.SetDamage( 0.1 );// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated
  223. }
  224. if ( m_takedamage == DAMAGE_NO )
  225. return;
  226. //DevMsg( 1, "traceattack: %f\n", ainfo.GetDamage() );
  227. AddMultiDamage( info, this );
  228. }
  229. //=========================================================
  230. // TakeDamage - take damage.
  231. //=========================================================
  232. int CNPC_BaseTurret::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  233. {
  234. if ( m_takedamage == DAMAGE_NO )
  235. return 0;
  236. float flDamage = inputInfo.GetDamage();
  237. if (!m_iOn)
  238. flDamage /= 10.0;
  239. m_iHealth -= flDamage;
  240. if (m_iHealth <= 0)
  241. {
  242. m_iHealth = 0;
  243. m_takedamage = DAMAGE_NO;
  244. m_flDamageTime = gpGlobals->curtime;
  245. // ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place???
  246. SetUse(NULL);
  247. SetThink(&CNPC_BaseTurret::TurretDeath);
  248. SetNextThink( gpGlobals->curtime + 0.1 );
  249. m_OnDeactivate.FireOutput(this, this);
  250. return 0;
  251. }
  252. if (m_iHealth <= 10)
  253. {
  254. if (m_iOn)
  255. {
  256. m_fBeserk = 1;
  257. SetThink(&CNPC_BaseTurret::SearchThink);
  258. }
  259. }
  260. return 1;
  261. }
  262. int CNPC_BaseTurret::OnTakeDamage( const CTakeDamageInfo &info )
  263. {
  264. int retVal = 0;
  265. if (!m_takedamage)
  266. return 0;
  267. switch( m_lifeState )
  268. {
  269. case LIFE_ALIVE:
  270. retVal = OnTakeDamage_Alive( info );
  271. if ( m_iHealth <= 0 )
  272. {
  273. IPhysicsObject *pPhysics = VPhysicsGetObject();
  274. if ( pPhysics )
  275. {
  276. pPhysics->EnableCollisions( false );
  277. }
  278. Event_Killed( info );
  279. Event_Dying();
  280. }
  281. return retVal;
  282. break;
  283. case LIFE_DYING:
  284. return OnTakeDamage_Dying( info );
  285. default:
  286. case LIFE_DEAD:
  287. return OnTakeDamage_Dead( info );
  288. }
  289. }
  290. void CNPC_BaseTurret::SetTurretAnim( TURRET_ANIM anim )
  291. {
  292. /*
  293. if (GetSequence() != anim)
  294. {
  295. switch(anim)
  296. {
  297. case TURRET_ANIM_FIRE:
  298. case TURRET_ANIM_SPIN:
  299. if (GetSequence() != TURRET_ANIM_FIRE && GetSequence() != TURRET_ANIM_SPIN)
  300. {
  301. m_flCycle = 0;
  302. }
  303. break;
  304. default:
  305. m_flCycle = 0;
  306. break;
  307. }
  308. SetSequence( anim );
  309. ResetSequenceInfo( );
  310. switch(anim)
  311. {
  312. case TURRET_ANIM_RETIRE:
  313. m_flCycle = 255;
  314. m_flPlaybackRate = -1.0; //play the animation backwards
  315. break;
  316. case TURRET_ANIM_DIE:
  317. m_flPlaybackRate = 1.0;
  318. break;
  319. }
  320. //ALERT(at_console, "Turret anim #%d\n", anim);
  321. }
  322. */
  323. if (GetSequence() != anim)
  324. {
  325. SetSequence( anim );
  326. ResetSequenceInfo( );
  327. switch(anim)
  328. {
  329. case TURRET_ANIM_FIRE:
  330. case TURRET_ANIM_SPIN:
  331. if (GetSequence() != TURRET_ANIM_FIRE && GetSequence() != TURRET_ANIM_SPIN)
  332. {
  333. SetCycle( 0 );
  334. }
  335. break;
  336. case TURRET_ANIM_RETIRE:
  337. SetCycle( 1.0 );
  338. m_flPlaybackRate = -1.0; //play the animation backwards
  339. break;
  340. case TURRET_ANIM_DIE:
  341. SetCycle( 0.0 );
  342. m_flPlaybackRate = 1.0;
  343. break;
  344. default:
  345. SetCycle( 0 );
  346. break;
  347. }
  348. }
  349. }
  350. //=========================================================
  351. // Initialize - set up the turret, initial think
  352. //=========================================================
  353. void CNPC_BaseTurret::Initialize(void)
  354. {
  355. m_iOn = 0;
  356. m_fBeserk = 0;
  357. m_iSpin = 0;
  358. SetBoneController( 0, 0 );
  359. SetBoneController( 1, 0 );
  360. if (m_iBaseTurnRate == 0) m_iBaseTurnRate = TURRET_TURNRATE;
  361. if (m_flMaxWait == 0) m_flMaxWait = TURRET_MAXWAIT;
  362. QAngle angles = GetAbsAngles();
  363. m_flStartYaw = angles.y;
  364. if (m_iOrientation == TURRET_ORIENTATION_CEILING)
  365. {
  366. angles.x = 180;
  367. angles.y += 180;
  368. if( angles.y > 360 )
  369. angles.y -= 360;
  370. SetAbsAngles( angles );
  371. // pev->idealpitch = 180; //not used?
  372. Vector view_ofs = GetViewOffset();
  373. view_ofs.z = -view_ofs.z;
  374. SetViewOffset( view_ofs );
  375. // pev->effects |= EF_INVLIGHT; //no need
  376. }
  377. m_vecGoalAngles.x = 0;
  378. if (m_iAutoStart)
  379. {
  380. m_flLastSight = gpGlobals->curtime + m_flMaxWait;
  381. SetThink(&CNPC_BaseTurret::AutoSearchThink);
  382. SetNextThink( gpGlobals->curtime + 0.1 );
  383. }
  384. else
  385. {
  386. SetThink( &CBaseEntity::SUB_DoNothing );
  387. }
  388. }
  389. //=========================================================
  390. // ActiveThink -
  391. //=========================================================
  392. void CNPC_BaseTurret::ActiveThink(void)
  393. {
  394. int fAttack = 0;
  395. SetNextThink( gpGlobals->curtime + 0.1 );
  396. StudioFrameAdvance( );
  397. if ( (!m_iOn) || (GetEnemy() == NULL) )
  398. {
  399. SetEnemy( NULL );
  400. m_flLastSight = gpGlobals->curtime + m_flMaxWait;
  401. SetThink(&CNPC_BaseTurret::SearchThink);
  402. return;
  403. }
  404. // if it's dead, look for something new
  405. if ( !GetEnemy()->IsAlive() )
  406. {
  407. if (!m_flLastSight)
  408. {
  409. m_flLastSight = gpGlobals->curtime + 0.5; // continue-shooting timeout
  410. }
  411. else
  412. {
  413. if (gpGlobals->curtime > m_flLastSight)
  414. {
  415. SetEnemy( NULL );
  416. m_flLastSight = gpGlobals->curtime + m_flMaxWait;
  417. SetThink(&CNPC_BaseTurret::SearchThink);
  418. return;
  419. }
  420. }
  421. }
  422. Vector vecMid = EyePosition();
  423. Vector vecMidEnemy = GetEnemy()->BodyTarget(vecMid, false);
  424. // Look for our current enemy
  425. int fEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() );
  426. //We want to look at the enemy's eyes so we don't jitter
  427. Vector vecDirToEnemyEyes = vecMidEnemy - vecMid;
  428. // NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 1.0 );
  429. float flDistToEnemy = vecDirToEnemyEyes.Length();
  430. VectorNormalize( vecDirToEnemyEyes );
  431. QAngle vecAnglesToEnemy;
  432. VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy );
  433. // Current enmey is not visible.
  434. if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE))
  435. {
  436. if (!m_flLastSight)
  437. m_flLastSight = gpGlobals->curtime + 0.5;
  438. else
  439. {
  440. // Should we look for a new target?
  441. if (gpGlobals->curtime > m_flLastSight)
  442. {
  443. SetEnemy( NULL );
  444. m_flLastSight = gpGlobals->curtime + m_flMaxWait;
  445. SetThink(&CNPC_BaseTurret::SearchThink);
  446. return;
  447. }
  448. }
  449. fEnemyVisible = 0;
  450. }
  451. else
  452. {
  453. m_vecLastSight = vecMidEnemy;
  454. }
  455. Vector forward;
  456. AngleVectors( m_vecCurAngles, &forward );
  457. Vector2D vec2LOS = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).AsVector2D();
  458. vec2LOS.NormalizeInPlace();
  459. float flDot = vec2LOS.Dot( forward.AsVector2D() );
  460. if ( flDot <= 0.866 )
  461. fAttack = FALSE;
  462. else
  463. fAttack = TRUE;
  464. //forward
  465. //NDebugOverlay::Line(vecMuzzle, vecMid + ( forward ), 255,0,0, false, 0.1);
  466. //LOS
  467. //NDebugOverlay::Line(vecMuzzle, vecMid + ( vecDirToEnemyEyes * 200 ), 0,0,255, false, 0.1);
  468. // fire the gun
  469. if (m_iSpin && ((fAttack) || (m_fBeserk)))
  470. {
  471. Shoot(vecMid, forward );
  472. SetTurretAnim(TURRET_ANIM_FIRE);
  473. }
  474. else
  475. {
  476. SetTurretAnim(TURRET_ANIM_SPIN);
  477. }
  478. //move the gun
  479. if (m_fBeserk)
  480. {
  481. if (random->RandomInt(0,9) == 0)
  482. {
  483. m_vecGoalAngles.y = random->RandomFloat(0,360);
  484. m_vecGoalAngles.x = random->RandomFloat(0,90) - 90 * m_iOrientation;
  485. CTakeDamageInfo info;
  486. info.SetAttacker(this);
  487. info.SetInflictor(this);
  488. info.SetDamage( 1 );
  489. info.SetDamageType( DMG_GENERIC );
  490. TakeDamage( info ); // don't beserk forever
  491. return;
  492. }
  493. }
  494. else if (fEnemyVisible)
  495. {
  496. if (vecAnglesToEnemy.y > 360)
  497. vecAnglesToEnemy.y -= 360;
  498. if (vecAnglesToEnemy.y < 0)
  499. vecAnglesToEnemy.y += 360;
  500. //ALERT(at_console, "[%.2f]", vec.x);
  501. if (vecAnglesToEnemy.x < -180)
  502. vecAnglesToEnemy.x += 360;
  503. if (vecAnglesToEnemy.x > 180)
  504. vecAnglesToEnemy.x -= 360;
  505. // now all numbers should be in [1...360]
  506. // pin to turret limitations to [-90...14]
  507. if (m_iOrientation == TURRET_ORIENTATION_FLOOR)
  508. {
  509. if (vecAnglesToEnemy.x > 90)
  510. vecAnglesToEnemy.x = 90;
  511. else if (vecAnglesToEnemy.x < m_iMinPitch)
  512. vecAnglesToEnemy.x = m_iMinPitch;
  513. }
  514. else
  515. {
  516. if (vecAnglesToEnemy.x < -90)
  517. vecAnglesToEnemy.x = -90;
  518. else if (vecAnglesToEnemy.x > -m_iMinPitch)
  519. vecAnglesToEnemy.x = -m_iMinPitch;
  520. }
  521. //DevMsg( 1, "->[%.2f]\n", vec.x);
  522. m_vecGoalAngles.y = vecAnglesToEnemy.y;
  523. m_vecGoalAngles.x = vecAnglesToEnemy.x;
  524. }
  525. SpinUpCall();
  526. MoveTurret();
  527. }
  528. //=========================================================
  529. // SearchThink
  530. // This search function will sit with the turret deployed and look for a new target.
  531. // After a set amount of time, the barrel will spin down. After m_flMaxWait, the turret will
  532. // retact.
  533. //=========================================================
  534. void CNPC_BaseTurret::SearchThink(void)
  535. {
  536. // ensure rethink
  537. SetTurretAnim(TURRET_ANIM_SPIN);
  538. StudioFrameAdvance( );
  539. SetNextThink( gpGlobals->curtime + 0.1 );
  540. if (m_flSpinUpTime == 0 && m_flMaxSpin)
  541. m_flSpinUpTime = gpGlobals->curtime + m_flMaxSpin;
  542. Ping( );
  543. CBaseEntity *pEnemy = GetEnemy();
  544. // If we have a target and we're still healthy
  545. if (pEnemy != NULL)
  546. {
  547. if (!pEnemy->IsAlive() )
  548. pEnemy = NULL;// Dead enemy forces a search for new one
  549. }
  550. // Acquire Target
  551. if (pEnemy == NULL)
  552. {
  553. GetSenses()->Look(TURRET_RANGE);
  554. pEnemy = BestEnemy();
  555. if ( pEnemy && !FVisible( pEnemy ) )
  556. pEnemy = NULL;
  557. }
  558. // If we've found a target, spin up the barrel and start to attack
  559. if (pEnemy != NULL)
  560. {
  561. m_flLastSight = 0;
  562. m_flSpinUpTime = 0;
  563. SetThink(&CNPC_BaseTurret::ActiveThink);
  564. }
  565. else
  566. {
  567. // Are we out of time, do we need to retract?
  568. if (gpGlobals->curtime > m_flLastSight)
  569. {
  570. //Before we retrace, make sure that we are spun down.
  571. m_flLastSight = 0;
  572. m_flSpinUpTime = 0;
  573. SetThink(&CNPC_BaseTurret::Retire);
  574. }
  575. // should we stop the spin?
  576. else if ((m_flSpinUpTime) && (gpGlobals->curtime > m_flSpinUpTime))
  577. {
  578. SpinDownCall();
  579. }
  580. // generic hunt for new victims
  581. m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_fTurnRate);
  582. if (m_vecGoalAngles.y >= 360)
  583. m_vecGoalAngles.y -= 360;
  584. MoveTurret();
  585. }
  586. SetEnemy( pEnemy );
  587. }
  588. //=========================================================
  589. // AutoSearchThink -
  590. //=========================================================
  591. void CNPC_BaseTurret::AutoSearchThink(void)
  592. {
  593. // ensure rethink
  594. StudioFrameAdvance( );
  595. SetNextThink( gpGlobals->curtime + 0.3 );
  596. // If we have a target and we're still healthy
  597. CBaseEntity *pEnemy = GetEnemy();
  598. if (pEnemy != NULL)
  599. {
  600. if (!pEnemy->IsAlive() )
  601. {
  602. pEnemy = NULL;
  603. }
  604. }
  605. // Acquire Target
  606. if (pEnemy == NULL)
  607. {
  608. GetSenses()->Look( TURRET_RANGE );
  609. pEnemy = BestEnemy();
  610. if ( pEnemy && !FVisible( pEnemy ) )
  611. pEnemy = NULL;
  612. }
  613. if (pEnemy != NULL)
  614. {
  615. SetThink(&CNPC_BaseTurret::Deploy);
  616. CPASAttenuationFilter filter( this );
  617. EmitSound( filter, entindex(), "Turret.Alert" );
  618. }
  619. SetEnemy( pEnemy );
  620. }
  621. extern short g_sModelIndexSmoke;
  622. //=========================================================
  623. // TurretDeath - I die as I have lived, beyond my means
  624. //=========================================================
  625. void CNPC_BaseTurret::TurretDeath(void)
  626. {
  627. StudioFrameAdvance( );
  628. SetNextThink( gpGlobals->curtime + 0.1 );
  629. if (m_lifeState != LIFE_DEAD)
  630. {
  631. m_lifeState = LIFE_DEAD;
  632. CPASAttenuationFilter filter( this );
  633. EmitSound( filter, entindex(), "Turret.Die" );
  634. StopSound( entindex(), "Turret.Spinup" );
  635. if (m_iOrientation == TURRET_ORIENTATION_FLOOR)
  636. m_vecGoalAngles.x = -14;
  637. else
  638. m_vecGoalAngles.x = 90;//-90;
  639. SetTurretAnim(TURRET_ANIM_DIE);
  640. EyeOn( );
  641. }
  642. EyeOff( );
  643. if (m_flDamageTime + random->RandomFloat( 0, 2 ) > gpGlobals->curtime)
  644. {
  645. // lots of smoke
  646. Vector pos;
  647. CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos );
  648. pos.z = CollisionProp()->GetCollisionOrigin().z;
  649. CBroadcastRecipientFilter filter;
  650. te->Smoke( filter, 0.0, &pos,
  651. g_sModelIndexSmoke,
  652. 2.5,
  653. 10 );
  654. }
  655. if (m_flDamageTime + random->RandomFloat( 0, 5 ) > gpGlobals->curtime)
  656. {
  657. Vector vecSrc;
  658. CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &vecSrc );
  659. g_pEffects->Sparks( vecSrc );
  660. }
  661. if (IsSequenceFinished() && !MoveTurret() && m_flDamageTime + 5 < gpGlobals->curtime)
  662. {
  663. m_flPlaybackRate = 0;
  664. SetThink( NULL );
  665. }
  666. }
  667. //=========================================================
  668. // Deploy - go active
  669. //=========================================================
  670. void CNPC_BaseTurret::Deploy(void)
  671. {
  672. SetNextThink( gpGlobals->curtime + 0.1 );
  673. StudioFrameAdvance( );
  674. if (GetSequence() != TURRET_ANIM_DEPLOY)
  675. {
  676. m_iOn = 1;
  677. SetTurretAnim(TURRET_ANIM_DEPLOY);
  678. CPASAttenuationFilter filter( this );
  679. EmitSound( filter, entindex(), "Turret.Deploy" );
  680. m_OnActivate.FireOutput(this, this);
  681. }
  682. if (IsSequenceFinished())
  683. {
  684. Vector curmins, curmaxs;
  685. curmins = WorldAlignMins();
  686. curmaxs = WorldAlignMaxs();
  687. curmaxs.z = m_iDeployHeight;
  688. curmins.z = -m_iDeployHeight;
  689. SetCollisionBounds( curmins, curmaxs );
  690. m_vecCurAngles.x = 0;
  691. QAngle angles = GetAbsAngles();
  692. if (m_iOrientation == TURRET_ORIENTATION_CEILING)
  693. {
  694. m_vecCurAngles.y = UTIL_AngleMod( angles.y + 180 );
  695. }
  696. else
  697. {
  698. m_vecCurAngles.y = UTIL_AngleMod( angles.y );
  699. }
  700. SetTurretAnim(TURRET_ANIM_SPIN);
  701. m_flPlaybackRate = 0;
  702. SetThink(&CNPC_BaseTurret::SearchThink);
  703. }
  704. m_flLastSight = gpGlobals->curtime + m_flMaxWait;
  705. }
  706. //=========================================================
  707. // Retire - stop being active
  708. //=========================================================
  709. void CNPC_BaseTurret::Retire(void)
  710. {
  711. // make the turret level
  712. m_vecGoalAngles.x = 0;
  713. m_vecGoalAngles.y = m_flStartYaw;
  714. SetNextThink( gpGlobals->curtime + 0.1 );
  715. StudioFrameAdvance( );
  716. EyeOff( );
  717. if (!MoveTurret())
  718. {
  719. if (m_iSpin)
  720. {
  721. SpinDownCall();
  722. }
  723. else if (GetSequence() != TURRET_ANIM_RETIRE)
  724. {
  725. SetTurretAnim(TURRET_ANIM_RETIRE);
  726. CPASAttenuationFilter filter( this );
  727. EmitSound( filter, entindex(), "Turret.Undeploy" );
  728. m_OnDeactivate.FireOutput(this, this);
  729. }
  730. //else if (IsSequenceFinished())
  731. else if( GetSequence() == TURRET_ANIM_RETIRE && GetCycle() <= 0.0 )
  732. {
  733. m_iOn = 0;
  734. m_flLastSight = 0;
  735. //SetTurretAnim(TURRET_ANIM_NONE);
  736. Vector curmins, curmaxs;
  737. curmins = WorldAlignMins();
  738. curmaxs = WorldAlignMaxs();
  739. curmaxs.z = m_iRetractHeight;
  740. curmins.z = -m_iRetractHeight;
  741. SetCollisionBounds( curmins, curmaxs );
  742. if (m_iAutoStart)
  743. {
  744. SetThink(&CNPC_BaseTurret::AutoSearchThink);
  745. SetNextThink( gpGlobals->curtime + 0.1 );
  746. }
  747. else
  748. {
  749. SetThink( &CBaseEntity::SUB_DoNothing );
  750. }
  751. }
  752. }
  753. else
  754. {
  755. SetTurretAnim(TURRET_ANIM_SPIN);
  756. }
  757. }
  758. //=========================================================
  759. // Ping - make the pinging noise every second while searching
  760. //=========================================================
  761. void CNPC_BaseTurret::Ping(void)
  762. {
  763. if (m_flPingTime == 0)
  764. m_flPingTime = gpGlobals->curtime + 1;
  765. else if (m_flPingTime <= gpGlobals->curtime)
  766. {
  767. m_flPingTime = gpGlobals->curtime + 1;
  768. CPASAttenuationFilter filter( this );
  769. EmitSound( filter, entindex(), "Turret.Ping" );
  770. EyeOn( );
  771. }
  772. else if (m_eyeBrightness > 0)
  773. {
  774. EyeOff( );
  775. }
  776. }
  777. //=========================================================
  778. // MoveTurret - handle turret rotation
  779. // returns 1 if the turret moved.
  780. //=========================================================
  781. int CNPC_BaseTurret::MoveTurret(void)
  782. {
  783. int bMoved = 0;
  784. if (m_vecCurAngles.x != m_vecGoalAngles.x)
  785. {
  786. float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ;
  787. m_vecCurAngles.x += 0.1 * m_fTurnRate * flDir;
  788. // if we started below the goal, and now we're past, peg to goal
  789. if (flDir == 1)
  790. {
  791. if (m_vecCurAngles.x > m_vecGoalAngles.x)
  792. m_vecCurAngles.x = m_vecGoalAngles.x;
  793. }
  794. else
  795. {
  796. if (m_vecCurAngles.x < m_vecGoalAngles.x)
  797. m_vecCurAngles.x = m_vecGoalAngles.x;
  798. }
  799. if (m_iOrientation == TURRET_ORIENTATION_FLOOR)
  800. SetBoneController(1, m_vecCurAngles.x);
  801. else
  802. SetBoneController(1, -m_vecCurAngles.x);
  803. bMoved = 1;
  804. }
  805. if (m_vecCurAngles.y != m_vecGoalAngles.y)
  806. {
  807. float flDir = m_vecGoalAngles.y > m_vecCurAngles.y ? 1 : -1 ;
  808. float flDist = fabs(m_vecGoalAngles.y - m_vecCurAngles.y);
  809. if (flDist > 180)
  810. {
  811. flDist = 360 - flDist;
  812. flDir = -flDir;
  813. }
  814. if (flDist > 30)
  815. {
  816. if (m_fTurnRate < m_iBaseTurnRate * 10)
  817. {
  818. m_fTurnRate += m_iBaseTurnRate;
  819. }
  820. }
  821. else if (m_fTurnRate > 45)
  822. {
  823. m_fTurnRate -= m_iBaseTurnRate;
  824. }
  825. else
  826. {
  827. m_fTurnRate += m_iBaseTurnRate;
  828. }
  829. m_vecCurAngles.y += 0.1 * m_fTurnRate * flDir;
  830. if (m_vecCurAngles.y < 0)
  831. m_vecCurAngles.y += 360;
  832. else if (m_vecCurAngles.y >= 360)
  833. m_vecCurAngles.y -= 360;
  834. if (flDist < (0.05 * m_iBaseTurnRate))
  835. m_vecCurAngles.y = m_vecGoalAngles.y;
  836. QAngle angles = GetAbsAngles();
  837. //ALERT(at_console, "%.2f -> %.2f\n", m_vecCurAngles.y, y);
  838. if (m_iOrientation == TURRET_ORIENTATION_FLOOR)
  839. SetBoneController(0, m_vecCurAngles.y - angles.y );
  840. else
  841. SetBoneController(0, angles.y - 180 - m_vecCurAngles.y );
  842. bMoved = 1;
  843. }
  844. if (!bMoved)
  845. m_fTurnRate = m_iBaseTurnRate;
  846. //DevMsg(1, "(%.2f, %.2f)->(%.2f, %.2f)\n", m_vecCurAngles.x,
  847. // m_vecCurAngles.y, m_vecGoalAngles.x, m_vecGoalAngles.y);
  848. return bMoved;
  849. }
  850. void CNPC_BaseTurret::TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  851. {
  852. if ( !ShouldToggle( useType, m_iOn ) )
  853. return;
  854. if (m_iOn)
  855. {
  856. SetEnemy( NULL );
  857. SetNextThink( gpGlobals->curtime + 0.1 );
  858. m_iAutoStart = FALSE;// switching off a turret disables autostart
  859. //!!!! this should spin down first!!BUGBUG
  860. SetThink(&CNPC_BaseTurret::Retire);
  861. }
  862. else
  863. {
  864. SetNextThink( gpGlobals->curtime + 0.1 ); // turn on delay
  865. // if the turret is flagged as an autoactivate turret, re-enable it's ability open self.
  866. if ( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE )
  867. {
  868. m_iAutoStart = TRUE;
  869. }
  870. SetThink(&CNPC_BaseTurret::Deploy);
  871. }
  872. }
  873. void CNPC_BaseTurret::InputDeactivate( inputdata_t &inputdata )
  874. {
  875. if( m_iOn && m_lifeState == LIFE_ALIVE )
  876. {
  877. SetEnemy( NULL );
  878. SetNextThink( gpGlobals->curtime + 0.1 );
  879. m_iAutoStart = FALSE;// switching off a turret disables autostart
  880. //!!!! this should spin down first!!BUGBUG
  881. SetThink(&CNPC_BaseTurret::Retire);
  882. }
  883. }
  884. void CNPC_BaseTurret::InputActivate( inputdata_t &inputdata )
  885. {
  886. if( !m_iOn && m_lifeState == LIFE_ALIVE )
  887. {
  888. SetNextThink( gpGlobals->curtime + 0.1 ); // turn on delay
  889. // if the turret is flagged as an autoactivate turret, re-enable it's ability open self.
  890. if ( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE )
  891. {
  892. m_iAutoStart = TRUE;
  893. }
  894. SetThink(&CNPC_BaseTurret::Deploy);
  895. }
  896. }
  897. //=========================================================
  898. // EyeOn - turn on light on the turret
  899. //=========================================================
  900. void CNPC_BaseTurret::EyeOn(void)
  901. {
  902. if (m_pEyeGlow)
  903. {
  904. if (m_eyeBrightness != 255)
  905. {
  906. m_eyeBrightness = 255;
  907. }
  908. m_pEyeGlow->SetBrightness( m_eyeBrightness );
  909. }
  910. }
  911. //=========================================================
  912. // EyeOn - turn off light on the turret
  913. //=========================================================
  914. void CNPC_BaseTurret::EyeOff(void)
  915. {
  916. if (m_pEyeGlow)
  917. {
  918. if (m_eyeBrightness > 0)
  919. {
  920. m_eyeBrightness = MAX( 0, m_eyeBrightness - 30 );
  921. m_pEyeGlow->SetBrightness( m_eyeBrightness );
  922. }
  923. }
  924. }
  925. void CNPC_BaseTurret::Event_Killed( const CTakeDamageInfo &info )
  926. {
  927. BaseClass::Event_Killed( info );
  928. SetMoveType( MOVETYPE_FLY );
  929. }
  930. //===
  931. class CNPC_MiniTurret : public CNPC_BaseTurret
  932. {
  933. DECLARE_CLASS( CNPC_MiniTurret, CNPC_BaseTurret );
  934. public:
  935. void Spawn( );
  936. void Precache(void);
  937. // other functions
  938. void Shoot(Vector &vecSrc, Vector &vecDirToEnemy);
  939. };
  940. class CNPC_Turret : public CNPC_BaseTurret
  941. {
  942. DECLARE_CLASS( CNPC_Turret, CNPC_BaseTurret );
  943. public:
  944. DECLARE_DATADESC();
  945. void Spawn(void);
  946. void Precache(void);
  947. // Think functions
  948. void SpinUpCall(void);
  949. void SpinDownCall(void);
  950. // other functions
  951. void Shoot(Vector &vecSrc, Vector &vecDirToEnemy);
  952. private:
  953. int m_iStartSpin;
  954. };
  955. BEGIN_DATADESC(CNPC_Turret)
  956. DEFINE_FIELD( m_iStartSpin, FIELD_INTEGER ),
  957. END_DATADESC()
  958. LINK_ENTITY_TO_CLASS( monster_turret, CNPC_Turret );
  959. LINK_ENTITY_TO_CLASS( monster_miniturret, CNPC_MiniTurret );
  960. ConVar sk_turret_health ( "sk_turret_health","50");
  961. ConVar sk_miniturret_health ( "sk_miniturret_health","40");
  962. ConVar sk_sentry_health ( "sk_sentry_health","40");
  963. void CNPC_Turret::Spawn()
  964. {
  965. Precache( );
  966. SetModel( "models/turret.mdl" );
  967. m_iHealth = sk_turret_health.GetFloat();
  968. m_HackedGunPos = Vector( 0, 0, 12.75 );
  969. m_flMaxSpin = TURRET_MAXSPIN;
  970. Vector view_ofs( 0, 0, 12.75 );
  971. SetViewOffset( view_ofs );
  972. CNPC_BaseTurret::Spawn( );
  973. m_iRetractHeight = 16;
  974. m_iDeployHeight = 32;
  975. m_iMinPitch = -90;
  976. UTIL_SetSize(this, Vector(-32, -32, -m_iRetractHeight), Vector(32, 32, m_iRetractHeight));
  977. SetThink(&CNPC_BaseTurret::Initialize);
  978. m_pEyeGlow = CSprite::SpriteCreate( TURRET_GLOW_SPRITE, GetAbsOrigin(), FALSE );
  979. m_pEyeGlow->SetTransparency( kRenderGlow, 255, 0, 0, 0, kRenderFxNoDissipation );
  980. m_pEyeGlow->SetAttachment( this, 2 );
  981. m_eyeBrightness = 0;
  982. SetNextThink( gpGlobals->curtime + 0.3 );
  983. }
  984. void CNPC_Turret::Precache()
  985. {
  986. CNPC_BaseTurret::Precache( );
  987. PrecacheModel ("models/turret.mdl");
  988. PrecacheModel (TURRET_GLOW_SPRITE);
  989. PrecacheModel( "sprites/xspark4.vmt" );
  990. PrecacheScriptSound( "Turret.Shoot" );
  991. PrecacheScriptSound( "Turret.SpinUpCall" );
  992. PrecacheScriptSound( "Turret.Spinup" );
  993. PrecacheScriptSound( "Turret.SpinDownCall" );
  994. //precache sounds
  995. }
  996. void CNPC_Turret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
  997. {
  998. CPASAttenuationFilter filter( this );
  999. FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 );
  1000. EmitSound( filter, entindex(), "Turret.Shoot" );
  1001. DoMuzzleFlash();
  1002. }
  1003. void CNPC_Turret::SpinUpCall(void)
  1004. {
  1005. StudioFrameAdvance( );
  1006. SetNextThink( gpGlobals->curtime + 0.1 );
  1007. // Are we already spun up? If not start the two stage process.
  1008. if (!m_iSpin)
  1009. {
  1010. SetTurretAnim(TURRET_ANIM_SPIN);
  1011. // for the first pass, spin up the the barrel
  1012. if (!m_iStartSpin)
  1013. {
  1014. SetNextThink( gpGlobals->curtime + 1.0 ); //spinup delay
  1015. CPASAttenuationFilter filter( this );
  1016. EmitSound( filter, entindex(), "Turret.SpinUpCall" );
  1017. m_iStartSpin = 1;
  1018. m_flPlaybackRate = 0.1;
  1019. }
  1020. // after the barrel is spun up, turn on the hum
  1021. else if (m_flPlaybackRate >= 1.0)
  1022. {
  1023. SetNextThink( gpGlobals->curtime + 0.1 );// retarget delay
  1024. CPASAttenuationFilter filter( this );
  1025. EmitSound( filter, entindex(), "Turret.Spinup" );
  1026. SetThink(&CNPC_BaseTurret::ActiveThink);
  1027. m_iStartSpin = 0;
  1028. m_iSpin = 1;
  1029. }
  1030. else
  1031. {
  1032. m_flPlaybackRate += 0.075;
  1033. }
  1034. }
  1035. if (m_iSpin)
  1036. {
  1037. SetThink(&CNPC_BaseTurret::ActiveThink);
  1038. }
  1039. }
  1040. void CNPC_Turret::SpinDownCall(void)
  1041. {
  1042. if (m_iSpin)
  1043. {
  1044. CPASAttenuationFilter filter( this );
  1045. SetTurretAnim(TURRET_ANIM_SPIN);
  1046. if ( m_flPlaybackRate == 1.0)
  1047. {
  1048. StopSound( entindex(), "Turret.Spinup" );
  1049. EmitSound( filter, entindex(), "Turret.SpinDownCall" );
  1050. }
  1051. m_flPlaybackRate -= 0.02;
  1052. if (m_flPlaybackRate <= 0)
  1053. {
  1054. m_flPlaybackRate = 0;
  1055. m_iSpin = 0;
  1056. }
  1057. }
  1058. }
  1059. void CNPC_MiniTurret::Spawn()
  1060. {
  1061. Precache( );
  1062. SetModel( "models/miniturret.mdl" );
  1063. m_iHealth = sk_miniturret_health.GetFloat();
  1064. m_HackedGunPos = Vector( 0, 0, 12.75 );
  1065. m_flMaxSpin = 0;
  1066. Vector view_ofs( 0, 0, 12.75 );
  1067. SetViewOffset( view_ofs );
  1068. CNPC_BaseTurret::Spawn( );
  1069. m_iRetractHeight = 16;
  1070. m_iDeployHeight = 32;
  1071. m_iMinPitch = -90;
  1072. UTIL_SetSize(this, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight));
  1073. SetThink(&CNPC_MiniTurret::Initialize);
  1074. SetNextThink(gpGlobals->curtime + 0.3);
  1075. if (( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) && !( m_spawnflags & SF_MONSTER_TURRET_STARTINACTIVE ))
  1076. {
  1077. m_iAutoStart = true;
  1078. }
  1079. }
  1080. void CNPC_MiniTurret::Precache()
  1081. {
  1082. CNPC_BaseTurret::Precache( );
  1083. m_iAmmoType = GetAmmoDef()->Index("9mmRound");
  1084. PrecacheScriptSound( "Turret.Shoot" );
  1085. PrecacheModel ("models/miniturret.mdl");
  1086. }
  1087. void CNPC_MiniTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
  1088. {
  1089. FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 );
  1090. CPASAttenuationFilter filter( this );
  1091. EmitSound( filter, entindex(), "Turret.Shoot" );
  1092. DoMuzzleFlash();
  1093. }
  1094. //=========================================================
  1095. // Sentry gun - smallest turret, placed near grunt entrenchments
  1096. //=========================================================
  1097. class CNPC_Sentry : public CNPC_BaseTurret
  1098. {
  1099. DECLARE_CLASS( CNPC_Sentry, CNPC_BaseTurret );
  1100. public:
  1101. void Spawn( );
  1102. void Precache(void);
  1103. // other functions
  1104. void Shoot(Vector &vecSrc, Vector &vecDirToEnemy);
  1105. int OnTakeDamage_Alive(const CTakeDamageInfo &info);
  1106. void Event_Killed( const CTakeDamageInfo &info );
  1107. void SentryTouch( CBaseEntity *pOther );
  1108. DECLARE_DATADESC();
  1109. private:
  1110. bool m_bStartedDeploy; //set to true when the turret begins its deploy
  1111. };
  1112. BEGIN_DATADESC( CNPC_Sentry )
  1113. DEFINE_ENTITYFUNC( SentryTouch ),
  1114. DEFINE_FIELD( m_bStartedDeploy, FIELD_BOOLEAN ),
  1115. END_DATADESC()
  1116. LINK_ENTITY_TO_CLASS( monster_sentry, CNPC_Sentry );
  1117. void CNPC_Sentry::Precache()
  1118. {
  1119. BaseClass::Precache( );
  1120. m_iAmmoType = GetAmmoDef()->Index("9mmRound");
  1121. PrecacheScriptSound( "Sentry.Shoot" );
  1122. PrecacheScriptSound( "Sentry.Die" );
  1123. PrecacheModel ("models/sentry.mdl");
  1124. }
  1125. void CNPC_Sentry::Spawn()
  1126. {
  1127. Precache( );
  1128. SetModel( "models/sentry.mdl" );
  1129. m_iHealth = sk_sentry_health.GetFloat();
  1130. m_HackedGunPos = Vector( 0, 0, 48 );
  1131. SetViewOffset( Vector(0,0,48) );
  1132. m_flMaxWait = 1E6;
  1133. m_flMaxSpin = 1E6;
  1134. BaseClass::Spawn();
  1135. SetSequence( TURRET_ANIM_RETIRE );
  1136. SetCycle( 0.0 );
  1137. m_flPlaybackRate = 0.0;
  1138. m_iRetractHeight = 64;
  1139. m_iDeployHeight = 64;
  1140. m_iMinPitch = -60;
  1141. UTIL_SetSize(this, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight));
  1142. SetTouch(&CNPC_Sentry::SentryTouch);
  1143. SetThink(&CNPC_Sentry::Initialize);
  1144. SetNextThink(gpGlobals->curtime + 0.3);
  1145. m_bStartedDeploy = false;
  1146. }
  1147. void CNPC_Sentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
  1148. {
  1149. FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 );
  1150. CPASAttenuationFilter filter( this );
  1151. EmitSound( filter, entindex(), "Sentry.Shoot" );
  1152. DoMuzzleFlash();
  1153. }
  1154. int CNPC_Sentry::OnTakeDamage_Alive(const CTakeDamageInfo &info)
  1155. {
  1156. if ( m_takedamage == DAMAGE_NO )
  1157. return 0;
  1158. if (!m_iOn && !m_bStartedDeploy)
  1159. {
  1160. m_bStartedDeploy = true;
  1161. SetThink( &CNPC_Sentry::Deploy );
  1162. SetUse( NULL );
  1163. SetNextThink( gpGlobals->curtime + 0.1 );
  1164. }
  1165. m_iHealth -= info.GetDamage();
  1166. if (m_iHealth <= 0)
  1167. {
  1168. m_iHealth = 0;
  1169. m_takedamage = DAMAGE_NO;
  1170. m_flDamageTime = gpGlobals->curtime;
  1171. SetUse(NULL);
  1172. SetThink(&CNPC_BaseTurret::TurretDeath); //should be SentryDeath ?
  1173. SetNextThink( gpGlobals->curtime + 0.1 );
  1174. m_OnDeactivate.FireOutput(this, this);
  1175. return 0;
  1176. }
  1177. return 1;
  1178. }
  1179. void CNPC_Sentry::Event_Killed( const CTakeDamageInfo &info )
  1180. {
  1181. CPASAttenuationFilter filter( this );
  1182. EmitSound( filter, entindex(), "Sentry.Die" );
  1183. StopSound( entindex(), "Turret.Spinup" );
  1184. AddSolidFlags( FSOLID_NOT_STANDABLE );
  1185. Vector vecSrc;
  1186. QAngle vecAng;
  1187. GetAttachment( 2, vecSrc, vecAng );
  1188. te->Smoke( filter, 0.0, &vecSrc,
  1189. g_sModelIndexSmoke,
  1190. 2.5,
  1191. 10 );
  1192. g_pEffects->Sparks( vecSrc );
  1193. BaseClass::Event_Killed( info );
  1194. }
  1195. void CNPC_Sentry::SentryTouch( CBaseEntity *pOther )
  1196. {
  1197. //trigger the sentry to turn on if a monster or player touches it
  1198. if ( pOther && (pOther->IsPlayer() || FBitSet ( pOther->GetFlags(), FL_NPC )) )
  1199. {
  1200. CTakeDamageInfo info;
  1201. info.SetAttacker( pOther );
  1202. info.SetInflictor( pOther );
  1203. info.SetDamage( 0 );
  1204. TakeDamage(info);
  1205. }
  1206. }