summaryrefslogtreecommitdiff
path: root/game/server/tf/tf_turret.h
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/tf_turret.h
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/tf/tf_turret.h')
-rw-r--r--game/server/tf/tf_turret.h1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/game/server/tf/tf_turret.h b/game/server/tf/tf_turret.h
new file mode 100644
index 0000000..e8406d6
--- /dev/null
+++ b/game/server/tf/tf_turret.h
@@ -0,0 +1,1342 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef TF_TURRET_H
+#define TF_TURRET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "ehandle.h"
+#include "tf_shareddefs.h"
+
+#if 0
+extern Vector VecBModelOrigin( entvars_t* pevBModel );
+
+#define TURRET_SHOTS 2
+#define TURRET_RANGE (100 * 12)
+#define TURRET_SPREAD Vector( 0, 0, 0 )
+#define TURRET_TURNRATE 30 //angles per 0.1 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
+
+typedef enum
+{
+ TURRET_ANIM_NONE = 0,
+ TURRET_ANIM_FIRE,
+ TURRET_ANIM_SPIN,
+ TURRET_ANIM_DEPLOY,
+ TURRET_ANIM_RETIRE,
+ TURRET_ANIM_DIE,
+} TURRET_ANIM;
+
+class CBaseTurret : public CBaseMonster
+{
+public:
+ void Spawn(void);
+ virtual void Precache(void);
+ void KeyValue( KeyValueData *pkvd );
+ void EXPORT TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+ virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
+ virtual int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
+ virtual int Classify(void);
+
+ int BloodColor( void ) { return DONT_BLEED; }
+ void GibMonster( void ) {} // UNDONE: Throw turret gibs?
+
+ // Think functions
+
+ void EXPORT ActiveThink(void);
+ void EXPORT SearchThink(void);
+ void EXPORT AutoSearchThink(void);
+ void EXPORT TurretDeath(void);
+
+ virtual void EXPORT SpinDownCall(void) { m_iSpin = 0; }
+ virtual void EXPORT SpinUpCall(void) { m_iSpin = 1; }
+
+ // void SpinDown(void);
+ // float EXPORT SpinDownCall( void ) { return SpinDown(); }
+
+ // virtual float SpinDown(void) { return 0;}
+ // virtual float Retire(void) { return 0;}
+
+ void EXPORT Deploy(void);
+ void EXPORT Retire(void);
+
+ void EXPORT Initialize(void);
+
+ virtual void Ping(void);
+ virtual void EyeOn(void);
+ virtual void EyeOff(void);
+
+ virtual int Save( CSave &save );
+ virtual int Restore( CRestore &restore );
+
+ static TYPEDESCRIPTION m_SaveData[];
+
+ // other functions
+ void SetTurretAnim(TURRET_ANIM anim);
+ int MoveTurret(void);
+ virtual void Shoot(Vector &vecSrc, Vector &vecDirToEnemy) { };
+
+ float m_flMaxSpin; // Max time to spin the barrel w/o a target
+ int m_iSpin;
+
+ 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_iOrientation; // 0 = floor, 1 = Ceiling
+ 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;
+ float m_flLastSight; // Last time we saw a target
+ float m_flMaxWait; // Max time to seach w/o a target
+ int m_iSearchSpeed; // Not Used!
+
+ // movement
+ float m_flStartYaw;
+ Vector m_vecCurAngles;
+ Vector m_vecGoalAngles;
+
+
+ float m_flPingTime; // Time until the next ping, used when searching
+ float m_flSpinUpTime; // Amount of time until the barrel should spin down when searching
+};
+
+
+TYPEDESCRIPTION CBaseTurret::m_SaveData[] =
+{
+ DEFINE_FIELD( CBaseTurret, m_flMaxSpin, FIELD_FLOAT ),
+ DEFINE_FIELD( CBaseTurret, m_iSpin, FIELD_INTEGER ),
+
+ DEFINE_FIELD( CBaseTurret, m_pEyeGlow, FIELD_CLASSPTR ),
+ DEFINE_FIELD( CBaseTurret, m_eyeBrightness, FIELD_INTEGER ),
+ DEFINE_FIELD( CBaseTurret, m_iDeployHeight, FIELD_INTEGER ),
+ DEFINE_FIELD( CBaseTurret, m_iRetractHeight, FIELD_INTEGER ),
+ DEFINE_FIELD( CBaseTurret, m_iMinPitch, FIELD_INTEGER ),
+
+ DEFINE_FIELD( CBaseTurret, m_iBaseTurnRate, FIELD_INTEGER ),
+ DEFINE_FIELD( CBaseTurret, m_fTurnRate, FIELD_FLOAT ),
+ DEFINE_FIELD( CBaseTurret, m_iOrientation, FIELD_INTEGER ),
+ DEFINE_FIELD( CBaseTurret, m_iOn, FIELD_INTEGER ),
+ DEFINE_FIELD( CBaseTurret, m_fBeserk, FIELD_INTEGER ),
+ DEFINE_FIELD( CBaseTurret, m_iAutoStart, FIELD_INTEGER ),
+
+
+ DEFINE_FIELD( CBaseTurret, m_vecLastSight, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( CBaseTurret, m_flLastSight, FIELD_TIME ),
+ DEFINE_FIELD( CBaseTurret, m_flMaxWait, FIELD_FLOAT ),
+ DEFINE_FIELD( CBaseTurret, m_iSearchSpeed, FIELD_INTEGER ),
+
+ DEFINE_FIELD( CBaseTurret, m_flStartYaw, FIELD_FLOAT ),
+ DEFINE_FIELD( CBaseTurret, m_vecCurAngles, FIELD_VECTOR ),
+ DEFINE_FIELD( CBaseTurret, m_vecGoalAngles, FIELD_VECTOR ),
+
+ DEFINE_FIELD( CBaseTurret, m_flPingTime, FIELD_TIME ),
+ DEFINE_FIELD( CBaseTurret, m_flSpinUpTime, FIELD_TIME ),
+};
+
+IMPLEMENT_SAVERESTORE( CBaseTurret, CBaseMonster );
+
+class CTurret : public CBaseTurret
+{
+public:
+ void Spawn(void);
+ void Precache(void);
+ // Think functions
+ void SpinUpCall(void);
+ void SpinDownCall(void);
+
+ virtual int Save( CSave &save );
+ virtual int Restore( CRestore &restore );
+
+ static TYPEDESCRIPTION m_SaveData[];
+
+ // other functions
+ void Shoot(Vector &vecSrc, Vector &vecDirToEnemy);
+
+private:
+ int m_iStartSpin;
+
+};
+TYPEDESCRIPTION CTurret::m_SaveData[] =
+{
+ DEFINE_FIELD( CTurret, m_iStartSpin, FIELD_INTEGER ),
+};
+
+IMPLEMENT_SAVERESTORE( CTurret, CBaseTurret );
+
+
+class CMiniTurret : public CBaseTurret
+{
+public:
+ void Spawn( );
+ void Precache(void);
+ // other functions
+ void Shoot(Vector &vecSrc, Vector &vecDirToEnemy);
+};
+
+
+LINK_ENTITY_TO_CLASS( monster_turret, CTurret );
+LINK_ENTITY_TO_CLASS( monster_miniturret, CMiniTurret );
+
+void CBaseTurret::KeyValue( KeyValueData *pkvd )
+{
+ if (FStrEq(pkvd->szKeyName, "maxsleep"))
+ {
+ m_flMaxWait = atof(pkvd->szValue);
+ pkvd->fHandled = TRUE;
+ }
+ else if (FStrEq(pkvd->szKeyName, "orientation"))
+ {
+ m_iOrientation = atoi(pkvd->szValue);
+ pkvd->fHandled = TRUE;
+
+ }
+ else if (FStrEq(pkvd->szKeyName, "searchspeed"))
+ {
+ m_iSearchSpeed = atoi(pkvd->szValue);
+ pkvd->fHandled = TRUE;
+
+ }
+ else if (FStrEq(pkvd->szKeyName, "turnrate"))
+ {
+ m_iBaseTurnRate = atoi(pkvd->szValue);
+ pkvd->fHandled = TRUE;
+ }
+ else if (FStrEq(pkvd->szKeyName, "style") ||
+ FStrEq(pkvd->szKeyName, "height") ||
+ FStrEq(pkvd->szKeyName, "value1") ||
+ FStrEq(pkvd->szKeyName, "value2") ||
+ FStrEq(pkvd->szKeyName, "value3"))
+ pkvd->fHandled = TRUE;
+ else
+ CBaseMonster::KeyValue( pkvd );
+}
+
+
+void CBaseTurret::Spawn()
+{
+ Precache( );
+ pev->nextthink = gpGlobals->time + 1;
+ pev->movetype = MOVETYPE_FLY;
+ pev->sequence = 0;
+ pev->frame = 0;
+ pev->solid = SOLID_SLIDEBOX;
+
+ // Make turrets invulnerable in multiplayer
+ if (gpGlobals->deathmatch)
+ pev->takedamage = DAMAGE_NO;
+ else
+ pev->takedamage = DAMAGE_AIM;
+
+ SetBits (pev->flags, FL_MONSTER);
+ SetUse( &CBaseTurret::TurretUse );
+
+ // Start turrets automatically in multiplayer
+ if (gpGlobals->deathmatch)
+ m_iAutoStart = TRUE;
+
+ if (( pev->spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE )
+ && !( pev->spawnflags & SF_MONSTER_TURRET_STARTINACTIVE ))
+ {
+ m_iAutoStart = TRUE;
+ }
+
+ ResetSequenceInfo( );
+ SetBoneController( 0, 0 );
+ SetBoneController( 1, 0 );
+ m_flFieldOfView = VIEW_FIELD_FULL;
+ // m_flSightRange = TURRET_RANGE;
+}
+
+
+void CBaseTurret::Precache( )
+{
+ PRECACHE_SOUND ("turret/tu_fire1.wav");
+ PRECACHE_SOUND ("turret/tu_ping.wav");
+ PRECACHE_SOUND ("turret/tu_active2.wav");
+ PRECACHE_SOUND ("turret/tu_die.wav");
+ PRECACHE_SOUND ("turret/tu_die2.wav");
+ PRECACHE_SOUND ("turret/tu_die3.wav");
+ // PRECACHE_SOUND ("turret/tu_retract.wav"); // just use deploy sound to save memory
+ PRECACHE_SOUND ("turret/tu_deploy.wav");
+ PRECACHE_SOUND ("turret/tu_spinup.wav");
+ PRECACHE_SOUND ("turret/tu_spindown.wav");
+ PRECACHE_SOUND ("turret/tu_search.wav");
+ PRECACHE_SOUND ("turret/tu_alert.wav");
+}
+
+#define TURRET_GLOW_SPRITE "sprites/flare3.spr"
+
+void CTurret::Spawn()
+{
+ Precache( );
+ SET_MODEL(ENT(pev), "models/turret.mdl");
+ pev->health = gSkillData.turretHealth;
+ m_HackedGunPos = Vector( 0, 0, 12.75 );
+ m_flMaxSpin = TURRET_MAXSPIN;
+ pev->view_ofs.z = 12.75;
+
+ CBaseTurret::Spawn( );
+
+ m_iRetractHeight = 16;
+ m_iDeployHeight = 32;
+ m_iMinPitch = -15;
+ UTIL_SetSize(pev, Vector(-32, -32, -m_iRetractHeight), Vector(32, 32, m_iRetractHeight));
+
+ SetThink(&CTurret::Initialize);
+
+ m_pEyeGlow = CSprite::SpriteCreate( TURRET_GLOW_SPRITE, pev->origin, FALSE );
+ m_pEyeGlow->SetTransparency( kRenderGlow, 255, 0, 0, 0, kRenderFxNoDissipation );
+ m_pEyeGlow->SetAttachment( edict(), 2 );
+ m_eyeBrightness = 0;
+
+ pev->nextthink = gpGlobals->time + 0.3;
+}
+
+void CTurret::Precache()
+{
+ CBaseTurret::Precache( );
+ PRECACHE_MODEL ("models/turret.mdl");
+ PRECACHE_MODEL (TURRET_GLOW_SPRITE);
+}
+
+void CMiniTurret::Spawn()
+{
+ Precache( );
+ SET_MODEL(ENT(pev), "models/miniturret.mdl");
+ pev->health = gSkillData.miniturretHealth;
+ m_HackedGunPos = Vector( 0, 0, 12.75 );
+ m_flMaxSpin = 0;
+ pev->view_ofs.z = 12.75;
+
+ CBaseTurret::Spawn( );
+ m_iRetractHeight = 16;
+ m_iDeployHeight = 32;
+ m_iMinPitch = -15;
+ UTIL_SetSize(pev, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight));
+
+ SetThink(&CMiniTurret::Initialize);
+ pev->nextthink = gpGlobals->time + 0.3;
+}
+
+
+void CMiniTurret::Precache()
+{
+ CBaseTurret::Precache( );
+ PRECACHE_MODEL ("models/miniturret.mdl");
+ PRECACHE_SOUND("weapons/hks1.wav");
+ PRECACHE_SOUND("weapons/hks2.wav");
+ PRECACHE_SOUND("weapons/hks3.wav");
+}
+
+void CBaseTurret::Initialize(void)
+{
+ m_iOn = 0;
+ m_fBeserk = 0;
+ m_iSpin = 0;
+
+ SetBoneController( 0, 0 );
+ SetBoneController( 1, 0 );
+
+ if (m_iBaseTurnRate == 0)
+ {
+ // Make turrets turn faster in multiplayer
+ if (gpGlobals->deathmatch)
+ m_iBaseTurnRate = TURRET_TURNRATE * 2;
+ else
+ m_iBaseTurnRate = TURRET_TURNRATE;
+ }
+ if (m_flMaxWait == 0)
+ {
+ // Make turrets retarget faster
+ //if (gpGlobals->deathmatch)
+ //m_flMaxWait = 1;
+ //else
+ m_flMaxWait = TURRET_MAXWAIT;
+ }
+ m_flStartYaw = pev->angles.y;
+ if (m_iOrientation == 1)
+ {
+ pev->idealpitch = 180;
+ pev->angles.x = 180;
+ pev->view_ofs.z = -pev->view_ofs.z;
+ pev->effects |= EF_INVLIGHT;
+ pev->angles.y = pev->angles.y + 180;
+ if (pev->angles.y > 360)
+ pev->angles.y = pev->angles.y - 360;
+ }
+
+ m_vecGoalAngles.x = 0;
+
+ if (m_iAutoStart)
+ {
+ m_flLastSight = gpGlobals->time + m_flMaxWait;
+ SetThink(&CBaseTurret::AutoSearchThink);
+ pev->nextthink = gpGlobals->time + .1;
+ }
+ else
+ SetThink(&CBaseTurret::SUB_DoNothing);
+}
+
+void CBaseTurret::TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ if ( !ShouldToggle( useType, m_iOn ) )
+ return;
+
+ if (m_iOn)
+ {
+ m_hEnemy = NULL;
+ pev->nextthink = gpGlobals->time + 0.1;
+ m_iAutoStart = FALSE;// switching off a turret disables autostart
+ //!!!! this should spin down first!!BUGBUG
+ SetThink(&CBaseTurret::Retire);
+ }
+ else
+ {
+ pev->nextthink = gpGlobals->time + 0.1; // turn on delay
+
+ // if the turret is flagged as an autoactivate turret, re-enable it's ability open self.
+ if ( pev->spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE )
+ {
+ m_iAutoStart = TRUE;
+ }
+
+ SetThink(&CBaseTurret::Deploy);
+ }
+}
+
+
+void CBaseTurret::Ping( void )
+{
+ // make the pinging noise every second while searching
+ if (m_flPingTime == 0)
+ m_flPingTime = gpGlobals->time + 1;
+ else if (m_flPingTime <= gpGlobals->time)
+ {
+ m_flPingTime = gpGlobals->time + 1;
+ EMIT_SOUND(ENT(pev), CHAN_ITEM, "turret/tu_ping.wav", 1, ATTN_NORM);
+ 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;
+
+ pev->nextthink = gpGlobals->time + 0.1;
+ StudioFrameAdvance( );
+
+ if ((!m_iOn) || (m_hEnemy == NULL))
+ {
+ m_hEnemy = NULL;
+ m_flLastSight = gpGlobals->time + m_flMaxWait;
+ SetThink(&CBaseTurret::SearchThink);
+ return;
+ }
+
+ // if it's dead, look for something new
+ if ( !m_hEnemy->IsAlive() )
+ {
+ if (!m_flLastSight)
+ {
+ m_flLastSight = gpGlobals->time + 0.5; // continue-shooting timeout
+ }
+ else
+ {
+ if (gpGlobals->time > m_flLastSight)
+ {
+ m_hEnemy = NULL;
+ m_flLastSight = gpGlobals->time + m_flMaxWait;
+ SetThink(&CBaseTurret::SearchThink);
+ return;
+ }
+ }
+ }
+
+ Vector vecMid = pev->origin + pev->view_ofs;
+ Vector vecMidEnemy = m_hEnemy->BodyTarget( vecMid );
+
+ // Look for our current enemy
+ int fEnemyVisible = FBoxVisible(pev, m_hEnemy->pev, vecMidEnemy );
+
+ vecDirToEnemy = vecMidEnemy - vecMid; // calculate dir and dist to enemy
+ float flDistToEnemy = vecDirToEnemy.Length();
+
+ Vector vec = UTIL_VecToAngles(vecMidEnemy - vecMid);
+
+ // Current enmey is not visible.
+ if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE))
+ {
+ if (!m_flLastSight)
+ {
+ m_flLastSight = gpGlobals->time + 0.5;
+ }
+ else
+ {
+ // Should we look for a new target?
+ if (gpGlobals->time > m_flLastSight)
+ {
+ m_hEnemy = NULL;
+ m_flLastSight = gpGlobals->time + m_flMaxWait;
+ SetThink(&CBaseTurret::SearchThink);
+ return;
+ }
+ }
+ fEnemyVisible = 0;
+ }
+ else
+ {
+ m_vecLastSight = vecMidEnemy;
+ }
+
+ UTIL_MakeAimVectors(m_vecCurAngles);
+
+ /*
+ ALERT( at_console, "%.0f %.0f : %.2f %.2f %.2f\n",
+ m_vecCurAngles.x, m_vecCurAngles.y,
+ gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_forward.z );
+ */
+
+ Vector vecLOS = vecDirToEnemy; //vecMid - m_vecLastSight;
+ vecLOS = vecLOS.Normalize();
+
+ // Is the Gun looking at the target
+ if (DotProduct(vecLOS, gpGlobals->v_forward) <= 0.866) // 30 degree slop
+ fAttack = FALSE;
+ else
+ fAttack = TRUE;
+
+ // fire the gun
+ if (m_iSpin && ((fAttack) || (m_fBeserk)))
+ {
+ Vector vecSrc, vecAng;
+ GetAttachment( 0, vecSrc, vecAng );
+ SetTurretAnim(TURRET_ANIM_FIRE);
+ // In deathmatch, they just shoot right at you
+ if (gpGlobals->deathmatch)
+ {
+ // Adjust for feigning spies
+ vecAng = m_hEnemy->Center() - vecSrc;
+ UTIL_MakeVectors(vecAng);
+ Shoot(vecSrc, vecAng);
+ }
+ else
+ {
+ Shoot(vecSrc, gpGlobals->v_forward );
+ }
+ }
+ else
+ {
+ SetTurretAnim(TURRET_ANIM_SPIN);
+ }
+
+ //move the gun
+ if (m_fBeserk)
+ {
+ if (RANDOM_LONG(0,9) == 0)
+ {
+ m_vecGoalAngles.y = RANDOM_FLOAT(0,360);
+ m_vecGoalAngles.x = RANDOM_FLOAT(0,90) - 90 * m_iOrientation;
+ TakeDamage(pev,pev,1, DMG_GENERIC); // don't beserk forever
+ return;
+ }
+ }
+ else if (fEnemyVisible)
+ {
+ if (vec.y > 360)
+ vec.y -= 360;
+
+ if (vec.y < 0)
+ vec.y += 360;
+
+ //ALERT(at_console, "[%.2f]", vec.x);
+
+ if (vec.x < -180)
+ vec.x += 360;
+
+ if (vec.x > 180)
+ vec.x -= 360;
+
+ // now all numbers should be in [1...360]
+ // pin to turret limitations to [-90...15]
+
+ if (m_iOrientation == 0)
+ {
+ if (vec.x > 90)
+ vec.x = 90;
+ else if (vec.x < m_iMinPitch)
+ vec.x = m_iMinPitch;
+ }
+ else
+ {
+ if (vec.x < -90)
+ vec.x = -90;
+ else if (vec.x > -m_iMinPitch)
+ vec.x = -m_iMinPitch;
+ }
+
+ // ALERT(at_console, "->[%.2f]\n", vec.x);
+
+ m_vecGoalAngles.y = vec.y;
+ m_vecGoalAngles.x = vec.x;
+
+ }
+
+ SpinUpCall();
+ MoveTurret();
+}
+
+
+void CTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
+{
+ // Make turrets more dangerous in multiplayer
+ if (gpGlobals->deathmatch)
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_12MM, 1, 20 );
+ else
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_12MM, 1 );
+ EMIT_SOUND(ENT(pev), CHAN_WEAPON, "turret/tu_fire1.wav", 1, 0.6);
+ pev->effects = pev->effects | EF_MUZZLEFLASH;
+}
+
+
+void CMiniTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
+{
+ // Make turrets more dangerous in multiplayer
+ if (gpGlobals->deathmatch)
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_12MM, 1, 15 );
+ else
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_9MM, 1 );
+
+ switch(RANDOM_LONG(0,2))
+ {
+ case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks1.wav", 1, ATTN_NORM); break;
+ case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks2.wav", 1, ATTN_NORM); break;
+ case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks3.wav", 1, ATTN_NORM); break;
+ }
+ pev->effects = pev->effects | EF_MUZZLEFLASH;
+}
+
+
+void CBaseTurret::Deploy(void)
+{
+ pev->nextthink = gpGlobals->time + 0.1;
+ StudioFrameAdvance( );
+
+ if (pev->sequence != TURRET_ANIM_DEPLOY)
+ {
+ m_iOn = 1;
+ SetTurretAnim(TURRET_ANIM_DEPLOY);
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_deploy.wav", TURRET_MACHINE_VOLUME, ATTN_NORM);
+ SUB_UseTargets( this, USE_ON, 0 );
+ }
+
+ if (m_fSequenceFinished)
+ {
+ pev->maxs.z = m_iDeployHeight;
+ pev->mins.z = -m_iDeployHeight;
+ UTIL_SetSize(pev, pev->mins, pev->maxs);
+
+ m_vecCurAngles.x = 0;
+
+ if (m_iOrientation == 1)
+ {
+ m_vecCurAngles.y = UTIL_AngleMod( pev->angles.y + 180 );
+ }
+ else
+ {
+ m_vecCurAngles.y = UTIL_AngleMod( pev->angles.y );
+ }
+
+ SetTurretAnim(TURRET_ANIM_SPIN);
+ pev->framerate = 0;
+ SetThink(&CBaseTurret::SearchThink);
+ }
+
+ m_flLastSight = gpGlobals->time + m_flMaxWait;
+}
+
+void CBaseTurret::Retire(void)
+{
+ // make the turret level
+ m_vecGoalAngles.x = 0;
+ m_vecGoalAngles.y = m_flStartYaw;
+
+ pev->nextthink = gpGlobals->time + 0.1;
+
+ StudioFrameAdvance( );
+
+ EyeOff( );
+
+ if (!MoveTurret())
+ {
+ if (m_iSpin)
+ {
+ SpinDownCall();
+ }
+ else if (pev->sequence != TURRET_ANIM_RETIRE)
+ {
+ SetTurretAnim(TURRET_ANIM_RETIRE);
+ EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "turret/tu_deploy.wav", TURRET_MACHINE_VOLUME, ATTN_NORM, 0, 120);
+ SUB_UseTargets( this, USE_OFF, 0 );
+ }
+ else if (m_fSequenceFinished)
+ {
+ m_iOn = 0;
+ m_flLastSight = 0;
+ SetTurretAnim(TURRET_ANIM_NONE);
+ pev->maxs.z = m_iRetractHeight;
+ pev->mins.z = -m_iRetractHeight;
+ UTIL_SetSize(pev, pev->mins, pev->maxs);
+ if (m_iAutoStart)
+ {
+ SetThink(&CBaseTurret::AutoSearchThink);
+ pev->nextthink = gpGlobals->time + .1;
+ }
+ else
+ SetThink(&CBaseTurret::SUB_DoNothing);
+ }
+ }
+ else
+ {
+ SetTurretAnim(TURRET_ANIM_SPIN);
+ }
+}
+
+
+void CTurret::SpinUpCall(void)
+{
+ StudioFrameAdvance( );
+ pev->nextthink = gpGlobals->time + 0.1;
+
+ // Are we already spun up? If not start the two stage process.
+ if (!m_iSpin)
+ {
+ SetTurretAnim( TURRET_ANIM_SPIN );
+ // for the first pass, spin up the the barrel
+ if (!m_iStartSpin)
+ {
+ pev->nextthink = gpGlobals->time + 1.0; // spinup delay
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_spinup.wav", TURRET_MACHINE_VOLUME, ATTN_NORM);
+ m_iStartSpin = 1;
+ pev->framerate = 0.1;
+ }
+ // after the barrel is spun up, turn on the hum
+ else if (pev->framerate >= 1.0)
+ {
+ pev->nextthink = gpGlobals->time + 0.1; // retarget delay
+ EMIT_SOUND(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", TURRET_MACHINE_VOLUME, ATTN_NORM);
+ SetThink(&CTurret::ActiveThink);
+ m_iStartSpin = 0;
+ m_iSpin = 1;
+ }
+ else
+ {
+ pev->framerate += 0.075;
+ }
+ }
+
+ if (m_iSpin)
+ {
+ SetThink(&CTurret::ActiveThink);
+ }
+}
+
+
+void CTurret::SpinDownCall(void)
+{
+ if (m_iSpin)
+ {
+ SetTurretAnim( TURRET_ANIM_SPIN );
+ if (pev->framerate == 1.0)
+ {
+ EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100);
+ EMIT_SOUND(ENT(pev), CHAN_ITEM, "turret/tu_spindown.wav", TURRET_MACHINE_VOLUME, ATTN_NORM);
+ }
+ pev->framerate -= 0.02;
+ if (pev->framerate <= 0)
+ {
+ pev->framerate = 0;
+ m_iSpin = 0;
+ }
+ }
+}
+
+
+void CBaseTurret::SetTurretAnim(TURRET_ANIM anim)
+{
+ if (pev->sequence != anim)
+ {
+ switch(anim)
+ {
+ case TURRET_ANIM_FIRE:
+ case TURRET_ANIM_SPIN:
+ if (pev->sequence != TURRET_ANIM_FIRE && pev->sequence != TURRET_ANIM_SPIN)
+ {
+ pev->frame = 0;
+ }
+ break;
+ default:
+ pev->frame = 0;
+ break;
+ }
+
+ pev->sequence = anim;
+ ResetSequenceInfo( );
+
+ switch(anim)
+ {
+ case TURRET_ANIM_RETIRE:
+ pev->frame = 255;
+ pev->framerate = -1.0;
+ break;
+ case TURRET_ANIM_DIE:
+ pev->framerate = 1.0;
+ break;
+ }
+ //ALERT(at_console, "Turret anim #%d\n", anim);
+ }
+}
+
+
+//
+// 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
+ SetTurretAnim(TURRET_ANIM_SPIN);
+ StudioFrameAdvance( );
+ pev->nextthink = gpGlobals->time + 0.1;
+
+ if (m_flSpinUpTime == 0 && m_flMaxSpin)
+ m_flSpinUpTime = gpGlobals->time + m_flMaxSpin;
+
+ Ping( );
+
+ // If we have a target and we're still healthy
+ if (m_hEnemy != NULL)
+ {
+ if (!m_hEnemy->IsAlive() )
+ m_hEnemy = NULL;// Dead enemy forces a search for new one
+ }
+
+
+ // Acquire Target
+ if (m_hEnemy == NULL)
+ {
+ Look(TURRET_RANGE);
+ m_hEnemy = BestVisibleEnemy();
+ }
+
+ // If we've found a target, spin up the barrel and start to attack
+ if (m_hEnemy != NULL)
+ {
+ m_flLastSight = 0;
+ m_flSpinUpTime = 0;
+ SetThink(&CBaseTurret::ActiveThink);
+ }
+ else
+ {
+ // Are we out of time, do we need to retract?
+ if (gpGlobals->time > m_flLastSight)
+ {
+ //Before we retrace, make sure that we are spun down.
+ m_flLastSight = 0;
+ m_flSpinUpTime = 0;
+ SetThink(&CBaseTurret::Retire);
+ }
+ // should we stop the spin?
+ else if ((m_flSpinUpTime) && (gpGlobals->time > m_flSpinUpTime))
+ {
+ SpinDownCall();
+ }
+
+ // generic hunt for new victims
+ m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_fTurnRate);
+ 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( );
+
+ // Think slower in Multiplayer
+ pev->nextthink = gpGlobals->time + 2;
+
+ // If we have a target and we're still healthy
+
+ if (m_hEnemy != NULL)
+ {
+ if (!m_hEnemy->IsAlive() )
+ m_hEnemy = NULL;// Dead enemy forces a search for new one
+ }
+
+ // Acquire Target
+
+ if (m_hEnemy == NULL)
+ {
+ Look( TURRET_RANGE );
+ m_hEnemy = BestVisibleEnemy();
+ }
+
+ if (m_hEnemy != NULL)
+ {
+ SetThink(&CBaseTurret::Deploy);
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_alert.wav", TURRET_MACHINE_VOLUME, ATTN_NORM);
+ }
+}
+
+
+void CBaseTurret :: TurretDeath( void )
+{
+ BOOL iActive = FALSE;
+
+ StudioFrameAdvance( );
+ pev->nextthink = gpGlobals->time + 0.1;
+
+ if (pev->deadflag != DEAD_DEAD)
+ {
+ pev->deadflag = DEAD_DEAD;
+
+ float flRndSound = RANDOM_FLOAT ( 0 , 1 );
+
+ if ( flRndSound <= 0.33 )
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die.wav", 1.0, ATTN_NORM);
+ else if ( flRndSound <= 0.66 )
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die2.wav", 1.0, ATTN_NORM);
+ else
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die3.wav", 1.0, ATTN_NORM);
+
+ EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100);
+
+ if (m_iOrientation == 0)
+ m_vecGoalAngles.x = -15;
+ else
+ m_vecGoalAngles.x = -90;
+
+ SetTurretAnim(TURRET_ANIM_DIE);
+
+ EyeOn( );
+ }
+
+ EyeOff( );
+
+ if (pev->dmgtime + RANDOM_FLOAT( 0, 2 ) > gpGlobals->time)
+ {
+ // lots of smoke
+ MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
+ WRITE_BYTE( TE_SMOKE );
+ WRITE_COORD( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ) );
+ WRITE_COORD( RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ) );
+ WRITE_COORD( pev->origin.z - m_iOrientation * 64 );
+ WRITE_SHORT( g_sModelIndexSmoke );
+ WRITE_BYTE( 25 ); // scale * 10
+ WRITE_BYTE( 10 - m_iOrientation * 5); // framerate
+ MESSAGE_END();
+ }
+
+ if (pev->dmgtime + RANDOM_FLOAT( 0, 5 ) > gpGlobals->time)
+ {
+ Vector vecSrc = Vector( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ), RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ), 0 );
+ if (m_iOrientation == 0)
+ vecSrc = vecSrc + Vector( 0, 0, RANDOM_FLOAT( pev->origin.z, pev->absmax.z ) );
+ else
+ vecSrc = vecSrc + Vector( 0, 0, RANDOM_FLOAT( pev->absmin.z, pev->origin.z ) );
+
+ UTIL_Sparks( vecSrc );
+ }
+
+ if (m_fSequenceFinished && !MoveTurret( ) && pev->dmgtime + 5 < gpGlobals->time)
+ {
+ pev->framerate = 0;
+ SetThink( NULL );
+ }
+}
+
+
+
+void CBaseTurret :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
+{
+ if ( ptr->iHitgroup == 10 )
+ {
+ // hit armor
+ if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) )
+ {
+ UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) );
+ pev->dmgtime = gpGlobals->time;
+ }
+
+ flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated
+ }
+
+ if ( !pev->takedamage )
+ return;
+
+ AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
+}
+
+// take damage. bitsDamageType indicates type of damage sustained, ie: DMG_BULLET
+
+int CBaseTurret::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType)
+{
+ if ( !pev->takedamage )
+ return 0;
+
+ if (!m_iOn)
+ flDamage /= 10.0;
+
+ pev->health -= flDamage;
+ if (pev->health <= 0)
+ {
+ pev->health = 0;
+ pev->takedamage = DAMAGE_NO;
+ pev->dmgtime = gpGlobals->time;
+
+ ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place???
+
+ SetUse(NULL);
+ SetThink(&CBaseTurret::TurretDeath);
+ SUB_UseTargets( this, USE_ON, 0 ); // wake up others
+ pev->nextthink = gpGlobals->time + 0.1;
+
+ return 0;
+ }
+
+ if (pev->health <= 10)
+ {
+ if (m_iOn && (1 || RANDOM_LONG(0, 0x7FFF) > 800))
+ {
+ m_fBeserk = 1;
+ SetThink(&CBaseTurret::SearchThink);
+ }
+ }
+
+ return 1;
+}
+
+int CBaseTurret::MoveTurret(void)
+{
+ int state = 0;
+ // any x movement?
+
+ if (m_vecCurAngles.x != m_vecGoalAngles.x)
+ {
+ float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ;
+
+ m_vecCurAngles.x += 0.1 * m_iBaseTurnRate * flDir;
+
+ // if we started below the goal, and now we're past, peg to goal
+ if (flDir == 1)
+ {
+ if (m_vecCurAngles.x > m_vecGoalAngles.x)
+ m_vecCurAngles.x = m_vecGoalAngles.x;
+ }
+ else
+ {
+ if (m_vecCurAngles.x < m_vecGoalAngles.x)
+ m_vecCurAngles.x = m_vecGoalAngles.x;
+ }
+
+ if (m_iOrientation == 0)
+ SetBoneController(1, -m_vecCurAngles.x);
+ else
+ SetBoneController(1, m_vecCurAngles.x);
+ state = 1;
+ }
+
+ if (m_vecCurAngles.y != m_vecGoalAngles.y)
+ {
+ float flDir = m_vecGoalAngles.y > m_vecCurAngles.y ? 1 : -1 ;
+ float flDist = fabs(m_vecGoalAngles.y - m_vecCurAngles.y);
+
+ if (flDist > 180)
+ {
+ flDist = 360 - flDist;
+ flDir = -flDir;
+ }
+ if (flDist > 30)
+ {
+ if (m_fTurnRate < m_iBaseTurnRate * 10)
+ {
+ m_fTurnRate += m_iBaseTurnRate;
+ }
+ }
+ else if (m_fTurnRate > 45)
+ {
+ m_fTurnRate -= m_iBaseTurnRate;
+ }
+ else
+ {
+ m_fTurnRate += m_iBaseTurnRate;
+ }
+
+ m_vecCurAngles.y += 0.1 * m_fTurnRate * flDir;
+
+ if (m_vecCurAngles.y < 0)
+ m_vecCurAngles.y += 360;
+ else if (m_vecCurAngles.y >= 360)
+ m_vecCurAngles.y -= 360;
+
+ if (flDist < (0.05 * m_iBaseTurnRate))
+ m_vecCurAngles.y = m_vecGoalAngles.y;
+
+ //ALERT(at_console, "%.2f -> %.2f\n", m_vecCurAngles.y, y);
+ if (m_iOrientation == 0)
+ SetBoneController(0, m_vecCurAngles.y - pev->angles.y );
+ else
+ SetBoneController(0, pev->angles.y - 180 - m_vecCurAngles.y );
+ state = 1;
+ }
+
+ if (!state)
+ m_fTurnRate = m_iBaseTurnRate;
+
+ //ALERT(at_console, "(%.2f, %.2f)->(%.2f, %.2f)\n", m_vecCurAngles.x,
+ // m_vecCurAngles.y, m_vecGoalAngles.x, m_vecGoalAngles.y);
+ return state;
+}
+
+//
+// ID as a machine
+//
+int CBaseTurret::Classify ( void )
+{
+ if (m_iOn || m_iAutoStart)
+ return CLASS_MACHINE;
+ return CLASS_NONE;
+}
+
+
+
+
+//=========================================================
+// Sentry gun - smallest turret, placed near grunt entrenchments
+//=========================================================
+class CSentry : public CBaseTurret
+{
+public:
+ void Spawn( );
+ void Precache(void);
+ // other functions
+ void Shoot(Vector &vecSrc, Vector &vecDirToEnemy);
+ int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
+ void EXPORT SentryTouch( CBaseEntity *pOther );
+ void EXPORT SentryDeath( void );
+
+};
+
+LINK_ENTITY_TO_CLASS( monster_sentry, CSentry );
+
+void CSentry::Precache()
+{
+ CBaseTurret::Precache( );
+ PRECACHE_MODEL ("models/sentry.mdl");
+}
+
+void CSentry::Spawn()
+{
+ Precache( );
+ SET_MODEL(ENT(pev), "models/sentry.mdl");
+ pev->health = gSkillData.sentryHealth;
+ m_HackedGunPos = Vector( 0, 0, 48 );
+ pev->view_ofs.z = 48;
+ m_flMaxWait = 1E6;
+ m_flMaxSpin = 1E6;
+
+ CBaseTurret::Spawn();
+ m_iRetractHeight = 64;
+ m_iDeployHeight = 64;
+ m_iMinPitch = -60;
+ UTIL_SetSize(pev, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight));
+
+ SetTouch(&CSentry::SentryTouch);
+ SetThink(&CSentry::Initialize);
+ pev->nextthink = gpGlobals->time + 0.3;
+}
+
+void CSentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
+{
+ // Make turrets more dangerous in multiplayer
+ if (gpGlobals->deathmatch)
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_12MM, 1, 10 );
+ else
+ FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_MP5, 1 );
+
+ switch(RANDOM_LONG(0,2))
+ {
+ case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks1.wav", 1, ATTN_NORM); break;
+ case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks2.wav", 1, ATTN_NORM); break;
+ case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks3.wav", 1, ATTN_NORM); break;
+ }
+ pev->effects = pev->effects | EF_MUZZLEFLASH;
+}
+
+int CSentry::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType)
+{
+ if ( !pev->takedamage )
+ return 0;
+
+ if (!m_iOn)
+ {
+ SetThink( &CSentry::Deploy );
+ SetUse( NULL );
+ pev->nextthink = gpGlobals->time + 0.1;
+ }
+
+ pev->health -= flDamage;
+ if (pev->health <= 0)
+ {
+ pev->health = 0;
+ pev->takedamage = DAMAGE_NO;
+ pev->dmgtime = gpGlobals->time;
+
+ ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place???
+
+ SetUse(NULL);
+ SetThink(&CSentry::SentryDeath);
+ SUB_UseTargets( this, USE_ON, 0 ); // wake up others
+ pev->nextthink = gpGlobals->time + 0.1;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+
+void CSentry::SentryTouch( CBaseEntity *pOther )
+{
+ if (pOther && (pOther->pev->flags & (FL_CLIENT | FL_MONSTER)))
+ {
+ TakeDamage(pOther->pev, pOther->pev, 0, 0 );
+ }
+}
+
+
+void CSentry :: SentryDeath( void )
+{
+ BOOL iActive = FALSE;
+
+ StudioFrameAdvance( );
+ pev->nextthink = gpGlobals->time + 0.1;
+
+ if (pev->deadflag != DEAD_DEAD)
+ {
+ pev->deadflag = DEAD_DEAD;
+
+ float flRndSound = RANDOM_FLOAT ( 0 , 1 );
+
+ if ( flRndSound <= 0.33 )
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die.wav", 1.0, ATTN_NORM);
+ else if ( flRndSound <= 0.66 )
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die2.wav", 1.0, ATTN_NORM);
+ else
+ EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die3.wav", 1.0, ATTN_NORM);
+
+ EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100);
+
+ SetBoneController( 0, 0 );
+ SetBoneController( 1, 0 );
+
+ SetTurretAnim(TURRET_ANIM_DIE);
+
+ pev->solid = SOLID_NOT;
+ pev->angles.y = UTIL_AngleMod( pev->angles.y + RANDOM_LONG( 0, 2 ) * 120 );
+
+ EyeOn( );
+ }
+
+ EyeOff( );
+
+ Vector vecSrc, vecAng;
+ GetAttachment( 1, vecSrc, vecAng );
+
+ if (pev->dmgtime + RANDOM_FLOAT( 0, 2 ) > gpGlobals->time)
+ {
+ // lots of smoke
+ MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
+ WRITE_BYTE( TE_SMOKE );
+ WRITE_COORD( vecSrc.x + RANDOM_FLOAT( -16, 16 ) );
+ WRITE_COORD( vecSrc.y + RANDOM_FLOAT( -16, 16 ) );
+ WRITE_COORD( vecSrc.z - 32 );
+ WRITE_SHORT( g_sModelIndexSmoke );
+ WRITE_BYTE( 15 ); // scale * 10
+ WRITE_BYTE( 8 ); // framerate
+ MESSAGE_END();
+ }
+
+ if (pev->dmgtime + RANDOM_FLOAT( 0, 8 ) > gpGlobals->time)
+ {
+ UTIL_Sparks( vecSrc );
+ }
+
+ if (m_fSequenceFinished && pev->dmgtime + 5 < gpGlobals->time)
+ {
+ pev->framerate = 0;
+ SetThink( NULL );
+ }
+}
+
+#endif
+
+#endif // TF_TURRET_H \ No newline at end of file