summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_apache.cpp
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/hl1/hl1_npc_apache.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/hl1/hl1_npc_apache.cpp')
-rw-r--r--game/server/hl1/hl1_npc_apache.cpp766
1 files changed, 766 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_apache.cpp b/game/server/hl1/hl1_npc_apache.cpp
new file mode 100644
index 0000000..ee48d73
--- /dev/null
+++ b/game/server/hl1/hl1_npc_apache.cpp
@@ -0,0 +1,766 @@
+//========= 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 "soundent.h"
+#include "game.h"
+#include "npcevent.h"
+#include "entitylist.h"
+#include "activitylist.h"
+#include "hl1_basegrenade.h"
+#include "animation.h"
+#include "IEffects.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "ammodef.h"
+#include "soundenvelope.h"
+#include "hl1_CBaseHelicopter.h"
+#include "ndebugoverlay.h"
+#include "smoke_trail.h"
+#include "beam_shared.h"
+#include "grenade_homer.h"
+
+#define HOMER_TRAIL0_LIFE 0.1
+#define HOMER_TRAIL1_LIFE 0.2
+#define HOMER_TRAIL2_LIFE 3.0// 1.0
+
+#define SF_NOTRANSITION 128
+
+extern short g_sModelIndexFireball;
+
+class CNPC_Apache : public CBaseHelicopter
+{
+ DECLARE_CLASS( CNPC_Apache, CBaseHelicopter );
+public:
+ DECLARE_DATADESC();
+
+ void Spawn( void );
+ void Precache( void );
+
+ int BloodColor( void ) { return DONT_BLEED; }
+ Class_T Classify( void ) { return CLASS_HUMAN_MILITARY; };
+ void InitializeRotorSound( void );
+ void LaunchRocket( Vector &viewDir, int damage, int radius, Vector vecLaunchPoint );
+
+ void Flight( void );
+
+ bool FireGun( void );
+ void AimRocketGun( void );
+ void FireRocket( void );
+ void DyingThink( void );
+
+ int ObjectCaps( void );
+
+ void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
+
+/* bool OnInternalDrawModel( ClientModelRenderInfo_t *pInfo )
+ {
+ BaseClass::OnInteralDrawModel( pInfo );
+ Vector origin = GetAbsOrigin();
+ origin.z += 32;
+ SetAbsOrigin( origin );
+ }*/
+
+/*( void SetAbsOrigin( const Vector& absOrigin )
+ {
+ ((Vector&)absOrigin).z += 32;
+ BaseClass::SetAbsOrigin( absOrigin );
+ }*/
+
+
+
+ /* int Save( CSave &save );
+ int Restore( CRestore &restore );
+ static TYPEDESCRIPTION m_SaveData[];
+
+ void Spawn( void );
+ void Precache( void );
+
+
+ void Killed( entvars_t *pevAttacker, int iGib );
+ void GibMonster( void );
+
+ void EXPORT HuntThink( void );
+ void EXPORT FlyTouch( CBaseEntity *pOther );
+ void EXPORT CrashTouch( CBaseEntity *pOther );
+ void EXPORT DyingThink( void );
+ void EXPORT StartupUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ void EXPORT NullThink( void );
+
+ void ShowDamage( void );
+ void Flight( void );
+ void FireRocket( void );
+ BOOL FireGun( void );
+
+ int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
+ void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);*/
+
+ int m_iRockets;
+ float m_flForce;
+ float m_flNextRocket;
+
+ int m_iAmmoType;
+
+ Vector m_vecTarget;
+ Vector m_posTarget;
+
+ Vector m_vecDesired;
+ Vector m_posDesired;
+
+ Vector m_vecGoal;
+
+ QAngle m_angGun;
+
+ int m_iSoundState; // don't save this
+
+ int m_iExplode;
+ int m_iBodyGibs;
+ int m_nDebrisModel;
+
+ float m_flGoalSpeed;
+
+ CHandle<SmokeTrail> m_hSmoke;
+ CBeam *m_pBeam;
+};
+
+BEGIN_DATADESC( CNPC_Apache )
+ DEFINE_FIELD( m_iRockets, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flForce, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flNextRocket, FIELD_TIME ),
+ DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ),
+ DEFINE_FIELD( m_posTarget, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_vecDesired, FIELD_VECTOR ),
+ DEFINE_FIELD( m_posDesired, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_vecGoal, FIELD_VECTOR ),
+ DEFINE_FIELD( m_angGun, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flLastSeen, FIELD_TIME ),
+ DEFINE_FIELD( m_flPrevSeen, FIELD_TIME ),
+// DEFINE_FIELD( m_iSoundState, FIELD_INTEGER ),
+// DEFINE_FIELD( m_iSpriteTexture, FIELD_INTEGER ),
+// DEFINE_FIELD( m_iExplode, FIELD_INTEGER ),
+// DEFINE_FIELD( m_iBodyGibs, FIELD_INTEGER ),
+ DEFINE_FIELD( m_pBeam, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( m_hSmoke, FIELD_EHANDLE ),
+END_DATADESC()
+
+ConVar sk_apache_health( "sk_apache_health","100");
+
+static Vector s_vecSurroundingMins( -300, -300, -172);
+static Vector s_vecSurroundingMaxs(300, 300, 8);
+
+
+void CNPC_Apache::Spawn( void )
+{
+ Precache( );
+ SetModel( "models/apache.mdl" );
+
+ BaseClass::Spawn();
+
+ AddFlag( FL_NPC );
+ SetSolid( SOLID_BBOX );
+ SetMoveType( MOVETYPE_STEP );
+ AddFlag( FL_FLY );
+
+
+ m_iHealth = sk_apache_health.GetFloat();
+
+ m_flFieldOfView = -0.707; // 270 degrees
+
+ m_fHelicopterFlags = BITS_HELICOPTER_MISSILE_ON | BITS_HELICOPTER_GUN_ON;
+
+ InitBoneControllers();
+
+ SetRenderColor( 255, 255, 255, 255 );
+
+ m_iRockets = 10;
+
+ UTIL_SetSize( this, Vector( -32, -32, -32 ), Vector( 32, 32, 32 ) );
+
+ //CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &s_vecSurroundingMins, &s_vecSurroundingMaxs );
+ //AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
+
+ m_hSmoke = NULL;
+}
+
+LINK_ENTITY_TO_CLASS ( monster_apache, CNPC_Apache );
+
+int CNPC_Apache::ObjectCaps( void )
+{
+ if ( GetSpawnFlags() & SF_NOTRANSITION )
+ return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION;
+ else
+ return BaseClass::ObjectCaps();
+}
+
+void CNPC_Apache::Precache( void )
+{
+ // Get to tha chopper!
+ PrecacheModel( "models/apache.mdl" );
+ PrecacheScriptSound( "Apache.Rotor" );
+ m_nDebrisModel = PrecacheModel( "models/metalplategibs_green.mdl" );
+
+ // Gun
+ PrecacheScriptSound( "Apache.FireGun" );
+ m_iAmmoType = GetAmmoDef()->Index("9mmRound");
+
+ // Rockets
+ UTIL_PrecacheOther( "grenade_homer" );
+ PrecacheScriptSound( "Apache.RPG" );
+ PrecacheModel( "models/weapons/w_missile.mdl" );
+
+ BaseClass::Precache();
+}
+
+void CNPC_Apache::InitializeRotorSound( void )
+{
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+
+ CPASAttenuationFilter filter( this );
+ m_pRotorSound = controller.SoundCreate( filter, entindex(), "Apache.Rotor" );
+
+ BaseClass::InitializeRotorSound();
+}
+
+void CNPC_Apache::Flight( void )
+{
+ StudioFrameAdvance( );
+
+ float flDistToDesiredPosition = (GetAbsOrigin() - m_vecDesiredPosition).Length();
+ // NDebugOverlay::Line(GetAbsOrigin(), m_vecDesiredPosition, 0,0,255, true, 0.1);
+
+ if (m_flGoalSpeed < 800 )
+ m_flGoalSpeed += GetAcceleration();
+
+
+// Vector vecGoalOrientation;
+ if (flDistToDesiredPosition > 250) // 500
+ {
+ Vector v1 = (m_vecTargetPosition - GetAbsOrigin());
+ Vector v2 = (m_vecDesiredPosition - GetAbsOrigin());
+
+ VectorNormalize( v1 );
+ VectorNormalize( v2 );
+
+ if (m_flLastSeen + 90 > gpGlobals->curtime && DotProduct( v1, v2 ) > 0.25)
+ {
+ m_vecGoalOrientation = ( m_vecTargetPosition - GetAbsOrigin());
+ }
+ else
+ {
+ m_vecGoalOrientation = (m_vecDesiredPosition - GetAbsOrigin());
+ }
+ VectorNormalize( m_vecGoalOrientation );
+ }
+ else
+ {
+ AngleVectors( GetGoalEnt()->GetAbsAngles(), &m_vecGoalOrientation );
+ }
+// SetGoalOrientation( vecGoalOrientation );
+
+
+ if (GetGoalEnt())
+ {
+ // ALERT( at_console, "%.0f\n", flLength );
+ if ( HasReachedTarget() )
+ {
+ // If we get this close to the desired position, it's assumed that we've reached
+ // the desired position, so move on.
+
+ // Fire target that I've reached my goal
+ m_AtTarget.FireOutput( GetGoalEnt(), this );
+
+ OnReachedTarget( GetGoalEnt() );
+
+ SetGoalEnt( gEntList.FindEntityByName( NULL, GetGoalEnt()->m_target ) );
+
+ if (GetGoalEnt())
+ {
+ m_vecDesiredPosition = GetGoalEnt()->GetAbsOrigin();
+
+// Vector vecGoalOrientation;
+ AngleVectors( GetGoalEnt()->GetAbsAngles(), &m_vecGoalOrientation );
+
+// SetGoalOrientation( vecGoalOrientation );
+
+ flDistToDesiredPosition = (GetAbsOrigin() - m_vecDesiredPosition).Length();
+ }
+ }
+ }
+ else
+ {
+ // If we can't find a new target, just stay where we are.
+ m_vecDesiredPosition = GetAbsOrigin();
+ }
+
+ // tilt model 5 degrees
+ QAngle angAdj = QAngle( 5.0, 0, 0 );
+
+ // estimate where I'll be facing in one seconds
+ Vector forward, right, up;
+ AngleVectors( GetAbsAngles() + GetLocalAngularVelocity() * 2 + angAdj, &forward, &right, &up );
+ // Vector vecEst1 = GetAbsOrigin() + pev->velocity + gpGlobals->v_up * m_flForce - Vector( 0, 0, 384 );
+ // float flSide = DotProduct( m_posDesired - vecEst1, gpGlobals->v_right );
+
+
+ QAngle angVel = GetLocalAngularVelocity();
+ float flSide = DotProduct( m_vecGoalOrientation, right );
+
+ if (flSide < 0)
+ {
+ if ( angVel.y < 60)
+ {
+ angVel.y += 8; // 9 * (3.0/2.0);
+ }
+ }
+ else
+ {
+ if ( angVel.y > -60)
+ {
+ angVel.y -= 8; // 9 * (3.0/2.0);
+ }
+ }
+ angVel.y *= 0.98;
+ SetLocalAngularVelocity( angVel );
+
+ Vector vecVel = GetAbsVelocity();
+
+ // estimate where I'll be in two seconds
+ AngleVectors( GetAbsAngles() + GetLocalAngularVelocity() * 1 + angAdj, &forward, &right, &up );
+ Vector vecEst = GetAbsOrigin() + vecVel * 2.0 + up * m_flForce * 20 - Vector( 0, 0, 384 * 2 );
+
+ // add immediate force
+ AngleVectors( GetAbsAngles() + angAdj, &forward, &right, &up );
+
+ vecVel.x += up.x * m_flForce;
+ vecVel.y += up.y * m_flForce;
+ vecVel.z += up.z * m_flForce;
+ // add gravity
+ vecVel.z -= 38.4; // 32ft/sec
+
+ float flSpeed = vecVel.Length();
+ float flDir = DotProduct( Vector( forward.x, forward.y, 0 ), Vector( vecVel.x, vecVel.y, 0 ) );
+ if (flDir < 0)
+ flSpeed = -flSpeed;
+
+ float flDist = DotProduct( m_vecDesiredPosition - vecEst, forward );
+
+ // float flSlip = DotProduct( pev->velocity, gpGlobals->v_right );
+ float flSlip = -DotProduct( m_vecDesiredPosition - vecEst, right );
+
+ angVel = GetLocalAngularVelocity();
+ // fly sideways
+ if (flSlip > 0)
+ {
+ if (GetAbsAngles().z > -30 && angVel.z > -15)
+ angVel.z -= 4;
+ else
+ angVel.z += 2;
+ }
+ else
+ {
+
+ if (GetAbsAngles().z < 30 && angVel.z < 15)
+ angVel.z += 4;
+ else
+ angVel.z -= 2;
+ }
+ SetLocalAngularVelocity( angVel );
+
+ // sideways drag
+ vecVel.x = vecVel.x * (1.0 - fabs( right.x ) * 0.05);
+ vecVel.y = vecVel.y * (1.0 - fabs( right.y ) * 0.05);
+ vecVel.z = vecVel.z * (1.0 - fabs( right.z ) * 0.05);
+
+ // general drag
+ vecVel = vecVel * 0.995;
+
+ // Set final computed velocity
+ SetAbsVelocity( vecVel );
+
+ // apply power to stay correct height
+ if (m_flForce < 80 && vecEst.z < m_vecDesiredPosition.z)
+ {
+ m_flForce += 12;
+ }
+ else if (m_flForce > 30)
+ {
+ if (vecEst.z > m_vecDesiredPosition.z)
+ m_flForce -= 8;
+ }
+
+
+ angVel = GetLocalAngularVelocity();
+ // pitch forward or back to get to target
+ if (flDist > 0 && flSpeed < m_flGoalSpeed && GetAbsAngles().x + angVel.x < 40)
+ {
+ // ALERT( at_console, "F " );
+ // lean forward
+ angVel.x += 12.0;
+ }
+ else if (flDist < 0 && flSpeed > -50 && GetAbsAngles().x + angVel.x > -20)
+ {
+ // ALERT( at_console, "B " );
+ // lean backward
+ angVel.x -= 12.0;
+ }
+ else if (GetAbsAngles().x + angVel.x < 0)
+ {
+ // ALERT( at_console, "f " );
+ angVel.x += 4.0;
+ }
+ else if (GetAbsAngles().x + angVel.x > 0)
+ {
+ // ALERT( at_console, "b " );
+ angVel.x -= 4.0;
+ }
+
+ // Set final computed angular velocity
+ SetLocalAngularVelocity( angVel );
+
+ // ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", GetAbsOrigin().x, pev->velocity.x, flDist, flSpeed, GetAbsAngles().x, pev->avelocity.x, m_flForce );
+ // ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", GetAbsOrigin().z, pev->velocity.z, vecEst.z, m_posDesired.z, m_flForce );
+}
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+
+#define CHOPPER_AP_GUN_TIP 0
+#define CHOPPER_AP_GUN_BASE 1
+
+#define CHOPPER_BC_GUN_YAW 0
+#define CHOPPER_BC_GUN_PITCH 1
+#define CHOPPER_BC_POD_PITCH 2
+
+bool CNPC_Apache::FireGun( )
+{
+ if ( !GetEnemy() )
+ return false;
+
+ Vector vForward, vRight, vUp;
+
+ AngleVectors( GetAbsAngles(), &vForward, &vUp, &vRight );
+
+ Vector posGun;
+ QAngle angGun;
+ GetAttachment( 1, posGun, angGun );
+
+ Vector vecTarget = (m_vecTargetPosition - posGun);
+
+ VectorNormalize( vecTarget );
+
+ Vector vecOut;
+
+ vecOut.x = DotProduct( vForward, vecTarget );
+ vecOut.y = -DotProduct( vUp, vecTarget );
+ vecOut.z = DotProduct( vRight, vecTarget );
+
+ QAngle angles;
+
+ VectorAngles( vecOut, angles );
+
+ angles.y = AngleNormalize(angles.y);
+ angles.x = AngleNormalize(angles.x);
+
+ if (angles.x > m_angGun.x)
+ m_angGun.x = MIN( angles.x, m_angGun.x + 12 );
+ if (angles.x < m_angGun.x)
+ m_angGun.x = MAX( angles.x, m_angGun.x - 12 );
+ if (angles.y > m_angGun.y)
+ m_angGun.y = MIN( angles.y, m_angGun.y + 12 );
+ if (angles.y < m_angGun.y)
+ m_angGun.y = MAX( angles.y, m_angGun.y - 12 );
+
+ // hacks - shouldn't be hardcoded, oh well.
+ // limit it so it doesn't pop if you try to set it to the max value
+ m_angGun.y = clamp( m_angGun.y, -89.9, 89.9 );
+ m_angGun.x = clamp( m_angGun.x, -9.9, 44.9 );
+
+ m_angGun.y = SetBoneController( 0, m_angGun.y );
+ m_angGun.x = SetBoneController( 1, m_angGun.x );
+
+ Vector posBarrel;
+ QAngle angBarrel;
+ GetAttachment( 0, posBarrel, angBarrel );
+
+ Vector forward;
+ AngleVectors( angBarrel + m_angGun, &forward );
+
+ Vector2D vec2LOS = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).AsVector2D();
+ vec2LOS.NormalizeInPlace();
+
+ float flDot = vec2LOS.Dot( forward.AsVector2D() );
+
+ //forward
+// NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + ( forward * 200 ), 255,0,0, false, 0.1);
+ //LOS
+// NDebugOverlay::Line( posGun, m_vecTargetPosition , 0,0,255, false, 0.1);
+// NDebugOverlay::Box( GetAbsOrigin(), s_vecSurroundingMins, s_vecSurroundingMaxs, 0, 255,0, false, 0.1);
+
+ if ( flDot > 0.98 )
+ {
+ CPASAttenuationFilter filter( this, 0.2f );
+
+ EmitSound( filter, entindex(), "Apache.FireGun" );//<<TEMP>>temp sound
+
+ // gun is a bit dodgy, just fire at the target if we are close
+ FireBullets( 1, posGun, vecTarget, VECTOR_CONE_4DEGREES, 8192, m_iAmmoType, 2 );
+
+ return true;
+ }
+
+ return false;
+}
+
+void CNPC_Apache::FireRocket( void )
+{
+ static float side = 1.0;
+ static int count;
+ Vector vForward, vRight, vUp;
+
+
+ AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
+ Vector vecSrc = GetAbsOrigin() + 1.5 * ( vForward * 21 + vRight * 70 * side + vUp * -79 );
+
+ // pick firing pod to launch from
+ switch( m_iRockets % 5)
+ {
+ case 0: vecSrc = vecSrc + vRight * 10; break;
+ case 1: vecSrc = vecSrc - vRight * 10; break;
+ case 2: vecSrc = vecSrc + vUp * 10; break;
+ case 3: vecSrc = vecSrc - vUp * 10; break;
+ case 4: break;
+ }
+
+ Vector vTargetDir = m_vecTargetPosition - GetAbsOrigin();
+ VectorNormalize ( vTargetDir );
+ LaunchRocket( vTargetDir, 100, 150, vecSrc);
+
+ m_iRockets--;
+
+ side = - side;
+}
+void CNPC_Apache::AimRocketGun( void )
+{
+ Vector vForward, vRight, vUp;
+
+ if (m_iRockets <= 0)
+ return;
+
+ Vector vTargetDir = m_vecTargetPosition - GetAbsOrigin();
+ VectorNormalize ( vTargetDir );
+
+ AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
+ Vector vecEst = ( vForward * 800 + GetAbsVelocity());
+ VectorNormalize ( vecEst );
+
+ trace_t tr1;
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr1);
+
+// NDebugOverlay::Line(GetAbsOrigin(), tr1.endpos, 255,255,0, false, 0.1);
+
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vTargetDir * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr1);
+
+// NDebugOverlay::Line(GetAbsOrigin(), tr1.endpos, 0,255,0, false, 0.1);
+
+ // ALERT( at_console, "%d %d %d %4.2f\n", GetAbsAngles().x < 0, DotProduct( pev->velocity, gpGlobals->v_forward ) > -100, m_flNextRocket < gpGlobals->curtime, DotProduct( m_vecTarget, vecEst ) );
+
+ if ((m_iRockets % 2) == 1)
+ {
+ FireRocket( );
+ m_flNextRocket = gpGlobals->curtime + 0.5;
+ if (m_iRockets <= 0)
+ {
+ m_flNextRocket = gpGlobals->curtime + 10;
+ m_iRockets = 10;
+ }
+ }
+ else if (DotProduct( GetAbsVelocity(), vForward ) > -100 && m_flNextRocket < gpGlobals->curtime)
+ {
+ if (m_flLastSeen + 60 > gpGlobals->curtime)
+ {
+ if (GetEnemy() != NULL)
+ {
+ // make sure it's a good shot
+ //if (DotProduct( vTargetDir, vecEst) > 0.5)
+ {
+ trace_t tr;
+
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr);
+
+ // NDebugOverlay::Line(GetAbsOrigin(), tr.endpos, 255,0,255, false, 5);
+
+// if ((tr.endpos - m_vecTargetPosition).Length() < 512)
+ if ((tr.endpos - m_vecTargetPosition).Length() < 1024)
+ FireRocket( );
+ }
+ }
+ else
+ {
+ trace_t tr;
+
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr);
+ // just fire when close
+ if ((tr.endpos - m_vecTargetPosition).Length() < 512)
+ FireRocket( );
+ }
+ }
+ }
+}
+
+#define MISSILE_HOMING_STRENGTH 0.3
+#define MISSILE_HOMING_DELAY 5.0
+#define MISSILE_HOMING_RAMP_UP 0.5
+#define MISSILE_HOMING_DURATION 1.0
+#define MISSILE_HOMING_RAMP_DOWN 0.5
+
+void CNPC_Apache::LaunchRocket( Vector &viewDir, int damage, int radius, Vector vecLaunchPoint )
+{
+
+ CGrenadeHomer *pGrenade = CGrenadeHomer::CreateGrenadeHomer(
+ MAKE_STRING("models/weapons/w_missile.mdl"),
+ MAKE_STRING( "Apache.RPG" ),
+ vecLaunchPoint, vec3_angle, edict() );
+ pGrenade->Spawn( );
+ pGrenade->SetHoming(MISSILE_HOMING_STRENGTH, MISSILE_HOMING_DELAY,
+ MISSILE_HOMING_RAMP_UP, MISSILE_HOMING_DURATION,
+ MISSILE_HOMING_RAMP_DOWN);
+ pGrenade->SetDamage( damage );
+ pGrenade->m_DmgRadius = radius;
+
+ pGrenade->Launch( this, GetEnemy(), viewDir * 1500, 500, 0, HOMER_SMOKE_TRAIL_ON );
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Lame, temporary death
+//-----------------------------------------------------------------------------
+void CNPC_Apache::DyingThink( void )
+{
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if( gpGlobals->curtime > m_flNextCrashExplosion )
+ {
+ CPASFilter pasFilter( GetAbsOrigin() );
+ Vector pos;
+
+ pos = GetAbsOrigin();
+ pos.x += random->RandomFloat( -150, 150 );
+ pos.y += random->RandomFloat( -150, 150 );
+ pos.z += random->RandomFloat( -150, -50 );
+
+ te->Explosion( pasFilter, 0.0, &pos, g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
+
+ Vector vecSize = Vector( 500, 500, 60 );
+ CPVSFilter pvsFilter( GetAbsOrigin() );
+
+ te->BreakModel( pvsFilter, 0.0, GetAbsOrigin(), vec3_angle, vecSize, vec3_origin,
+ m_nDebrisModel, 100, 0, 2.5, BREAK_METAL );
+
+ m_flNextCrashExplosion = gpGlobals->curtime + random->RandomFloat( 0.3, 0.5 );
+ }
+
+ QAngle angVel = GetLocalAngularVelocity();
+ if( angVel.y < 400 )
+ {
+ angVel.y *= 1.1;
+ SetLocalAngularVelocity( angVel );
+ }
+
+ Vector vecImpulse( 0, 0, -38.4 ); // gravity - 32ft/sec
+ ApplyAbsVelocityImpulse( vecImpulse );
+
+ if( m_hSmoke )
+ {
+ m_hSmoke->SetLifetime(0.1f);
+ m_hSmoke = NULL;
+ }
+
+}
+
+
+
+void CNPC_Apache::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+
+ CTakeDamageInfo dmgInfo = info;
+
+ // HITGROUPS don't work currently.
+ // ignore blades
+ //if (ptr->hitgroup == 6 && (info.GetDamageType() & (DMG_ENERGYBEAM|DMG_BULLET|DMG_CLUB)))
+ // return;
+
+ // hit hard, hits cockpit
+ if (info.GetDamage() > 50 || ptr->hitgroup == 1 || ptr->hitgroup == 2 || ptr->hitgroup == 3 )
+ {
+ // ALERT( at_console, "%map .0f\n", flDamage );
+ AddMultiDamage( dmgInfo, this );
+
+ if ( info.GetDamage() > 50 )
+ {
+ if ( m_hSmoke == NULL && (m_hSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
+ {
+ m_hSmoke->m_Opacity = 1.0f;
+ m_hSmoke->m_SpawnRate = 60;
+ m_hSmoke->m_ParticleLifetime = 1.3f;
+ m_hSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
+ m_hSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
+ m_hSmoke->m_StartSize = 12;
+ m_hSmoke->m_EndSize = 64;
+ m_hSmoke->m_SpawnRadius = 8;
+ m_hSmoke->m_MinSpeed = 2;
+ m_hSmoke->m_MaxSpeed = 24;
+
+ m_hSmoke->SetLifetime( 1e6 );
+ m_hSmoke->FollowEntity( this );
+
+ }
+ }
+ }
+ else
+ {
+ // do half damage in the body
+ dmgInfo.ScaleDamage(0.5);
+ AddMultiDamage( dmgInfo, this );
+ g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
+ }
+
+ if ( m_iHealth < 10 )
+ {
+ if ( m_hSmoke == NULL && (m_hSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
+ {
+ m_hSmoke->m_Opacity = 1.0f;
+ m_hSmoke->m_SpawnRate = 60;
+ m_hSmoke->m_ParticleLifetime = 1.3f;
+ m_hSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
+ m_hSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
+ m_hSmoke->m_StartSize = 12;
+ m_hSmoke->m_EndSize = 64;
+ m_hSmoke->m_SpawnRadius = 8;
+ m_hSmoke->m_MinSpeed = 2;
+ m_hSmoke->m_MaxSpeed = 24;
+
+ m_hSmoke->SetLifetime( 1e6 );
+ m_hSmoke->FollowEntity( this );
+
+ }
+ }
+}