aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/hl2/npc_turret_ceiling.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mp/src/game/server/hl2/npc_turret_ceiling.cpp')
-rw-r--r--mp/src/game/server/hl2/npc_turret_ceiling.cpp2252
1 files changed, 1126 insertions, 1126 deletions
diff --git a/mp/src/game/server/hl2/npc_turret_ceiling.cpp b/mp/src/game/server/hl2/npc_turret_ceiling.cpp
index 76f431b2..ec8b4207 100644
--- a/mp/src/game/server/hl2/npc_turret_ceiling.cpp
+++ b/mp/src/game/server/hl2/npc_turret_ceiling.cpp
@@ -1,1127 +1,1127 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "ai_basenpc.h"
-#include "ai_senses.h"
-#include "ai_memory.h"
-#include "engine/IEngineSound.h"
-#include "ammodef.h"
-#include "Sprite.h"
-#include "hl2/hl2_player.h"
-#include "soundenvelope.h"
-#include "explode.h"
-#include "IEffects.h"
-#include "animation.h"
-#include "basehlcombatweapon_shared.h"
-#include "iservervehicle.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-//Debug visualization
-ConVar g_debug_turret_ceiling( "g_debug_turret_ceiling", "0" );
-
-#define CEILING_TURRET_MODEL "models/combine_turrets/ceiling_turret.mdl"
-#define CEILING_TURRET_GLOW_SPRITE "sprites/glow1.vmt"
-/* // we now inherit these from the ai_basenpc baseclass
-#define CEILING_TURRET_BC_YAW "aim_yaw"
-#define CEILING_TURRET_BC_PITCH "aim_pitch"
-*/
-#define CEILING_TURRET_RANGE 1500
-#define CEILING_TURRET_SPREAD VECTOR_CONE_2DEGREES
-#define CEILING_TURRET_MAX_WAIT 5
-#define CEILING_TURRET_PING_TIME 1.0f //LPB!!
-
-#define CEILING_TURRET_VOICE_PITCH_LOW 45
-#define CEILING_TURRET_VOICE_PITCH_HIGH 100
-
-//Aiming variables
-#define CEILING_TURRET_MAX_NOHARM_PERIOD 0.0f
-#define CEILING_TURRET_MAX_GRACE_PERIOD 3.0f
-
-//Spawnflags
-#define SF_CEILING_TURRET_AUTOACTIVATE 0x00000020
-#define SF_CEILING_TURRET_STARTINACTIVE 0x00000040
-#define SF_CEILING_TURRET_NEVERRETIRE 0x00000080
-#define SF_CEILING_TURRET_OUT_OF_AMMO 0x00000100
-
-//Heights
-#define CEILING_TURRET_RETRACT_HEIGHT 24
-#define CEILING_TURRET_DEPLOY_HEIGHT 64
-
-//Activities
-int ACT_CEILING_TURRET_OPEN;
-int ACT_CEILING_TURRET_CLOSE;
-int ACT_CEILING_TURRET_OPEN_IDLE;
-int ACT_CEILING_TURRET_CLOSED_IDLE;
-int ACT_CEILING_TURRET_FIRE;
-int ACT_CEILING_TURRET_DRYFIRE;
-
-//Turret states
-enum turretState_e
-{
- TURRET_SEARCHING,
- TURRET_AUTO_SEARCHING,
- TURRET_ACTIVE,
- TURRET_DEPLOYING,
- TURRET_RETIRING,
- TURRET_DEAD,
-};
-
-//Eye states
-enum eyeState_t
-{
- TURRET_EYE_SEE_TARGET, //Sees the target, bright and big
- TURRET_EYE_SEEKING_TARGET, //Looking for a target, blinking (bright)
- TURRET_EYE_DORMANT, //Not active
- TURRET_EYE_DEAD, //Completely invisible
- TURRET_EYE_DISABLED, //Turned off, must be reactivated before it'll deploy again (completely invisible)
-};
-
-//
-// Ceiling Turret
-//
-
-class CNPC_CeilingTurret : public CAI_BaseNPC
-{
- DECLARE_CLASS( CNPC_CeilingTurret, CAI_BaseNPC );
-public:
-
- CNPC_CeilingTurret( void );
- ~CNPC_CeilingTurret( void );
-
- void Precache( void );
- void Spawn( void );
-
- // Think functions
- void Retire( void );
- void Deploy( void );
- void ActiveThink( void );
- void SearchThink( void );
- void AutoSearchThink( void );
- void DeathThink( void );
-
- // Inputs
- void InputToggle( inputdata_t &inputdata );
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
-
- void SetLastSightTime();
-
- float MaxYawSpeed( void );
-
- int OnTakeDamage( const CTakeDamageInfo &inputInfo );
-
- virtual bool CanBeAnEnemyOf( CBaseEntity *pEnemy );
-
- Class_T Classify( void )
- {
- if( m_bEnabled )
- return CLASS_COMBINE;
-
- return CLASS_NONE;
- }
-
- bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
-
- Vector EyeOffset( Activity nActivity )
- {
- Vector vecEyeOffset(0,0,-64);
- GetEyePosition( GetModelPtr(), vecEyeOffset );
- return vecEyeOffset;
- }
-
- Vector EyePosition( void )
- {
- return GetAbsOrigin() + EyeOffset(GetActivity());
- }
-
- Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget )
- {
- return VECTOR_CONE_5DEGREES * ((CBaseHLCombatWeapon::GetDefaultProficiencyValues())[ WEAPON_PROFICIENCY_PERFECT ].spreadscale);
- }
-
-protected:
-
- bool PreThink( turretState_e state );
- void Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy );
- void SetEyeState( eyeState_t state );
- void Ping( void );
- void Toggle( void );
- void Enable( void );
- void Disable( void );
- void SpinUp( void );
- void SpinDown( void );
- void SetHeight( float height );
-
- bool UpdateFacing( void );
-
- int m_iAmmoType;
- int m_iMinHealthDmg;
-
- bool m_bAutoStart;
- bool m_bActive; //Denotes the turret is deployed and looking for targets
- bool m_bBlinkState;
- bool m_bEnabled; //Denotes whether the turret is able to deploy or not
-
- float m_flShotTime;
- float m_flLastSight;
- float m_flPingTime;
-
- QAngle m_vecGoalAngles;
-
- CSprite *m_pEyeGlow;
-
- COutputEvent m_OnDeploy;
- COutputEvent m_OnRetire;
- COutputEvent m_OnTipped;
-
- DECLARE_DATADESC();
-};
-
-//Datatable
-BEGIN_DATADESC( CNPC_CeilingTurret )
-
- DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
- DEFINE_KEYFIELD( m_iMinHealthDmg, FIELD_INTEGER, "minhealthdmg" ),
- DEFINE_FIELD( m_bAutoStart, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bBlinkState, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_flShotTime, FIELD_TIME ),
- DEFINE_FIELD( m_flLastSight, FIELD_TIME ),
- DEFINE_FIELD( m_flPingTime, FIELD_TIME ),
- DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ),
- DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ),
-
- DEFINE_THINKFUNC( Retire ),
- DEFINE_THINKFUNC( Deploy ),
- DEFINE_THINKFUNC( ActiveThink ),
- DEFINE_THINKFUNC( SearchThink ),
- DEFINE_THINKFUNC( AutoSearchThink ),
- DEFINE_THINKFUNC( DeathThink ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
-
- DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ),
- DEFINE_OUTPUT( m_OnRetire, "OnRetire" ),
- DEFINE_OUTPUT( m_OnTipped, "OnTipped" ),
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( npc_turret_ceiling, CNPC_CeilingTurret );
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CNPC_CeilingTurret::CNPC_CeilingTurret( void )
-{
- m_bActive = false;
- m_pEyeGlow = NULL;
- m_iAmmoType = -1;
- m_iMinHealthDmg = 0;
- m_bAutoStart = false;
- m_flPingTime = 0;
- m_flShotTime = 0;
- m_flLastSight = 0;
- m_bBlinkState = false;
- m_bEnabled = false;
-
- m_vecGoalAngles.Init();
-}
-
-CNPC_CeilingTurret::~CNPC_CeilingTurret( void )
-{
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Precache
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Precache( void )
-{
- PrecacheModel( CEILING_TURRET_MODEL );
- PrecacheModel( CEILING_TURRET_GLOW_SPRITE );
-
- // Activities
- ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN );
- ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSE );
- ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSED_IDLE );
- ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN_IDLE );
- ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_FIRE );
- ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_DRYFIRE );
-
- PrecacheScriptSound( "NPC_CeilingTurret.Retire" );
- PrecacheScriptSound( "NPC_CeilingTurret.Deploy" );
- PrecacheScriptSound( "NPC_CeilingTurret.Move" );
- PrecacheScriptSound( "NPC_CeilingTurret.Active" );
- PrecacheScriptSound( "NPC_CeilingTurret.Alert" );
- PrecacheScriptSound( "NPC_CeilingTurret.ShotSounds" );
- PrecacheScriptSound( "NPC_CeilingTurret.Ping" );
- PrecacheScriptSound( "NPC_CeilingTurret.Die" );
-
- PrecacheScriptSound( "NPC_FloorTurret.DryFire" );
-
- BaseClass::Precache();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Spawn the entity
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Spawn( void )
-{
- Precache();
-
- SetModel( CEILING_TURRET_MODEL );
-
- BaseClass::Spawn();
-
- m_HackedGunPos = Vector( 0, 0, 12.75 );
- SetViewOffset( EyeOffset( ACT_IDLE ) );
- m_flFieldOfView = 0.0f;
- m_takedamage = DAMAGE_YES;
- m_iHealth = 1000;
- m_bloodColor = BLOOD_COLOR_MECH;
-
- SetSolid( SOLID_BBOX );
- AddSolidFlags( FSOLID_NOT_STANDABLE );
-
- SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
-
- AddFlag( FL_AIMTARGET );
- AddEFlags( EFL_NO_DISSOLVE );
-
- SetPoseParameter( m_poseAim_Yaw, 0 );
- SetPoseParameter( m_poseAim_Pitch, 0 );
-
- m_iAmmoType = GetAmmoDef()->Index( "AR2" );
-
- //Create our eye sprite
- m_pEyeGlow = CSprite::SpriteCreate( CEILING_TURRET_GLOW_SPRITE, GetLocalOrigin(), false );
- m_pEyeGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation );
- m_pEyeGlow->SetAttachment( this, 2 );
-
- //Set our autostart state
- m_bAutoStart = !!( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE );
- m_bEnabled = ( ( m_spawnflags & SF_CEILING_TURRET_STARTINACTIVE ) == false );
-
- //Do we start active?
- if ( m_bAutoStart && m_bEnabled )
- {
- SetThink( &CNPC_CeilingTurret::AutoSearchThink );
- SetEyeState( TURRET_EYE_DORMANT );
- }
- else
- {
- SetEyeState( TURRET_EYE_DISABLED );
- }
-
- //Stagger our starting times
- SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ) );
-
- // Don't allow us to skip animation setup because our attachments are critical to us!
- SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CNPC_CeilingTurret::OnTakeDamage( const CTakeDamageInfo &inputInfo )
-{
- if ( !m_takedamage )
- return 0;
-
- CTakeDamageInfo info = inputInfo;
-
- if ( m_bActive == false )
- info.ScaleDamage( 0.1f );
-
- // If attacker can't do at least the min required damage to us, don't take any damage from them
- if ( info.GetDamage() < m_iMinHealthDmg )
- return 0;
-
- m_iHealth -= info.GetDamage();
-
- if ( m_iHealth <= 0 )
- {
- m_iHealth = 0;
- m_takedamage = DAMAGE_NO;
-
- RemoveFlag( FL_NPC ); // why are they set in the first place???
-
- //FIXME: This needs to throw a ragdoll gib or something other than animating the retraction -- jdw
-
- ExplosionCreate( GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false );
- SetThink( &CNPC_CeilingTurret::DeathThink );
-
- StopSound( "NPC_CeilingTurret.Alert" );
-
- m_OnDamaged.FireOutput( info.GetInflictor(), this );
-
- SetNextThink( gpGlobals->curtime + 0.1f );
-
- return 0;
- }
-
- return 1;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Retract and stop attacking
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Retire( void )
-{
- if ( PreThink( TURRET_RETIRING ) )
- return;
-
- //Level out the turret
- m_vecGoalAngles = GetAbsAngles();
- SetNextThink( gpGlobals->curtime );
-
- //Set ourselves to close
- if ( GetActivity() != ACT_CEILING_TURRET_CLOSE )
- {
- //Set our visible state to dormant
- SetEyeState( TURRET_EYE_DORMANT );
-
- SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
-
- //If we're done moving to our desired facing, close up
- if ( UpdateFacing() == false )
- {
- SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE );
- EmitSound( "NPC_CeilingTurret.Retire" );
-
- //Notify of the retraction
- m_OnRetire.FireOutput( NULL, this );
- }
- }
- else if ( IsActivityFinished() )
- {
- SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
-
- m_bActive = false;
- m_flLastSight = 0;
-
- SetActivity( (Activity) ACT_CEILING_TURRET_CLOSED_IDLE );
-
- //Go back to auto searching
- if ( m_bAutoStart )
- {
- SetThink( &CNPC_CeilingTurret::AutoSearchThink );
- SetNextThink( gpGlobals->curtime + 0.05f );
- }
- else
- {
- //Set our visible state to dormant
- SetEyeState( TURRET_EYE_DISABLED );
- SetThink( &CNPC_CeilingTurret::SUB_DoNothing );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Deploy and start attacking
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Deploy( void )
-{
- if ( PreThink( TURRET_DEPLOYING ) )
- return;
-
- m_vecGoalAngles = GetAbsAngles();
-
- SetNextThink( gpGlobals->curtime );
-
- //Show we've seen a target
- SetEyeState( TURRET_EYE_SEE_TARGET );
-
- //Open if we're not already
- if ( GetActivity() != ACT_CEILING_TURRET_OPEN )
- {
- m_bActive = true;
- SetActivity( (Activity) ACT_CEILING_TURRET_OPEN );
- EmitSound( "NPC_CeilingTurret.Deploy" );
-
- //Notify we're deploying
- m_OnDeploy.FireOutput( NULL, this );
- }
-
- //If we're done, then start searching
- if ( IsActivityFinished() )
- {
- SetHeight( CEILING_TURRET_DEPLOY_HEIGHT );
-
- SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
-
- m_flShotTime = gpGlobals->curtime + 1.0f;
-
- m_flPlaybackRate = 0;
- SetThink( &CNPC_CeilingTurret::SearchThink );
-
- EmitSound( "NPC_CeilingTurret.Move" );
- }
-
- SetLastSightTime();
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::SetLastSightTime()
-{
- if( HasSpawnFlags( SF_CEILING_TURRET_NEVERRETIRE ) )
- {
- m_flLastSight = FLT_MAX;
- }
- else
- {
- m_flLastSight = gpGlobals->curtime + CEILING_TURRET_MAX_WAIT;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the speed at which the turret can face a target
-//-----------------------------------------------------------------------------
-float CNPC_CeilingTurret::MaxYawSpeed( void )
-{
- //TODO: Scale by difficulty?
- return 360.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Causes the turret to face its desired angles
-//-----------------------------------------------------------------------------
-bool CNPC_CeilingTurret::UpdateFacing( void )
-{
- bool bMoved = false;
- matrix3x4_t localToWorld;
-
- GetAttachment( LookupAttachment( "eyes" ), localToWorld );
-
- Vector vecGoalDir;
- AngleVectors( m_vecGoalAngles, &vecGoalDir );
-
- Vector vecGoalLocalDir;
- VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir );
-
- if ( g_debug_turret_ceiling.GetBool() )
- {
- Vector vecMuzzle, vecMuzzleDir;
- QAngle vecMuzzleAng;
-
- GetAttachment( "eyes", vecMuzzle, vecMuzzleAng );
- AngleVectors( vecMuzzleAng, &vecMuzzleDir );
-
- NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 );
- NDebugOverlay::Cross3D( vecMuzzle+(vecMuzzleDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 );
- NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecMuzzleDir*256), 255, 255, 0, false, 0.05 );
-
- NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 );
- NDebugOverlay::Cross3D( vecMuzzle+(vecGoalDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 );
- NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecGoalDir*256), 255, 0, 0, false, 0.05 );
- }
-
- QAngle vecGoalLocalAngles;
- VectorAngles( vecGoalLocalDir, vecGoalLocalAngles );
-
- // Update pitch
- float flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed() ) );
-
- SetPoseParameter( m_poseAim_Pitch, GetPoseParameter( m_poseAim_Pitch ) + ( flDiff / 1.5f ) );
-
- if ( fabs( flDiff ) > 0.1f )
- {
- bMoved = true;
- }
-
- // Update yaw
- flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed() ) );
-
- SetPoseParameter( m_poseAim_Yaw, GetPoseParameter( m_poseAim_Yaw ) + ( flDiff / 1.5f ) );
-
- if ( fabs( flDiff ) > 0.1f )
- {
- bMoved = true;
- }
-
- InvalidateBoneCache();
-
- return bMoved;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_CeilingTurret::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
-{
- CBaseEntity *pHitEntity = NULL;
- if ( BaseClass::FVisible( pEntity, traceMask, &pHitEntity ) )
- return true;
-
- // If we hit something that's okay to hit anyway, still fire
- if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() )
- {
- if (IRelationType(pHitEntity) == D_HT)
- return true;
- }
-
- if (ppBlocker)
- {
- *ppBlocker = pHitEntity;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Allows the turret to fire on targets if they're visible
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::ActiveThink( void )
-{
- //Allow descended classes a chance to do something before the think function
- if ( PreThink( TURRET_ACTIVE ) )
- return;
-
- //Update our think time
- SetNextThink( gpGlobals->curtime + 0.1f );
-
- //If we've become inactive, go back to searching
- if ( ( m_bActive == false ) || ( GetEnemy() == NULL ) )
- {
- SetEnemy( NULL );
- SetLastSightTime();
- SetThink( &CNPC_CeilingTurret::SearchThink );
- m_vecGoalAngles = GetAbsAngles();
- return;
- }
-
- //Get our shot positions
- Vector vecMid = EyePosition();
- Vector vecMidEnemy = GetEnemy()->GetAbsOrigin();
-
- //Store off our last seen location
- UpdateEnemyMemory( GetEnemy(), vecMidEnemy );
-
- //Look for our current enemy
- bool bEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() ) && GetEnemy()->IsAlive();
-
- //Calculate dir and dist to enemy
- Vector vecDirToEnemy = vecMidEnemy - vecMid;
- float flDistToEnemy = VectorNormalize( vecDirToEnemy );
-
- //We want to look at the enemy's eyes so we don't jitter
- Vector vecDirToEnemyEyes = GetEnemy()->WorldSpaceCenter() - vecMid;
- VectorNormalize( vecDirToEnemyEyes );
-
- QAngle vecAnglesToEnemy;
- VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy );
-
- //Draw debug info
- if ( g_debug_turret_ceiling.GetBool() )
- {
- NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
- NDebugOverlay::Cross3D( GetEnemy()->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
- NDebugOverlay::Line( vecMid, GetEnemy()->WorldSpaceCenter(), 0, 255, 0, false, 0.05 );
-
- NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
- NDebugOverlay::Cross3D( vecMidEnemy, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
- NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.05f );
- }
-
- //Current enemy is not visible
- if ( ( bEnemyVisible == false ) || ( flDistToEnemy > CEILING_TURRET_RANGE ))
- {
- if ( m_flLastSight )
- {
- m_flLastSight = gpGlobals->curtime + 0.5f;
- }
- else if ( gpGlobals->curtime > m_flLastSight )
- {
- // Should we look for a new target?
- ClearEnemyMemory();
- SetEnemy( NULL );
- SetLastSightTime();
- SetThink( &CNPC_CeilingTurret::SearchThink );
- m_vecGoalAngles = GetAbsAngles();
-
- SpinDown();
-
- return;
- }
-
- bEnemyVisible = false;
- }
-
- Vector vecMuzzle, vecMuzzleDir;
- QAngle vecMuzzleAng;
-
- GetAttachment( "eyes", vecMuzzle, vecMuzzleAng );
- AngleVectors( vecMuzzleAng, &vecMuzzleDir );
-
- if ( m_flShotTime < gpGlobals->curtime )
- {
- //Fire the gun
- if ( DotProduct( vecDirToEnemy, vecMuzzleDir ) >= 0.9848 ) // 10 degree slop
- {
- if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
- {
- SetActivity( (Activity) ACT_CEILING_TURRET_DRYFIRE );
- }
- else
- {
- SetActivity( (Activity) ACT_CEILING_TURRET_FIRE );
- }
-
- //Fire the weapon
- Shoot( vecMuzzle, vecMuzzleDir );
- }
- }
- else
- {
- SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
- }
-
- //If we can see our enemy, face it
- if ( bEnemyVisible )
- {
- m_vecGoalAngles.y = vecAnglesToEnemy.y;
- m_vecGoalAngles.x = vecAnglesToEnemy.x;
- }
-
- //Turn to face
- UpdateFacing();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Target doesn't exist or has eluded us, so search for one
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::SearchThink( void )
-{
- //Allow descended classes a chance to do something before the think function
- if ( PreThink( TURRET_SEARCHING ) )
- return;
-
- SetNextThink( gpGlobals->curtime + 0.05f );
-
- SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
-
- //If our enemy has died, pick a new enemy
- if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
- {
- SetEnemy( NULL );
- }
-
- //Acquire the target
- if ( GetEnemy() == NULL )
- {
- GetSenses()->Look( CEILING_TURRET_RANGE );
- CBaseEntity *pEnemy = BestEnemy();
- if ( pEnemy )
- {
- SetEnemy( pEnemy );
- }
- }
-
- //If we've found a target, spin up the barrel and start to attack
- if ( GetEnemy() != NULL )
- {
- //Give players a grace period
- if ( GetEnemy()->IsPlayer() )
- {
- m_flShotTime = gpGlobals->curtime + 0.5f;
- }
- else
- {
- m_flShotTime = gpGlobals->curtime + 0.1f;
- }
-
- m_flLastSight = 0;
- SetThink( &CNPC_CeilingTurret::ActiveThink );
- SetEyeState( TURRET_EYE_SEE_TARGET );
-
- SpinUp();
- EmitSound( "NPC_CeilingTurret.Active" );
- return;
- }
-
- //Are we out of time and need to retract?
- if ( gpGlobals->curtime > m_flLastSight )
- {
- //Before we retrace, make sure that we are spun down.
- m_flLastSight = 0;
- SetThink( &CNPC_CeilingTurret::Retire );
- return;
- }
-
- //Display that we're scanning
- m_vecGoalAngles.x = 15.0f;
- m_vecGoalAngles.y = GetAbsAngles().y + ( sin( gpGlobals->curtime * 2.0f ) * 45.0f );
-
- //Turn and ping
- UpdateFacing();
- Ping();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Watch for a target to wander into our view
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::AutoSearchThink( void )
-{
- //Allow descended classes a chance to do something before the think function
- if ( PreThink( TURRET_AUTO_SEARCHING ) )
- return;
-
- //Spread out our thinking
- SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2f, 0.4f ) );
-
- //If the enemy is dead, find a new one
- if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
- {
- SetEnemy( NULL );
- }
-
- //Acquire Target
- if ( GetEnemy() == NULL )
- {
- GetSenses()->Look( CEILING_TURRET_RANGE );
- SetEnemy( BestEnemy() );
- }
-
- //Deploy if we've got an active target
- if ( GetEnemy() != NULL )
- {
- SetThink( &CNPC_CeilingTurret::Deploy );
- EmitSound( "NPC_CeilingTurret.Alert" );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Fire!
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy )
-{
- if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
- {
- EmitSound( "NPC_FloorTurret.DryFire");
- EmitSound( "NPC_CeilingTurret.Activate" );
-
- if ( RandomFloat( 0, 1 ) > 0.7 )
- {
- m_flShotTime = gpGlobals->curtime + random->RandomFloat( 0.5, 1.5 );
- }
- else
- {
- m_flShotTime = gpGlobals->curtime;
- }
- return;
- }
-
- FireBulletsInfo_t info;
-
- if ( GetEnemy() != NULL )
- {
- Vector vecDir = GetActualShootTrajectory( vecSrc );
-
- info.m_vecSrc = vecSrc;
- info.m_vecDirShooting = vecDir;
- info.m_iTracerFreq = 1;
- info.m_iShots = 1;
- info.m_pAttacker = this;
- info.m_vecSpread = VECTOR_CONE_PRECALCULATED;
- info.m_flDistance = MAX_COORD_RANGE;
- info.m_iAmmoType = m_iAmmoType;
- }
- else
- {
- // Just shoot where you're facing!
- Vector vecMuzzle, vecMuzzleDir;
- QAngle vecMuzzleAng;
-
- info.m_vecSrc = vecSrc;
- info.m_vecDirShooting = vecDirToEnemy;
- info.m_iTracerFreq = 1;
- info.m_iShots = 1;
- info.m_pAttacker = this;
- info.m_vecSpread = GetAttackSpread( NULL, NULL );
- info.m_flDistance = MAX_COORD_RANGE;
- info.m_iAmmoType = m_iAmmoType;
- }
-
- FireBullets( info );
- EmitSound( "NPC_CeilingTurret.ShotSounds" );
- DoMuzzleFlash();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Allows a generic think function before the others are called
-// Input : state - which state the turret is currently in
-//-----------------------------------------------------------------------------
-bool CNPC_CeilingTurret::PreThink( turretState_e state )
-{
- CheckPVSCondition();
-
- //Animate
- StudioFrameAdvance();
-
- //Do not interrupt current think function
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets the state of the glowing eye attached to the turret
-// Input : state - state the eye should be in
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::SetEyeState( eyeState_t state )
-{
- //Must have a valid eye to affect
- if ( m_pEyeGlow == NULL )
- return;
-
- //Set the state
- switch( state )
- {
- default:
- case TURRET_EYE_SEE_TARGET: //Fade in and scale up
- m_pEyeGlow->SetColor( 255, 0, 0 );
- m_pEyeGlow->SetBrightness( 164, 0.1f );
- m_pEyeGlow->SetScale( 0.4f, 0.1f );
- break;
-
- case TURRET_EYE_SEEKING_TARGET: //Ping-pongs
-
- //Toggle our state
- m_bBlinkState = !m_bBlinkState;
- m_pEyeGlow->SetColor( 255, 128, 0 );
-
- if ( m_bBlinkState )
- {
- //Fade up and scale up
- m_pEyeGlow->SetScale( 0.25f, 0.1f );
- m_pEyeGlow->SetBrightness( 164, 0.1f );
- }
- else
- {
- //Fade down and scale down
- m_pEyeGlow->SetScale( 0.2f, 0.1f );
- m_pEyeGlow->SetBrightness( 64, 0.1f );
- }
-
- break;
-
- case TURRET_EYE_DORMANT: //Fade out and scale down
- m_pEyeGlow->SetColor( 0, 255, 0 );
- m_pEyeGlow->SetScale( 0.1f, 0.5f );
- m_pEyeGlow->SetBrightness( 64, 0.5f );
- break;
-
- case TURRET_EYE_DEAD: //Fade out slowly
- m_pEyeGlow->SetColor( 255, 0, 0 );
- m_pEyeGlow->SetScale( 0.1f, 3.0f );
- m_pEyeGlow->SetBrightness( 0, 3.0f );
- break;
-
- case TURRET_EYE_DISABLED:
- m_pEyeGlow->SetColor( 0, 255, 0 );
- m_pEyeGlow->SetScale( 0.1f, 1.0f );
- m_pEyeGlow->SetBrightness( 0, 1.0f );
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Make a pinging noise so the player knows where we are
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Ping( void )
-{
- //See if it's time to ping again
- if ( m_flPingTime > gpGlobals->curtime )
- return;
-
- //Ping!
- EmitSound( "NPC_CeilingTurret.Ping" );
-
- SetEyeState( TURRET_EYE_SEEKING_TARGET );
-
- m_flPingTime = gpGlobals->curtime + CEILING_TURRET_PING_TIME;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Toggle the turret's state
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Toggle( void )
-{
- //Toggle the state
- if ( m_bEnabled )
- {
- Disable();
- }
- else
- {
- Enable();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Enable the turret and deploy
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Enable( void )
-{
- m_bEnabled = true;
-
- // if the turret is flagged as an autoactivate turret, re-enable its ability open self.
- if ( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE )
- {
- m_bAutoStart = true;
- }
-
- SetThink( &CNPC_CeilingTurret::Deploy );
- SetNextThink( gpGlobals->curtime + 0.05f );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Retire the turret until enabled again
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::Disable( void )
-{
- m_bEnabled = false;
- m_bAutoStart = false;
-
- SetEnemy( NULL );
- SetThink( &CNPC_CeilingTurret::Retire );
- SetNextThink( gpGlobals->curtime + 0.1f );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Toggle the turret's state via input function
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::InputToggle( inputdata_t &inputdata )
-{
- Toggle();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::InputEnable( inputdata_t &inputdata )
-{
- Enable();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::InputDisable( inputdata_t &inputdata )
-{
- Disable();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::SpinUp( void )
-{
-}
-
-#define CEILING_TURRET_MIN_SPIN_DOWN 1.0f
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::SpinDown( void )
-{
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::DeathThink( void )
-{
- if ( PreThink( TURRET_DEAD ) )
- return;
-
- //Level out our angles
- m_vecGoalAngles = GetAbsAngles();
- SetNextThink( gpGlobals->curtime );
-
- if ( m_lifeState != LIFE_DEAD )
- {
- m_lifeState = LIFE_DEAD;
-
- EmitSound( "NPC_CeilingTurret.Die" );
-
- SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE );
- }
-
- // lots of smoke
- Vector pos;
- CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos );
-
- CBroadcastRecipientFilter filter;
-
- te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, 2.5, 10 );
-
- g_pEffects->Sparks( pos );
-
- if ( IsActivityFinished() && ( UpdateFacing() == false ) )
- {
- SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
-
- m_flPlaybackRate = 0;
- SetThink( NULL );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : height -
-//-----------------------------------------------------------------------------
-void CNPC_CeilingTurret::SetHeight( float height )
-{
- Vector forward, right, up;
- AngleVectors( GetLocalAngles(), &forward, &right, &up );
-
- Vector mins = ( forward * -16.0f ) + ( right * -16.0f );
- Vector maxs = ( forward * 16.0f ) + ( right * 16.0f ) + ( up * -height );
-
- if ( mins.x > maxs.x )
- {
- V_swap( mins.x, maxs.x );
- }
-
- if ( mins.y > maxs.y )
- {
- V_swap( mins.y, maxs.y );
- }
-
- if ( mins.z > maxs.z )
- {
- V_swap( mins.z, maxs.z );
- }
-
- SetCollisionBounds( mins, maxs );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEnemy -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_CeilingTurret::CanBeAnEnemyOf( CBaseEntity *pEnemy )
-{
- // If we're out of ammo, make friendly companions ignore us
- if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
- {
- if ( pEnemy->Classify() == CLASS_PLAYER_ALLY_VITAL )
- return false;
- }
-
- return BaseClass::CanBeAnEnemyOf( pEnemy );
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "ai_basenpc.h"
+#include "ai_senses.h"
+#include "ai_memory.h"
+#include "engine/IEngineSound.h"
+#include "ammodef.h"
+#include "Sprite.h"
+#include "hl2/hl2_player.h"
+#include "soundenvelope.h"
+#include "explode.h"
+#include "IEffects.h"
+#include "animation.h"
+#include "basehlcombatweapon_shared.h"
+#include "iservervehicle.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//Debug visualization
+ConVar g_debug_turret_ceiling( "g_debug_turret_ceiling", "0" );
+
+#define CEILING_TURRET_MODEL "models/combine_turrets/ceiling_turret.mdl"
+#define CEILING_TURRET_GLOW_SPRITE "sprites/glow1.vmt"
+/* // we now inherit these from the ai_basenpc baseclass
+#define CEILING_TURRET_BC_YAW "aim_yaw"
+#define CEILING_TURRET_BC_PITCH "aim_pitch"
+*/
+#define CEILING_TURRET_RANGE 1500
+#define CEILING_TURRET_SPREAD VECTOR_CONE_2DEGREES
+#define CEILING_TURRET_MAX_WAIT 5
+#define CEILING_TURRET_PING_TIME 1.0f //LPB!!
+
+#define CEILING_TURRET_VOICE_PITCH_LOW 45
+#define CEILING_TURRET_VOICE_PITCH_HIGH 100
+
+//Aiming variables
+#define CEILING_TURRET_MAX_NOHARM_PERIOD 0.0f
+#define CEILING_TURRET_MAX_GRACE_PERIOD 3.0f
+
+//Spawnflags
+#define SF_CEILING_TURRET_AUTOACTIVATE 0x00000020
+#define SF_CEILING_TURRET_STARTINACTIVE 0x00000040
+#define SF_CEILING_TURRET_NEVERRETIRE 0x00000080
+#define SF_CEILING_TURRET_OUT_OF_AMMO 0x00000100
+
+//Heights
+#define CEILING_TURRET_RETRACT_HEIGHT 24
+#define CEILING_TURRET_DEPLOY_HEIGHT 64
+
+//Activities
+int ACT_CEILING_TURRET_OPEN;
+int ACT_CEILING_TURRET_CLOSE;
+int ACT_CEILING_TURRET_OPEN_IDLE;
+int ACT_CEILING_TURRET_CLOSED_IDLE;
+int ACT_CEILING_TURRET_FIRE;
+int ACT_CEILING_TURRET_DRYFIRE;
+
+//Turret states
+enum turretState_e
+{
+ TURRET_SEARCHING,
+ TURRET_AUTO_SEARCHING,
+ TURRET_ACTIVE,
+ TURRET_DEPLOYING,
+ TURRET_RETIRING,
+ TURRET_DEAD,
+};
+
+//Eye states
+enum eyeState_t
+{
+ TURRET_EYE_SEE_TARGET, //Sees the target, bright and big
+ TURRET_EYE_SEEKING_TARGET, //Looking for a target, blinking (bright)
+ TURRET_EYE_DORMANT, //Not active
+ TURRET_EYE_DEAD, //Completely invisible
+ TURRET_EYE_DISABLED, //Turned off, must be reactivated before it'll deploy again (completely invisible)
+};
+
+//
+// Ceiling Turret
+//
+
+class CNPC_CeilingTurret : public CAI_BaseNPC
+{
+ DECLARE_CLASS( CNPC_CeilingTurret, CAI_BaseNPC );
+public:
+
+ CNPC_CeilingTurret( void );
+ ~CNPC_CeilingTurret( void );
+
+ void Precache( void );
+ void Spawn( void );
+
+ // Think functions
+ void Retire( void );
+ void Deploy( void );
+ void ActiveThink( void );
+ void SearchThink( void );
+ void AutoSearchThink( void );
+ void DeathThink( void );
+
+ // Inputs
+ void InputToggle( inputdata_t &inputdata );
+ void InputEnable( inputdata_t &inputdata );
+ void InputDisable( inputdata_t &inputdata );
+
+ void SetLastSightTime();
+
+ float MaxYawSpeed( void );
+
+ int OnTakeDamage( const CTakeDamageInfo &inputInfo );
+
+ virtual bool CanBeAnEnemyOf( CBaseEntity *pEnemy );
+
+ Class_T Classify( void )
+ {
+ if( m_bEnabled )
+ return CLASS_COMBINE;
+
+ return CLASS_NONE;
+ }
+
+ bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
+
+ Vector EyeOffset( Activity nActivity )
+ {
+ Vector vecEyeOffset(0,0,-64);
+ GetEyePosition( GetModelPtr(), vecEyeOffset );
+ return vecEyeOffset;
+ }
+
+ Vector EyePosition( void )
+ {
+ return GetAbsOrigin() + EyeOffset(GetActivity());
+ }
+
+ Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget )
+ {
+ return VECTOR_CONE_5DEGREES * ((CBaseHLCombatWeapon::GetDefaultProficiencyValues())[ WEAPON_PROFICIENCY_PERFECT ].spreadscale);
+ }
+
+protected:
+
+ bool PreThink( turretState_e state );
+ void Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy );
+ void SetEyeState( eyeState_t state );
+ void Ping( void );
+ void Toggle( void );
+ void Enable( void );
+ void Disable( void );
+ void SpinUp( void );
+ void SpinDown( void );
+ void SetHeight( float height );
+
+ bool UpdateFacing( void );
+
+ int m_iAmmoType;
+ int m_iMinHealthDmg;
+
+ bool m_bAutoStart;
+ bool m_bActive; //Denotes the turret is deployed and looking for targets
+ bool m_bBlinkState;
+ bool m_bEnabled; //Denotes whether the turret is able to deploy or not
+
+ float m_flShotTime;
+ float m_flLastSight;
+ float m_flPingTime;
+
+ QAngle m_vecGoalAngles;
+
+ CSprite *m_pEyeGlow;
+
+ COutputEvent m_OnDeploy;
+ COutputEvent m_OnRetire;
+ COutputEvent m_OnTipped;
+
+ DECLARE_DATADESC();
+};
+
+//Datatable
+BEGIN_DATADESC( CNPC_CeilingTurret )
+
+ DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
+ DEFINE_KEYFIELD( m_iMinHealthDmg, FIELD_INTEGER, "minhealthdmg" ),
+ DEFINE_FIELD( m_bAutoStart, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bBlinkState, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flShotTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flLastSight, FIELD_TIME ),
+ DEFINE_FIELD( m_flPingTime, FIELD_TIME ),
+ DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ),
+ DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ),
+
+ DEFINE_THINKFUNC( Retire ),
+ DEFINE_THINKFUNC( Deploy ),
+ DEFINE_THINKFUNC( ActiveThink ),
+ DEFINE_THINKFUNC( SearchThink ),
+ DEFINE_THINKFUNC( AutoSearchThink ),
+ DEFINE_THINKFUNC( DeathThink ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+
+ DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ),
+ DEFINE_OUTPUT( m_OnRetire, "OnRetire" ),
+ DEFINE_OUTPUT( m_OnTipped, "OnTipped" ),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( npc_turret_ceiling, CNPC_CeilingTurret );
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CNPC_CeilingTurret::CNPC_CeilingTurret( void )
+{
+ m_bActive = false;
+ m_pEyeGlow = NULL;
+ m_iAmmoType = -1;
+ m_iMinHealthDmg = 0;
+ m_bAutoStart = false;
+ m_flPingTime = 0;
+ m_flShotTime = 0;
+ m_flLastSight = 0;
+ m_bBlinkState = false;
+ m_bEnabled = false;
+
+ m_vecGoalAngles.Init();
+}
+
+CNPC_CeilingTurret::~CNPC_CeilingTurret( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Precache( void )
+{
+ PrecacheModel( CEILING_TURRET_MODEL );
+ PrecacheModel( CEILING_TURRET_GLOW_SPRITE );
+
+ // Activities
+ ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN );
+ ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSE );
+ ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSED_IDLE );
+ ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN_IDLE );
+ ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_FIRE );
+ ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_DRYFIRE );
+
+ PrecacheScriptSound( "NPC_CeilingTurret.Retire" );
+ PrecacheScriptSound( "NPC_CeilingTurret.Deploy" );
+ PrecacheScriptSound( "NPC_CeilingTurret.Move" );
+ PrecacheScriptSound( "NPC_CeilingTurret.Active" );
+ PrecacheScriptSound( "NPC_CeilingTurret.Alert" );
+ PrecacheScriptSound( "NPC_CeilingTurret.ShotSounds" );
+ PrecacheScriptSound( "NPC_CeilingTurret.Ping" );
+ PrecacheScriptSound( "NPC_CeilingTurret.Die" );
+
+ PrecacheScriptSound( "NPC_FloorTurret.DryFire" );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Spawn the entity
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Spawn( void )
+{
+ Precache();
+
+ SetModel( CEILING_TURRET_MODEL );
+
+ BaseClass::Spawn();
+
+ m_HackedGunPos = Vector( 0, 0, 12.75 );
+ SetViewOffset( EyeOffset( ACT_IDLE ) );
+ m_flFieldOfView = 0.0f;
+ m_takedamage = DAMAGE_YES;
+ m_iHealth = 1000;
+ m_bloodColor = BLOOD_COLOR_MECH;
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+
+ SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
+
+ AddFlag( FL_AIMTARGET );
+ AddEFlags( EFL_NO_DISSOLVE );
+
+ SetPoseParameter( m_poseAim_Yaw, 0 );
+ SetPoseParameter( m_poseAim_Pitch, 0 );
+
+ m_iAmmoType = GetAmmoDef()->Index( "AR2" );
+
+ //Create our eye sprite
+ m_pEyeGlow = CSprite::SpriteCreate( CEILING_TURRET_GLOW_SPRITE, GetLocalOrigin(), false );
+ m_pEyeGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation );
+ m_pEyeGlow->SetAttachment( this, 2 );
+
+ //Set our autostart state
+ m_bAutoStart = !!( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE );
+ m_bEnabled = ( ( m_spawnflags & SF_CEILING_TURRET_STARTINACTIVE ) == false );
+
+ //Do we start active?
+ if ( m_bAutoStart && m_bEnabled )
+ {
+ SetThink( &CNPC_CeilingTurret::AutoSearchThink );
+ SetEyeState( TURRET_EYE_DORMANT );
+ }
+ else
+ {
+ SetEyeState( TURRET_EYE_DISABLED );
+ }
+
+ //Stagger our starting times
+ SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ) );
+
+ // Don't allow us to skip animation setup because our attachments are critical to us!
+ SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CNPC_CeilingTurret::OnTakeDamage( const CTakeDamageInfo &inputInfo )
+{
+ if ( !m_takedamage )
+ return 0;
+
+ CTakeDamageInfo info = inputInfo;
+
+ if ( m_bActive == false )
+ info.ScaleDamage( 0.1f );
+
+ // If attacker can't do at least the min required damage to us, don't take any damage from them
+ if ( info.GetDamage() < m_iMinHealthDmg )
+ return 0;
+
+ m_iHealth -= info.GetDamage();
+
+ if ( m_iHealth <= 0 )
+ {
+ m_iHealth = 0;
+ m_takedamage = DAMAGE_NO;
+
+ RemoveFlag( FL_NPC ); // why are they set in the first place???
+
+ //FIXME: This needs to throw a ragdoll gib or something other than animating the retraction -- jdw
+
+ ExplosionCreate( GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false );
+ SetThink( &CNPC_CeilingTurret::DeathThink );
+
+ StopSound( "NPC_CeilingTurret.Alert" );
+
+ m_OnDamaged.FireOutput( info.GetInflictor(), this );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ return 0;
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retract and stop attacking
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Retire( void )
+{
+ if ( PreThink( TURRET_RETIRING ) )
+ return;
+
+ //Level out the turret
+ m_vecGoalAngles = GetAbsAngles();
+ SetNextThink( gpGlobals->curtime );
+
+ //Set ourselves to close
+ if ( GetActivity() != ACT_CEILING_TURRET_CLOSE )
+ {
+ //Set our visible state to dormant
+ SetEyeState( TURRET_EYE_DORMANT );
+
+ SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
+
+ //If we're done moving to our desired facing, close up
+ if ( UpdateFacing() == false )
+ {
+ SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE );
+ EmitSound( "NPC_CeilingTurret.Retire" );
+
+ //Notify of the retraction
+ m_OnRetire.FireOutput( NULL, this );
+ }
+ }
+ else if ( IsActivityFinished() )
+ {
+ SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
+
+ m_bActive = false;
+ m_flLastSight = 0;
+
+ SetActivity( (Activity) ACT_CEILING_TURRET_CLOSED_IDLE );
+
+ //Go back to auto searching
+ if ( m_bAutoStart )
+ {
+ SetThink( &CNPC_CeilingTurret::AutoSearchThink );
+ SetNextThink( gpGlobals->curtime + 0.05f );
+ }
+ else
+ {
+ //Set our visible state to dormant
+ SetEyeState( TURRET_EYE_DISABLED );
+ SetThink( &CNPC_CeilingTurret::SUB_DoNothing );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deploy and start attacking
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Deploy( void )
+{
+ if ( PreThink( TURRET_DEPLOYING ) )
+ return;
+
+ m_vecGoalAngles = GetAbsAngles();
+
+ SetNextThink( gpGlobals->curtime );
+
+ //Show we've seen a target
+ SetEyeState( TURRET_EYE_SEE_TARGET );
+
+ //Open if we're not already
+ if ( GetActivity() != ACT_CEILING_TURRET_OPEN )
+ {
+ m_bActive = true;
+ SetActivity( (Activity) ACT_CEILING_TURRET_OPEN );
+ EmitSound( "NPC_CeilingTurret.Deploy" );
+
+ //Notify we're deploying
+ m_OnDeploy.FireOutput( NULL, this );
+ }
+
+ //If we're done, then start searching
+ if ( IsActivityFinished() )
+ {
+ SetHeight( CEILING_TURRET_DEPLOY_HEIGHT );
+
+ SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
+
+ m_flShotTime = gpGlobals->curtime + 1.0f;
+
+ m_flPlaybackRate = 0;
+ SetThink( &CNPC_CeilingTurret::SearchThink );
+
+ EmitSound( "NPC_CeilingTurret.Move" );
+ }
+
+ SetLastSightTime();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::SetLastSightTime()
+{
+ if( HasSpawnFlags( SF_CEILING_TURRET_NEVERRETIRE ) )
+ {
+ m_flLastSight = FLT_MAX;
+ }
+ else
+ {
+ m_flLastSight = gpGlobals->curtime + CEILING_TURRET_MAX_WAIT;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the speed at which the turret can face a target
+//-----------------------------------------------------------------------------
+float CNPC_CeilingTurret::MaxYawSpeed( void )
+{
+ //TODO: Scale by difficulty?
+ return 360.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Causes the turret to face its desired angles
+//-----------------------------------------------------------------------------
+bool CNPC_CeilingTurret::UpdateFacing( void )
+{
+ bool bMoved = false;
+ matrix3x4_t localToWorld;
+
+ GetAttachment( LookupAttachment( "eyes" ), localToWorld );
+
+ Vector vecGoalDir;
+ AngleVectors( m_vecGoalAngles, &vecGoalDir );
+
+ Vector vecGoalLocalDir;
+ VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir );
+
+ if ( g_debug_turret_ceiling.GetBool() )
+ {
+ Vector vecMuzzle, vecMuzzleDir;
+ QAngle vecMuzzleAng;
+
+ GetAttachment( "eyes", vecMuzzle, vecMuzzleAng );
+ AngleVectors( vecMuzzleAng, &vecMuzzleDir );
+
+ NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 );
+ NDebugOverlay::Cross3D( vecMuzzle+(vecMuzzleDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 );
+ NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecMuzzleDir*256), 255, 255, 0, false, 0.05 );
+
+ NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 );
+ NDebugOverlay::Cross3D( vecMuzzle+(vecGoalDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 );
+ NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecGoalDir*256), 255, 0, 0, false, 0.05 );
+ }
+
+ QAngle vecGoalLocalAngles;
+ VectorAngles( vecGoalLocalDir, vecGoalLocalAngles );
+
+ // Update pitch
+ float flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed() ) );
+
+ SetPoseParameter( m_poseAim_Pitch, GetPoseParameter( m_poseAim_Pitch ) + ( flDiff / 1.5f ) );
+
+ if ( fabs( flDiff ) > 0.1f )
+ {
+ bMoved = true;
+ }
+
+ // Update yaw
+ flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed() ) );
+
+ SetPoseParameter( m_poseAim_Yaw, GetPoseParameter( m_poseAim_Yaw ) + ( flDiff / 1.5f ) );
+
+ if ( fabs( flDiff ) > 0.1f )
+ {
+ bMoved = true;
+ }
+
+ InvalidateBoneCache();
+
+ return bMoved;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEntity -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CNPC_CeilingTurret::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
+{
+ CBaseEntity *pHitEntity = NULL;
+ if ( BaseClass::FVisible( pEntity, traceMask, &pHitEntity ) )
+ return true;
+
+ // If we hit something that's okay to hit anyway, still fire
+ if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() )
+ {
+ if (IRelationType(pHitEntity) == D_HT)
+ return true;
+ }
+
+ if (ppBlocker)
+ {
+ *ppBlocker = pHitEntity;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows the turret to fire on targets if they're visible
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::ActiveThink( void )
+{
+ //Allow descended classes a chance to do something before the think function
+ if ( PreThink( TURRET_ACTIVE ) )
+ return;
+
+ //Update our think time
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ //If we've become inactive, go back to searching
+ if ( ( m_bActive == false ) || ( GetEnemy() == NULL ) )
+ {
+ SetEnemy( NULL );
+ SetLastSightTime();
+ SetThink( &CNPC_CeilingTurret::SearchThink );
+ m_vecGoalAngles = GetAbsAngles();
+ return;
+ }
+
+ //Get our shot positions
+ Vector vecMid = EyePosition();
+ Vector vecMidEnemy = GetEnemy()->GetAbsOrigin();
+
+ //Store off our last seen location
+ UpdateEnemyMemory( GetEnemy(), vecMidEnemy );
+
+ //Look for our current enemy
+ bool bEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() ) && GetEnemy()->IsAlive();
+
+ //Calculate dir and dist to enemy
+ Vector vecDirToEnemy = vecMidEnemy - vecMid;
+ float flDistToEnemy = VectorNormalize( vecDirToEnemy );
+
+ //We want to look at the enemy's eyes so we don't jitter
+ Vector vecDirToEnemyEyes = GetEnemy()->WorldSpaceCenter() - vecMid;
+ VectorNormalize( vecDirToEnemyEyes );
+
+ QAngle vecAnglesToEnemy;
+ VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy );
+
+ //Draw debug info
+ if ( g_debug_turret_ceiling.GetBool() )
+ {
+ NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
+ NDebugOverlay::Cross3D( GetEnemy()->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
+ NDebugOverlay::Line( vecMid, GetEnemy()->WorldSpaceCenter(), 0, 255, 0, false, 0.05 );
+
+ NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
+ NDebugOverlay::Cross3D( vecMidEnemy, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
+ NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.05f );
+ }
+
+ //Current enemy is not visible
+ if ( ( bEnemyVisible == false ) || ( flDistToEnemy > CEILING_TURRET_RANGE ))
+ {
+ if ( m_flLastSight )
+ {
+ m_flLastSight = gpGlobals->curtime + 0.5f;
+ }
+ else if ( gpGlobals->curtime > m_flLastSight )
+ {
+ // Should we look for a new target?
+ ClearEnemyMemory();
+ SetEnemy( NULL );
+ SetLastSightTime();
+ SetThink( &CNPC_CeilingTurret::SearchThink );
+ m_vecGoalAngles = GetAbsAngles();
+
+ SpinDown();
+
+ return;
+ }
+
+ bEnemyVisible = false;
+ }
+
+ Vector vecMuzzle, vecMuzzleDir;
+ QAngle vecMuzzleAng;
+
+ GetAttachment( "eyes", vecMuzzle, vecMuzzleAng );
+ AngleVectors( vecMuzzleAng, &vecMuzzleDir );
+
+ if ( m_flShotTime < gpGlobals->curtime )
+ {
+ //Fire the gun
+ if ( DotProduct( vecDirToEnemy, vecMuzzleDir ) >= 0.9848 ) // 10 degree slop
+ {
+ if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
+ {
+ SetActivity( (Activity) ACT_CEILING_TURRET_DRYFIRE );
+ }
+ else
+ {
+ SetActivity( (Activity) ACT_CEILING_TURRET_FIRE );
+ }
+
+ //Fire the weapon
+ Shoot( vecMuzzle, vecMuzzleDir );
+ }
+ }
+ else
+ {
+ SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
+ }
+
+ //If we can see our enemy, face it
+ if ( bEnemyVisible )
+ {
+ m_vecGoalAngles.y = vecAnglesToEnemy.y;
+ m_vecGoalAngles.x = vecAnglesToEnemy.x;
+ }
+
+ //Turn to face
+ UpdateFacing();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Target doesn't exist or has eluded us, so search for one
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::SearchThink( void )
+{
+ //Allow descended classes a chance to do something before the think function
+ if ( PreThink( TURRET_SEARCHING ) )
+ return;
+
+ SetNextThink( gpGlobals->curtime + 0.05f );
+
+ SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );
+
+ //If our enemy has died, pick a new enemy
+ if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
+ {
+ SetEnemy( NULL );
+ }
+
+ //Acquire the target
+ if ( GetEnemy() == NULL )
+ {
+ GetSenses()->Look( CEILING_TURRET_RANGE );
+ CBaseEntity *pEnemy = BestEnemy();
+ if ( pEnemy )
+ {
+ SetEnemy( pEnemy );
+ }
+ }
+
+ //If we've found a target, spin up the barrel and start to attack
+ if ( GetEnemy() != NULL )
+ {
+ //Give players a grace period
+ if ( GetEnemy()->IsPlayer() )
+ {
+ m_flShotTime = gpGlobals->curtime + 0.5f;
+ }
+ else
+ {
+ m_flShotTime = gpGlobals->curtime + 0.1f;
+ }
+
+ m_flLastSight = 0;
+ SetThink( &CNPC_CeilingTurret::ActiveThink );
+ SetEyeState( TURRET_EYE_SEE_TARGET );
+
+ SpinUp();
+ EmitSound( "NPC_CeilingTurret.Active" );
+ return;
+ }
+
+ //Are we out of time and need to retract?
+ if ( gpGlobals->curtime > m_flLastSight )
+ {
+ //Before we retrace, make sure that we are spun down.
+ m_flLastSight = 0;
+ SetThink( &CNPC_CeilingTurret::Retire );
+ return;
+ }
+
+ //Display that we're scanning
+ m_vecGoalAngles.x = 15.0f;
+ m_vecGoalAngles.y = GetAbsAngles().y + ( sin( gpGlobals->curtime * 2.0f ) * 45.0f );
+
+ //Turn and ping
+ UpdateFacing();
+ Ping();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Watch for a target to wander into our view
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::AutoSearchThink( void )
+{
+ //Allow descended classes a chance to do something before the think function
+ if ( PreThink( TURRET_AUTO_SEARCHING ) )
+ return;
+
+ //Spread out our thinking
+ SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2f, 0.4f ) );
+
+ //If the enemy is dead, find a new one
+ if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
+ {
+ SetEnemy( NULL );
+ }
+
+ //Acquire Target
+ if ( GetEnemy() == NULL )
+ {
+ GetSenses()->Look( CEILING_TURRET_RANGE );
+ SetEnemy( BestEnemy() );
+ }
+
+ //Deploy if we've got an active target
+ if ( GetEnemy() != NULL )
+ {
+ SetThink( &CNPC_CeilingTurret::Deploy );
+ EmitSound( "NPC_CeilingTurret.Alert" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire!
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy )
+{
+ if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
+ {
+ EmitSound( "NPC_FloorTurret.DryFire");
+ EmitSound( "NPC_CeilingTurret.Activate" );
+
+ if ( RandomFloat( 0, 1 ) > 0.7 )
+ {
+ m_flShotTime = gpGlobals->curtime + random->RandomFloat( 0.5, 1.5 );
+ }
+ else
+ {
+ m_flShotTime = gpGlobals->curtime;
+ }
+ return;
+ }
+
+ FireBulletsInfo_t info;
+
+ if ( GetEnemy() != NULL )
+ {
+ Vector vecDir = GetActualShootTrajectory( vecSrc );
+
+ info.m_vecSrc = vecSrc;
+ info.m_vecDirShooting = vecDir;
+ info.m_iTracerFreq = 1;
+ info.m_iShots = 1;
+ info.m_pAttacker = this;
+ info.m_vecSpread = VECTOR_CONE_PRECALCULATED;
+ info.m_flDistance = MAX_COORD_RANGE;
+ info.m_iAmmoType = m_iAmmoType;
+ }
+ else
+ {
+ // Just shoot where you're facing!
+ Vector vecMuzzle, vecMuzzleDir;
+ QAngle vecMuzzleAng;
+
+ info.m_vecSrc = vecSrc;
+ info.m_vecDirShooting = vecDirToEnemy;
+ info.m_iTracerFreq = 1;
+ info.m_iShots = 1;
+ info.m_pAttacker = this;
+ info.m_vecSpread = GetAttackSpread( NULL, NULL );
+ info.m_flDistance = MAX_COORD_RANGE;
+ info.m_iAmmoType = m_iAmmoType;
+ }
+
+ FireBullets( info );
+ EmitSound( "NPC_CeilingTurret.ShotSounds" );
+ DoMuzzleFlash();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows a generic think function before the others are called
+// Input : state - which state the turret is currently in
+//-----------------------------------------------------------------------------
+bool CNPC_CeilingTurret::PreThink( turretState_e state )
+{
+ CheckPVSCondition();
+
+ //Animate
+ StudioFrameAdvance();
+
+ //Do not interrupt current think function
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the state of the glowing eye attached to the turret
+// Input : state - state the eye should be in
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::SetEyeState( eyeState_t state )
+{
+ //Must have a valid eye to affect
+ if ( m_pEyeGlow == NULL )
+ return;
+
+ //Set the state
+ switch( state )
+ {
+ default:
+ case TURRET_EYE_SEE_TARGET: //Fade in and scale up
+ m_pEyeGlow->SetColor( 255, 0, 0 );
+ m_pEyeGlow->SetBrightness( 164, 0.1f );
+ m_pEyeGlow->SetScale( 0.4f, 0.1f );
+ break;
+
+ case TURRET_EYE_SEEKING_TARGET: //Ping-pongs
+
+ //Toggle our state
+ m_bBlinkState = !m_bBlinkState;
+ m_pEyeGlow->SetColor( 255, 128, 0 );
+
+ if ( m_bBlinkState )
+ {
+ //Fade up and scale up
+ m_pEyeGlow->SetScale( 0.25f, 0.1f );
+ m_pEyeGlow->SetBrightness( 164, 0.1f );
+ }
+ else
+ {
+ //Fade down and scale down
+ m_pEyeGlow->SetScale( 0.2f, 0.1f );
+ m_pEyeGlow->SetBrightness( 64, 0.1f );
+ }
+
+ break;
+
+ case TURRET_EYE_DORMANT: //Fade out and scale down
+ m_pEyeGlow->SetColor( 0, 255, 0 );
+ m_pEyeGlow->SetScale( 0.1f, 0.5f );
+ m_pEyeGlow->SetBrightness( 64, 0.5f );
+ break;
+
+ case TURRET_EYE_DEAD: //Fade out slowly
+ m_pEyeGlow->SetColor( 255, 0, 0 );
+ m_pEyeGlow->SetScale( 0.1f, 3.0f );
+ m_pEyeGlow->SetBrightness( 0, 3.0f );
+ break;
+
+ case TURRET_EYE_DISABLED:
+ m_pEyeGlow->SetColor( 0, 255, 0 );
+ m_pEyeGlow->SetScale( 0.1f, 1.0f );
+ m_pEyeGlow->SetBrightness( 0, 1.0f );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make a pinging noise so the player knows where we are
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Ping( void )
+{
+ //See if it's time to ping again
+ if ( m_flPingTime > gpGlobals->curtime )
+ return;
+
+ //Ping!
+ EmitSound( "NPC_CeilingTurret.Ping" );
+
+ SetEyeState( TURRET_EYE_SEEKING_TARGET );
+
+ m_flPingTime = gpGlobals->curtime + CEILING_TURRET_PING_TIME;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle the turret's state
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Toggle( void )
+{
+ //Toggle the state
+ if ( m_bEnabled )
+ {
+ Disable();
+ }
+ else
+ {
+ Enable();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Enable the turret and deploy
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Enable( void )
+{
+ m_bEnabled = true;
+
+ // if the turret is flagged as an autoactivate turret, re-enable its ability open self.
+ if ( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE )
+ {
+ m_bAutoStart = true;
+ }
+
+ SetThink( &CNPC_CeilingTurret::Deploy );
+ SetNextThink( gpGlobals->curtime + 0.05f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retire the turret until enabled again
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::Disable( void )
+{
+ m_bEnabled = false;
+ m_bAutoStart = false;
+
+ SetEnemy( NULL );
+ SetThink( &CNPC_CeilingTurret::Retire );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle the turret's state via input function
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::InputToggle( inputdata_t &inputdata )
+{
+ Toggle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::InputEnable( inputdata_t &inputdata )
+{
+ Enable();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::InputDisable( inputdata_t &inputdata )
+{
+ Disable();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::SpinUp( void )
+{
+}
+
+#define CEILING_TURRET_MIN_SPIN_DOWN 1.0f
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::SpinDown( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::DeathThink( void )
+{
+ if ( PreThink( TURRET_DEAD ) )
+ return;
+
+ //Level out our angles
+ m_vecGoalAngles = GetAbsAngles();
+ SetNextThink( gpGlobals->curtime );
+
+ if ( m_lifeState != LIFE_DEAD )
+ {
+ m_lifeState = LIFE_DEAD;
+
+ EmitSound( "NPC_CeilingTurret.Die" );
+
+ SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE );
+ }
+
+ // lots of smoke
+ Vector pos;
+ CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos );
+
+ CBroadcastRecipientFilter filter;
+
+ te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, 2.5, 10 );
+
+ g_pEffects->Sparks( pos );
+
+ if ( IsActivityFinished() && ( UpdateFacing() == false ) )
+ {
+ SetHeight( CEILING_TURRET_RETRACT_HEIGHT );
+
+ m_flPlaybackRate = 0;
+ SetThink( NULL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : height -
+//-----------------------------------------------------------------------------
+void CNPC_CeilingTurret::SetHeight( float height )
+{
+ Vector forward, right, up;
+ AngleVectors( GetLocalAngles(), &forward, &right, &up );
+
+ Vector mins = ( forward * -16.0f ) + ( right * -16.0f );
+ Vector maxs = ( forward * 16.0f ) + ( right * 16.0f ) + ( up * -height );
+
+ if ( mins.x > maxs.x )
+ {
+ V_swap( mins.x, maxs.x );
+ }
+
+ if ( mins.y > maxs.y )
+ {
+ V_swap( mins.y, maxs.y );
+ }
+
+ if ( mins.z > maxs.z )
+ {
+ V_swap( mins.z, maxs.z );
+ }
+
+ SetCollisionBounds( mins, maxs );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEnemy -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CNPC_CeilingTurret::CanBeAnEnemyOf( CBaseEntity *pEnemy )
+{
+ // If we're out of ammo, make friendly companions ignore us
+ if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO )
+ {
+ if ( pEnemy->Classify() == CLASS_PLAYER_ALLY_VITAL )
+ return false;
+ }
+
+ return BaseClass::CanBeAnEnemyOf( pEnemy );
} \ No newline at end of file