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.

1166 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 "props.h"
  20. #include "rope.h"
  21. #include "rope_shared.h"
  22. #include "basehlcombatweapon_shared.h"
  23. #include "iservervehicle.h"
  24. #include "physics_prop_ragdoll.h"
  25. #include "portal_util_shared.h"
  26. #include "prop_portal.h"
  27. #include "portal_player.h"
  28. #include "world.h"
  29. #include "ai_baseactor.h" // for Glados ent playing VCDs
  30. #include "sceneentity.h" // precacheing vcds
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. #define SECURITY_CAMERA_MODEL "models/props/security_camera.mdl"
  34. #define SECURITY_CAMERA_BC_YAW "aim_yaw"
  35. #define SECURITY_CAMERA_BC_PITCH "aim_pitch"
  36. #define SECURITY_CAMERA_RANGE 1500
  37. #define SECURITY_CAMERA_SPREAD VECTOR_CONE_2DEGREES
  38. #define SECURITY_CAMERA_MAX_WAIT 5
  39. #define SECURITY_CAMERA_PING_TIME 1.0f //LPB!!
  40. #define SECURITY_CAMERA_NUM_ROPES 2
  41. #define SECURITY_CAMERA_GLOW_SPRITE "sprites/glow1.vmt"
  42. //Aiming variables
  43. #define SECURITY_CAMERA_MAX_NOHARM_PERIOD 0.0f
  44. #define SECURITY_CAMERA_MAX_GRACE_PERIOD 3.0f
  45. //Spawnflags
  46. #define SF_SECURITY_CAMERA_AUTOACTIVATE 0x00000020
  47. #define SF_SECURITY_CAMERA_STARTINACTIVE 0x00000040
  48. #define SF_SECURITY_CAMERA_NEVERRETIRE 0x00000080
  49. #define SF_SECURITY_CAMERA_OUT_OF_AMMO 0x00000100
  50. #define CAMERA_DESTROYED_SCENE_1 "scenes/general/generic_security_camera_destroyed-1.vcd"
  51. #define CAMERA_DESTROYED_SCENE_2 "scenes/general/generic_security_camera_destroyed-2.vcd"
  52. #define CAMERA_DESTROYED_SCENE_3 "scenes/general/generic_security_camera_destroyed-3.vcd"
  53. #define CAMERA_DESTROYED_SCENE_4 "scenes/general/generic_security_camera_destroyed-4.vcd"
  54. #define CAMERA_DESTROYED_SCENE_5 "scenes/general/generic_security_camera_destroyed-5.vcd"
  55. //Heights
  56. #define SECURITY_CAMERA_YAW_SPEED 7.0f
  57. #define SECURITY_CAMERA_TOTAL_TO_KNOCK_DOWN 33
  58. //Turret states
  59. enum turretState_e
  60. {
  61. TURRET_SEARCHING,
  62. TURRET_AUTO_SEARCHING,
  63. TURRET_ACTIVE,
  64. TURRET_DEPLOYING,
  65. TURRET_RETIRING,
  66. TURRET_DEAD,
  67. };
  68. // Forces glados actor to play reaction scenes when player dismounts camera.
  69. void PlayDismountSounds( void );
  70. //
  71. // Security Camera
  72. //
  73. class CNPC_SecurityCamera : public CNPCBaseInteractive<CAI_BaseNPC>, public CDefaultPlayerPickupVPhysics
  74. {
  75. DECLARE_CLASS( CNPC_SecurityCamera, CNPCBaseInteractive<CAI_BaseNPC> );
  76. public:
  77. CNPC_SecurityCamera( void );
  78. ~CNPC_SecurityCamera( void );
  79. void Precache( void );
  80. virtual void CreateSounds( void );
  81. virtual void StopLoopingSounds( void );
  82. virtual void Spawn( void );
  83. virtual void Activate( void );
  84. bool CreateVPhysics( void );
  85. virtual void UpdateOnRemove( void );
  86. virtual void NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params );
  87. virtual int ObjectCaps( void );
  88. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  89. // Think functions
  90. void Retire( void );
  91. void Deploy( void );
  92. void ActiveThink( void );
  93. void SearchThink( void );
  94. void DeathThink( void );
  95. // Inputs
  96. void InputToggle( inputdata_t &inputdata );
  97. void InputEnable( inputdata_t &inputdata );
  98. void InputDisable( inputdata_t &inputdata );
  99. void InputRagdoll( inputdata_t &inputdata );
  100. void SetLastSightTime();
  101. int OnTakeDamage( const CTakeDamageInfo &inputInfo );
  102. virtual void PlayerPenetratingVPhysics( void );
  103. bool OnAttemptPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
  104. bool ShouldSavePhysics() { return true; }
  105. virtual bool CanBeAnEnemyOf( CBaseEntity *pEnemy );
  106. Class_T Classify( void )
  107. {
  108. if( m_bEnabled )
  109. return CLASS_COMBINE;
  110. return CLASS_NONE;
  111. }
  112. bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
  113. Vector EyeOffset( Activity nActivity )
  114. {
  115. Vector vForward;
  116. GetVectors( &vForward, 0, 0 );
  117. return vForward * 10.0f;
  118. }
  119. Vector EyePosition( void )
  120. {
  121. return GetAbsOrigin() + EyeOffset(GetActivity());
  122. }
  123. protected:
  124. bool PreThink( turretState_e state );
  125. void Ping( void );
  126. void Toggle( void );
  127. void Enable( void );
  128. void Disable( void );
  129. void RopesOn( void );
  130. void RopesOff( void );
  131. void EyeOn( void );
  132. void EyeOff( void );
  133. bool UpdateFacing( void );
  134. private:
  135. CHandle<CRopeKeyframe> m_hRopes[ SECURITY_CAMERA_NUM_ROPES ];
  136. CHandle<CSprite> m_hEyeGlow;
  137. bool m_bAutoStart;
  138. bool m_bActive; //Denotes the turret is deployed and looking for targets
  139. bool m_bBlinkState;
  140. bool m_bEnabled; //Denotes whether the turret is able to deploy or not
  141. float m_flLastSight;
  142. float m_flPingTime;
  143. QAngle m_vecGoalAngles;
  144. QAngle m_vecCurrentAngles;
  145. Vector m_vNoisePos;
  146. int m_iTicksTillNextNoise;
  147. CSoundPatch *m_pMovementSound;
  148. COutputEvent m_OnDeploy;
  149. COutputEvent m_OnRetire;
  150. DECLARE_DATADESC();
  151. };
  152. //Datatable
  153. BEGIN_DATADESC( CNPC_SecurityCamera )
  154. DEFINE_ARRAY( m_hRopes, FIELD_EHANDLE, SECURITY_CAMERA_NUM_ROPES ),
  155. DEFINE_FIELD( m_hEyeGlow, FIELD_EHANDLE ),
  156. DEFINE_FIELD( m_bAutoStart, FIELD_BOOLEAN ),
  157. DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
  158. DEFINE_FIELD( m_bBlinkState, FIELD_BOOLEAN ),
  159. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  160. DEFINE_FIELD( m_flLastSight, FIELD_TIME ),
  161. DEFINE_FIELD( m_flPingTime, FIELD_TIME ),
  162. DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ),
  163. DEFINE_FIELD( m_vecCurrentAngles, FIELD_VECTOR ),
  164. DEFINE_FIELD( m_vNoisePos, FIELD_VECTOR ),
  165. DEFINE_FIELD( m_iTicksTillNextNoise, FIELD_INTEGER ),
  166. DEFINE_SOUNDPATCH( m_pMovementSound ),
  167. DEFINE_THINKFUNC( Retire ),
  168. DEFINE_THINKFUNC( Deploy ),
  169. DEFINE_THINKFUNC( ActiveThink ),
  170. DEFINE_THINKFUNC( SearchThink ),
  171. DEFINE_THINKFUNC( DeathThink ),
  172. // Inputs
  173. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  174. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  175. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  176. DEFINE_INPUTFUNC( FIELD_VOID, "Ragdoll", InputRagdoll ),
  177. DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ),
  178. DEFINE_OUTPUT( m_OnRetire, "OnRetire" ),
  179. END_DATADESC()
  180. LINK_ENTITY_TO_CLASS( npc_security_camera, CNPC_SecurityCamera );
  181. //-----------------------------------------------------------------------------
  182. // Constructor
  183. //-----------------------------------------------------------------------------
  184. CNPC_SecurityCamera::CNPC_SecurityCamera( void )
  185. {
  186. m_bActive = false;
  187. m_bAutoStart = false;
  188. m_flPingTime = 0;
  189. m_flLastSight = 0;
  190. m_bBlinkState = false;
  191. m_bEnabled = false;
  192. m_vecCurrentAngles = QAngle( 0.0f, 0.0f, 0.0f );
  193. m_vecGoalAngles.Init();
  194. m_vNoisePos = Vector( 0.0f, 0.0f, 0.0f );
  195. m_iTicksTillNextNoise = 5;
  196. m_pMovementSound = NULL;
  197. m_hEyeGlow = NULL;
  198. }
  199. CNPC_SecurityCamera::~CNPC_SecurityCamera( void )
  200. {
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose: Precache
  204. //-----------------------------------------------------------------------------
  205. void CNPC_SecurityCamera::Precache( void )
  206. {
  207. PrecacheModel( SECURITY_CAMERA_MODEL );
  208. PrecacheScriptSound( "Portalgun.pedestal_rotate_loop" );
  209. // Scenes for when the player dismounts a security camera. Spoken only if Aperture_AI actor is in the
  210. PrecacheInstancedScene( CAMERA_DESTROYED_SCENE_1 );
  211. PrecacheInstancedScene( CAMERA_DESTROYED_SCENE_2 );
  212. PrecacheInstancedScene( CAMERA_DESTROYED_SCENE_3 );
  213. PrecacheInstancedScene( CAMERA_DESTROYED_SCENE_4 );
  214. PrecacheInstancedScene( CAMERA_DESTROYED_SCENE_5 );
  215. BaseClass::Precache();
  216. }
  217. void CNPC_SecurityCamera::CreateSounds()
  218. {
  219. if (!m_pMovementSound)
  220. {
  221. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  222. CPASAttenuationFilter filter( this );
  223. m_pMovementSound = controller.SoundCreate( filter, entindex(), "Portalgun.pedestal_rotate_loop" );
  224. controller.Play( m_pMovementSound, 0, 100 );
  225. }
  226. }
  227. void CNPC_SecurityCamera::StopLoopingSounds()
  228. {
  229. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  230. controller.SoundDestroy( m_pMovementSound );
  231. m_pMovementSound = NULL;
  232. BaseClass::StopLoopingSounds();
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose: Spawn the entity
  236. //-----------------------------------------------------------------------------
  237. void CNPC_SecurityCamera::Spawn( void )
  238. {
  239. Precache();
  240. SetModel( SECURITY_CAMERA_MODEL );
  241. BaseClass::Spawn();
  242. m_HackedGunPos = Vector( 0, 0, 12.75 );
  243. SetViewOffset( EyeOffset( ACT_IDLE ) );
  244. m_flFieldOfView = VIEW_FIELD_FULL;
  245. m_takedamage = DAMAGE_NO;
  246. m_iHealth = 1000;
  247. m_bloodColor = BLOOD_COLOR_MECH;
  248. SetSolid( SOLID_BBOX );
  249. AddSolidFlags( FSOLID_NOT_STANDABLE );
  250. SetCollisionBounds( Vector( -16.0f, -16.0f, -16.0f ), Vector( 16.0f, 16.0f, 16.0f ) );
  251. RemoveFlag( FL_AIMTARGET );
  252. AddEFlags( EFL_NO_DISSOLVE );
  253. SetPoseParameter( SECURITY_CAMERA_BC_YAW, 0 );
  254. SetPoseParameter( SECURITY_CAMERA_BC_PITCH, 0 );
  255. //Set our autostart state
  256. m_bAutoStart = !!( m_spawnflags & SF_SECURITY_CAMERA_AUTOACTIVATE );
  257. m_bEnabled = ( ( m_spawnflags & SF_SECURITY_CAMERA_STARTINACTIVE ) == false );
  258. //Do we start active?
  259. if ( m_bAutoStart && m_bEnabled )
  260. {
  261. SetThink( &CNPC_SecurityCamera::SearchThink );
  262. }
  263. //Stagger our starting times
  264. SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ) );
  265. CreateVPhysics();
  266. }
  267. void CNPC_SecurityCamera::Activate( void )
  268. {
  269. BaseClass::Activate();
  270. CreateSounds();
  271. RopesOn();
  272. EyeOn();
  273. }
  274. bool CNPC_SecurityCamera::CreateVPhysics( void )
  275. {
  276. IPhysicsObject *pPhysics = VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_STANDABLE, false );
  277. if ( !pPhysics )
  278. DevMsg( "npc_turret_floor unable to spawn physics object!\n" );
  279. else
  280. pPhysics->EnableMotion( false );
  281. return true;
  282. }
  283. void CNPC_SecurityCamera::UpdateOnRemove( void )
  284. {
  285. RopesOff();
  286. EyeOff();
  287. BaseClass::UpdateOnRemove();
  288. }
  289. void CNPC_SecurityCamera::NotifySystemEvent(CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
  290. {
  291. // On teleport, we record a pointer to the portal we are arriving at
  292. if ( eventType == NOTIFY_EVENT_TELEPORT )
  293. {
  294. RopesOff();
  295. RopesOn();
  296. }
  297. BaseClass::NotifySystemEvent( pNotify, eventType, params );
  298. }
  299. int CNPC_SecurityCamera::ObjectCaps( void )
  300. {
  301. IPhysicsObject *pPhysics = VPhysicsGetObject();
  302. if ( !pPhysics || !pPhysics->IsMotionEnabled() )
  303. return BaseClass::ObjectCaps();
  304. return ( BaseClass::ObjectCaps() | FCAP_USE_IN_RADIUS | FCAP_USE_ONGROUND | FCAP_IMPULSE_USE );
  305. }
  306. void CNPC_SecurityCamera::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  307. {
  308. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  309. if ( pPlayer )
  310. pPlayer->PickupObject( this );
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Purpose:
  314. //-----------------------------------------------------------------------------
  315. int CNPC_SecurityCamera::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  316. {
  317. if ( !m_takedamage )
  318. return 0;
  319. CTakeDamageInfo info = inputInfo;
  320. if ( m_bActive == false )
  321. info.ScaleDamage( 0.1f );
  322. m_iHealth -= info.GetDamage();
  323. if ( m_iHealth <= 0 )
  324. {
  325. m_iHealth = 0;
  326. m_takedamage = DAMAGE_NO;
  327. RemoveFlag( FL_NPC ); // why are they set in the first place???
  328. ExplosionCreate( GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false );
  329. SetThink( &CNPC_SecurityCamera::DeathThink );
  330. StopSound( "NPC_SecurityCamera.Alert" );
  331. m_OnDamaged.FireOutput( info.GetInflictor(), this );
  332. SetNextThink( gpGlobals->curtime + 0.1f );
  333. return 0;
  334. }
  335. return 1;
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose: We override this code because otherwise we start to move into the
  339. // tricky realm of player avoidance. Since we don't go through the
  340. // normal NPC thinking but we ARE an NPC (...) we miss a bunch of
  341. // book keeping. This means we can become invisible and then never
  342. // reappear.
  343. //-----------------------------------------------------------------------------
  344. void CNPC_SecurityCamera::PlayerPenetratingVPhysics( void )
  345. {
  346. // We don't care!
  347. }
  348. bool CNPC_SecurityCamera::OnAttemptPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  349. {
  350. return !m_bActive;
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Purpose: Shut down
  354. //-----------------------------------------------------------------------------
  355. void CNPC_SecurityCamera::Retire( void )
  356. {
  357. if ( PreThink( TURRET_RETIRING ) )
  358. return;
  359. //Level out the turret
  360. m_vecGoalAngles = GetAbsAngles();
  361. SetNextThink( gpGlobals->curtime );
  362. //Set ourselves to close
  363. if ( m_bActive )
  364. {
  365. //Notify of the retraction
  366. m_OnRetire.FireOutput( NULL, this );
  367. }
  368. m_bActive = false;
  369. m_flLastSight = 0;
  370. SetThink( &CNPC_SecurityCamera::SUB_DoNothing );
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose: Start up
  374. //-----------------------------------------------------------------------------
  375. void CNPC_SecurityCamera::Deploy( void )
  376. {
  377. if ( PreThink( TURRET_DEPLOYING ) )
  378. return;
  379. m_vecGoalAngles = GetAbsAngles();
  380. SetNextThink( gpGlobals->curtime );
  381. if ( !m_bActive )
  382. {
  383. m_bActive = true;
  384. //Notify we're deploying
  385. m_OnDeploy.FireOutput( NULL, this );
  386. }
  387. m_flPlaybackRate = 0;
  388. SetThink( &CNPC_SecurityCamera::SearchThink );
  389. //EmitSound( "NPC_SecurityCamera.Move" );
  390. SetLastSightTime();
  391. }
  392. //-----------------------------------------------------------------------------
  393. //-----------------------------------------------------------------------------
  394. void CNPC_SecurityCamera::SetLastSightTime()
  395. {
  396. if( HasSpawnFlags( SF_SECURITY_CAMERA_NEVERRETIRE ) )
  397. {
  398. m_flLastSight = FLT_MAX;
  399. }
  400. else
  401. {
  402. m_flLastSight = gpGlobals->curtime + SECURITY_CAMERA_MAX_WAIT;
  403. }
  404. }
  405. //-----------------------------------------------------------------------------
  406. // Purpose: Causes the turret to face its desired angles
  407. //-----------------------------------------------------------------------------
  408. bool CNPC_SecurityCamera::UpdateFacing( void )
  409. {
  410. bool bMoved = false;
  411. if ( m_vecCurrentAngles.x < m_vecGoalAngles.x )
  412. {
  413. m_vecCurrentAngles.x += SECURITY_CAMERA_YAW_SPEED;
  414. if ( m_vecCurrentAngles.x > m_vecGoalAngles.x )
  415. m_vecCurrentAngles.x = m_vecGoalAngles.x;
  416. bMoved = true;
  417. }
  418. if ( m_vecCurrentAngles.y < m_vecGoalAngles.y )
  419. {
  420. m_vecCurrentAngles.y += SECURITY_CAMERA_YAW_SPEED;
  421. if ( m_vecCurrentAngles.y > m_vecGoalAngles.y )
  422. m_vecCurrentAngles.y = m_vecGoalAngles.y;
  423. bMoved = true;
  424. }
  425. if ( m_vecCurrentAngles.x > m_vecGoalAngles.x )
  426. {
  427. m_vecCurrentAngles.x -= SECURITY_CAMERA_YAW_SPEED;
  428. if ( m_vecCurrentAngles.x < m_vecGoalAngles.x )
  429. m_vecCurrentAngles.x = m_vecGoalAngles.x;
  430. bMoved = true;
  431. }
  432. if ( m_vecCurrentAngles.y > m_vecGoalAngles.y )
  433. {
  434. m_vecCurrentAngles.y -= SECURITY_CAMERA_YAW_SPEED;
  435. if ( m_vecCurrentAngles.y < m_vecGoalAngles.y )
  436. m_vecCurrentAngles.y = m_vecGoalAngles.y;
  437. bMoved = true;
  438. }
  439. if ( bMoved )
  440. {
  441. if ( m_pMovementSound )
  442. {
  443. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  444. controller.SoundChangeVolume( m_pMovementSound, RandomFloat( 0.7f, 0.9f ), 0.05f );
  445. }
  446. // Update pitch
  447. int iPose = LookupPoseParameter( SECURITY_CAMERA_BC_PITCH );
  448. SetPoseParameter( iPose, m_vecCurrentAngles.x );
  449. // Update yaw
  450. iPose = LookupPoseParameter( SECURITY_CAMERA_BC_YAW );
  451. SetPoseParameter( iPose, m_vecCurrentAngles.y );
  452. InvalidateBoneCache();
  453. }
  454. else
  455. {
  456. if ( m_pMovementSound )
  457. {
  458. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  459. controller.SoundChangeVolume( m_pMovementSound, 0.0f, 0.05f );
  460. }
  461. }
  462. return bMoved;
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Purpose:
  466. // Input : *pEntity -
  467. // Output : Returns true on success, false on failure.
  468. //-----------------------------------------------------------------------------
  469. bool CNPC_SecurityCamera::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
  470. {
  471. CBaseEntity *pHitEntity = NULL;
  472. if ( BaseClass::FVisible( pEntity, traceMask, &pHitEntity ) )
  473. return true;
  474. // If we hit something that's okay to hit anyway, still fire
  475. if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() )
  476. {
  477. if (IRelationType(pHitEntity) == D_HT)
  478. return true;
  479. }
  480. if (ppBlocker)
  481. {
  482. *ppBlocker = pHitEntity;
  483. }
  484. return false;
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose: Allows the turret to fire on targets if they're visible
  488. //-----------------------------------------------------------------------------
  489. void CNPC_SecurityCamera::ActiveThink( void )
  490. {
  491. //Allow descended classes a chance to do something before the think function
  492. if ( PreThink( TURRET_ACTIVE ) )
  493. return;
  494. //Update our think time
  495. SetNextThink( gpGlobals->curtime + 0.1f );
  496. CBaseEntity *pEnemy = GetEnemy();
  497. //If we've become inactive, go back to searching
  498. if ( m_bActive == false || !pEnemy )
  499. {
  500. SetEnemy( NULL );
  501. SetLastSightTime();
  502. SetThink( &CNPC_SecurityCamera::SearchThink );
  503. m_vecGoalAngles = GetAbsAngles();
  504. return;
  505. }
  506. //Get our shot positions
  507. Vector vecMid = EyePosition();
  508. Vector vecMidEnemy = pEnemy->GetAbsOrigin();
  509. //Store off our last seen location
  510. UpdateEnemyMemory( pEnemy, vecMidEnemy );
  511. //Look for our current enemy
  512. bool bEnemyVisible = pEnemy->IsAlive() && FInViewCone( pEnemy ) && FVisible( pEnemy );
  513. //Calculate dir and dist to enemy
  514. Vector vecDirToEnemy = vecMidEnemy - vecMid;
  515. float flDistToEnemy = VectorNormalize( vecDirToEnemy );
  516. CProp_Portal *pPortal = NULL;
  517. if ( pEnemy->IsAlive() )
  518. {
  519. pPortal = FInViewConeThroughPortal( pEnemy );
  520. if ( pPortal && FVisibleThroughPortal( pPortal, pEnemy ) )
  521. {
  522. // Translate our target across the portal
  523. Vector vecMidEnemyTransformed;
  524. UTIL_Portal_PointTransform( pPortal->m_hLinkedPortal->MatrixThisToLinked(), vecMidEnemy, vecMidEnemyTransformed );
  525. //Calculate dir and dist to enemy
  526. Vector vecDirToEnemyTransformed = vecMidEnemyTransformed - vecMid;
  527. float flDistToEnemyTransformed = VectorNormalize( vecDirToEnemyTransformed );
  528. // If it's not visible through normal means or the enemy is closer through the portal, use the translated info
  529. if ( !bEnemyVisible || flDistToEnemyTransformed < flDistToEnemy )
  530. {
  531. bEnemyVisible = true;
  532. vecMidEnemy = vecMidEnemyTransformed;
  533. vecDirToEnemy = vecDirToEnemyTransformed;
  534. flDistToEnemy = flDistToEnemyTransformed;
  535. }
  536. else
  537. {
  538. pPortal = NULL;
  539. }
  540. }
  541. else
  542. {
  543. pPortal = NULL;
  544. }
  545. }
  546. // Add noise to the look position
  547. --m_iTicksTillNextNoise;
  548. if ( m_iTicksTillNextNoise <= 0 && flDistToEnemy < 256.0f )
  549. {
  550. m_vNoisePos.x = RandomFloat( -8.0f, 8.0f );
  551. m_vNoisePos.y = RandomFloat( -8.0f, 8.0f );
  552. m_vNoisePos.z = RandomFloat( 0.0f, 32.0f );
  553. m_iTicksTillNextNoise = RandomInt( 5, 30 );
  554. }
  555. //We want to look at the enemy's eyes so we don't jitter
  556. Vector vEnemyEyes = pEnemy->EyePosition();
  557. if ( pPortal )
  558. {
  559. UTIL_Portal_PointTransform( pPortal->m_hLinkedPortal->MatrixThisToLinked(), vEnemyEyes, vEnemyEyes );
  560. }
  561. Vector vecDirToEnemyEyes = ( vEnemyEyes + m_vNoisePos ) - vecMid;
  562. VectorNormalize( vecDirToEnemyEyes );
  563. QAngle vecAnglesToEnemy;
  564. VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy );
  565. Vector vForward, vRight, vUp;
  566. GetVectors( &vForward, &vRight, &vUp );
  567. vecAnglesToEnemy.x = acosf( vecDirToEnemyEyes.Dot( -vUp ) ) * ( 180.0f / M_PI );
  568. Vector vProjectedDirToEnemyEyes = vecDirToEnemyEyes - vecDirToEnemyEyes.Dot( vUp ) * vUp;
  569. VectorNormalize( vProjectedDirToEnemyEyes );
  570. if ( vProjectedDirToEnemyEyes.IsZero() )
  571. vecAnglesToEnemy.y = m_vecGoalAngles.y;
  572. else
  573. {
  574. if ( vProjectedDirToEnemyEyes.Dot( vForward ) > 0.0f )
  575. vecAnglesToEnemy.y = acosf( vProjectedDirToEnemyEyes.Dot( vRight ) ) * ( 180.0f / M_PI ) - 90.0f;
  576. else
  577. vecAnglesToEnemy.y = -acosf( vProjectedDirToEnemyEyes.Dot( vRight ) ) * ( 180.0f / M_PI ) - 90.0f;
  578. }
  579. vecAnglesToEnemy.y = AngleNormalize( vecAnglesToEnemy.y );
  580. //Current enemy is not visible
  581. if ( ( bEnemyVisible == false ) || ( flDistToEnemy > SECURITY_CAMERA_RANGE ) )
  582. {
  583. if ( gpGlobals->curtime > m_flLastSight )
  584. {
  585. // Should we look for a new target?
  586. ClearEnemyMemory();
  587. SetEnemy( NULL );
  588. SetLastSightTime();
  589. SetThink( &CNPC_SecurityCamera::SearchThink );
  590. m_vecGoalAngles = GetAbsAngles();
  591. return;
  592. }
  593. bEnemyVisible = false;
  594. }
  595. //If we can see our enemy, face it
  596. if ( bEnemyVisible )
  597. {
  598. m_vecGoalAngles.y = vecAnglesToEnemy.y;
  599. m_vecGoalAngles.x = vecAnglesToEnemy.x;
  600. m_flLastSight = gpGlobals->curtime + 0.5f;
  601. }
  602. //Turn to face
  603. UpdateFacing();
  604. // Update rope positions
  605. for ( int iRope = 0; iRope < SECURITY_CAMERA_NUM_ROPES; ++iRope )
  606. {
  607. if ( m_hRopes[ iRope ] )
  608. {
  609. m_hRopes[ iRope ]->EndpointsChanged();
  610. }
  611. }
  612. }
  613. //-----------------------------------------------------------------------------
  614. // Purpose: Target doesn't exist or has eluded us, so search for one
  615. //-----------------------------------------------------------------------------
  616. void CNPC_SecurityCamera::SearchThink( void )
  617. {
  618. //Allow descended classes a chance to do something before the think function
  619. if ( PreThink( TURRET_SEARCHING ) )
  620. return;
  621. SetNextThink( gpGlobals->curtime + 0.1f );
  622. //If our enemy has died, pick a new enemy
  623. if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
  624. {
  625. SetEnemy( NULL );
  626. }
  627. //Acquire the target
  628. if ( GetEnemy() == NULL )
  629. {
  630. CBaseEntity *pEnemy = NULL;
  631. //CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  632. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  633. {
  634. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  635. if ( pPlayer && pPlayer->IsAlive() )
  636. {
  637. if ( FInViewCone( pPlayer ) && FVisible( pPlayer ) )
  638. {
  639. pEnemy = pPlayer;
  640. break;
  641. }
  642. else
  643. {
  644. CProp_Portal *pPortal = FInViewConeThroughPortal( pPlayer );
  645. if ( pPortal && FVisibleThroughPortal( pPortal, pPlayer ) )
  646. {
  647. pEnemy = pPlayer;
  648. break;
  649. }
  650. }
  651. }
  652. }
  653. if ( pEnemy )
  654. {
  655. SetEnemy( pEnemy );
  656. }
  657. }
  658. //If we've found a target follow it
  659. if ( GetEnemy() != NULL )
  660. {
  661. m_flLastSight = 0;
  662. m_bActive = true;
  663. SetThink( &CNPC_SecurityCamera::ActiveThink );
  664. //EmitSound( "NPC_CeilingTurret.Active" );
  665. return;
  666. }
  667. --m_iTicksTillNextNoise;
  668. if ( m_iTicksTillNextNoise <= 0 )
  669. {
  670. //Display that we're scanning
  671. m_vecGoalAngles.x = RandomFloat( -10.0f, 30.0f );
  672. m_vecGoalAngles.y = RandomFloat( -80.0f, 80.0f );
  673. m_iTicksTillNextNoise = RandomInt( 10, 35 );
  674. }
  675. //Turn and ping
  676. //UpdateFacing();
  677. Ping();
  678. }
  679. //-----------------------------------------------------------------------------
  680. // Purpose: Allows a generic think function before the others are called
  681. // Input : state - which state the turret is currently in
  682. //-----------------------------------------------------------------------------
  683. bool CNPC_SecurityCamera::PreThink( turretState_e state )
  684. {
  685. CheckPVSCondition();
  686. //Animate
  687. StudioFrameAdvance();
  688. //Do not interrupt current think function
  689. return false;
  690. }
  691. //-----------------------------------------------------------------------------
  692. // Purpose: Make a pinging noise so the player knows where we are
  693. //-----------------------------------------------------------------------------
  694. void CNPC_SecurityCamera::Ping( void )
  695. {
  696. //See if it's time to ping again
  697. if ( m_flPingTime > gpGlobals->curtime )
  698. return;
  699. //Ping!
  700. //EmitSound( "NPC_CeilingTurret.Ping" );
  701. m_flPingTime = gpGlobals->curtime + SECURITY_CAMERA_PING_TIME;
  702. }
  703. //-----------------------------------------------------------------------------
  704. // Purpose: Toggle the turret's state
  705. //-----------------------------------------------------------------------------
  706. void CNPC_SecurityCamera::Toggle( void )
  707. {
  708. //Toggle the state
  709. if ( m_bEnabled )
  710. {
  711. Disable();
  712. }
  713. else
  714. {
  715. Enable();
  716. }
  717. }
  718. //-----------------------------------------------------------------------------
  719. // Purpose: Enable the turret and deploy
  720. //-----------------------------------------------------------------------------
  721. void CNPC_SecurityCamera::Enable( void )
  722. {
  723. m_bEnabled = true;
  724. // if the turret is flagged as an autoactivate turret, re-enable its ability open self.
  725. if ( m_spawnflags & SF_SECURITY_CAMERA_AUTOACTIVATE )
  726. {
  727. m_bAutoStart = true;
  728. }
  729. SetThink( &CNPC_SecurityCamera::Deploy );
  730. SetNextThink( gpGlobals->curtime + 0.05f );
  731. }
  732. //-----------------------------------------------------------------------------
  733. // Purpose: Retire the turret until enabled again
  734. //-----------------------------------------------------------------------------
  735. void CNPC_SecurityCamera::Disable( void )
  736. {
  737. m_bEnabled = false;
  738. m_bAutoStart = false;
  739. SetEnemy( NULL );
  740. SetThink( &CNPC_SecurityCamera::Retire );
  741. SetNextThink( gpGlobals->curtime + 0.1f );
  742. }
  743. void CNPC_SecurityCamera::RopesOn( void )
  744. {
  745. for ( int iRope = 0; iRope < SECURITY_CAMERA_NUM_ROPES; ++iRope )
  746. {
  747. // Make a rope if it doesn't exist
  748. if ( !m_hRopes[ iRope ] )
  749. {
  750. CFmtStr str;
  751. int iStartIndex = LookupAttachment( str.sprintf( "Wire%i_A", iRope + 1 ) );
  752. int iEndIndex = LookupAttachment( str.sprintf( "Wire%i_B", iRope + 1 ) );
  753. m_hRopes[ iRope ] = CRopeKeyframe::Create( this, this, iStartIndex, iEndIndex );
  754. if ( m_hRopes[ iRope ] )
  755. {
  756. m_hRopes[ iRope ]->m_Width = 0.7;
  757. m_hRopes[ iRope ]->m_nSegments = ROPE_MAX_SEGMENTS;
  758. m_hRopes[ iRope ]->EnableWind( false );
  759. m_hRopes[ iRope ]->SetupHangDistance( 9.0f );
  760. m_hRopes[ iRope ]->m_bConstrainBetweenEndpoints = true;
  761. }
  762. }
  763. }
  764. }
  765. void CNPC_SecurityCamera::RopesOff( void )
  766. {
  767. for ( int iRope = 0; iRope < SECURITY_CAMERA_NUM_ROPES; ++iRope )
  768. {
  769. // Remove rope if it's alive
  770. if ( m_hRopes[ iRope ] )
  771. {
  772. UTIL_Remove( m_hRopes[ iRope ] );
  773. m_hRopes[ iRope ] = NULL;
  774. }
  775. }
  776. }
  777. void CNPC_SecurityCamera::EyeOn( void )
  778. {
  779. if ( !m_hEyeGlow )
  780. {
  781. // Create our eye sprite
  782. m_hEyeGlow = CSprite::SpriteCreate( SECURITY_CAMERA_GLOW_SPRITE, GetLocalOrigin(), false );
  783. if ( !m_hEyeGlow )
  784. return;
  785. m_hEyeGlow->SetTransparency( kRenderWorldGlow, 255, 0, 0, 128, kRenderFxNoDissipation );
  786. m_hEyeGlow->SetAttachment( this, LookupAttachment( "light" ) );
  787. m_hEyeGlow->SetScale( 0.3f, 1.0f );
  788. }
  789. }
  790. void CNPC_SecurityCamera::EyeOff( void )
  791. {
  792. if ( m_hEyeGlow != NULL )
  793. {
  794. UTIL_Remove( m_hEyeGlow );
  795. m_hEyeGlow = NULL;
  796. }
  797. }
  798. //-----------------------------------------------------------------------------
  799. // Purpose:
  800. //-----------------------------------------------------------------------------
  801. void CNPC_SecurityCamera::InputToggle( inputdata_t &inputdata )
  802. {
  803. Toggle();
  804. }
  805. //-----------------------------------------------------------------------------
  806. // Purpose:
  807. //-----------------------------------------------------------------------------
  808. void CNPC_SecurityCamera::InputEnable( inputdata_t &inputdata )
  809. {
  810. Enable();
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Purpose:
  814. //-----------------------------------------------------------------------------
  815. void CNPC_SecurityCamera::InputDisable( inputdata_t &inputdata )
  816. {
  817. Disable();
  818. }
  819. void CNPC_SecurityCamera::InputRagdoll( inputdata_t &inputdata )
  820. {
  821. if ( !m_bEnabled )
  822. return;
  823. // Leave decal on wall (may want to disable this once decal for where cam touches wall is made)
  824. Vector vForward;
  825. GetVectors( &vForward, NULL, NULL );
  826. trace_t tr;
  827. UTIL_TraceLine ( GetAbsOrigin() + 10.0f * vForward, GetAbsOrigin() -60.0f * vForward, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  828. if ( tr.m_pEnt )
  829. UTIL_DecalTrace( &tr, "SecurityCamera.Detachment" );
  830. // Disable it's AI
  831. Disable();
  832. SetThink( &CNPC_SecurityCamera::DeathThink );
  833. EyeOff();
  834. // Make it move
  835. IPhysicsObject *pPhysics = VPhysicsGetObject();
  836. if ( !pPhysics || pPhysics->IsMotionEnabled() )
  837. return;
  838. pPhysics->EnableMotion( true );
  839. pPhysics->Wake();
  840. PlayDismountSounds();
  841. }
  842. //-----------------------------------------------------------------------------
  843. // Purpose:
  844. //-----------------------------------------------------------------------------
  845. void CNPC_SecurityCamera::DeathThink( void )
  846. {
  847. if ( PreThink( TURRET_DEAD ) )
  848. return;
  849. // Level out our angles
  850. m_vecGoalAngles.x = 120.0f;
  851. m_vecGoalAngles.y = 0.0f;
  852. SetNextThink( gpGlobals->curtime + 0.1f );
  853. if ( m_lifeState != LIFE_DEAD )
  854. {
  855. m_lifeState = LIFE_DEAD;
  856. //EmitSound( "NPC_CeilingTurret.Die" );
  857. }
  858. // lots of smoke
  859. Vector pos;
  860. CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos );
  861. CBroadcastRecipientFilter filter;
  862. te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, 2.5, 10 );
  863. g_pEffects->Sparks( pos );
  864. if ( !UpdateFacing() )
  865. {
  866. m_flPlaybackRate = 0;
  867. SetThink( NULL );
  868. }
  869. }
  870. //-----------------------------------------------------------------------------
  871. // Purpose:
  872. // Input : *pEnemy -
  873. // Output : Returns true on success, false on failure.
  874. //-----------------------------------------------------------------------------
  875. bool CNPC_SecurityCamera::CanBeAnEnemyOf( CBaseEntity *pEnemy )
  876. {
  877. // If we're out of ammo, make friendly companions ignore us
  878. if ( m_spawnflags & SF_SECURITY_CAMERA_OUT_OF_AMMO )
  879. {
  880. if ( pEnemy->Classify() == CLASS_PLAYER_ALLY_VITAL )
  881. return false;
  882. }
  883. return BaseClass::CanBeAnEnemyOf( pEnemy );
  884. }
  885. void PlayDismountSounds()
  886. {
  887. // Play GLaDOS's audio reaction
  888. CPortal_Player* pPlayer = ToPortalPlayer( UTIL_PlayerByIndex( 1 ) );
  889. CAI_BaseActor* pGlaDOS = (CAI_BaseActor*)gEntList.FindEntityByName( NULL, "Aperture_AI" );
  890. if ( !pPlayer || !pGlaDOS )
  891. {
  892. DevMsg( 2, "Could not play CNPC_SecurityCamera dismount scene, make sure actor named 'Aperture_AI' is present in map.\n" );
  893. return;
  894. }
  895. IGameEvent *event = gameeventmanager->CreateEvent( "security_camera_detached" );
  896. if ( event )
  897. {
  898. gameeventmanager->FireEvent( event );
  899. }
  900. // If glados is currently talking, don't let her talk over herself or interrupt a potentially important speech.
  901. // Should we play the dismount sound after she's done? or is that too disjointed from the camera dismounting act to make sense...
  902. if ( IsRunningScriptedScene( pGlaDOS, false ) )
  903. {
  904. return;
  905. }
  906. pPlayer->IncNumCamerasDetatched();
  907. int iNumCamerasDetatched = pPlayer->GetNumCamerasDetatched();
  908. // If they've knocked down every one possible, play special '1' sound.
  909. if ( iNumCamerasDetatched == SECURITY_CAMERA_TOTAL_TO_KNOCK_DOWN )
  910. {
  911. InstancedScriptedScene( pGlaDOS, CAMERA_DESTROYED_SCENE_1 );
  912. }
  913. else // iNumCamerasDetatched < SECURITY_CAMERA_TOTAL_TO_KNOCK_DOWN
  914. {
  915. // Play different sounds based on progress towards security camera knockdown total.
  916. switch ( iNumCamerasDetatched )
  917. {
  918. case 1:
  919. InstancedScriptedScene( pGlaDOS, CAMERA_DESTROYED_SCENE_2 );
  920. break;
  921. case 2:
  922. InstancedScriptedScene( pGlaDOS, CAMERA_DESTROYED_SCENE_3 );
  923. break;
  924. case 3:
  925. InstancedScriptedScene( pGlaDOS, CAMERA_DESTROYED_SCENE_4 );
  926. break;
  927. default:
  928. InstancedScriptedScene( pGlaDOS, CAMERA_DESTROYED_SCENE_5 );
  929. break;
  930. }
  931. }
  932. }