diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /sp/src/game/server/hl2/npc_turret.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'sp/src/game/server/hl2/npc_turret.cpp')
| -rw-r--r-- | sp/src/game/server/hl2/npc_turret.cpp | 1190 |
1 files changed, 1190 insertions, 0 deletions
diff --git a/sp/src/game/server/hl2/npc_turret.cpp b/sp/src/game/server/hl2/npc_turret.cpp new file mode 100644 index 00000000..78c1c737 --- /dev/null +++ b/sp/src/game/server/hl2/npc_turret.cpp @@ -0,0 +1,1190 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// TODO:
+// Take advantage of new NPC fields like GetEnemy() and get rid of that OFFSET() stuff
+// Revisit enemy validation stuff, maybe it's not necessary with the newest NPC code
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "Sprite.h"
+#include "basecombatweapon.h"
+#include "ai_basenpc.h"
+#include "AI_Senses.h"
+#include "AI_Memory.h"
+#include "gamerules.h"
+#include "ammodef.h"
+#include "ndebugoverlay.h"
+#include "IEffects.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+
+class CSprite;
+
+#define TURRET_RANGE (100 * 12)
+#define TURRET_SPREAD VECTOR_CONE_5DEGREES
+#define TURRET_TURNRATE 360 // max angles per second
+#define TURRET_MAXWAIT 15 // seconds turret will stay active w/o a target
+#define TURRET_MAXSPIN 5 // seconds turret barrel will spin w/o a target
+#define TURRET_MACHINE_VOLUME 0.5
+
+#define TURRET_BC_YAW "aim_yaw"
+#define TURRET_BC_PITCH "aim_pitch"
+
+#define TURRET_ORIENTATION_FLOOR 0
+#define TURRET_ORIENTATION_CEILING 1
+
+//=========================================================
+// private activities
+//=========================================================
+int ACT_TURRET_OPEN;
+int ACT_TURRET_CLOSE;
+int ACT_TURRET_OPEN_IDLE;
+int ACT_TURRET_CLOSED_IDLE;
+int ACT_TURRET_FIRE;
+int ACT_TURRET_RELOAD;
+
+// ===============================================
+// Private spawn flags (must be above (1<<15))
+// ===============================================
+#define SF_NPC_TURRET_AUTOACTIVATE 0x00000020
+#define SF_NPC_TURRET_STARTINACTIVE 0x00000040
+
+extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud
+
+ConVar sk_miniturret_health( "sk_miniturret_health","0");
+ConVar sk_sentry_health( "sk_sentry_health","0");
+ConVar sk_turret_health( "sk_turret_health","0");
+
+class CBaseTurret : public CAI_BaseNPC
+{
+ DECLARE_CLASS( CBaseTurret, CAI_BaseNPC );
+public:
+ void Spawn(void);
+ virtual void Precache(void);
+ bool KeyValue( const char *szKeyName, const char *szValue );
+ //void TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+ virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
+ virtual int OnTakeDamage( const CTakeDamageInfo &info );
+ virtual Class_T Classify(void);
+
+ int BloodColor( void ) { return DONT_BLEED; }
+ bool Event_Gibbed( void ) { return FALSE; } // UNDONE: Throw turret gibs?
+
+ Vector EyeOffset( Activity nActivity );
+ Vector EyePosition( void );
+
+ // Inputs
+ void InputToggle( inputdata_t &inputdata );
+
+ // Think functions
+
+ void ActiveThink(void);
+ void SearchThink(void);
+ void AutoSearchThink(void);
+ void TurretDeath(void);
+
+ void Deploy(void);
+ void Retire(void);
+
+ void Initialize(void);
+
+ virtual void Ping(void);
+ virtual void EyeOn(void);
+ virtual void EyeOff(void);
+
+ DECLARE_DATADESC();
+
+ // other functions
+ int MoveTurret(void);
+ virtual void Shoot(const Vector &vecSrc, const Vector &vecDirToEnemy) { };
+
+ CSprite *m_pEyeGlow;
+ int m_eyeBrightness;
+
+ int m_iDeployHeight;
+ int m_iRetractHeight;
+ int m_iMinPitch;
+
+ int m_iBaseTurnRate; // angles per second
+ float m_fTurnRate; // actual turn rate
+ int m_iOn;
+ int m_fBeserk; // Sometimes this bitch will just freak out
+ int m_iAutoStart; // true if the turret auto deploys when a target
+ // enters its range
+
+ Vector m_vecLastSight; // Last seen position
+ float m_flLastSight; // Last time we saw a target
+ float m_flMaxWait; // Max time to search w/o a target
+
+ // movement
+ float m_flStartYaw;
+ QAngle m_vecGoalAngles;
+
+ int m_iAmmoType;
+
+ float m_flPingTime; // Time until the next ping, used when searching
+ float m_flDamageTime; // Time we last took damage.
+
+ COutputEvent m_OnDeploy;
+ COutputEvent m_OnRetire;
+
+ // external
+ //COutputEvent m_OnDamaged;
+ //COutputEvent m_OnDeath;
+ //COutputEvent m_OnHalfHealth;
+ //COutputEHANDLE m_OnFoundEnemy;
+ //COutputEvent m_OnLostEnemyLOS;
+ //COutputEvent m_OnLostEnemy;
+ //COutputEHANDLE m_OnFoundPlayer;
+ //COutputEvent m_OnLostPlayerLOS;
+ //COutputEvent m_OnLostPlayer;
+ //COutputEvent m_OnHearWorld;
+ //COutputEvent m_OnHearPlayer;
+ //COutputEvent m_OnHearCombat;
+
+};
+
+
+BEGIN_DATADESC( CBaseTurret )
+
+ DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_eyeBrightness, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iDeployHeight, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iRetractHeight, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iMinPitch, FIELD_INTEGER ),
+
+ DEFINE_FIELD( m_iBaseTurnRate, FIELD_INTEGER ),
+ DEFINE_FIELD( m_fTurnRate, FIELD_FLOAT ),
+ DEFINE_FIELD( m_iOn, FIELD_INTEGER ),
+ DEFINE_FIELD( m_fBeserk, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iAutoStart, FIELD_INTEGER ),
+
+ DEFINE_FIELD( m_vecLastSight, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_flLastSight, FIELD_TIME ),
+ DEFINE_FIELD( m_flMaxWait, FIELD_FLOAT ),
+
+ DEFINE_FIELD( m_flStartYaw, FIELD_FLOAT ),
+ DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ),
+
+ DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
+
+ DEFINE_FIELD( m_flPingTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flDamageTime, FIELD_TIME ),
+
+ // Function pointers
+ //DEFINE_USEFUNC( TurretUse ),
+ DEFINE_THINKFUNC( ActiveThink ),
+ DEFINE_THINKFUNC( SearchThink ),
+ DEFINE_THINKFUNC( AutoSearchThink ),
+ DEFINE_THINKFUNC( TurretDeath ),
+ DEFINE_THINKFUNC( Deploy ),
+ DEFINE_THINKFUNC( Retire ),
+ DEFINE_THINKFUNC( Initialize ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
+
+ // Outputs
+ DEFINE_OUTPUT(m_OnDeploy, "OnDeploy"),
+ DEFINE_OUTPUT(m_OnRetire, "OnRetire"),
+
+END_DATADESC()
+
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+Vector CBaseTurret::EyeOffset( Activity nActivity )
+{
+ return Vector( 0, 0, -20 );
+}
+
+
+Vector CBaseTurret::EyePosition( void )
+{
+ Vector vecOrigin;
+ QAngle vecAngles;
+
+ GetAttachment( "eyes", vecOrigin, vecAngles );
+ return vecOrigin;
+}
+
+bool CBaseTurret::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if (FStrEq(szKeyName, "maxsleep"))
+ {
+ m_flMaxWait = atof(szValue);
+ }
+ else if (FStrEq(szKeyName, "turnrate"))
+ {
+ m_iBaseTurnRate = atoi(szValue);
+ }
+ else if (FStrEq(szKeyName, "style") ||
+ FStrEq(szKeyName, "height") ||
+ FStrEq(szKeyName, "value1") ||
+ FStrEq(szKeyName, "value2") ||
+ FStrEq(szKeyName, "value3"))
+ {
+ }
+ else
+ return BaseClass::KeyValue( szKeyName, szValue );
+
+ return true;
+}
+
+
+void CBaseTurret::Spawn()
+{
+ Precache( );
+ SetNextThink( gpGlobals->curtime + 1 );
+ SetMoveType( MOVETYPE_FLY );
+ m_nSequence = 0;
+ m_flCycle = 0;
+ SetSolid( SOLID_SLIDEBOX );
+ m_takedamage = DAMAGE_YES;
+ AddFlag( FL_AIMTARGET );
+
+ m_iAmmoType = g_pGameRules->GetAmmoDef()->Index("SMG1");
+
+ AddFlag( FL_NPC );
+
+ if (( m_spawnflags & SF_NPC_TURRET_AUTOACTIVATE ) && !( m_spawnflags & SF_NPC_TURRET_STARTINACTIVE ))
+ {
+ m_iAutoStart = true;
+ }
+
+ ResetSequenceInfo( );
+
+ SetPoseParameter( TURRET_BC_YAW, 0 );
+ SetPoseParameter( TURRET_BC_PITCH, 0 );
+
+ // Activities
+ ADD_CUSTOM_ACTIVITY( CBaseTurret, ACT_TURRET_OPEN );
+ ADD_CUSTOM_ACTIVITY( CBaseTurret, ACT_TURRET_CLOSE );
+ ADD_CUSTOM_ACTIVITY( CBaseTurret, ACT_TURRET_CLOSED_IDLE );
+ ADD_CUSTOM_ACTIVITY( CBase`matTurret, ACT_TURRET_OPEN_IDLE );
+ ADD_CUSTOM_ACTIVITY( CBaseTurret, ACT_TURRET_FIRE );
+ ADD_CUSTOM_ACTIVITY( CBaseTurret, ACT_TURRET_RELOAD );
+}
+
+
+void CBaseTurret::Precache( )
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "NPC_Turret.Ping" );
+ PrecacheScriptSound( "NPC_Turret.Deploy" );
+ PrecacheScriptSound( "NPC_Turret.Retire" );
+ PrecacheScriptSound( "NPC_Turret.Alert" );
+ PrecacheScriptSound( "NPC_Turret.Die" );
+}
+
+void CBaseTurret::Initialize(void)
+{
+ m_iOn = 0;
+ m_fBeserk = 0;
+
+ SetPoseParameter( TURRET_BC_YAW, 0 );
+ SetPoseParameter( TURRET_BC_PITCH, 0 );
+
+ if (m_iBaseTurnRate == 0) m_iBaseTurnRate = TURRET_TURNRATE;
+ if (m_flMaxWait == 0) m_flMaxWait = TURRET_MAXWAIT;
+
+ m_vecGoalAngles = GetAngles();
+
+ if (m_iAutoStart)
+ {
+ m_flLastSight = gpGlobals->curtime + m_flMaxWait;
+ SetThink(AutoSearchThink);
+ SetNextThink( gpGlobals->curtime + .1 );
+ }
+ else
+ SetThink(SUB_DoNothing);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler for toggling the turret on/off.
+//-----------------------------------------------------------------------------
+void CBaseTurret::InputToggle( inputdata_t &inputdata )
+{
+ //if ( !ShouldToggle( useType, m_iOn ) )
+ // return;
+
+ if (m_iOn)
+ {
+ SetEnemy( NULL );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ m_iAutoStart = FALSE;// switching off a turret disables autostart
+ //!!!! this should spin down first!!BUGBUG
+ SetThink(Retire);
+ }
+ else
+ {
+ SetNextThink( gpGlobals->curtime + 0.1f ); // turn on delay
+
+ // if the turret is flagged as an autoactivate turret, re-enable its ability open self.
+ if ( m_spawnflags & SF_NPC_TURRET_AUTOACTIVATE )
+ {
+ m_iAutoStart = TRUE;
+ }
+
+ SetThink(Deploy);
+ }
+}
+
+
+void CBaseTurret::Ping( void )
+{
+ // make the pinging noise every second while searching
+ if (m_flPingTime == 0)
+ m_flPingTime = gpGlobals->curtime + 1;
+ else if (m_flPingTime <= gpGlobals->curtime)
+ {
+ m_flPingTime = gpGlobals->curtime + 1;
+ EmitSound( "NPC_Turret.Ping" );
+ EyeOn( );
+ }
+ else if (m_eyeBrightness > 0)
+ {
+ EyeOff( );
+ }
+}
+
+
+void CBaseTurret::EyeOn( )
+{
+ if (m_pEyeGlow)
+ {
+ if (m_eyeBrightness != 255)
+ {
+ m_eyeBrightness = 255;
+ }
+ m_pEyeGlow->SetBrightness( m_eyeBrightness );
+ }
+}
+
+
+void CBaseTurret::EyeOff( )
+{
+ if (m_pEyeGlow)
+ {
+ if (m_eyeBrightness > 0)
+ {
+ m_eyeBrightness = MAX( 0, m_eyeBrightness - 30 );
+ m_pEyeGlow->SetBrightness( m_eyeBrightness );
+ }
+ }
+}
+
+
+void CBaseTurret::ActiveThink(void)
+{
+ int fAttack = 0;
+ Vector vecDirToEnemy;
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ StudioFrameAdvance( );
+
+ if ((!m_iOn) || (GetEnemy() == NULL))
+ {
+ SetEnemy( NULL );
+ m_flLastSight = gpGlobals->curtime + m_flMaxWait;
+ SetThink(SearchThink);
+ return;
+ }
+
+ // if it's dead, look for something new
+ if ( !GetEnemy()->IsAlive() )
+ {
+ if (!m_flLastSight)
+ {
+ m_flLastSight = gpGlobals->curtime + 0.5; // continue-shooting timeout
+ }
+ else
+ {
+ if (gpGlobals->curtime > m_flLastSight)
+ {
+ SetEnemy( NULL );
+ m_flLastSight = gpGlobals->curtime + m_flMaxWait;
+ SetThink(SearchThink);
+ return;
+ }
+ }
+ }
+
+ Vector vecMid = EyePosition( );
+ Vector vecMidEnemy = GetEnemy()->BodyTarget(vecMid);
+
+ // g_pEffects->Sparks( vecMid );
+ // g_pEffects->Sparks( vecMidEnemy );
+
+ // Look for our current enemy
+ //int fEnemyVisible = FBoxVisible( this, GetEnemy(), vecMidEnemy );
+ int fEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() );
+
+ vecDirToEnemy = vecMidEnemy - vecMid; // calculate dir and dist to enemy
+ // NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.1 );
+
+ float flDistToEnemy = vecDirToEnemy.Length();
+
+ QAngle vecAnglesToEnemy;
+ VectorNormalize( vecDirToEnemy );
+ VectorAngles( vecDirToEnemy, vecAnglesToEnemy );
+
+ // Current enmey is not visible.
+ if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE))
+ {
+ // DevMsg( "lost you\n" );
+
+ if (!m_flLastSight)
+ {
+ m_flLastSight = gpGlobals->curtime + 0.5;
+ }
+ else
+ {
+ // Should we look for a new target?
+ if (gpGlobals->curtime > m_flLastSight)
+ {
+ ClearEnemyMemory();
+ SetEnemy( NULL );
+ m_flLastSight = gpGlobals->curtime + m_flMaxWait;
+ SetThink(SearchThink);
+ return;
+ }
+ }
+ fEnemyVisible = 0;
+ }
+ else
+ {
+ m_vecLastSight = vecMidEnemy;
+ }
+
+ Vector vecLOS = vecDirToEnemy; //vecMid - m_vecLastSight;
+ VectorNormalize( vecLOS );
+
+ Vector vecMuzzle, vecMuzzleDir;
+ QAngle vecMuzzleAng;
+ GetAttachment( "eyes", vecMuzzle, vecMuzzleAng );
+
+ AngleVectors( vecMuzzleAng, &vecMuzzleDir );
+
+ // Is the Gun looking at the target
+ if (DotProduct(vecLOS, vecMuzzleDir) <= 0.9848) // 10 degree slop
+ {
+ fAttack = FALSE;
+ }
+ else
+ {
+ fAttack = TRUE;
+ }
+
+ // fire the gun
+ if (fAttack || m_fBeserk)
+ {
+ m_Activity = ACT_RESET;
+ SetActivity( (Activity)ACT_TURRET_FIRE );
+ Shoot(vecMuzzle, vecMuzzleDir );
+ }
+ else
+ {
+ SetActivity( (Activity)ACT_TURRET_OPEN_IDLE );
+ }
+
+ //move the gun
+ if (m_fBeserk)
+ {
+ // DevMsg( "berserk" );
+
+ if (random->RandomInt(0,9) == 0)
+ {
+ m_vecGoalAngles.y = random->RandomFloat(-180,180);
+ m_vecGoalAngles.x = random->RandomFloat(-90,90);
+ OnTakeDamage( CTakeDamageInfo( this, this, 1, DMG_GENERIC ) ); // don't beserk forever
+ return;
+ }
+ }
+ else if (fEnemyVisible)
+ {
+ // DevMsg( "->[%.2f]\n", vec.x);
+ m_vecGoalAngles.y = vecAnglesToEnemy.y;
+ m_vecGoalAngles.x = vecAnglesToEnemy.x;
+
+ }
+
+ MoveTurret();
+}
+
+
+void CBaseTurret::Deploy(void)
+{
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ StudioFrameAdvance( );
+
+ if ( m_Activity != ACT_TURRET_OPEN )
+ {
+ m_iOn = 1;
+ SetActivity( (Activity)ACT_TURRET_OPEN );
+ EmitSound( "NPC_Turret.Deploy" );
+
+ m_OnDeploy.FireOutput(NULL, this);
+ }
+
+ if (m_fSequenceFinished)
+ {
+ Vector curmins, curmaxs;
+ curmins = WorldAlignMins();
+ curmaxs = WorldAlignMaxs();
+
+ curmaxs.z = m_iDeployHeight;
+ curmins.z = -m_iDeployHeight;
+
+ SetCollisionBounds( curmins, curmaxs );
+
+ Relink();
+
+ SetActivity( (Activity)ACT_TURRET_OPEN_IDLE );
+
+ m_flPlaybackRate = 0;
+ SetThink(SearchThink);
+ }
+
+ m_flLastSight = gpGlobals->curtime + m_flMaxWait;
+}
+
+void CBaseTurret::Retire(void)
+{
+ // make the turret level
+ m_vecGoalAngles = GetAngles( );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ StudioFrameAdvance( );
+
+ EyeOff( );
+
+ if ( m_Activity != ACT_TURRET_CLOSE )
+ {
+ SetActivity( (Activity)ACT_TURRET_OPEN_IDLE );
+
+ if (!MoveTurret())
+ {
+ SetActivity( (Activity)ACT_TURRET_CLOSE );
+ EmitSound( "NPC_Turret.Retire" );
+
+ m_OnRetire.FireOutput(NULL, this);
+ }
+ }
+ else if (m_fSequenceFinished)
+ {
+ m_iOn = 0;
+ m_flLastSight = 0;
+
+ SetActivity( (Activity)ACT_TURRET_CLOSED_IDLE );
+
+ Vector curmins, curmaxs;
+ curmins = WorldAlignMins();
+ curmaxs = WorldAlignMaxs();
+
+ curmaxs.z = m_iRetractHeight;
+ curmins.z = -m_iRetractHeight;
+
+ SetCollisionBounds( curmins, curmaxs );
+ Relink();
+
+ if (m_iAutoStart)
+ {
+ SetThink(AutoSearchThink);
+ SetNextThink( gpGlobals->curtime + .1 );
+ }
+ else
+ {
+ SetThink(SUB_DoNothing);
+ }
+ }
+}
+
+
+//
+// This search function will sit with the turret deployed and look for a new target.
+// After a set amount of time, the barrel will spin down. After m_flMaxWait, the turret will
+// retact.
+//
+void CBaseTurret::SearchThink(void)
+{
+ // ensure rethink
+ SetActivity( (Activity)ACT_TURRET_OPEN_IDLE );
+
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ Ping( );
+
+ // If we have a target and we're still healthy
+ if (GetEnemy() != NULL)
+ {
+ if (!GetEnemy()->IsAlive() )
+ SetEnemy( NULL );// Dead enemy forces a search for new one
+ }
+
+ // Acquire Target
+ if (GetEnemy() == NULL)
+ {
+ GetSenses()->Look(TURRET_RANGE);
+ SetEnemy( BestEnemy() );
+ }
+
+ // If we've found a target, spin up the barrel and start to attack
+ if (GetEnemy() != NULL)
+ {
+ m_flLastSight = 0;
+ SetThink(ActiveThink);
+ }
+ else
+ {
+ // Are we out of time, do we need to retract?
+ if (gpGlobals->curtime > m_flLastSight)
+ {
+ //Before we retrace, make sure that we are spun down.
+ m_flLastSight = 0;
+ SetThink(Retire);
+ }
+
+ // generic hunt for new victims
+ m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_iBaseTurnRate);
+ if (m_vecGoalAngles.y >= 360)
+ m_vecGoalAngles.y -= 360;
+
+ MoveTurret();
+ }
+}
+
+
+//
+// This think function will deploy the turret when something comes into range. This is for
+// automatically activated turrets.
+//
+void CBaseTurret::AutoSearchThink(void)
+{
+ // ensure rethink
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2, 0.3 ) );
+
+ // If we have a target and we're still healthy
+
+ if (GetEnemy() != NULL)
+ {
+ if (!GetEnemy()->IsAlive() )
+ SetEnemy( NULL );// Dead enemy forces a search for new one
+ }
+
+ // Acquire Target
+
+ if (GetEnemy() == NULL)
+ {
+ GetSenses()->Look( TURRET_RANGE );
+ SetEnemy( BestEnemy() );
+ }
+
+ if (GetEnemy() != NULL)
+ {
+ SetThink(Deploy);
+ EmitSound( "NPC_Turret.Alert" );
+ }
+}
+
+
+void CBaseTurret :: TurretDeath( void )
+{
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if (m_lifeState != LIFE_DEAD)
+ {
+ m_lifeState = LIFE_DEAD;
+
+ EmitSound( "NPC_Turret.Die" );
+
+ SetActivity( (Activity)ACT_TURRET_CLOSE );
+
+ EyeOn( );
+ }
+
+ EyeOff( );
+
+ if (m_flDamageTime + random->RandomFloat( 0, 2 ) > gpGlobals->curtime)
+ {
+ // lots of smoke
+ Vector pos;
+ CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos );
+ pos.z = CollisionProp()->GetCollisionOrigin().z;
+
+ CBroadcastRecipientFilter filter;
+ te->Smoke( filter, 0.0, &pos,
+ g_sModelIndexSmoke,
+ 2.5,
+ 10 );
+ }
+
+ if (m_flDamageTime + random->RandomFloat( 0, 5 ) > gpGlobals->curtime)
+ {
+ Vector vecSrc;
+ CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &vecSrc );
+ g_pEffects->Sparks( vecSrc );
+ }
+
+ if (m_fSequenceFinished && !MoveTurret( ) && m_flDamageTime + 5 < gpGlobals->curtime)
+ {
+ m_flPlaybackRate = 0;
+ SetThink( NULL );
+ }
+}
+
+
+
+void CBaseTurret::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr )
+{
+ CTakeDamageInfo info = inputInfo;
+
+ if ( ptr->hitgroup == 10 )
+ {
+ // hit armor
+ if ( m_flDamageTime != gpGlobals->curtime || (random->RandomInt(0,10) < 1) )
+ {
+ g_pEffects->Ricochet( ptr->endpos, (vecDir*-1.0f) );
+ m_flDamageTime = gpGlobals->curtime;
+ }
+
+ info.SetDamage( 0.1 );// don't hurt the NPC much, but allow bits_COND_LIGHT_DAMAGE to be generated
+ }
+
+ if ( !m_takedamage )
+ return;
+
+ AddMultiDamage( info, this );
+}
+
+
+int CBaseTurret::OnTakeDamage( const CTakeDamageInfo &inputInfo )
+{
+ if ( !m_takedamage )
+ return 0;
+
+ CTakeDamageInfo info = inputInfo;
+
+ if (!m_iOn)
+ info.ScaleDamage( 0.1f );
+
+ m_iHealth -= info.GetDamage();
+ if (m_iHealth <= 0)
+ {
+ m_iHealth = 0;
+ m_takedamage = DAMAGE_NO;
+ m_flDamageTime = gpGlobals->curtime;
+
+ RemoveFlag( FL_NPC ); // why are they set in the first place???
+
+ SetThink(TurretDeath);
+
+ m_OnDamaged.FireOutput( info.GetInflictor(), this );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ return 0;
+ }
+
+ if (m_iHealth <= 10)
+ {
+ if (m_iOn && (1 || random->RandomInt(0, 0x7FFF) > 800))
+ {
+ m_fBeserk = 1;
+ SetThink(SearchThink);
+ }
+ }
+
+ return 1;
+}
+
+
+int CBaseTurret::MoveTurret(void)
+{
+ bool bDidMove = false;
+ int iPose;
+
+ matrix3x4_t localToWorld;
+
+ GetAttachment( LookupAttachment( "eyes" ), localToWorld );
+
+ Vector vecGoalDir;
+ AngleVectors( m_vecGoalAngles, &vecGoalDir );
+
+ Vector vecGoalLocalDir;
+ VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir );
+
+ QAngle vecGoalLocalAngles;
+ VectorAngles( vecGoalLocalDir, vecGoalLocalAngles );
+
+ float flDiff;
+ QAngle vecNewAngles;
+
+ // update pitch
+ flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1 * m_iBaseTurnRate ) );
+ iPose = LookupPoseParameter( TURRET_BC_PITCH );
+ SetPoseParameter( iPose, GetPoseParameter( iPose ) + flDiff / 1.5 );
+
+ if (fabs(flDiff) > 0.1)
+ {
+ bDidMove = true;
+ }
+
+ // update yaw, with acceleration
+#if 0
+ float flDist = AngleNormalize( vecGoalLocalAngles.y );
+ float flNewDist;
+ float flNewTurnRate;
+
+ ChangeDistance( 0.1, flDist, 0.0, m_fTurnRate, m_iBaseTurnRate, m_iBaseTurnRate * 4, flNewDist, flNewTurnRate );
+ m_fTurnRate = flNewTurnRate;
+ flDiff = flDist - flNewDist;
+#else
+ flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1 * m_iBaseTurnRate ) );
+#endif
+
+ iPose = LookupPoseParameter( TURRET_BC_YAW );
+ SetPoseParameter( iPose, GetPoseParameter( iPose ) + flDiff / 1.5 );
+ if (fabs(flDiff) > 0.1)
+ {
+ bDidMove = true;
+ }
+
+ if (bDidMove)
+ {
+ // DevMsg( "(%.2f, %.2f)\n", AngleNormalize( vecGoalLocalAngles.x ), AngleNormalize( vecGoalLocalAngles.y ) );
+ }
+ return bDidMove;
+}
+
+//
+// ID as a machine
+//
+Class_T CBaseTurret::Classify ( void )
+
+{
+ if (m_iOn || m_iAutoStart)
+ {
+ return CLASS_MILITARY;
+ }
+
+ return CLASS_MILITARY;
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+class CCeilingTurret : public CBaseTurret
+{
+ DECLARE_CLASS( CCeilingTurret, CBaseTurret );
+public:
+ void Spawn(void);
+ void Precache(void);
+
+ // other functions
+ void Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy );
+};
+
+#define TURRET_GLOW_SPRITE "sprites/glow01.vmt"
+
+LINK_ENTITY_TO_CLASS( npc_turret_ceiling, CCeilingTurret );
+
+void CCeilingTurret::Spawn()
+{
+ Precache( );
+
+ SetModel( "models/combine_turrets/ceiling_turret.mdl" );
+
+ BaseClass::Spawn( );
+
+ m_iHealth = sk_turret_health.GetFloat();
+ m_HackedGunPos = Vector( 0, 0, 12.75 );
+
+ AngleVectors( GetAngles(), NULL, NULL, &m_vecViewOffset );
+ m_vecViewOffset = m_vecViewOffset * Vector( 0, 0, -64 );
+
+ m_flFieldOfView = VIEW_FIELD_FULL;
+
+ m_iRetractHeight = 16;
+ m_iDeployHeight = 32;
+ m_iMinPitch = -45;
+ UTIL_SetSize(this, Vector(-32, -32, -m_iRetractHeight), Vector(32, 32, m_iRetractHeight));
+
+ SetThink(Initialize);
+
+ m_pEyeGlow = CSprite::SpriteCreate( TURRET_GLOW_SPRITE, GetOrigin(), FALSE );
+ m_pEyeGlow->SetTransparency( kRenderGlow, 255, 0, 0, 0, kRenderFxNoDissipation );
+ m_pEyeGlow->SetAttachment( this, 2 );
+ m_eyeBrightness = 0;
+
+ SetNextThink( gpGlobals->curtime + 0.3; );
+}
+
+void CCeilingTurret::Precache()
+{
+ PrecacheModel( "models/combine_turrets/ceiling_turret.mdl");
+ PrecacheModel( TURRET_GLOW_SPRITE );
+
+ PrecacheScriptSound( "CeilingTurret.Shoot" );
+ BaseClass::Precache();
+}
+
+void CCeilingTurret::Shoot(const Vector &vecSrc, const Vector &vecDirToEnemy)
+{
+ //NDebugOverlay::Line( vecSrc, vecSrc + vecDirToEnemy * 512, 0, 255, 255, false, 0.1 );
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 );
+ EmitSound( "CeilingTurret.Shoot" );
+
+ DoMuzzleFlash();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if 0
+
+class CMiniTurret : public CBaseTurret
+{
+ DECLARE_CLASS( CMiniTurret, CBaseTurret );
+public:
+ void Spawn( );
+ void Precache(void);
+ // other functions
+ void Shoot(const Vector &vecSrc, const Vector &vecDirToEnemy);
+};
+
+LINK_ENTITY_TO_CLASS( npc_miniturret, CMiniTurret );
+
+
+void CMiniTurret::Spawn()
+{
+ Precache( );
+ SetModel( "models/miniturret.mdl" );
+ m_iHealth = sk_miniturret_health.GetFloat();
+ m_HackedGunPos = Vector( 0, 0, 12.75 );
+ m_vecViewOffset.z = 12.75;
+ m_flFieldOfView = VIEW_FIELD_NARROW;
+
+ CBaseTurret::Spawn( );
+
+ m_iAmmoType = g_pGameRules->GetAmmoDef()->Index("Pistol");
+
+ m_iRetractHeight = 16;
+ m_iDeployHeight = 32;
+ m_iMinPitch = -45;
+ UTIL_SetSize(this, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight));
+
+ SetThink(Initialize);
+ SetNextThink( gpGlobals->curtime + 0.3; );
+}
+
+
+void CMiniTurret::Precache()
+{
+ PrecacheModel ("models/miniturret.mdl");
+
+ PrecacheScriptSound( "MiniTurret.Shoot" );
+
+ BaseClass::Precache();
+}
+
+
+
+void CMiniTurret::Shoot(const Vector &vecSrc, const Vector &vecDirToEnemy)
+{
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 );
+
+ EmitSound( "MiniTurret.Shoot" );
+
+ DoMuzzleFlash();
+}
+
+
+#endif
+
+
+//=========================================================
+// Sentry gun - smallest turret, placed near grunt entrenchments
+//=========================================================
+class CSentry : public CBaseTurret
+{
+ DECLARE_CLASS( CSentry, CBaseTurret );
+public:
+ void Spawn( );
+ void Precache(void);
+ // other functions
+ void Shoot(const Vector &vecSrc, const Vector &vecDirToEnemy);
+ int OnTakeDamage( const CTakeDamageInfo &info );
+ void SentryTouch( CBaseEntity *pOther );
+ void SentryDeath( void );
+
+protected:
+
+ DECLARE_DATADESC();
+};
+
+
+BEGIN_DATADESC( CSentry )
+
+ // Function pointers
+ DEFINE_ENTITYFUNC( SentryTouch ),
+ DEFINE_THINKFUNC( SentryDeath ),
+
+END_DATADESC()
+
+
+LINK_ENTITY_TO_CLASS( NPC_sentry, CSentry );
+
+void CSentry::Precache()
+{
+ PrecacheModel ("models/sentry.mdl");
+
+ PrecacheScriptSound( "Sentry.Shoot" );
+ PrecacheScriptSound( "Sentry.Die" );
+
+ BaseClass::Precache();
+}
+
+void CSentry::Spawn()
+{
+ Precache( );
+ SetModel( "models/sentry.mdl" );
+ m_iHealth = sk_sentry_health.GetFloat();
+ m_HackedGunPos = Vector( 0, 0, 48 );
+ m_vecViewOffset.z = 48;
+ m_flMaxWait = 1E6;
+
+ CBaseTurret::Spawn();
+ m_iRetractHeight = 64;
+ m_iDeployHeight = 64;
+ m_iMinPitch = -60;
+ UTIL_SetSize(this, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight));
+
+ SetTouch(SentryTouch);
+ SetThink(Initialize);
+ SetNextThink( gpGlobals->curtime + 0.3; );
+}
+
+void CSentry::Shoot(const Vector &vecSrc, const Vector &vecDirToEnemy)
+{
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 );
+
+ EmitSound( "Sentry.Shoot" );
+
+ DoMuzzleFlash();
+}
+
+
+int CSentry::OnTakeDamage( const CTakeDamageInfo &info )
+{
+ if ( !m_takedamage )
+ return 0;
+
+ if (!m_iOn)
+ {
+ SetThink( Deploy );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ }
+
+ m_iHealth -= info.GetDamage();
+ if (m_iHealth <= 0)
+ {
+ m_iHealth = 0;
+ m_takedamage = DAMAGE_NO;
+ m_flDamageTime = gpGlobals->curtime;
+
+ RemoveFlag( FL_NPC ); // why are they set in the first place???
+
+ SetThink(SentryDeath);
+ m_OnDamaged.FireOutput( info.GetInflictor(), this );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ return 0;
+ }
+
+ return 1;
+}
+
+
+void CSentry::SentryTouch( CBaseEntity *pOther )
+{
+ if ( pOther && (pOther->IsPlayer() || (pOther->GetFlags() & FL_NPC)) )
+ {
+ OnTakeDamage( CTakeDamageInfo( pOther, pOther, 0, 0 ) );
+ }
+}
+
+
+void CSentry :: SentryDeath( void )
+{
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if (m_lifeState != LIFE_DEAD)
+ {
+ m_lifeState = LIFE_DEAD;
+
+ EmitSound( "Sentry.Die" );
+
+ SetPoseParameter( TURRET_BC_YAW, 0 );
+ SetPoseParameter( TURRET_BC_PITCH, 0 );
+
+ SetActivity( (Activity)ACT_TURRET_CLOSE );
+
+ SetSolid( SOLID_NOT );
+ QAngle angles = GetAngles();
+ angles.y = UTIL_AngleMod( GetAngles().y + random->RandomInt( 0, 2 ) * 120 );
+ SetAngles( angles );
+
+ EyeOn( );
+ }
+
+ EyeOff( );
+
+ Vector vecSrc;
+ QAngle vecAng;
+ GetAttachment( "eyes", vecSrc, vecAng );
+
+ if (m_flDamageTime + random->RandomFloat( 0, 2 ) > gpGlobals->curtime)
+ {
+ // lots of smoke
+ Vector pos = vecSrc + Vector( random->RandomFloat( -16, 16 ),
+ random->RandomFloat( -16, 16 ),
+ -32 );
+
+ CBroadcastRecipientFilter filter;
+ te->Smoke( filter, 0.0, &pos,
+ g_sModelIndexSmoke,
+ 1.5,
+ 8 );
+ }
+
+ if (m_flDamageTime + random->RandomFloat( 0, 8 ) > gpGlobals->curtime)
+ {
+ g_pEffects->Sparks( vecSrc );
+ }
+
+ if (m_fSequenceFinished && m_flDamageTime + 5 < gpGlobals->curtime)
+ {
+ m_flPlaybackRate = 0;
+ SetThink( NULL );
+ }
+}
+
|