diff options
Diffstat (limited to 'game/server/hl1/hl1_npc_osprey.cpp')
| -rw-r--r-- | game/server/hl1/hl1_npc_osprey.cpp | 1589 |
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 ); +} + |