diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_turret.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/hl1/hl1_npc_turret.cpp')
| -rw-r--r-- | game/server/hl1/hl1_npc_turret.cpp | 1509 |
1 files changed, 1509 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_turret.cpp b/game/server/hl1/hl1_npc_turret.cpp new file mode 100644 index 0000000..9084b57 --- /dev/null +++ b/game/server/hl1/hl1_npc_turret.cpp @@ -0,0 +1,1509 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "ai_default.h" +#include "ai_task.h" +#include "ai_schedule.h" +#include "ai_node.h" +#include "ai_hull.h" +#include "ai_hint.h" +#include "ai_memory.h" +#include "ai_route.h" +#include "ai_motor.h" +#include "ai_senses.h" +#include "soundent.h" +#include "game.h" +#include "npcevent.h" +#include "entitylist.h" +#include "activitylist.h" +#include "animation.h" +#include "basecombatweapon.h" +#include "IEffects.h" +#include "vstdlib/random.h" +#include "engine/IEngineSound.h" +#include "ammodef.h" +#include "Sprite.h" + +#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 + +typedef enum +{ +// TURRET_ANIM_NONE = 0, + TURRET_ANIM_FIRE = 0, + TURRET_ANIM_SPIN, + TURRET_ANIM_DEPLOY, + TURRET_ANIM_RETIRE, + TURRET_ANIM_DIE, +} TURRET_ANIM; + +#define SF_MONSTER_TURRET_AUTOACTIVATE 32 +#define SF_MONSTER_TURRET_STARTINACTIVE 64 + +#define TURRET_GLOW_SPRITE "sprites/flare3.vmt" + +#define TURRET_ORIENTATION_FLOOR 0 +#define TURRET_ORIENTATION_CEILING 1 + +class CNPC_BaseTurret : public CAI_BaseNPC +{ + DECLARE_CLASS( CNPC_BaseTurret, CAI_BaseNPC ); +public: + void Spawn(void); + virtual void Precache(void); + void EXPORT TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ); + + virtual int OnTakeDamage( const CTakeDamageInfo &info ); + virtual int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ); + + Class_T Classify( void ); + + // 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 EXPORT Deploy(void); + void EXPORT Retire(void); + + void EXPORT Initialize(void); + + virtual void Ping(void); + virtual void EyeOn(void); + virtual void EyeOff(void); + + + void InputActivate( inputdata_t &inputdata ); + void InputDeactivate( inputdata_t &inputdata ); + + void Event_Killed( const CTakeDamageInfo &info ); + virtual bool ShouldFadeOnDeath( void ) { return false; } + bool ShouldGib( const CTakeDamageInfo &info ) { return false; } + + // 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; + QAngle 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 + + float m_flDamageTime; + + int m_iAmmoType; + + COutputEvent m_OnActivate; + COutputEvent m_OnDeactivate; + + //DEFINE_CUSTOM_AI; + DECLARE_DATADESC(); +}; + +BEGIN_DATADESC( CNPC_BaseTurret ) + + //FIELDS + DEFINE_FIELD( m_flMaxSpin, FIELD_FLOAT ), + DEFINE_FIELD( m_iSpin, FIELD_INTEGER ), + + 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_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_flStartYaw, FIELD_FLOAT ), + DEFINE_FIELD( m_vecCurAngles, FIELD_VECTOR ), + DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ), + + DEFINE_FIELD( m_flPingTime, FIELD_TIME ), + DEFINE_FIELD( m_flSpinUpTime, FIELD_TIME ), + + DEFINE_FIELD( m_flDamageTime, FIELD_TIME ), + //DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ), + + //KEYFIELDS + DEFINE_KEYFIELD( m_flMaxWait, FIELD_FLOAT, "maxsleep" ), + DEFINE_KEYFIELD( m_iOrientation, FIELD_INTEGER, "orientation" ), + DEFINE_KEYFIELD( m_iSearchSpeed, FIELD_INTEGER, "searchspeed" ), + DEFINE_KEYFIELD( m_iBaseTurnRate, FIELD_INTEGER, "turnrate" ), + + //Use + DEFINE_USEFUNC( TurretUse ), + + //Thinks + DEFINE_THINKFUNC( ActiveThink ), + DEFINE_THINKFUNC( SearchThink ), + DEFINE_THINKFUNC( AutoSearchThink ), + DEFINE_THINKFUNC( TurretDeath ), + DEFINE_THINKFUNC( SpinDownCall ), + DEFINE_THINKFUNC( SpinUpCall ), + DEFINE_THINKFUNC( Deploy ), + DEFINE_THINKFUNC( Retire ), + DEFINE_THINKFUNC( Initialize ), + + //Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), + DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ), + + //Outputs + DEFINE_OUTPUT( m_OnActivate, "OnActivate"), + DEFINE_OUTPUT( m_OnDeactivate, "OnDeactivate"), + +END_DATADESC() + + +void CNPC_BaseTurret::Spawn() +{ + Precache( ); + SetNextThink( gpGlobals->curtime + 1 ); + SetMoveType( MOVETYPE_FLY ); + SetSequence( 0 ); + SetCycle( 0 ); + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_NOT_STANDABLE ); + m_takedamage = DAMAGE_YES; + AddFlag( FL_AIMTARGET ); + + AddFlag( FL_NPC ); + SetUse( &CNPC_BaseTurret::TurretUse ); + + if (( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) + && !( m_spawnflags & SF_MONSTER_TURRET_STARTINACTIVE )) + { + m_iAutoStart = true; + } + + ResetSequenceInfo( ); + + SetBoneController(0, 0); + SetBoneController(1, 0); + + m_flFieldOfView = VIEW_FIELD_FULL; + + m_bloodColor = DONT_BLEED; + m_flDamageTime = 0; + + if ( GetSpawnFlags() & SF_MONSTER_TURRET_STARTINACTIVE ) + { + SetTurretAnim( TURRET_ANIM_RETIRE ); + SetCycle( 0.0f ); + m_flPlaybackRate = 0.0f; + } +} + + +void CNPC_BaseTurret::Precache() +{ + m_iAmmoType = GetAmmoDef()->Index("12mmRound"); + + PrecacheScriptSound( "Turret.Alert" ); + PrecacheScriptSound( "Turret.Die" ); + PrecacheScriptSound( "Turret.Deploy" ); + PrecacheScriptSound( "Turret.Undeploy" ); + PrecacheScriptSound( "Turret.Ping" ); + PrecacheScriptSound( "Turret.Shoot" ); +} + +Class_T CNPC_BaseTurret::Classify( void ) +{ + if (m_iOn || m_iAutoStart) + return CLASS_MACHINE; + return CLASS_NONE; +} + +//========================================================= +// TraceAttack - being attacked +//========================================================= +void CNPC_BaseTurret::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) +{ + CTakeDamageInfo ainfo = info; + + if ( ptr->hitgroup == 10 ) + { + // hit armor + if ( m_flDamageTime != gpGlobals->curtime || (random->RandomInt(0,10) < 1) ) + { + g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal ); + m_flDamageTime = gpGlobals->curtime; + } + + ainfo.SetDamage( 0.1 );// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated + } + + if ( m_takedamage == DAMAGE_NO ) + return; + + //DevMsg( 1, "traceattack: %f\n", ainfo.GetDamage() ); + + AddMultiDamage( info, this ); +} + +//========================================================= +// TakeDamage - take damage. +//========================================================= +int CNPC_BaseTurret::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) +{ + if ( m_takedamage == DAMAGE_NO ) + return 0; + + float flDamage = inputInfo.GetDamage(); + + if (!m_iOn) + flDamage /= 10.0; + + m_iHealth -= flDamage; + if (m_iHealth <= 0) + { + m_iHealth = 0; + m_takedamage = DAMAGE_NO; + m_flDamageTime = gpGlobals->curtime; + +// ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? + + SetUse(NULL); + SetThink(&CNPC_BaseTurret::TurretDeath); + SetNextThink( gpGlobals->curtime + 0.1 ); + + m_OnDeactivate.FireOutput(this, this); + + return 0; + } + + if (m_iHealth <= 10) + { + if (m_iOn) + { + m_fBeserk = 1; + SetThink(&CNPC_BaseTurret::SearchThink); + } + } + return 1; +} + +int CNPC_BaseTurret::OnTakeDamage( const CTakeDamageInfo &info ) +{ + int retVal = 0; + + if (!m_takedamage) + return 0; + + switch( m_lifeState ) + { + case LIFE_ALIVE: + retVal = OnTakeDamage_Alive( info ); + if ( m_iHealth <= 0 ) + { + IPhysicsObject *pPhysics = VPhysicsGetObject(); + if ( pPhysics ) + { + pPhysics->EnableCollisions( false ); + } + + Event_Killed( info ); + Event_Dying(); + } + return retVal; + break; + + case LIFE_DYING: + return OnTakeDamage_Dying( info ); + + default: + case LIFE_DEAD: + return OnTakeDamage_Dead( info ); + } +} + +void CNPC_BaseTurret::SetTurretAnim( TURRET_ANIM anim ) +{ + /* + if (GetSequence() != anim) + { + switch(anim) + { + case TURRET_ANIM_FIRE: + case TURRET_ANIM_SPIN: + if (GetSequence() != TURRET_ANIM_FIRE && GetSequence() != TURRET_ANIM_SPIN) + { + m_flCycle = 0; + } + break; + default: + m_flCycle = 0; + break; + } + + SetSequence( anim ); + + ResetSequenceInfo( ); + + switch(anim) + { + case TURRET_ANIM_RETIRE: + m_flCycle = 255; + m_flPlaybackRate = -1.0; //play the animation backwards + break; + case TURRET_ANIM_DIE: + m_flPlaybackRate = 1.0; + break; + } + //ALERT(at_console, "Turret anim #%d\n", anim); + } + */ + + if (GetSequence() != anim) + { + SetSequence( anim ); + + ResetSequenceInfo( ); + + switch(anim) + { + case TURRET_ANIM_FIRE: + case TURRET_ANIM_SPIN: + if (GetSequence() != TURRET_ANIM_FIRE && GetSequence() != TURRET_ANIM_SPIN) + { + SetCycle( 0 ); + } + break; + case TURRET_ANIM_RETIRE: + SetCycle( 1.0 ); + m_flPlaybackRate = -1.0; //play the animation backwards + break; + case TURRET_ANIM_DIE: + SetCycle( 0.0 ); + m_flPlaybackRate = 1.0; + break; + default: + SetCycle( 0 ); + break; + } + + + } +} + +//========================================================= +// Initialize - set up the turret, initial think +//========================================================= +void CNPC_BaseTurret::Initialize(void) +{ + m_iOn = 0; + m_fBeserk = 0; + m_iSpin = 0; + + SetBoneController( 0, 0 ); + SetBoneController( 1, 0 ); + + if (m_iBaseTurnRate == 0) m_iBaseTurnRate = TURRET_TURNRATE; + if (m_flMaxWait == 0) m_flMaxWait = TURRET_MAXWAIT; + + QAngle angles = GetAbsAngles(); + m_flStartYaw = angles.y; + if (m_iOrientation == TURRET_ORIENTATION_CEILING) + { + angles.x = 180; + angles.y += 180; + if( angles.y > 360 ) + angles.y -= 360; + SetAbsAngles( angles ); + +// pev->idealpitch = 180; //not used? + + Vector view_ofs = GetViewOffset(); + view_ofs.z = -view_ofs.z; + SetViewOffset( view_ofs ); + +// pev->effects |= EF_INVLIGHT; //no need + } + + m_vecGoalAngles.x = 0; + + if (m_iAutoStart) + { + m_flLastSight = gpGlobals->curtime + m_flMaxWait; + SetThink(&CNPC_BaseTurret::AutoSearchThink); + + SetNextThink( gpGlobals->curtime + 0.1 ); + } + else + { + SetThink( &CBaseEntity::SUB_DoNothing ); + } +} + +//========================================================= +// ActiveThink - +//========================================================= +void CNPC_BaseTurret::ActiveThink(void) +{ + int fAttack = 0; + + SetNextThink( gpGlobals->curtime + 0.1 ); + StudioFrameAdvance( ); + + if ( (!m_iOn) || (GetEnemy() == NULL) ) + { + SetEnemy( NULL ); + m_flLastSight = gpGlobals->curtime + m_flMaxWait; + SetThink(&CNPC_BaseTurret::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(&CNPC_BaseTurret::SearchThink); + return; + } + } + } + + Vector vecMid = EyePosition(); + Vector vecMidEnemy = GetEnemy()->BodyTarget(vecMid, false); + + // Look for our current enemy + int fEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() ); + + //We want to look at the enemy's eyes so we don't jitter + Vector vecDirToEnemyEyes = vecMidEnemy - vecMid; +// NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 1.0 ); + + float flDistToEnemy = vecDirToEnemyEyes.Length(); + + VectorNormalize( vecDirToEnemyEyes ); + + QAngle vecAnglesToEnemy; + VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy ); + + // Current enmey is not visible. + if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE)) + { + if (!m_flLastSight) + m_flLastSight = gpGlobals->curtime + 0.5; + else + { + // Should we look for a new target? + if (gpGlobals->curtime > m_flLastSight) + { + SetEnemy( NULL ); + m_flLastSight = gpGlobals->curtime + m_flMaxWait; + SetThink(&CNPC_BaseTurret::SearchThink); + return; + } + } + fEnemyVisible = 0; + } + else + { + m_vecLastSight = vecMidEnemy; + } + + Vector forward; + AngleVectors( m_vecCurAngles, &forward ); + + Vector2D vec2LOS = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).AsVector2D(); + vec2LOS.NormalizeInPlace(); + + float flDot = vec2LOS.Dot( forward.AsVector2D() ); + + if ( flDot <= 0.866 ) + fAttack = FALSE; + else + fAttack = TRUE; + + //forward + //NDebugOverlay::Line(vecMuzzle, vecMid + ( forward ), 255,0,0, false, 0.1); + //LOS + //NDebugOverlay::Line(vecMuzzle, vecMid + ( vecDirToEnemyEyes * 200 ), 0,0,255, false, 0.1); + + // fire the gun + if (m_iSpin && ((fAttack) || (m_fBeserk))) + { + Shoot(vecMid, forward ); + SetTurretAnim(TURRET_ANIM_FIRE); + } + else + { + SetTurretAnim(TURRET_ANIM_SPIN); + } + + //move the gun + if (m_fBeserk) + { + if (random->RandomInt(0,9) == 0) + { + m_vecGoalAngles.y = random->RandomFloat(0,360); + m_vecGoalAngles.x = random->RandomFloat(0,90) - 90 * m_iOrientation; + + CTakeDamageInfo info; + info.SetAttacker(this); + info.SetInflictor(this); + info.SetDamage( 1 ); + info.SetDamageType( DMG_GENERIC ); + + TakeDamage( info ); // don't beserk forever + return; + } + } + else if (fEnemyVisible) + { + if (vecAnglesToEnemy.y > 360) + vecAnglesToEnemy.y -= 360; + + if (vecAnglesToEnemy.y < 0) + vecAnglesToEnemy.y += 360; + + //ALERT(at_console, "[%.2f]", vec.x); + + if (vecAnglesToEnemy.x < -180) + vecAnglesToEnemy.x += 360; + + if (vecAnglesToEnemy.x > 180) + vecAnglesToEnemy.x -= 360; + + // now all numbers should be in [1...360] + // pin to turret limitations to [-90...14] + + if (m_iOrientation == TURRET_ORIENTATION_FLOOR) + { + if (vecAnglesToEnemy.x > 90) + vecAnglesToEnemy.x = 90; + else if (vecAnglesToEnemy.x < m_iMinPitch) + vecAnglesToEnemy.x = m_iMinPitch; + } + else + { + if (vecAnglesToEnemy.x < -90) + vecAnglesToEnemy.x = -90; + else if (vecAnglesToEnemy.x > -m_iMinPitch) + vecAnglesToEnemy.x = -m_iMinPitch; + } + + //DevMsg( 1, "->[%.2f]\n", vec.x); + + m_vecGoalAngles.y = vecAnglesToEnemy.y; + m_vecGoalAngles.x = vecAnglesToEnemy.x; + + } + + SpinUpCall(); + MoveTurret(); +} + +//========================================================= +// SearchThink +// 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 CNPC_BaseTurret::SearchThink(void) +{ + // ensure rethink + SetTurretAnim(TURRET_ANIM_SPIN); + StudioFrameAdvance( ); + SetNextThink( gpGlobals->curtime + 0.1 ); + + if (m_flSpinUpTime == 0 && m_flMaxSpin) + m_flSpinUpTime = gpGlobals->curtime + m_flMaxSpin; + + Ping( ); + + CBaseEntity *pEnemy = GetEnemy(); + + // If we have a target and we're still healthy + if (pEnemy != NULL) + { + if (!pEnemy->IsAlive() ) + pEnemy = NULL;// Dead enemy forces a search for new one + } + + + // Acquire Target + if (pEnemy == NULL) + { + GetSenses()->Look(TURRET_RANGE); + pEnemy = BestEnemy(); + + if ( pEnemy && !FVisible( pEnemy ) ) + pEnemy = NULL; + } + + // If we've found a target, spin up the barrel and start to attack + if (pEnemy != NULL) + { + m_flLastSight = 0; + m_flSpinUpTime = 0; + SetThink(&CNPC_BaseTurret::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; + m_flSpinUpTime = 0; + SetThink(&CNPC_BaseTurret::Retire); + } + // should we stop the spin? + else if ((m_flSpinUpTime) && (gpGlobals->curtime > 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(); + } + + SetEnemy( pEnemy ); +} + +//========================================================= +// AutoSearchThink - +//========================================================= +void CNPC_BaseTurret::AutoSearchThink(void) +{ + // ensure rethink + StudioFrameAdvance( ); + SetNextThink( gpGlobals->curtime + 0.3 ); + + // If we have a target and we're still healthy + + CBaseEntity *pEnemy = GetEnemy(); + + if (pEnemy != NULL) + { + if (!pEnemy->IsAlive() ) + { + pEnemy = NULL; + } + } + + // Acquire Target + + if (pEnemy == NULL) + { + GetSenses()->Look( TURRET_RANGE ); + pEnemy = BestEnemy(); + + if ( pEnemy && !FVisible( pEnemy ) ) + pEnemy = NULL; + } + + if (pEnemy != NULL) + { + SetThink(&CNPC_BaseTurret::Deploy); + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Turret.Alert" ); + } + + SetEnemy( pEnemy ); +} + +extern short g_sModelIndexSmoke; + +//========================================================= +// TurretDeath - I die as I have lived, beyond my means +//========================================================= +void CNPC_BaseTurret::TurretDeath(void) +{ + StudioFrameAdvance( ); + SetNextThink( gpGlobals->curtime + 0.1 ); + + if (m_lifeState != LIFE_DEAD) + { + m_lifeState = LIFE_DEAD; + + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Turret.Die" ); + + StopSound( entindex(), "Turret.Spinup" ); + + if (m_iOrientation == TURRET_ORIENTATION_FLOOR) + m_vecGoalAngles.x = -14; + else + m_vecGoalAngles.x = 90;//-90; + + SetTurretAnim(TURRET_ANIM_DIE); + + 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 (IsSequenceFinished() && !MoveTurret() && m_flDamageTime + 5 < gpGlobals->curtime) + { + m_flPlaybackRate = 0; + SetThink( NULL ); + } +} + +//========================================================= +// Deploy - go active +//========================================================= +void CNPC_BaseTurret::Deploy(void) +{ + SetNextThink( gpGlobals->curtime + 0.1 ); + StudioFrameAdvance( ); + + if (GetSequence() != TURRET_ANIM_DEPLOY) + { + m_iOn = 1; + SetTurretAnim(TURRET_ANIM_DEPLOY); + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Turret.Deploy" ); + m_OnActivate.FireOutput(this, this); + } + + if (IsSequenceFinished()) + { + Vector curmins, curmaxs; + curmins = WorldAlignMins(); + curmaxs = WorldAlignMaxs(); + + curmaxs.z = m_iDeployHeight; + curmins.z = -m_iDeployHeight; + + SetCollisionBounds( curmins, curmaxs ); + + m_vecCurAngles.x = 0; + + QAngle angles = GetAbsAngles(); + + if (m_iOrientation == TURRET_ORIENTATION_CEILING) + { + m_vecCurAngles.y = UTIL_AngleMod( angles.y + 180 ); + } + else + { + m_vecCurAngles.y = UTIL_AngleMod( angles.y ); + } + + SetTurretAnim(TURRET_ANIM_SPIN); + m_flPlaybackRate = 0; + SetThink(&CNPC_BaseTurret::SearchThink); + } + + m_flLastSight = gpGlobals->curtime + m_flMaxWait; +} + +//========================================================= +// Retire - stop being active +//========================================================= +void CNPC_BaseTurret::Retire(void) +{ + // make the turret level + m_vecGoalAngles.x = 0; + m_vecGoalAngles.y = m_flStartYaw; + + SetNextThink( gpGlobals->curtime + 0.1 ); + + StudioFrameAdvance( ); + + EyeOff( ); + + if (!MoveTurret()) + { + if (m_iSpin) + { + SpinDownCall(); + } + else if (GetSequence() != TURRET_ANIM_RETIRE) + { + SetTurretAnim(TURRET_ANIM_RETIRE); + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Turret.Undeploy" ); + m_OnDeactivate.FireOutput(this, this); + } + //else if (IsSequenceFinished()) + else if( GetSequence() == TURRET_ANIM_RETIRE && GetCycle() <= 0.0 ) + { + m_iOn = 0; + m_flLastSight = 0; + //SetTurretAnim(TURRET_ANIM_NONE); + + Vector curmins, curmaxs; + curmins = WorldAlignMins(); + curmaxs = WorldAlignMaxs(); + + curmaxs.z = m_iRetractHeight; + curmins.z = -m_iRetractHeight; + + SetCollisionBounds( curmins, curmaxs ); + if (m_iAutoStart) + { + SetThink(&CNPC_BaseTurret::AutoSearchThink); + SetNextThink( gpGlobals->curtime + 0.1 ); + } + else + { + SetThink( &CBaseEntity::SUB_DoNothing ); + } + } + } + else + { + SetTurretAnim(TURRET_ANIM_SPIN); + } +} + +//========================================================= +// Ping - make the pinging noise every second while searching +//========================================================= +void CNPC_BaseTurret::Ping(void) +{ + if (m_flPingTime == 0) + m_flPingTime = gpGlobals->curtime + 1; + else if (m_flPingTime <= gpGlobals->curtime) + { + m_flPingTime = gpGlobals->curtime + 1; + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Turret.Ping" ); + + EyeOn( ); + } + else if (m_eyeBrightness > 0) + { + EyeOff( ); + } +} + +//========================================================= +// MoveTurret - handle turret rotation +// returns 1 if the turret moved. +//========================================================= +int CNPC_BaseTurret::MoveTurret(void) +{ + int bMoved = 0; + + if (m_vecCurAngles.x != m_vecGoalAngles.x) + { + float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ; + + m_vecCurAngles.x += 0.1 * m_fTurnRate * 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 == TURRET_ORIENTATION_FLOOR) + SetBoneController(1, m_vecCurAngles.x); + else + SetBoneController(1, -m_vecCurAngles.x); + + bMoved = 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; + + QAngle angles = GetAbsAngles(); + + //ALERT(at_console, "%.2f -> %.2f\n", m_vecCurAngles.y, y); + + if (m_iOrientation == TURRET_ORIENTATION_FLOOR) + SetBoneController(0, m_vecCurAngles.y - angles.y ); + else + SetBoneController(0, angles.y - 180 - m_vecCurAngles.y ); + bMoved = 1; + } + + if (!bMoved) + m_fTurnRate = m_iBaseTurnRate; + + //DevMsg(1, "(%.2f, %.2f)->(%.2f, %.2f)\n", m_vecCurAngles.x, + // m_vecCurAngles.y, m_vecGoalAngles.x, m_vecGoalAngles.y); + + return bMoved; +} + +void CNPC_BaseTurret::TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !ShouldToggle( useType, m_iOn ) ) + return; + + if (m_iOn) + { + SetEnemy( NULL ); + SetNextThink( gpGlobals->curtime + 0.1 ); + m_iAutoStart = FALSE;// switching off a turret disables autostart + //!!!! this should spin down first!!BUGBUG + SetThink(&CNPC_BaseTurret::Retire); + } + else + { + SetNextThink( gpGlobals->curtime + 0.1 ); // turn on delay + + // if the turret is flagged as an autoactivate turret, re-enable it's ability open self. + if ( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) + { + m_iAutoStart = TRUE; + } + + SetThink(&CNPC_BaseTurret::Deploy); + } +} + +void CNPC_BaseTurret::InputDeactivate( inputdata_t &inputdata ) +{ + if( m_iOn && m_lifeState == LIFE_ALIVE ) + { + SetEnemy( NULL ); + SetNextThink( gpGlobals->curtime + 0.1 ); + m_iAutoStart = FALSE;// switching off a turret disables autostart + //!!!! this should spin down first!!BUGBUG + SetThink(&CNPC_BaseTurret::Retire); + } +} + +void CNPC_BaseTurret::InputActivate( inputdata_t &inputdata ) +{ + if( !m_iOn && m_lifeState == LIFE_ALIVE ) + { + SetNextThink( gpGlobals->curtime + 0.1 ); // turn on delay + + // if the turret is flagged as an autoactivate turret, re-enable it's ability open self. + if ( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) + { + m_iAutoStart = TRUE; + } + + SetThink(&CNPC_BaseTurret::Deploy); + } +} + + +//========================================================= +// EyeOn - turn on light on the turret +//========================================================= +void CNPC_BaseTurret::EyeOn(void) +{ + if (m_pEyeGlow) + { + if (m_eyeBrightness != 255) + { + m_eyeBrightness = 255; + } + m_pEyeGlow->SetBrightness( m_eyeBrightness ); + } +} + +//========================================================= +// EyeOn - turn off light on the turret +//========================================================= +void CNPC_BaseTurret::EyeOff(void) +{ + if (m_pEyeGlow) + { + if (m_eyeBrightness > 0) + { + m_eyeBrightness = MAX( 0, m_eyeBrightness - 30 ); + m_pEyeGlow->SetBrightness( m_eyeBrightness ); + } + } +} + +void CNPC_BaseTurret::Event_Killed( const CTakeDamageInfo &info ) +{ + BaseClass::Event_Killed( info ); + + SetMoveType( MOVETYPE_FLY ); +} + +//=== + +class CNPC_MiniTurret : public CNPC_BaseTurret +{ + DECLARE_CLASS( CNPC_MiniTurret, CNPC_BaseTurret ); + +public: + void Spawn( ); + void Precache(void); + + // other functions + void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); +}; + +class CNPC_Turret : public CNPC_BaseTurret +{ + DECLARE_CLASS( CNPC_Turret, CNPC_BaseTurret ); + +public: + DECLARE_DATADESC(); + + void Spawn(void); + void Precache(void); + + // Think functions + void SpinUpCall(void); + void SpinDownCall(void); + + // other functions + void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); + +private: + int m_iStartSpin; +}; + +BEGIN_DATADESC(CNPC_Turret) + DEFINE_FIELD( m_iStartSpin, FIELD_INTEGER ), +END_DATADESC() + +LINK_ENTITY_TO_CLASS( monster_turret, CNPC_Turret ); +LINK_ENTITY_TO_CLASS( monster_miniturret, CNPC_MiniTurret ); + +ConVar sk_turret_health ( "sk_turret_health","50"); +ConVar sk_miniturret_health ( "sk_miniturret_health","40"); +ConVar sk_sentry_health ( "sk_sentry_health","40"); + +void CNPC_Turret::Spawn() +{ + Precache( ); + SetModel( "models/turret.mdl" ); + m_iHealth = sk_turret_health.GetFloat(); + m_HackedGunPos = Vector( 0, 0, 12.75 ); + m_flMaxSpin = TURRET_MAXSPIN; + + Vector view_ofs( 0, 0, 12.75 ); + SetViewOffset( view_ofs ); + + CNPC_BaseTurret::Spawn( ); + + m_iRetractHeight = 16; + m_iDeployHeight = 32; + m_iMinPitch = -90; + UTIL_SetSize(this, Vector(-32, -32, -m_iRetractHeight), Vector(32, 32, m_iRetractHeight)); + + SetThink(&CNPC_BaseTurret::Initialize); + + m_pEyeGlow = CSprite::SpriteCreate( TURRET_GLOW_SPRITE, GetAbsOrigin(), FALSE ); + m_pEyeGlow->SetTransparency( kRenderGlow, 255, 0, 0, 0, kRenderFxNoDissipation ); + m_pEyeGlow->SetAttachment( this, 2 ); + + m_eyeBrightness = 0; + + SetNextThink( gpGlobals->curtime + 0.3 ); +} + +void CNPC_Turret::Precache() +{ + CNPC_BaseTurret::Precache( ); + PrecacheModel ("models/turret.mdl"); + PrecacheModel (TURRET_GLOW_SPRITE); + + PrecacheModel( "sprites/xspark4.vmt" ); + + PrecacheScriptSound( "Turret.Shoot" ); + + PrecacheScriptSound( "Turret.SpinUpCall" ); + PrecacheScriptSound( "Turret.Spinup" ); + PrecacheScriptSound( "Turret.SpinDownCall" ); + //precache sounds +} + +void CNPC_Turret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) +{ + CPASAttenuationFilter filter( this ); + + FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 ); + + EmitSound( filter, entindex(), "Turret.Shoot" ); + + DoMuzzleFlash(); +} + +void CNPC_Turret::SpinUpCall(void) +{ + StudioFrameAdvance( ); + SetNextThink( gpGlobals->curtime + 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) + { + SetNextThink( gpGlobals->curtime + 1.0 ); //spinup delay + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Turret.SpinUpCall" ); + m_iStartSpin = 1; + m_flPlaybackRate = 0.1; + } + // after the barrel is spun up, turn on the hum + else if (m_flPlaybackRate >= 1.0) + { + SetNextThink( gpGlobals->curtime + 0.1 );// retarget delay + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Turret.Spinup" ); + SetThink(&CNPC_BaseTurret::ActiveThink); + m_iStartSpin = 0; + m_iSpin = 1; + } + else + { + m_flPlaybackRate += 0.075; + } + } + + if (m_iSpin) + { + SetThink(&CNPC_BaseTurret::ActiveThink); + } +} + +void CNPC_Turret::SpinDownCall(void) +{ + if (m_iSpin) + { + CPASAttenuationFilter filter( this ); + + SetTurretAnim(TURRET_ANIM_SPIN); + + if ( m_flPlaybackRate == 1.0) + { + StopSound( entindex(), "Turret.Spinup" ); + EmitSound( filter, entindex(), "Turret.SpinDownCall" ); + } + m_flPlaybackRate -= 0.02; + if (m_flPlaybackRate <= 0) + { + m_flPlaybackRate = 0; + m_iSpin = 0; + } + } +} + +void CNPC_MiniTurret::Spawn() +{ + Precache( ); + + SetModel( "models/miniturret.mdl" ); + m_iHealth = sk_miniturret_health.GetFloat(); + m_HackedGunPos = Vector( 0, 0, 12.75 ); + m_flMaxSpin = 0; + + Vector view_ofs( 0, 0, 12.75 ); + SetViewOffset( view_ofs ); + + CNPC_BaseTurret::Spawn( ); + m_iRetractHeight = 16; + m_iDeployHeight = 32; + m_iMinPitch = -90; + UTIL_SetSize(this, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight)); + + SetThink(&CNPC_MiniTurret::Initialize); + SetNextThink(gpGlobals->curtime + 0.3); + + if (( m_spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) && !( m_spawnflags & SF_MONSTER_TURRET_STARTINACTIVE )) + { + m_iAutoStart = true; + } +} + + +void CNPC_MiniTurret::Precache() +{ + CNPC_BaseTurret::Precache( ); + + m_iAmmoType = GetAmmoDef()->Index("9mmRound"); + + PrecacheScriptSound( "Turret.Shoot" ); + + PrecacheModel ("models/miniturret.mdl"); +} + +void CNPC_MiniTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) +{ + FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 ); + + CPASAttenuationFilter filter( this ); + + EmitSound( filter, entindex(), "Turret.Shoot" ); + + DoMuzzleFlash(); +} + +//========================================================= +// Sentry gun - smallest turret, placed near grunt entrenchments +//========================================================= +class CNPC_Sentry : public CNPC_BaseTurret +{ + DECLARE_CLASS( CNPC_Sentry, CNPC_BaseTurret ); + +public: + void Spawn( ); + void Precache(void); + + // other functions + void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); + int OnTakeDamage_Alive(const CTakeDamageInfo &info); + void Event_Killed( const CTakeDamageInfo &info ); + void SentryTouch( CBaseEntity *pOther ); + + DECLARE_DATADESC(); + +private: + bool m_bStartedDeploy; //set to true when the turret begins its deploy +}; + +BEGIN_DATADESC( CNPC_Sentry ) + DEFINE_ENTITYFUNC( SentryTouch ), + DEFINE_FIELD( m_bStartedDeploy, FIELD_BOOLEAN ), +END_DATADESC() + +LINK_ENTITY_TO_CLASS( monster_sentry, CNPC_Sentry ); + +void CNPC_Sentry::Precache() +{ + BaseClass::Precache( ); + + m_iAmmoType = GetAmmoDef()->Index("9mmRound"); + + PrecacheScriptSound( "Sentry.Shoot" ); + PrecacheScriptSound( "Sentry.Die" ); + + PrecacheModel ("models/sentry.mdl"); +} + +void CNPC_Sentry::Spawn() +{ + Precache( ); + SetModel( "models/sentry.mdl" ); + m_iHealth = sk_sentry_health.GetFloat(); + m_HackedGunPos = Vector( 0, 0, 48 ); + + + SetViewOffset( Vector(0,0,48) ); + + m_flMaxWait = 1E6; + m_flMaxSpin = 1E6; + + BaseClass::Spawn(); + + SetSequence( TURRET_ANIM_RETIRE ); + SetCycle( 0.0 ); + m_flPlaybackRate = 0.0; + + m_iRetractHeight = 64; + m_iDeployHeight = 64; + m_iMinPitch = -60; + + UTIL_SetSize(this, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight)); + + SetTouch(&CNPC_Sentry::SentryTouch); + SetThink(&CNPC_Sentry::Initialize); + + SetNextThink(gpGlobals->curtime + 0.3); + + m_bStartedDeploy = false; +} + +void CNPC_Sentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) +{ + FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, m_iAmmoType, 1 ); + + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Sentry.Shoot" ); + + DoMuzzleFlash(); +} + +int CNPC_Sentry::OnTakeDamage_Alive(const CTakeDamageInfo &info) +{ + if ( m_takedamage == DAMAGE_NO ) + return 0; + + if (!m_iOn && !m_bStartedDeploy) + { + m_bStartedDeploy = true; + SetThink( &CNPC_Sentry::Deploy ); + SetUse( NULL ); + SetNextThink( gpGlobals->curtime + 0.1 ); + } + + m_iHealth -= info.GetDamage(); + if (m_iHealth <= 0) + { + m_iHealth = 0; + m_takedamage = DAMAGE_NO; + m_flDamageTime = gpGlobals->curtime; + + SetUse(NULL); + SetThink(&CNPC_BaseTurret::TurretDeath); //should be SentryDeath ? + SetNextThink( gpGlobals->curtime + 0.1 ); + + m_OnDeactivate.FireOutput(this, this); + + return 0; + } + + return 1; +} + +void CNPC_Sentry::Event_Killed( const CTakeDamageInfo &info ) +{ + CPASAttenuationFilter filter( this ); + EmitSound( filter, entindex(), "Sentry.Die" ); + + StopSound( entindex(), "Turret.Spinup" ); + + AddSolidFlags( FSOLID_NOT_STANDABLE ); + + Vector vecSrc; + QAngle vecAng; + GetAttachment( 2, vecSrc, vecAng ); + + te->Smoke( filter, 0.0, &vecSrc, + g_sModelIndexSmoke, + 2.5, + 10 ); + + g_pEffects->Sparks( vecSrc ); + + BaseClass::Event_Killed( info ); +} + + +void CNPC_Sentry::SentryTouch( CBaseEntity *pOther ) +{ + //trigger the sentry to turn on if a monster or player touches it + if ( pOther && (pOther->IsPlayer() || FBitSet ( pOther->GetFlags(), FL_NPC )) ) + { + CTakeDamageInfo info; + info.SetAttacker( pOther ); + info.SetInflictor( pOther ); + info.SetDamage( 0 ); + + TakeDamage(info); + } +} + |