summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_osprey.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/hl1/hl1_npc_osprey.cpp')
-rw-r--r--game/server/hl1/hl1_npc_osprey.cpp1589
1 files changed, 1589 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_osprey.cpp b/game/server/hl1/hl1_npc_osprey.cpp
new file mode 100644
index 0000000..65b2de8
--- /dev/null
+++ b/game/server/hl1/hl1_npc_osprey.cpp
@@ -0,0 +1,1589 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "beam_shared.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 "hl1_npc_hgrunt.h"
+#include "soundent.h"
+#include "game.h"
+#include "npcevent.h"
+#include "entitylist.h"
+#include "activitylist.h"
+#include "animation.h"
+#include "engine/IEngineSound.h"
+#include "ammodef.h"
+#include "basecombatweapon.h"
+#include "hl1_basegrenade.h"
+#include "soundenvelope.h"
+#include "hl1_CBaseHelicopter.h"
+#include "IEffects.h"
+#include "smoke_trail.h"
+
+extern short g_sModelIndexFireball;
+
+typedef struct
+{
+ int isValid;
+ EHANDLE hGrunt;
+ Vector vecOrigin;
+ Vector vecAngles;
+} t_ospreygrunt;
+
+#define LOADED_WITH_GRUNTS 0 //WORST NAME EVER!
+#define UNLOADING_GRUNTS 1
+#define GRUNTS_DEPLOYED 2 //Waiting for them to finish repelin
+#define SF_WAITFORTRIGGER 0x40
+#define MAX_CARRY 24
+#define DEFAULT_SPEED 250
+#define HELICOPTER_THINK_INTERVAL 0.1
+
+class CNPC_Osprey : public CBaseHelicopter
+{
+ DECLARE_CLASS( CNPC_Osprey, CBaseHelicopter );
+public:
+
+ int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
+
+ void Spawn( void );
+ void Precache( void );
+ Class_T Classify( void ) { return CLASS_NONE; };
+ int BloodColor( void ) { return DONT_BLEED; }
+
+ void FindAllThink( void );
+ void DeployThink( void );
+ bool HasDead( void );
+ void Flight( void );
+ void HoverThink( void );
+ CAI_BaseNPC *MakeGrunt( Vector vecSrc );
+
+ void InitializeRotorSound( void );
+ void PrescheduleThink( void );
+
+ void DyingThink( void );
+
+ void CrashTouch( CBaseEntity *pOther );
+
+/*
+
+ void CrashTouch( CBaseEntity *pOther );
+ void DyingThink( void );
+ void CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+*/
+ void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
+
+ float m_startTime;
+
+ float m_flIdealtilt;
+ float m_flRotortilt;
+
+ float m_flRightHealth;
+ float m_flLeftHealth;
+
+ int m_iUnits;
+ EHANDLE m_hGrunt[MAX_CARRY];
+ Vector m_vecOrigin[MAX_CARRY];
+ EHANDLE m_hRepel[4];
+
+ int m_iSoundState;
+ int m_iSpriteTexture;
+
+ int m_iPitch;
+
+ int m_iExplode;
+ int m_iTailGibs;
+ int m_iBodyGibs;
+ int m_iEngineGibs;
+
+ int m_iDoLeftSmokePuff;
+ int m_iDoRightSmokePuff;
+
+ int m_iRepelState;
+ float m_flPrevGoalVel;
+
+ int m_iRotorAngle;
+ int m_nDebrisModel;
+
+ CHandle<SmokeTrail> m_hLeftSmoke;
+ CHandle<SmokeTrail> m_hRightSmoke;
+
+ DECLARE_DATADESC();
+
+ int m_iNextCrashModel; //which gib to explode with next
+};
+
+LINK_ENTITY_TO_CLASS( monster_osprey, CNPC_Osprey );
+
+BEGIN_DATADESC( CNPC_Osprey )
+ DEFINE_FIELD( m_startTime, FIELD_TIME ),
+
+ DEFINE_FIELD( m_flIdealtilt, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flRotortilt, FIELD_FLOAT ),
+
+ DEFINE_FIELD( m_flRightHealth, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flLeftHealth, FIELD_FLOAT ),
+
+ DEFINE_FIELD( m_iRepelState, FIELD_INTEGER ),
+
+ DEFINE_FIELD( m_iUnits, FIELD_INTEGER ),
+ DEFINE_ARRAY( m_hGrunt, FIELD_EHANDLE, MAX_CARRY ),
+ DEFINE_ARRAY( m_vecOrigin, FIELD_POSITION_VECTOR, MAX_CARRY ),
+ DEFINE_ARRAY( m_hRepel, FIELD_EHANDLE, 4 ),
+
+ // DEFINE_FIELD( m_iTailGibs, FIELD_INTEGER ),
+ // DEFINE_FIELD( m_iBodyGibs, FIELD_INTEGER ),
+ // DEFINE_FIELD( m_iEngineGibs, FIELD_INTEGER ),
+ // DEFINE_FIELD( m_nDebrisModel, FIELD_INTEGER ),
+
+ // DEFINE_FIELD( m_iSoundState, FIELD_INTEGER ),
+ // DEFINE_FIELD( m_iSpriteTexture, FIELD_INTEGER ),
+ // DEFINE_FIELD( m_iPitch, FIELD_INTEGER ),
+
+ DEFINE_FIELD( m_flPrevGoalVel, FIELD_FLOAT ),
+ DEFINE_FIELD( m_iRotorAngle, FIELD_INTEGER ),
+
+ DEFINE_FIELD( m_hLeftSmoke, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hRightSmoke, FIELD_EHANDLE ),
+
+ DEFINE_FIELD( m_iNextCrashModel, FIELD_INTEGER ),
+
+ DEFINE_FIELD( m_iDoLeftSmokePuff, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iDoRightSmokePuff, FIELD_INTEGER ),
+
+ //DEFINE_FIELD( m_iExplode, FIELD_INTEGER ),
+
+ DEFINE_THINKFUNC( FindAllThink ),
+ DEFINE_THINKFUNC( DeployThink ),
+
+ DEFINE_ENTITYFUNC( CrashTouch ),
+
+/* DEFINE_FUNCTION ( HoverThink ),
+ DEFINE_FUNCTION ( DyingThink ),
+ DEFINE_FUNCTION ( CommandUse ),*/
+END_DATADESC()
+
+
+void CNPC_Osprey::Spawn( void )
+{
+ Precache( );
+ // motor
+ SetModel( "models/osprey.mdl" );
+
+ BaseClass::Spawn();
+
+ Vector mins, maxs;
+ ExtractBbox( 0, mins, maxs );
+ UTIL_SetSize( this, mins, maxs );
+ UTIL_SetOrigin( this, GetAbsOrigin() );
+
+ AddFlag( FL_NPC );
+ m_takedamage = DAMAGE_YES;
+ m_flRightHealth = 200;
+ m_flLeftHealth = 200;
+ m_iHealth = 400;
+
+ m_flFieldOfView = 0; // 180 degrees
+
+ SetSequence( 0 );
+ ResetSequenceInfo( );
+ SetCycle( random->RandomInt( 0,0xFF ) );
+
+// InitBoneControllers();
+
+ m_startTime = gpGlobals->curtime + 1;
+
+ //FindAllThink();
+// SetUse( CommandUse );
+
+/* if (!( m_spawnflags & SF_WAITFORTRIGGER))
+ {
+ SetThink( gpGlobals->curtime + 1.0 );
+ }*/
+
+ m_flMaxSpeed = (float)BASECHOPPER_MAX_SPEED / 2;
+
+ m_iRepelState = LOADED_WITH_GRUNTS;
+ m_flPrevGoalVel = 9999;
+
+ m_iRotorAngle = -1;
+ SetBoneController( 0, m_iRotorAngle );
+
+ m_hLeftSmoke = NULL;
+ m_hRightSmoke = NULL;
+}
+
+
+void CNPC_Osprey::Precache( void )
+{
+ UTIL_PrecacheOther( "monster_human_grunt" );
+
+ PrecacheModel("models/osprey.mdl");
+ PrecacheModel("models/HVR.mdl");
+
+ m_iSpriteTexture = PrecacheModel( "sprites/rope.vmt" );
+
+ m_iExplode = PrecacheModel( "sprites/fexplo.vmt" );
+ m_iTailGibs = PrecacheModel( "models/osprey_tailgibs.mdl" );
+ m_iBodyGibs = PrecacheModel( "models/osprey_bodygibs.mdl" );
+ m_iEngineGibs = PrecacheModel( "models/osprey_enginegibs.mdl" );
+
+ m_nDebrisModel = PrecacheModel( "models/mechgibs.mdl" );
+
+ PrecacheScriptSound( "Apache.RotorSpinup" );
+
+ BaseClass::Precache();
+}
+
+void CNPC_Osprey::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ float flDamage = info.GetDamage();
+
+ // Hit right engine
+ if (ptr->hitgroup == 3 )
+ {
+ if( m_flRightHealth <= 0 )
+ return;
+ else
+ m_flRightHealth -= flDamage;
+
+ if( m_flRightHealth <= 0 )
+ {
+ Assert( m_hRightSmoke == NULL );
+
+ if ( (m_hRightSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
+ {
+ m_hRightSmoke->m_Opacity = 1.0f;
+ m_hRightSmoke->m_SpawnRate = 60;
+ m_hRightSmoke->m_ParticleLifetime = 1.3f;
+ m_hRightSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
+ m_hRightSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
+ m_hRightSmoke->m_StartSize = 12;
+ m_hRightSmoke->m_EndSize = 80;
+ m_hRightSmoke->m_SpawnRadius = 8;
+ m_hRightSmoke->m_MinSpeed = 2;
+ m_hRightSmoke->m_MaxSpeed = 24;
+
+ m_hRightSmoke->SetLifetime( 1e6 );
+ m_hRightSmoke->FollowEntity( this, "right" );
+ }
+ }
+ }
+
+ // Hit left engine
+ if (ptr->hitgroup == 2 )
+ {
+ if( m_flLeftHealth <= 0 )
+ return;
+ else
+ m_flLeftHealth -= flDamage;
+
+ if( m_flLeftHealth <= 0 )
+ {
+ //create smoke trail
+ Assert( m_hLeftSmoke == NULL );
+
+ if ( (m_hLeftSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
+ {
+ m_hLeftSmoke->m_Opacity = 1.0f;
+ m_hLeftSmoke->m_SpawnRate = 60;
+ m_hLeftSmoke->m_ParticleLifetime = 1.3f;
+ m_hLeftSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
+ m_hLeftSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
+ m_hLeftSmoke->m_StartSize = 12;
+ m_hLeftSmoke->m_EndSize = 64;
+ m_hLeftSmoke->m_SpawnRadius = 8;
+ m_hLeftSmoke->m_MinSpeed = 2;
+ m_hLeftSmoke->m_MaxSpeed = 24;
+
+ m_hLeftSmoke->SetLifetime( 1e6 );
+ m_hLeftSmoke->FollowEntity( this, "left" );
+ }
+ }
+ }
+
+ // hit hard, hits cockpit, hits engines
+ if (flDamage > 50 || ptr->hitgroup == 1 || ptr->hitgroup == 2 || ptr->hitgroup == 3)
+ {
+ AddMultiDamage( info, this );
+ }
+ else
+ {
+ g_pEffects->Sparks( ptr->endpos );
+ }
+}
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CNPC_Osprey::InitializeRotorSound( void )
+{
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+
+ CPASAttenuationFilter filter( this );
+ m_pRotorSound = controller.SoundCreate( filter, entindex(), CHAN_STATIC, "Apache.RotorSpinup", 0.2 );
+
+ BaseClass::InitializeRotorSound();
+}
+
+void CNPC_Osprey::FindAllThink( void )
+{
+ CBaseEntity *pEntity = NULL;
+
+ m_iUnits = 0;
+ while ( ( pEntity = gEntList.FindEntityByClassname( pEntity, "monster_human_grunt" ) ) != NULL)
+ {
+ if ( m_iUnits > MAX_CARRY )
+ break;
+
+ if (pEntity->IsAlive())
+ {
+ m_hGrunt[m_iUnits] = pEntity;
+ m_vecOrigin[m_iUnits] = pEntity->GetAbsOrigin();
+ m_iUnits++;
+ }
+ }
+
+ if (m_iUnits == 0)
+ {
+ Msg( "osprey error: no grunts to resupply\n");
+ UTIL_Remove( this );
+ return;
+ }
+
+ m_startTime = 0.0f;
+}
+
+void CNPC_Osprey::DeployThink( void )
+{
+ Vector vecForward;
+ Vector vecRight;
+ Vector vecUp;
+ Vector vecSrc;
+
+ SetLocalAngularVelocity( QAngle ( 0, 0, 0 ) );
+ SetAbsVelocity( Vector ( 0, 0, 0 ) );
+
+ AngleVectors( GetAbsAngles(), &vecForward, &vecRight, &vecUp );
+
+ trace_t tr;
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -4096.0), MASK_SOLID_BRUSHONLY, this,COLLISION_GROUP_NONE, &tr);
+ CSoundEnt::InsertSound ( SOUND_DANGER, tr.endpos, 400, 0.3 );
+
+ vecSrc = GetAbsOrigin() + vecForward * 32 + vecRight * 100 + vecUp * -96;
+ m_hRepel[0] = MakeGrunt( vecSrc );
+
+ vecSrc = GetAbsOrigin() + vecForward * -64 + vecRight * 100 + vecUp * -96;
+ m_hRepel[1] = MakeGrunt( vecSrc );
+
+ vecSrc = GetAbsOrigin() + vecForward * 32 + vecRight * -100 + vecUp * -96;
+ m_hRepel[2] = MakeGrunt( vecSrc );
+
+ vecSrc = GetAbsOrigin() + vecForward * -64 + vecRight * -100 + vecUp * -96;
+ m_hRepel[3] = MakeGrunt( vecSrc );
+
+ m_iRepelState = GRUNTS_DEPLOYED;
+
+ HoverThink();
+}
+
+bool CNPC_Osprey::HasDead( )
+{
+ for (int i = 0; i < m_iUnits; i++)
+ {
+ if (m_hGrunt[i] == NULL || !m_hGrunt[i]->IsAlive())
+ {
+ return TRUE;
+ }
+ else
+ {
+ m_vecOrigin[i] = m_hGrunt[i]->GetAbsOrigin(); // send them to where they died
+ }
+ }
+ return FALSE;
+}
+
+void CNPC_Osprey::HoverThink( void )
+{
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ {
+ CBaseEntity *pRepel = (CBaseEntity*)m_hRepel[i];
+
+ if ( pRepel != NULL && pRepel->m_iHealth > 0 && pRepel->GetMoveType() == MOVETYPE_FLYGRAVITY )
+ {
+ break;
+ }
+ }
+
+ if ( i == 4 )
+ m_iRepelState = LOADED_WITH_GRUNTS;
+
+ if( m_iRepelState != LOADED_WITH_GRUNTS )
+ {
+ // angle of engines should approach vertical
+ m_iRotorAngle = UTIL_Approach(0, m_iRotorAngle, 5);
+ }
+}
+
+CAI_BaseNPC *CNPC_Osprey::MakeGrunt( Vector vecSrc )
+{
+ CBaseEntity *pEntity;
+ CAI_BaseNPC *pGrunt;
+
+ trace_t tr;
+ UTIL_TraceLine( vecSrc, vecSrc + Vector( 0, 0, -4096.0), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr);
+
+ if ( tr.m_pEnt && tr.m_pEnt->GetSolid() != SOLID_BSP)
+ return NULL;
+
+ for (int i = 0; i < m_iUnits; i++)
+ {
+ if (m_hGrunt[i] == NULL || !m_hGrunt[i]->IsAlive())
+ {
+ if (m_hGrunt[i] != NULL && m_hGrunt[i]->m_nRenderMode == kRenderNormal)
+ {
+ m_hGrunt[i]->SUB_StartFadeOut( );
+ }
+ pEntity = Create( "monster_human_grunt", vecSrc, GetAbsAngles() );
+ pGrunt = pEntity->MyNPCPointer( );
+ pGrunt->SetMoveType( MOVETYPE_FLYGRAVITY );
+ pGrunt->SetGravity( 0.0001 );
+
+ Vector spd = Vector( 0, 0, random->RandomFloat( -196, -128 ) );
+ pGrunt->SetLocalVelocity( spd );
+ pGrunt->SetActivity( ACT_GLIDE );
+ pGrunt->SetGroundEntity( NULL );
+
+ pGrunt->SetOwnerEntity(this);
+
+
+ CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.vmt", 1.0 );
+ pBeam->PointEntInit( vecSrc + Vector(0,0,112), pGrunt );
+ pBeam->SetBeamFlags( FBEAM_SOLID );
+ pBeam->SetColor( 255, 255, 255 );
+ pBeam->SetThink( &CBaseEntity::SUB_Remove );
+ pBeam->SetNextThink( gpGlobals->curtime + -4096.0 * tr.fraction / pGrunt->GetAbsVelocity().z + 0.5 );
+
+
+ // ALERT( at_console, "%d at %.0f %.0f %.0f\n", i, m_vecOrigin[i].x, m_vecOrigin[i].y, m_vecOrigin[i].z );
+ pGrunt->m_vecLastPosition = m_vecOrigin[i];
+ m_hGrunt[i] = pGrunt;
+ return pGrunt;
+ }
+ }
+ // ALERT( at_console, "none dead\n");
+ return NULL;
+}
+
+void CNPC_Osprey::Flight( void )
+{
+ if ( m_iRepelState == LOADED_WITH_GRUNTS )
+ {
+ BaseClass::Flight();
+
+ // adjust angle of osprey rotors
+ if ( m_angleVelocity > 0 )
+ {
+ m_iRotorAngle = UTIL_Approach(-45, m_iRotorAngle, 5 * (m_angleVelocity / 10));
+ }
+ else
+ {
+ m_iRotorAngle = UTIL_Approach(-1, m_iRotorAngle, 5);
+ }
+ SetBoneController( 0, m_iRotorAngle );
+
+ }
+}
+
+void CNPC_Osprey::PrescheduleThink( void )
+{
+ BaseClass::PrescheduleThink();
+
+ StudioFrameAdvance( );
+
+ if ( m_startTime != 0.0 && m_startTime <= gpGlobals->curtime )
+ FindAllThink();
+
+ if ( GetGoalEnt() )
+ {
+ if ( m_flPrevGoalVel != GetGoalEnt()->m_flSpeed )
+ {
+ if ( m_flPrevGoalVel == 0 && GetGoalEnt()->m_flSpeed != 0 )
+ {
+ if ( HasDead() && m_iRepelState == LOADED_WITH_GRUNTS )
+ m_iRepelState = UNLOADING_GRUNTS;
+ }
+
+ m_flPrevGoalVel = GetGoalEnt()->m_flSpeed;
+ }
+ }
+
+ if ( m_iRepelState == UNLOADING_GRUNTS )
+ DeployThink();
+ else if ( m_iRepelState == GRUNTS_DEPLOYED )
+ HoverThink();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Lame, temporary death
+//-----------------------------------------------------------------------------
+void CNPC_Osprey::DyingThink( void )
+{
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if( gpGlobals->curtime > m_flNextCrashExplosion )
+ {
+ CPASFilter filter( GetAbsOrigin() );
+ Vector pos;
+ QAngle dummy;
+
+ int rand = RandomInt(0,10);
+
+ if( rand < 4 )
+ {
+ int iAttach = LookupAttachment( rand % 2 ? "left" : "right" );
+ GetAttachment( iAttach, pos, dummy );
+ }
+ else
+ {
+ pos = GetAbsOrigin();
+ pos.x += random->RandomFloat( -150, 150 );
+ pos.y += random->RandomFloat( -150, 150 );
+ pos.z += random->RandomFloat( -150, -50 );
+ }
+
+ te->Explosion( filter, 0.0, &pos, g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
+ m_flNextCrashExplosion = gpGlobals->curtime + random->RandomFloat( 0.4, 0.7 );
+
+ Vector vecSize = Vector( 500, 500, 60 );
+
+ switch( m_iNextCrashModel )
+ {
+ case 0:
+ {
+ te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
+ vecSize, vec3_origin, m_iTailGibs, 100, 0, 2.5, BREAK_METAL );
+ break;
+ }
+ case 1:
+ {
+ te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
+ vecSize, vec3_origin, m_iBodyGibs, 100, 0, 2.5, BREAK_METAL );
+ break;
+ }
+ case 2:
+ {
+ te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
+ vecSize, vec3_origin, m_iEngineGibs, 100, 0, 2.5, BREAK_METAL );
+ break;
+ }
+ case 3:
+ {
+ te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
+ vecSize, vec3_origin, m_nDebrisModel, 100, 0, 2.5, BREAK_METAL );
+ break;
+ }
+ }
+
+ m_iNextCrashModel++;
+ if( m_iNextCrashModel > 3 ) m_iNextCrashModel = 0;
+ }
+
+ QAngle angVel = GetLocalAngularVelocity();
+ if( angVel.y < 400 )
+ {
+ angVel.y *= 1.1;
+ SetLocalAngularVelocity( angVel );
+ }
+ Vector vecImpulse( 0, 0, 0 );
+ // add gravity
+ vecImpulse.z -= 38.4; // 32ft/sec
+ ApplyAbsVelocityImpulse( vecImpulse );
+
+}
+
+void CNPC_Osprey::CrashTouch( CBaseEntity *pOther )
+{
+ Vector vecSize = Vector( 120, 120, 30 );
+ CPVSFilter filter( GetAbsOrigin() );
+
+ te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
+ vecSize, vec3_origin, m_iTailGibs, 100, 0, 2.5, BREAK_METAL );
+
+ if( m_hLeftSmoke )
+ {
+ m_hLeftSmoke->SetLifetime(0.1f);
+ m_hLeftSmoke = NULL;
+ }
+
+ if( m_hRightSmoke )
+ {
+ m_hRightSmoke->SetLifetime(0.1f);
+ m_hRightSmoke = NULL;
+ }
+
+ BaseClass::CrashTouch( pOther );
+}
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+BEGIN_DATADESC( CBaseHelicopter )
+
+ DEFINE_THINKFUNC( HelicopterThink ),
+ DEFINE_THINKFUNC( CallDyingThink ),
+ DEFINE_ENTITYFUNC( CrashTouch ),
+ DEFINE_ENTITYFUNC( FlyTouch ),
+
+ DEFINE_SOUNDPATCH( m_pRotorSound ),
+
+ DEFINE_FIELD( m_flForce, FIELD_FLOAT ),
+ DEFINE_FIELD( m_fHelicopterFlags, FIELD_INTEGER),
+ DEFINE_FIELD( m_vecDesiredFaceDir, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vecDesiredPosition,FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_vecGoalOrientation,FIELD_VECTOR ),
+ DEFINE_FIELD( m_flLastSeen, FIELD_TIME ),
+ DEFINE_FIELD( m_flPrevSeen, FIELD_TIME ),
+// DEFINE_FIELD( m_iSoundState, FIELD_INTEGER ), // Don't save, precached
+ DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vecTargetPosition, FIELD_POSITION_VECTOR ),
+
+ DEFINE_FIELD( m_angleVelocity, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flNextCrashExplosion, FIELD_TIME ),
+
+ DEFINE_FIELD( m_flMaxSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flMaxSpeedFiring, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ),
+ DEFINE_KEYFIELD( m_flInitialSpeed, FIELD_FLOAT, "InitialSpeed" ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_STRING, "ChangePathCorner", InputChangePathCorner),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate),
+
+ // Outputs
+ DEFINE_OUTPUT(m_AtTarget, "AtPathCorner" ),
+ DEFINE_OUTPUT(m_LeaveTarget, "LeavePathCorner" ),//<<TEMP>> Undone
+
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST( CBaseHelicopter, DT_BaseHelicopter )
+END_SEND_TABLE()
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+// Notes : Have your derived Helicopter's Spawn() function call this one FIRST
+//------------------------------------------------------------------------------
+void CBaseHelicopter::Precache( void )
+{
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+// Notes : Have your derived Helicopter's Spawn() function call this one FIRST
+//------------------------------------------------------------------------------
+void CBaseHelicopter::Spawn( void )
+{
+ Precache( );
+
+ SetSolid( SOLID_BBOX );
+ SetMoveType( MOVETYPE_STEP );
+ AddFlag( FL_FLY );
+
+ m_lifeState = LIFE_ALIVE;
+
+ // This base class assumes the helicopter has no guns or missiles.
+ // Set the appropriate flags in your derived class' Spawn() function.
+ m_fHelicopterFlags &= ~BITS_HELICOPTER_MISSILE_ON;
+ m_fHelicopterFlags &= ~BITS_HELICOPTER_GUN_ON;
+
+ m_pRotorSound = NULL;
+
+ SetCycle( 0 );
+ ResetSequenceInfo();
+
+ AddFlag( FL_NPC );
+
+ m_flMaxSpeed = BASECHOPPER_MAX_SPEED;
+ m_flMaxSpeedFiring = BASECHOPPER_MAX_FIRING_SPEED;
+ m_takedamage = DAMAGE_AIM;
+
+ // Don't start up if the level designer has asked the
+ // helicopter to start disabled.
+ if ( !(m_spawnflags & SF_AWAITINPUT) )
+ {
+ Startup();
+ SetNextThink( gpGlobals->curtime + 1.0f );
+ }
+
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+bool CBaseHelicopter::FireGun( void )
+{
+ return true;
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose : The main think function for the helicopters
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::HelicopterThink( void )
+{
+ SetNextThink( gpGlobals->curtime + HELICOPTER_THINK_INTERVAL );
+
+ // Don't keep this around for more than one frame.
+ ClearCondition( COND_ENEMY_DEAD );
+
+ // Animate and dispatch animation events.
+ DispatchAnimEvents( this );
+
+ PrescheduleThink();
+
+ ShowDamage( );
+
+ // -----------------------------------------------
+ // If AI is disabled, kill any motion and return
+ // -----------------------------------------------
+ if (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
+ {
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+ SetNextThink( gpGlobals->curtime + HELICOPTER_THINK_INTERVAL );
+ return;
+ }
+
+ Hunt();
+
+ HelicopterPostThink();
+}
+
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::Hunt( void )
+{
+ FlyPathCorners( );
+
+ if( HasCondition( COND_ENEMY_DEAD ) )
+ {
+ SetEnemy( NULL );
+ }
+
+ // Look for my best enemy. If I change enemies,
+ // be sure and change my prevseen/lastseen timers.
+ if( m_lifeState == LIFE_ALIVE )
+ {
+ GetSenses()->Look( 4092 );
+
+ ChooseEnemy();
+
+ if( HasEnemy() )
+ {
+ CheckEnemy( GetEnemy() );
+
+ if (FVisible( GetEnemy() ))
+ {
+ if (m_flLastSeen < gpGlobals->curtime - 2)
+ {
+ m_flPrevSeen = gpGlobals->curtime;
+ }
+
+ m_flLastSeen = gpGlobals->curtime;
+ m_vecTargetPosition = GetEnemy()->WorldSpaceCenter();
+ }
+ }
+ else
+ {
+ // look at where we're going instead
+ m_vecTargetPosition = m_vecDesiredPosition;
+ }
+ }
+ else
+ {
+ // If we're dead or dying, forget our enemy and don't look for new ones(sjb)
+ SetEnemy( NULL );
+ }
+
+ if ( 1 )
+ {
+ Vector targetDir = m_vecTargetPosition - GetAbsOrigin();
+ Vector desiredDir = m_vecDesiredPosition - GetAbsOrigin();
+
+ VectorNormalize( targetDir );
+ VectorNormalize( desiredDir );
+
+ if ( !IsCrashing() && m_flLastSeen + 5 > gpGlobals->curtime ) //&& DotProduct( targetDir, desiredDir) > 0.25)
+ {
+ // If we've seen the target recently, face the target.
+ //Msg( "Facing Target \n" );
+ m_vecDesiredFaceDir = targetDir;
+ }
+ else
+ {
+ // Face our desired position.
+ // Msg( "Facing Position\n" );
+ m_vecDesiredFaceDir = desiredDir;
+ }
+ }
+ else
+ {
+ // Face the way the path corner tells us to.
+ //Msg( "Facing my path corner\n" );
+ m_vecDesiredFaceDir = m_vecGoalOrientation;
+ }
+
+ Flight();
+
+ UpdatePlayerDopplerShift( );
+
+ // ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->curtime, m_flLastSeen, m_flPrevSeen );
+ if (m_fHelicopterFlags & BITS_HELICOPTER_GUN_ON)
+ {
+ if ( (m_flLastSeen + 1 > gpGlobals->curtime) && (m_flPrevSeen + 2 < gpGlobals->curtime) )
+ {
+ if (FireGun( ))
+ {
+ // slow down if we're firing
+ if (m_flGoalSpeed > m_flMaxSpeedFiring )
+ {
+ m_flGoalSpeed = m_flMaxSpeedFiring;
+ }
+ }
+ }
+ }
+
+ if (m_fHelicopterFlags & BITS_HELICOPTER_MISSILE_ON)
+ {
+ AimRocketGun();
+ }
+
+ // Finally, forget dead enemies.
+ if( GetEnemy() != NULL && !GetEnemy()->IsAlive() )
+ {
+ SetEnemy( NULL );
+ }
+}
+
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::FlyPathCorners( void )
+{
+
+ if ( GetGoalEnt() == NULL && m_target != NULL_STRING )// this monster has a target
+ {
+ SetGoalEnt( gEntList.FindEntityByName( NULL, m_target ) );
+ if (GetGoalEnt())
+ {
+ m_vecDesiredPosition = GetGoalEnt()->GetLocalOrigin();
+
+ // FIXME: orienation removed from path_corners!
+ AngleVectors( GetGoalEnt()->GetLocalAngles(), &m_vecGoalOrientation );
+ }
+ }
+
+ // walk route
+ 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();
+
+ // FIXME: orienation removed from path_corners!
+ AngleVectors( GetGoalEnt()->GetLocalAngles(), &m_vecGoalOrientation );
+
+ // NDebugOverlay::Box( m_vecDesiredPosition, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0,255,0, false, 30.0);
+ }
+ }
+ }
+ else
+ {
+ // If we can't find a new target, just stay where we are.
+ m_vecDesiredPosition = GetAbsOrigin();
+ }
+
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::UpdatePlayerDopplerShift( )
+{
+ // -----------------------------
+ // make rotor, engine sounds
+ // -----------------------------
+ if (m_iSoundState == 0)
+ {
+ // Sound startup.
+ InitializeRotorSound();
+ }
+ else
+ {
+ CBaseEntity *pPlayer = NULL;
+
+ // UNDONE: this needs to send different sounds to every player for multiplayer.
+ // FIXME: this isn't the correct way to find a player!!!
+ pPlayer = gEntList.FindEntityByName( NULL, "!player" );
+ if (pPlayer)
+ {
+ Vector dir = pPlayer->GetLocalOrigin() - GetLocalOrigin();
+ VectorNormalize(dir);
+
+ float velReceiver = -DotProduct( pPlayer->GetAbsVelocity(), dir );
+ float velTransmitter = -DotProduct( GetAbsVelocity(), dir );
+ // speed of sound == 13049in/s
+ int iPitch = 100 * ((1 - velReceiver / 13049) / (1 + velTransmitter / 13049));
+
+ // clamp pitch shifts
+ if (iPitch > 250)
+ iPitch = 250;
+ if (iPitch < 50)
+ iPitch = 50;
+
+ // Msg( "Pitch:%d\n", iPitch );
+
+ UpdateRotorSoundPitch( iPitch );
+ //Msg( "%.0f\n", pitch );
+ //Msg( "%.0f\n", flVol );
+ }
+ else
+ {
+ Msg( "Chopper didn't find a player!\n" );
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+void CBaseHelicopter::Flight( void )
+{
+ if( GetFlags() & FL_ONGROUND )
+ {
+ //This would be really bad.
+ SetGroundEntity( NULL );
+ }
+
+ // Generic speed up
+ if (m_flGoalSpeed < m_flMaxSpeed)
+ {
+ m_flGoalSpeed += GetAcceleration();
+ }
+
+// NDebugOverlay::Line(GetAbsOrigin(), m_vecDesiredPosition, 0,0,255, true, 0.1);
+
+ // estimate where I'll be facing in one seconds
+ Vector forward, right, up;
+ AngleVectors( GetLocalAngles() + GetLocalAngularVelocity() * 2, &forward, &right, &up );
+
+ QAngle angVel = GetLocalAngularVelocity();
+ float flSide = DotProduct( m_vecDesiredFaceDir, right );
+ if (flSide < 0)
+ {
+ if ( angVel.y < 8 )
+ {
+ angVel.y += 2;
+ }
+ else if (angVel.y < 60)
+ {
+ angVel.y += 8;
+ }
+ }
+ else
+ {
+ if ( angVel.y > -8 )
+ {
+ angVel.y -= 2;
+ }
+ else if (angVel.y > -60)
+ {
+ angVel.y -= 8;
+ }
+ }
+
+ angVel.y *= ( 0.98 ); // why?! (sjb)
+
+ // estimate where I'll be in two seconds
+ AngleVectors( GetLocalAngles() + angVel, NULL, NULL, &up );
+ Vector vecEst = GetAbsOrigin() + GetAbsVelocity() * 2.0 + up * m_flForce * 20 - Vector( 0, 0, 384 * 2 );
+
+ // add immediate force
+ AngleVectors( GetLocalAngles(), &forward, &right, &up );
+
+ Vector vecImpulse( 0, 0, 0 );
+ vecImpulse.x += up.x * m_flForce;
+ vecImpulse.y += up.y * m_flForce;
+ vecImpulse.z += up.z * m_flForce;
+
+ // add gravity
+ vecImpulse.z -= 38.4; // 32ft/sec
+ ApplyAbsVelocityImpulse( vecImpulse );
+
+ float flSpeed = GetAbsVelocity().Length();
+ float flDir = DotProduct( Vector( forward.x, forward.y, 0 ), Vector( GetAbsVelocity().x, GetAbsVelocity().y, 0 ) );
+ if (flDir < 0)
+ {
+ flSpeed = -flSpeed;
+ }
+
+ float flDist = DotProduct( m_vecDesiredPosition - vecEst, forward );
+
+// float flDist = (m_vecDesiredPosition - vecEst).Length();
+
+ // float flSlip = DotProduct( GetAbsVelocity(), right );
+ float flSlip = -DotProduct( m_vecDesiredPosition - vecEst, right );
+
+ // fly sideways
+ if (flSlip > 0)
+ {
+ if (GetLocalAngles().z > -30 && angVel.z > -15)
+ angVel.z -= 4;
+ else
+ angVel.z += 2;
+ }
+ else
+ {
+ if (GetLocalAngles().z < 30 && angVel.z < 15)
+ angVel.z += 4;
+ else
+ angVel.z -= 2;
+ }
+
+ // These functions contain code Ken wrote that used to be right here as part of the flight model,
+ // but we want different helicopter vehicles to have different drag characteristics, so I made
+ // them virtual functions (sjb)
+ ApplySidewaysDrag( right );
+ ApplyGeneralDrag();
+
+ // apply power to stay correct height
+ // FIXME: these need to be per class variables
+#define MAX_FORCE 80
+#define FORCE_POSDELTA 12
+#define FORCE_NEGDELTA 8
+
+ if (m_flForce < MAX_FORCE && vecEst.z < m_vecDesiredPosition.z)
+ {
+ m_flForce += FORCE_POSDELTA;
+ }
+ else if (m_flForce > 30)
+ {
+ if (vecEst.z > m_vecDesiredPosition.z)
+ m_flForce -= FORCE_NEGDELTA;
+ }
+
+ // pitch forward or back to get to target
+ //-----------------------------------------
+ // Pitch is reversed since Half-Life! (sjb)
+ //-----------------------------------------
+
+
+ // when we're way out, lean forward up to 40 degrees to accelerate to target
+ // not exceeding our goal speed.
+#if 0
+ float nodeSpeed = GetGoalEnt()->m_flSpeed;
+
+ if ( flDist > 300 && flSpeed < m_flGoalSpeed && GetLocalAngles().x + angVel.x < 25 )
+ {
+ angVel.x += 8.0; //lean forward
+ }
+ else if ( flDist < 500 && GetLocalAngles().x + angVel.x > -30 && nodeSpeed == 0)
+ {
+ angVel.x -= 8.0; // lean backwards as we approach the target
+ }
+ else if (GetLocalAngles().x + angVel.x < -20 )
+ {
+ // ALERT( at_console, "f " );
+ angVel.x += 2.0;
+ }
+ else if (GetLocalAngles().x + angVel.x > 20 )
+ {
+ // ALERT( at_console, "b " );
+ angVel.x -= 2.0;
+ }
+#else
+ m_angleVelocity = GetLocalAngles().x + angVel.x;
+ float angleX = angVel.x;
+
+// Msg("AngVel %f, %f\n", m_angleVelocity, flDist);
+ if (flDist > 128 && flSpeed < m_flGoalSpeed && m_angleVelocity < 30)
+ {
+ // ALERT( at_console, "F " );
+ // lean forward
+ angleX += 6;
+ }
+ else if (flDist < -128 && flSpeed > -50 && m_angleVelocity > -20)
+ {
+ // ALERT( at_console, "B " );
+ // lean backward
+ angleX -= 12.0;
+ }
+ else if ( (m_angleVelocity < -20) || (m_angleVelocity < 0 && flDist < 128) )
+ {
+ // ALERT( at_console, "f " );
+ if ( abs(m_angleVelocity) < 5 )
+ {
+ angleX += 1.0;
+ }
+ else
+ {
+ angleX += 4.0;
+ }
+ }
+ else if ( (m_angleVelocity > 20) || (m_angleVelocity > 0 && flDist < 128) )
+ {
+ // ALERT( at_console, "b " );
+ if ( abs(m_angleVelocity) < 5 )
+ {
+ angleX -= 1.0;
+ }
+ else
+ {
+ angleX -= 4.0;
+ }
+ }
+
+ angVel.x = angleX;
+ //Msg("AngVel.x %f %f\n", angVel.x, angleX );
+
+#endif
+
+ SetLocalAngularVelocity( angVel );
+ // ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", GetAbsOrigin().x, GetAbsVelocity().x, flDist, flSpeed, GetLocalAngles().x, m_vecAngVelocity.x, m_flForce );
+ // ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", GetAbsOrigin().z, GetAbsVelocity().z, vecEst.z, m_vecDesiredPosition.z, m_flForce );
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::InitializeRotorSound( void )
+{
+ if (m_pRotorSound)
+ {
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+
+ // Get the rotor sound started up.
+ controller.Play( m_pRotorSound, 0.0, 100 );
+ controller.SoundChangeVolume(m_pRotorSound, GetRotorVolume(), 2.0);
+ }
+
+ m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::UpdateRotorSoundPitch( int iPitch )
+{
+ if (m_pRotorSound)
+ {
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+ controller.SoundChangePitch( m_pRotorSound, iPitch, 0.1 );
+ controller.SoundChangeVolume( m_pRotorSound, GetRotorVolume(), 0.1 );
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::FlyTouch( CBaseEntity *pOther )
+{
+ // bounce if we hit something solid
+ if ( pOther->GetSolid() == SOLID_BSP)
+ {
+ const trace_t &tr = CBaseEntity::GetTouchTrace( );
+
+ // UNDONE, do a real bounce
+ ApplyAbsVelocityImpulse( tr.plane.normal * (GetAbsVelocity().Length() + 200) );
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::CrashTouch( CBaseEntity *pOther )
+{
+ // only crash if we hit something solid
+ if ( pOther->GetSolid() == SOLID_BSP)
+ {
+ SetTouch( NULL );
+ SetNextThink( gpGlobals->curtime );
+
+ CPASFilter filter( GetAbsOrigin() );
+ for (int i = 0; i < 5; i++)
+ {
+ Vector pos = GetAbsOrigin();
+
+ pos.x += random->RandomFloat( -150, 150 );
+ pos.y += random->RandomFloat( -150, 150 );
+ pos.z += random->RandomFloat( -150, -50 );
+ te->Explosion( filter, MIN( 0.99, i * 0.2 ), &pos, g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
+ }
+
+ UTIL_Remove( this );
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::DyingThink( void )
+{
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ SetLocalAngularVelocity( GetLocalAngularVelocity() * 1.02 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+int CBaseHelicopter::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+#if 0
+ // This code didn't port easily. WTF does it do? (sjb)
+ if (pevInflictor->m_owner == pev)
+ return 0;
+#endif
+
+ /*
+ if ( (bitsDamageType & DMG_BULLET) && flDamage > 50)
+ {
+ // clip bullet damage at 50
+ flDamage = 50;
+ }
+ */
+
+ return BaseClass::OnTakeDamage_Alive( info );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Override base class to add display of fly direction
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CBaseHelicopter::DrawDebugGeometryOverlays(void)
+{
+ if (m_pfnThink!= NULL)
+ {
+ // ------------------------------
+ // Draw route if requested
+ // ------------------------------
+ if (m_debugOverlays & OVERLAY_NPC_ROUTE_BIT)
+ {
+ NDebugOverlay::Line(GetAbsOrigin(), m_vecDesiredPosition, 0,0,255, true, 0);
+ }
+ }
+ BaseClass::DrawDebugGeometryOverlays();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CBaseHelicopter::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ CTakeDamageInfo dmgInfo = info;
+
+ // ALERT( at_console, "%d %.0f\n", ptr->iHitgroup, flDamage );
+
+ // 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 == 11 )
+ {
+ // ALERT( at_console, "%map .0f\n", flDamage );
+ AddMultiDamage( dmgInfo, this );
+// m_iDoSmokePuff = 3 + (info.GetDamage() / 5.0);
+ }
+ else
+ {
+ // do half damage in the body
+ dmgInfo.ScaleDamage(0.5);
+ AddMultiDamage( dmgInfo, this );
+ g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
+ }
+
+}
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHelicopter::NullThink( void )
+{
+ StudioFrameAdvance( );
+ SetNextThink( gpGlobals->curtime + 0.5f );
+}
+
+
+void CBaseHelicopter::Startup( void )
+{
+ m_flGoalSpeed = m_flInitialSpeed;
+ SetThink( &CBaseHelicopter::HelicopterThink );
+ SetTouch( &CBaseHelicopter::FlyTouch );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+
+void CBaseHelicopter::Event_Killed( const CTakeDamageInfo &info )
+{
+ m_lifeState = LIFE_DYING;
+
+ SetMoveType( MOVETYPE_FLYGRAVITY );
+ SetGravity( UTIL_ScaleForGravity( 240 ) ); // use a lower gravity
+
+ // Kill the rotor sound.
+
+ UTIL_SetSize( this, Vector( -32, -32, -64), Vector( 32, 32, 0) );
+ SetThink( &CBaseHelicopter::CallDyingThink );
+ SetTouch( &CBaseHelicopter::CrashTouch );
+
+ m_flNextCrashExplosion = gpGlobals->curtime + 0.0f;
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ m_iHealth = 0;
+ m_takedamage = DAMAGE_NO;
+
+/*
+ if (m_spawnflags & SF_NOWRECKAGE)
+ {
+ m_flNextRocket = gpGlobals->curtime + 4.0;
+ }
+ else
+ {
+ m_flNextRocket = gpGlobals->curtime + 15.0;
+ }
+*/
+ m_OnDeath.FireOutput( info.GetAttacker(), this );
+}
+
+
+void CBaseHelicopter::StopLoopingSounds()
+{
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+ controller.SoundDestroy( m_pRotorSound );
+ m_pRotorSound = NULL;
+
+ BaseClass::StopLoopingSounds();
+}
+
+void CBaseHelicopter::GibMonster( void )
+{
+}
+
+
+void CBaseHelicopter::ChangePathCorner( const char *pszName )
+{
+ if( m_lifeState != LIFE_ALIVE )
+ {
+ // Disregard this if dead or dying.
+ return;
+ }
+
+ if (GetGoalEnt())
+ {
+ SetGoalEnt( gEntList.FindEntityByName( NULL, pszName ) );
+
+ // I don't think we need to do this. The FLIGHT() code will do it for us (sjb)
+ if (GetGoalEnt())
+ {
+ m_vecDesiredPosition = GetGoalEnt()->GetLocalOrigin();
+ AngleVectors( GetGoalEnt()->GetLocalAngles(), &m_vecGoalOrientation );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Slams the chopper's current path corner and sends it to a new one.
+// This code does NOT check that the path from the current position
+// to the wished path corner is clear!
+//-----------------------------------------------------------------------------
+void CBaseHelicopter::InputChangePathCorner( inputdata_t &inputdata )
+{
+ ChangePathCorner( inputdata.value.String() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Call Startup for a helicopter that's been flagged to start disabled
+//-----------------------------------------------------------------------------
+void CBaseHelicopter::InputActivate( inputdata_t &inputdata )
+{
+ if( m_spawnflags & SF_AWAITINPUT )
+ {
+ Startup();
+
+ // Now clear the spawnflag to protect from
+ // subsequent calls.
+ m_spawnflags &= ~SF_AWAITINPUT;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+
+void CBaseHelicopter::ApplySidewaysDrag( const Vector &vecRight )
+{
+ Vector vecNewVelocity = GetAbsVelocity();
+ vecNewVelocity.x *= 1.0 - fabs( vecRight.x ) * 0.05;
+ vecNewVelocity.y *= 1.0 - fabs( vecRight.y ) * 0.05;
+ vecNewVelocity.z *= 1.0 - fabs( vecRight.z ) * 0.05;
+ SetAbsVelocity( vecNewVelocity );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CBaseHelicopter::ApplyGeneralDrag( void )
+{
+ Vector vecNewVelocity = GetAbsVelocity();
+ vecNewVelocity *= 0.995;
+ SetAbsVelocity( vecNewVelocity );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool CBaseHelicopter::ChooseEnemy( void )
+{
+ // See if there's a new enemy.
+ CBaseEntity *pNewEnemy;
+
+ pNewEnemy = BestEnemy();
+
+ if( ( pNewEnemy != GetEnemy() ) && pNewEnemy != NULL )
+ {
+ //New enemy! Clear the timers and set conditions.
+ SetEnemy( pNewEnemy );
+ m_flLastSeen = m_flPrevSeen = gpGlobals->curtime;
+ return true;
+ }
+ else
+ {
+ ClearCondition( COND_NEW_ENEMY );
+ return false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CBaseHelicopter::CheckEnemy( CBaseEntity *pEnemy )
+{
+ // -------------------
+ // If enemy is dead
+ // -------------------
+ if ( !pEnemy->IsAlive() )
+ {
+ SetCondition( COND_ENEMY_DEAD );
+ ClearCondition( COND_SEE_ENEMY );
+ ClearCondition( COND_ENEMY_OCCLUDED );
+ return;
+ }
+}
+
+bool CBaseHelicopter::HasReachedTarget( void )
+{
+ float flDist = (WorldSpaceCenter() - m_vecDesiredPosition).Length();
+
+ if( GetGoalEnt()->m_flSpeed <= 0 )
+ return ( flDist < 145 );
+ else
+ return( flDist < 512 );
+}
+