diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/hl2/npc_turret.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/hl2/npc_turret.cpp')
| -rw-r--r-- | sp/src/game/server/hl2/npc_turret.cpp | 2380 |
1 files changed, 1190 insertions, 1190 deletions
diff --git a/sp/src/game/server/hl2/npc_turret.cpp b/sp/src/game/server/hl2/npc_turret.cpp index 78c1c737..f4ed2d81 100644 --- a/sp/src/game/server/hl2/npc_turret.cpp +++ b/sp/src/game/server/hl2/npc_turret.cpp @@ -1,1190 +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 );
- }
-}
-
+//========= 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 ); + } +} + |