aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/hl2/vehicle_apc.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/hl2/vehicle_apc.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/hl2/vehicle_apc.cpp')
-rw-r--r--sp/src/game/server/hl2/vehicle_apc.cpp2210
1 files changed, 1105 insertions, 1105 deletions
diff --git a/sp/src/game/server/hl2/vehicle_apc.cpp b/sp/src/game/server/hl2/vehicle_apc.cpp
index 61a1ab60..80c4291f 100644
--- a/sp/src/game/server/hl2/vehicle_apc.cpp
+++ b/sp/src/game/server/hl2/vehicle_apc.cpp
@@ -1,1105 +1,1105 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-#include "cbase.h"
-#include "vehicle_apc.h"
-#include "ammodef.h"
-#include "IEffects.h"
-#include "engine/IEngineSound.h"
-#include "weapon_rpg.h"
-#include "in_buttons.h"
-#include "globalstate.h"
-#include "soundent.h"
-#include "ai_basenpc.h"
-#include "ndebugoverlay.h"
-#include "gib.h"
-#include "EntityFlame.h"
-#include "smoke_trail.h"
-#include "explode.h"
-#include "effect_dispatch_data.h"
-#include "te_effect_dispatch.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-
-#define ROCKET_ATTACK_RANGE_MAX 5500.0f
-#define ROCKET_ATTACK_RANGE_MIN 1250.0f
-
-#define MACHINE_GUN_ATTACK_RANGE_MAX 1250.0f
-#define MACHINE_GUN_ATTACK_RANGE_MIN 0.0f
-
-#define MACHINE_GUN_MAX_UP_PITCH 30
-#define MACHINE_GUN_MAX_DOWN_PITCH 10
-#define MACHINE_GUN_MAX_LEFT_YAW 30
-#define MACHINE_GUN_MAX_RIGHT_YAW 30
-
-#define MACHINE_GUN_BURST_SIZE 10
-#define MACHINE_GUN_BURST_TIME 0.075f
-#define MACHINE_GUN_BURST_PAUSE_TIME 2.0f
-
-#define ROCKET_SALVO_SIZE 5
-#define ROCKET_DELAY_TIME 1.5
-#define ROCKET_MIN_BURST_PAUSE_TIME 3
-#define ROCKET_MAX_BURST_PAUSE_TIME 4
-#define ROCKET_SPEED 800
-#define DEATH_VOLLEY_ROCKET_COUNT 4
-#define DEATH_VOLLEY_MIN_FIRE_TIME 0.333
-#define DEATH_VOLLEY_MAX_FIRE_TIME 0.166
-
-extern short g_sModelIndexFireball; // Echh...
-
-
-ConVar sk_apc_health( "sk_apc_health", "750" );
-
-
-#define APC_MAX_CHUNKS 3
-static const char *s_pChunkModelName[APC_MAX_CHUNKS] =
-{
- "models/gibs/helicopter_brokenpiece_01.mdl",
- "models/gibs/helicopter_brokenpiece_02.mdl",
- "models/gibs/helicopter_brokenpiece_03.mdl",
-};
-
-#define APC_MAX_GIBS 6
-static const char *s_pGibModelName[APC_MAX_GIBS] =
-{
- "models/combine_apc_destroyed_gib01.mdl",
- "models/combine_apc_destroyed_gib02.mdl",
- "models/combine_apc_destroyed_gib03.mdl",
- "models/combine_apc_destroyed_gib04.mdl",
- "models/combine_apc_destroyed_gib05.mdl",
- "models/combine_apc_destroyed_gib06.mdl",
-};
-
-
-LINK_ENTITY_TO_CLASS( prop_vehicle_apc, CPropAPC );
-
-
-BEGIN_DATADESC( CPropAPC )
-
- DEFINE_FIELD( m_flDangerSoundTime, FIELD_TIME ),
- DEFINE_FIELD( m_flHandbrakeTime, FIELD_TIME ),
- DEFINE_FIELD( m_bInitialHandbrake, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_nSmokeTrailCount, FIELD_INTEGER ),
- DEFINE_FIELD( m_flMachineGunTime, FIELD_TIME ),
- DEFINE_FIELD( m_iMachineGunBurstLeft, FIELD_INTEGER ),
-// DEFINE_FIELD( m_nMachineGunMuzzleAttachment, FIELD_INTEGER ),
-// DEFINE_FIELD( m_nMachineGunBaseAttachment, FIELD_INTEGER ),
-// DEFINE_FIELD( m_vecBarrelPos, FIELD_VECTOR ),
- DEFINE_FIELD( m_bInFiringCone, FIELD_BOOLEAN ),
-// DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hRocketTarget, FIELD_EHANDLE ),
- DEFINE_FIELD( m_iRocketSalvoLeft, FIELD_INTEGER ),
- DEFINE_FIELD( m_flRocketTime, FIELD_TIME ),
-// DEFINE_FIELD( m_nRocketAttachment, FIELD_INTEGER ),
- DEFINE_FIELD( m_nRocketSide, FIELD_INTEGER ),
- DEFINE_FIELD( m_hSpecificRocketTarget, FIELD_EHANDLE ),
- DEFINE_KEYFIELD( m_strMissileHint, FIELD_STRING, "missilehint" ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "Destroy", InputDestroy ),
- DEFINE_INPUTFUNC( FIELD_STRING, "FireMissileAt", InputFireMissileAt ),
-
- DEFINE_OUTPUT( m_OnDeath, "OnDeath" ),
- DEFINE_OUTPUT( m_OnFiredMissile, "OnFiredMissile" ),
- DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ),
- DEFINE_OUTPUT( m_OnDamagedByPlayer, "OnDamagedByPlayer" ),
-
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::Precache( void )
-{
- BaseClass::Precache();
-
- int i;
- for ( i = 0; i < APC_MAX_CHUNKS; ++i )
- {
- PrecacheModel( s_pChunkModelName[i] );
- }
-
- for ( i = 0; i < APC_MAX_GIBS; ++i )
- {
- PrecacheModel( s_pGibModelName[i] );
- }
-
- PrecacheScriptSound( "Weapon_AR2.Single" );
- PrecacheScriptSound( "PropAPC.FireRocket" );
- PrecacheScriptSound( "combine.door_lock" );
-}
-
-
-//------------------------------------------------
-// Spawn
-//------------------------------------------------
-void CPropAPC::Spawn( void )
-{
- BaseClass::Spawn();
- SetBlocksLOS( true );
- m_iHealth = m_iMaxHealth = sk_apc_health.GetFloat();
- SetCycle( 0 );
- m_iMachineGunBurstLeft = MACHINE_GUN_BURST_SIZE;
- m_iRocketSalvoLeft = ROCKET_SALVO_SIZE;
- m_nRocketSide = 0;
- m_lifeState = LIFE_ALIVE;
- m_bInFiringCone = false;
-
- m_flHandbrakeTime = gpGlobals->curtime + 0.1;
- m_bInitialHandbrake = false;
-
- // Reset the gun to a default pose.
- SetPoseParameter( "vehicle_weapon_pitch", 0 );
- SetPoseParameter( "vehicle_weapon_yaw", 90 );
-
- CreateAPCLaserDot();
-
- if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
- {
- AddFlag( FL_AIMTARGET );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Create a laser
-//-----------------------------------------------------------------------------
-void CPropAPC::CreateAPCLaserDot( void )
-{
- // Create a laser if we don't have one
- if ( m_hLaserDot == NULL )
- {
- m_hLaserDot = CreateLaserDot( GetAbsOrigin(), this, false );
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CPropAPC::ShouldAttractAutoAim( CBaseEntity *pAimingEnt )
-{
- if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE && pAimingEnt->IsPlayer() && GetDriver() )
- {
- return true;
- }
-
- return BaseClass::ShouldAttractAutoAim( pAimingEnt );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::Activate()
-{
- BaseClass::Activate();
-
- m_nRocketAttachment = LookupAttachment( "cannon_muzzle" );
- m_nMachineGunMuzzleAttachment = LookupAttachment( "muzzle" );
- m_nMachineGunBaseAttachment = LookupAttachment( "gun_base" );
-
- // NOTE: gun_ref must have the same position as gun_base, but rotates with the gun
- int nMachineGunRefAttachment = LookupAttachment( "gun_def" );
-
- Vector vecWorldBarrelPos;
- matrix3x4_t matRefToWorld;
- GetAttachment( m_nMachineGunMuzzleAttachment, vecWorldBarrelPos );
- GetAttachment( nMachineGunRefAttachment, matRefToWorld );
- VectorITransform( vecWorldBarrelPos, matRefToWorld, m_vecBarrelPos );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::UpdateOnRemove( void )
-{
- if ( m_hLaserDot )
- {
- UTIL_Remove( m_hLaserDot );
- m_hLaserDot = NULL;
- }
- BaseClass::UpdateOnRemove();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::CreateServerVehicle( void )
-{
- // Create our armed server vehicle
- m_pServerVehicle = new CAPCFourWheelServerVehicle();
- m_pServerVehicle->SetVehicle( this );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pMoveData -
-//-----------------------------------------------------------------------------
-Class_T CPropAPC::ClassifyPassenger( CBaseCombatCharacter *pPassenger, Class_T defaultClassification )
-{
- return CLASS_COMBINE;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Damage events as modified for the passenger of the APC, not the APC itself
-//-----------------------------------------------------------------------------
-float CPropAPC::PassengerDamageModifier( const CTakeDamageInfo &info )
-{
- CTakeDamageInfo DmgInfo = info;
-
- // bullets, slashing and headbutts don't hurt us in the apc, neither do rockets
- if( (DmgInfo.GetDamageType() & DMG_BULLET) || (DmgInfo.GetDamageType() & DMG_SLASH) ||
- (DmgInfo.GetDamageType() & DMG_CLUB) || (DmgInfo.GetDamageType() & DMG_BLAST) )
- return (0);
-
- // Accept everything else by default
- return 1.0;
-}
-
-
-//-----------------------------------------------------------------------------
-// position of eyes
-//-----------------------------------------------------------------------------
-Vector CPropAPC::EyePosition( )
-{
- Vector vecEyePosition;
- CollisionProp()->NormalizedToWorldSpace( Vector( 0.5, 0.5, 1.0 ), &vecEyePosition );
- return vecEyePosition;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-Vector CPropAPC::BodyTarget( const Vector &posSrc, bool bNoisy )
-{
- if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
- {
- return WorldSpaceCenter();
- }
-
- return BaseClass::BodyTarget( posSrc, bNoisy );
-}
-
-//-----------------------------------------------------------------------------
-// Add a smoke trail since we've taken more damage
-//-----------------------------------------------------------------------------
-void CPropAPC::AddSmokeTrail( const Vector &vecPos )
-{
- // Start this trail out with a bang!
- ExplosionCreate( vecPos, vec3_angle, this, 1000, 500.0f, SF_ENVEXPLOSION_NODAMAGE |
- SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE |
- SF_ENVEXPLOSION_NOFIREBALLSMOKE, 0 );
- UTIL_ScreenShake( vecPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
-
- if ( m_nSmokeTrailCount == MAX_SMOKE_TRAILS )
- return;
-
- SmokeTrail *pSmokeTrail = SmokeTrail::CreateSmokeTrail();
- if( !pSmokeTrail )
- return;
-
- // See if there's an attachment for this smoke trail
- char buf[32];
- Q_snprintf( buf, 32, "damage%d", m_nSmokeTrailCount );
- int nAttachment = LookupAttachment( buf );
-
- ++m_nSmokeTrailCount;
-
- pSmokeTrail->m_SpawnRate = 4;
- pSmokeTrail->m_ParticleLifetime = 5.0f;
- pSmokeTrail->m_StartColor.Init( 0.7f, 0.7f, 0.7f );
- pSmokeTrail->m_EndColor.Init( 0.6, 0.6, 0.6 );
- pSmokeTrail->m_StartSize = 32;
- pSmokeTrail->m_EndSize = 64;
- pSmokeTrail->m_SpawnRadius = 4;
- pSmokeTrail->m_Opacity = 0.5f;
- pSmokeTrail->m_MinSpeed = 16;
- pSmokeTrail->m_MaxSpeed = 16;
- pSmokeTrail->m_MinDirectedSpeed = 16.0f;
- pSmokeTrail->m_MaxDirectedSpeed = 16.0f;
- pSmokeTrail->SetLifetime( 5 );
- pSmokeTrail->SetParent( this, nAttachment );
-
- Vector vecForward( 0, 0, 1 );
- QAngle angles;
- VectorAngles( vecForward, angles );
-
- if ( nAttachment == 0 )
- {
- pSmokeTrail->SetAbsOrigin( vecPos );
- pSmokeTrail->SetAbsAngles( angles );
- }
- else
- {
- pSmokeTrail->SetLocalOrigin( vec3_origin );
- pSmokeTrail->SetLocalAngles( angles );
- }
-
- pSmokeTrail->SetMoveType( MOVETYPE_NONE );
-}
-
-
-//------------------------------------------------------------------------------
-// Pow!
-//------------------------------------------------------------------------------
-void CPropAPC::ExplodeAndThrowChunk( const Vector &vecExplosionPos )
-{
- ExplosionCreate( vecExplosionPos, vec3_angle, this, 1000, 500.0f,
- SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS |
- SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALLSMOKE, 0 );
- UTIL_ScreenShake( vecExplosionPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
-
- // Drop a flaming, smoking chunk.
- CGib *pChunk = CREATE_ENTITY( CGib, "gib" );
- pChunk->Spawn( "models/gibs/hgibs.mdl" );
- pChunk->SetBloodColor( DONT_BLEED );
-
- QAngle vecSpawnAngles;
- vecSpawnAngles.Random( -90, 90 );
- pChunk->SetAbsOrigin( vecExplosionPos );
- pChunk->SetAbsAngles( vecSpawnAngles );
-
- int nGib = random->RandomInt( 0, APC_MAX_CHUNKS - 1 );
- pChunk->Spawn( s_pChunkModelName[nGib] );
- pChunk->SetOwnerEntity( this );
- pChunk->m_lifeTime = random->RandomFloat( 6.0f, 8.0f );
- pChunk->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
- IPhysicsObject *pPhysicsObject = pChunk->VPhysicsInitNormal( SOLID_VPHYSICS, pChunk->GetSolidFlags(), false );
-
- // Set the velocity
- if ( pPhysicsObject )
- {
- pPhysicsObject->EnableMotion( true );
- Vector vecVelocity;
-
- QAngle angles;
- angles.x = random->RandomFloat( -40, 0 );
- angles.y = random->RandomFloat( 0, 360 );
- angles.z = 0.0f;
- AngleVectors( angles, &vecVelocity );
-
- vecVelocity *= random->RandomFloat( 300, 900 );
- vecVelocity += GetAbsVelocity();
-
- AngularImpulse angImpulse;
- angImpulse = RandomAngularImpulse( -180, 180 );
-
- pChunk->SetAbsVelocity( vecVelocity );
- pPhysicsObject->SetVelocity(&vecVelocity, &angImpulse );
- }
-
- CEntityFlame *pFlame = CEntityFlame::Create( pChunk, false );
- if ( pFlame != NULL )
- {
- pFlame->SetLifetime( pChunk->m_lifeTime );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Should we trigger a damage effect?
-//-----------------------------------------------------------------------------
-inline bool CPropAPC::ShouldTriggerDamageEffect( int nPrevHealth, int nEffectCount ) const
-{
- int nPrevRange = (int)( ((float)nPrevHealth / (float)GetMaxHealth()) * nEffectCount );
- int nRange = (int)( ((float)GetHealth() / (float)GetMaxHealth()) * nEffectCount );
- return ( nRange != nPrevRange );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::Event_Killed( const CTakeDamageInfo &info )
-{
- m_OnDeath.FireOutput( info.GetAttacker(), this );
-
- Vector vecAbsMins, vecAbsMaxs;
- CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
-
- Vector vecNormalizedMins, vecNormalizedMaxs;
- CollisionProp()->WorldToNormalizedSpace( vecAbsMins, &vecNormalizedMins );
- CollisionProp()->WorldToNormalizedSpace( vecAbsMaxs, &vecNormalizedMaxs );
-
- Vector vecAbsPoint;
- CPASFilter filter( GetAbsOrigin() );
- for (int i = 0; i < 5; i++)
- {
- CollisionProp()->RandomPointInBounds( vecNormalizedMins, vecNormalizedMaxs, &vecAbsPoint );
- te->Explosion( filter, random->RandomFloat( 0.0, 1.0 ), &vecAbsPoint,
- g_sModelIndexFireball, random->RandomInt( 4, 10 ),
- random->RandomInt( 8, 15 ),
- ( i < 2 ) ? TE_EXPLFLAG_NODLIGHTS : TE_EXPLFLAG_NOPARTICLES | TE_EXPLFLAG_NOFIREBALLSMOKE | TE_EXPLFLAG_NODLIGHTS,
- 100, 0 );
- }
-
- // TODO: make the gibs spawn in sync with the delayed explosions
- int nGibs = random->RandomInt( 1, 4 );
- for ( int i = 0; i < nGibs; i++)
- {
- // Throw a flaming, smoking chunk.
- CGib *pChunk = CREATE_ENTITY( CGib, "gib" );
- pChunk->Spawn( "models/gibs/hgibs.mdl" );
- pChunk->SetBloodColor( DONT_BLEED );
-
- QAngle vecSpawnAngles;
- vecSpawnAngles.Random( -90, 90 );
- pChunk->SetAbsOrigin( vecAbsPoint );
- pChunk->SetAbsAngles( vecSpawnAngles );
-
- int nGib = random->RandomInt( 0, APC_MAX_CHUNKS - 1 );
- pChunk->Spawn( s_pChunkModelName[nGib] );
- pChunk->SetOwnerEntity( this );
- pChunk->m_lifeTime = random->RandomFloat( 6.0f, 8.0f );
- pChunk->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
- IPhysicsObject *pPhysicsObject = pChunk->VPhysicsInitNormal( SOLID_VPHYSICS, pChunk->GetSolidFlags(), false );
-
- // Set the velocity
- if ( pPhysicsObject )
- {
- pPhysicsObject->EnableMotion( true );
- Vector vecVelocity;
-
- QAngle angles;
- angles.x = random->RandomFloat( -20, 20 );
- angles.y = random->RandomFloat( 0, 360 );
- angles.z = 0.0f;
- AngleVectors( angles, &vecVelocity );
-
- vecVelocity *= random->RandomFloat( 300, 900 );
- vecVelocity += GetAbsVelocity();
-
- AngularImpulse angImpulse;
- angImpulse = RandomAngularImpulse( -180, 180 );
-
- pChunk->SetAbsVelocity( vecVelocity );
- pPhysicsObject->SetVelocity(&vecVelocity, &angImpulse );
- }
-
- CEntityFlame *pFlame = CEntityFlame::Create( pChunk, false );
- if ( pFlame != NULL )
- {
- pFlame->SetLifetime( pChunk->m_lifeTime );
- }
- }
-
- UTIL_ScreenShake( vecAbsPoint, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
-
- if( hl2_episodic.GetBool() )
- {
- // EP1 perf hit
- Ignite( 6, false );
- }
- else
- {
- Ignite( 60, false );
- }
-
- m_lifeState = LIFE_DYING;
-
- // Spawn a lesser amount if the player is close
- m_iRocketSalvoLeft = DEATH_VOLLEY_ROCKET_COUNT;
- m_flRocketTime = gpGlobals->curtime;
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Blows it up!
-//-----------------------------------------------------------------------------
-void CPropAPC::InputDestroy( inputdata_t &inputdata )
-{
- CTakeDamageInfo info( this, this, m_iHealth, DMG_BLAST );
- info.SetDamagePosition( WorldSpaceCenter() );
- info.SetDamageForce( Vector( 0, 0, 1 ) );
- TakeDamage( info );
-}
-
-
-//-----------------------------------------------------------------------------
-// Aim the next rocket at a specific target
-//-----------------------------------------------------------------------------
-void CPropAPC::InputFireMissileAt( inputdata_t &inputdata )
-{
- string_t strMissileTarget = MAKE_STRING( inputdata.value.String() );
- CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, strMissileTarget, NULL, inputdata.pActivator, inputdata.pCaller );
- if ( pTarget == NULL )
- {
- DevWarning( "%s: Could not find target '%s'!\n", GetClassname(), STRING( strMissileTarget ) );
- return;
- }
-
- m_hSpecificRocketTarget = pTarget;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CPropAPC::OnTakeDamage( const CTakeDamageInfo &info )
-{
- if ( m_iHealth == 0 )
- return 0;
-
- m_OnDamaged.FireOutput( info.GetAttacker(), this );
-
- if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() )
- {
- m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this );
- }
-
- CTakeDamageInfo dmgInfo = info;
- if ( dmgInfo.GetDamageType() & (DMG_BLAST | DMG_AIRBOAT) )
- {
- int nPrevHealth = GetHealth();
-
- m_iHealth -= dmgInfo.GetDamage();
- if ( m_iHealth <= 0 )
- {
- m_iHealth = 0;
- Event_Killed( dmgInfo );
- return 0;
- }
-
- // Chain
-// BaseClass::OnTakeDamage( dmgInfo );
-
- // Spawn damage effects
- if ( nPrevHealth != GetHealth() )
- {
- if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_SMOKE_TRAILS ) )
- {
- AddSmokeTrail( dmgInfo.GetDamagePosition() );
- }
-
- if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) )
- {
- ExplodeAndThrowChunk( dmgInfo.GetDamagePosition() );
- }
- }
- }
- return 1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pMoveData -
-//-----------------------------------------------------------------------------
-void CPropAPC::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
-{
- BaseClass::ProcessMovement( pPlayer, pMoveData );
-
- if ( m_flDangerSoundTime > gpGlobals->curtime )
- return;
-
- QAngle vehicleAngles = GetLocalAngles();
- Vector vecStart = GetAbsOrigin();
- Vector vecDir;
-
- GetVectors( &vecDir, NULL, NULL );
-
- // Make danger sounds ahead of the APC
- trace_t tr;
- Vector vecSpot, vecLeftDir, vecRightDir;
-
- // lay down sound path
- vecSpot = vecStart + vecDir * 600;
- CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this );
-
- // put sounds a bit to left and right but slightly closer to APC to make a "cone" of sound
- // in front of it
- QAngle leftAngles = vehicleAngles;
- leftAngles[YAW] += 20;
- VehicleAngleVectors( leftAngles, &vecLeftDir, NULL, NULL );
- vecSpot = vecStart + vecLeftDir * 400;
- UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
- CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this );
-
- QAngle rightAngles = vehicleAngles;
- rightAngles[YAW] -= 20;
- VehicleAngleVectors( rightAngles, &vecRightDir, NULL, NULL );
- vecSpot = vecStart + vecRightDir * 400;
- UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
- CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this);
-
- m_flDangerSoundTime = gpGlobals->curtime + 0.3;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::Think( void )
-{
- BaseClass::Think();
-
- SetNextThink( gpGlobals->curtime );
-
- if ( !m_bInitialHandbrake ) // after initial timer expires, set the handbrake
- {
- m_bInitialHandbrake = true;
- m_VehiclePhysics.SetHandbrake( true );
- m_VehiclePhysics.Think();
- }
-
- StudioFrameAdvance();
-
- if ( IsSequenceFinished() )
- {
- int iSequence = SelectWeightedSequence( ACT_IDLE );
- if ( iSequence > ACTIVITY_NOT_AVAILABLE )
- {
- SetCycle( 0 );
- m_flAnimTime = gpGlobals->curtime;
- ResetSequence( iSequence );
- ResetClientsideFrame();
- }
- }
-
- if (m_debugOverlays & OVERLAY_NPC_KILL_BIT)
- {
- CTakeDamageInfo info( this, this, m_iHealth, DMG_BLAST );
- info.SetDamagePosition( WorldSpaceCenter() );
- info.SetDamageForce( Vector( 0, 0, 1 ) );
- TakeDamage( info );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Aims the secondary weapon at a target
-//-----------------------------------------------------------------------------
-void CPropAPC::AimSecondaryWeaponAt( CBaseEntity *pTarget )
-{
- m_hRocketTarget = pTarget;
-
- // Update the rocket target
- CreateAPCLaserDot();
-
- if ( m_hRocketTarget )
- {
- m_hLaserDot->SetAbsOrigin( m_hRocketTarget->BodyTarget( WorldSpaceCenter(), false ) );
- }
- SetLaserDotTarget( m_hLaserDot, m_hRocketTarget );
- EnableLaserDot( m_hLaserDot, m_hRocketTarget != NULL );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased )
-{
- switch( m_lifeState )
- {
- case LIFE_ALIVE:
- {
- int iButtons = ucmd->buttons;
- if ( iButtons & IN_ATTACK )
- {
- FireMachineGun();
- }
- else if ( iButtons & IN_ATTACK2 )
- {
- FireRocket();
- }
- }
- break;
-
- case LIFE_DYING:
- FireDying( );
- break;
-
- case LIFE_DEAD:
- return;
- }
-
- BaseClass::DriveVehicle( flFrameTime, ucmd, iButtonsDown, iButtonsReleased );
-}
-
-void CPropAPC::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
-{
- BaseClass::Use( pActivator, pCaller, useType, value );
-
- if ( pActivator->IsPlayer() )
- {
- EmitSound ( "combine.door_lock" );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Primary gun
-//-----------------------------------------------------------------------------
-void CPropAPC::AimPrimaryWeapon( const Vector &vecWorldTarget )
-{
- EntityMatrix parentMatrix;
- parentMatrix.InitFromEntity( this, m_nMachineGunBaseAttachment );
- Vector target = parentMatrix.WorldToLocal( vecWorldTarget );
-
- float quadTarget = target.LengthSqr();
- float quadTargetXY = target.x*target.x + target.y*target.y;
-
- // Target is too close! Can't aim at it
- if ( quadTarget > m_vecBarrelPos.LengthSqr() )
- {
- // We're trying to aim the offset barrel at an arbitrary point.
- // To calculate this, I think of the target as being on a sphere with
- // it's center at the origin of the gun.
- // The rotation we need is the opposite of the rotation that moves the target
- // along the surface of that sphere to intersect with the gun's shooting direction
- // To calculate that rotation, we simply calculate the intersection of the ray
- // coming out of the barrel with the target sphere (that's the new target position)
- // and use atan2() to get angles
-
- // angles from target pos to center
- float targetToCenterYaw = atan2( target.y, target.x );
- float centerToGunYaw = atan2( m_vecBarrelPos.y, sqrt( quadTarget - (m_vecBarrelPos.y*m_vecBarrelPos.y) ) );
-
- float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) );
- float centerToGunPitch = atan2( -m_vecBarrelPos.z, sqrt( quadTarget - (m_vecBarrelPos.z*m_vecBarrelPos.z) ) );
-
- QAngle angles;
- angles.Init( -RAD2DEG(targetToCenterPitch+centerToGunPitch), RAD2DEG( targetToCenterYaw + centerToGunYaw ), 0 );
-
- SetPoseParameter( "vehicle_weapon_yaw", angles.y );
- SetPoseParameter( "vehicle_weapon_pitch", angles.x );
- StudioFrameAdvance();
-
- float curPitch = GetPoseParameter( "vehicle_weapon_pitch" );
- float curYaw = GetPoseParameter( "vehicle_weapon_yaw" );
- m_bInFiringCone = (fabs(curPitch - angles.x) < 1e-3) && (fabs(curYaw - angles.y) < 1e-3);
- }
- else
- {
- m_bInFiringCone = false;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-const char *CPropAPC::GetTracerType( void )
-{
- return "HelicopterTracer";
-}
-
-
-//-----------------------------------------------------------------------------
-// Allows the shooter to change the impact effect of his bullets
-//-----------------------------------------------------------------------------
-void CPropAPC::DoImpactEffect( trace_t &tr, int nDamageType )
-{
- UTIL_ImpactTrace( &tr, nDamageType, "HelicopterImpact" );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::DoMuzzleFlash( void )
-{
- CEffectData data;
- data.m_nEntIndex = entindex();
- data.m_nAttachmentIndex = m_nMachineGunMuzzleAttachment;
- data.m_flScale = 1.0f;
- DispatchEffect( "ChopperMuzzleFlash", data );
-
- BaseClass::DoMuzzleFlash();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::FireMachineGun( void )
-{
- if ( m_flMachineGunTime > gpGlobals->curtime )
- return;
-
- // If we're still firing the salvo, fire quickly
- m_iMachineGunBurstLeft--;
- if ( m_iMachineGunBurstLeft > 0 )
- {
- m_flMachineGunTime = gpGlobals->curtime + MACHINE_GUN_BURST_TIME;
- }
- else
- {
- // Reload the salvo
- m_iMachineGunBurstLeft = MACHINE_GUN_BURST_SIZE;
- m_flMachineGunTime = gpGlobals->curtime + MACHINE_GUN_BURST_PAUSE_TIME;
- }
-
- Vector vecMachineGunShootPos;
- Vector vecMachineGunDir;
- GetAttachment( m_nMachineGunMuzzleAttachment, vecMachineGunShootPos, &vecMachineGunDir );
-
- // Fire the round
- int bulletType = GetAmmoDef()->Index("AR2");
- FireBullets( 1, vecMachineGunShootPos, vecMachineGunDir, VECTOR_CONE_8DEGREES, MAX_TRACE_LENGTH, bulletType, 1 );
- DoMuzzleFlash();
-
- EmitSound( "Weapon_AR2.Single" );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::GetRocketShootPosition( Vector *pPosition )
-{
- GetAttachment( m_nRocketAttachment, *pPosition );
-}
-
-
-//-----------------------------------------------------------------------------
-// Create a corpse
-//-----------------------------------------------------------------------------
-void CPropAPC::CreateCorpse( )
-{
- m_lifeState = LIFE_DEAD;
-
- for ( int i = 0; i < APC_MAX_GIBS; ++i )
- {
- CPhysicsProp *pGib = assert_cast<CPhysicsProp*>(CreateEntityByName( "prop_physics" ));
- pGib->SetAbsOrigin( GetAbsOrigin() );
- pGib->SetAbsAngles( GetAbsAngles() );
- pGib->SetAbsVelocity( GetAbsVelocity() );
- pGib->SetModel( s_pGibModelName[i] );
- pGib->Spawn();
- pGib->SetMoveType( MOVETYPE_VPHYSICS );
-
- float flMass = pGib->GetMass();
- if ( flMass < 200 )
- {
- Vector vecVelocity;
- pGib->GetMassCenter( &vecVelocity );
- vecVelocity -= WorldSpaceCenter();
- vecVelocity.z = fabs(vecVelocity.z);
- VectorNormalize( vecVelocity );
-
- // Apply a force that would make a 100kg mass travel 150 - 300 m/s
- float flRandomVel = random->RandomFloat( 150, 300 );
- vecVelocity *= (100 * flRandomVel) / flMass;
- vecVelocity.z += 100.0f;
- AngularImpulse angImpulse = RandomAngularImpulse( -500, 500 );
-
- IPhysicsObject *pObj = pGib->VPhysicsGetObject();
- if ( pObj != NULL )
- {
- pObj->AddVelocity( &vecVelocity, &angImpulse );
- }
- pGib->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
- }
- if( hl2_episodic.GetBool() )
- {
- // EP1 perf hit
- pGib->Ignite( 6, false );
- }
- else
- {
- pGib->Ignite( 60, false );
- }
- }
-
- AddSolidFlags( FSOLID_NOT_SOLID );
- AddEffects( EF_NODRAW );
- UTIL_Remove( this );
-}
-
-
-//-----------------------------------------------------------------------------
-// Death volley
-//-----------------------------------------------------------------------------
-void CPropAPC::FireDying( )
-{
- if ( m_flRocketTime > gpGlobals->curtime )
- return;
-
- Vector vecRocketOrigin;
- GetRocketShootPosition( &vecRocketOrigin );
-
- Vector vecDir;
- vecDir.Random( -1.0f, 1.0f );
- if ( vecDir.z < 0.0f )
- {
- vecDir.z *= -1.0f;
- }
-
- VectorNormalize( vecDir );
-
- Vector vecVelocity;
- VectorMultiply( vecDir, ROCKET_SPEED * random->RandomFloat( 0.75f, 1.25f ), vecVelocity );
-
- QAngle angles;
- VectorAngles( vecDir, angles );
-
- CAPCMissile *pRocket = (CAPCMissile *) CAPCMissile::Create( vecRocketOrigin, angles, vecVelocity, this );
- float flDeathTime = random->RandomFloat( 0.3f, 0.5f );
- if ( random->RandomFloat( 0.0f, 1.0f ) < 0.3f )
- {
- pRocket->ExplodeDelay( flDeathTime );
- }
- else
- {
- pRocket->AugerDelay( flDeathTime );
- }
-
- // Make erratic firing
- m_flRocketTime = gpGlobals->curtime + random->RandomFloat( DEATH_VOLLEY_MIN_FIRE_TIME, DEATH_VOLLEY_MAX_FIRE_TIME );
- if ( --m_iRocketSalvoLeft <= 0 )
- {
- CreateCorpse();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::FireRocket( void )
-{
- if ( m_flRocketTime > gpGlobals->curtime )
- return;
-
- // If we're still firing the salvo, fire quickly
- m_iRocketSalvoLeft--;
- if ( m_iRocketSalvoLeft > 0 )
- {
- m_flRocketTime = gpGlobals->curtime + ROCKET_DELAY_TIME;
- }
- else
- {
- // Reload the salvo
- m_iRocketSalvoLeft = ROCKET_SALVO_SIZE;
- m_flRocketTime = gpGlobals->curtime + random->RandomFloat( ROCKET_MIN_BURST_PAUSE_TIME, ROCKET_MAX_BURST_PAUSE_TIME );
- }
-
- Vector vecRocketOrigin;
- GetRocketShootPosition( &vecRocketOrigin );
-
- static float s_pSide[] = { 0.966, 0.866, 0.5, -0.5, -0.866, -0.966 };
-
- Vector forward;
- GetVectors( &forward, NULL, NULL );
-
- Vector vecDir;
- CrossProduct( Vector( 0, 0, 1 ), forward, vecDir );
- vecDir.z = 1.0f;
- vecDir.x *= s_pSide[m_nRocketSide];
- vecDir.y *= s_pSide[m_nRocketSide];
- if ( ++m_nRocketSide >= 6 )
- {
- m_nRocketSide = 0;
- }
-
- VectorNormalize( vecDir );
-
- Vector vecVelocity;
- VectorMultiply( vecDir, ROCKET_SPEED, vecVelocity );
-
- QAngle angles;
- VectorAngles( vecDir, angles );
-
- CAPCMissile *pRocket = (CAPCMissile *)CAPCMissile::Create( vecRocketOrigin, angles, vecVelocity, this );
- pRocket->IgniteDelay();
-
- if ( m_hSpecificRocketTarget )
- {
- pRocket->AimAtSpecificTarget( m_hSpecificRocketTarget );
- m_hSpecificRocketTarget = NULL;
- }
- else if ( m_strMissileHint != NULL_STRING )
- {
- pRocket->SetGuidanceHint( STRING( m_strMissileHint ) );
- }
-
- EmitSound( "PropAPC.FireRocket" );
- m_OnFiredMissile.FireOutput( this, this );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-float CPropAPC::MaxAttackRange() const
-{
- return ROCKET_ATTACK_RANGE_MAX;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAPC::OnRestore( void )
-{
- IServerVehicle *pServerVehicle = GetServerVehicle();
- if ( pServerVehicle != NULL )
- {
- // Restore the passenger information we're holding on to
- pServerVehicle->RestorePassengerInfo();
- }
-}
-
-//========================================================================================================================================
-// APC FOUR WHEEL PHYSICS VEHICLE SERVER VEHICLE
-//========================================================================================================================================
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAPCFourWheelServerVehicle::NPC_AimPrimaryWeapon( Vector vecTarget )
-{
- CPropAPC *pAPC = ((CPropAPC*)m_pVehicle);
- pAPC->AimPrimaryWeapon( vecTarget );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAPCFourWheelServerVehicle::NPC_AimSecondaryWeapon( Vector vecTarget )
-{
- // Add some random noise
-// Vector vecOffset = vecTarget + RandomVector( -128, 128 );
-// ((CPropAPC*)m_pVehicle)->AimSecondaryWeaponAt( vecOffset );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAPCFourWheelServerVehicle::Weapon_PrimaryRanges( float *flMinRange, float *flMaxRange )
-{
- *flMinRange = MACHINE_GUN_ATTACK_RANGE_MIN;
- *flMaxRange = MACHINE_GUN_ATTACK_RANGE_MAX;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAPCFourWheelServerVehicle::Weapon_SecondaryRanges( float *flMinRange, float *flMaxRange )
-{
- *flMinRange = ROCKET_ATTACK_RANGE_MIN;
- *flMaxRange = ROCKET_ATTACK_RANGE_MAX;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return the time at which this vehicle's primary weapon can fire again
-//-----------------------------------------------------------------------------
-float CAPCFourWheelServerVehicle::Weapon_PrimaryCanFireAt( void )
-{
- return ((CPropAPC*)m_pVehicle)->PrimaryWeaponFireTime();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return the time at which this vehicle's secondary weapon can fire again
-//-----------------------------------------------------------------------------
-float CAPCFourWheelServerVehicle::Weapon_SecondaryCanFireAt( void )
-{
- return ((CPropAPC*)m_pVehicle)->SecondaryWeaponFireTime();
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "vehicle_apc.h"
+#include "ammodef.h"
+#include "IEffects.h"
+#include "engine/IEngineSound.h"
+#include "weapon_rpg.h"
+#include "in_buttons.h"
+#include "globalstate.h"
+#include "soundent.h"
+#include "ai_basenpc.h"
+#include "ndebugoverlay.h"
+#include "gib.h"
+#include "EntityFlame.h"
+#include "smoke_trail.h"
+#include "explode.h"
+#include "effect_dispatch_data.h"
+#include "te_effect_dispatch.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#define ROCKET_ATTACK_RANGE_MAX 5500.0f
+#define ROCKET_ATTACK_RANGE_MIN 1250.0f
+
+#define MACHINE_GUN_ATTACK_RANGE_MAX 1250.0f
+#define MACHINE_GUN_ATTACK_RANGE_MIN 0.0f
+
+#define MACHINE_GUN_MAX_UP_PITCH 30
+#define MACHINE_GUN_MAX_DOWN_PITCH 10
+#define MACHINE_GUN_MAX_LEFT_YAW 30
+#define MACHINE_GUN_MAX_RIGHT_YAW 30
+
+#define MACHINE_GUN_BURST_SIZE 10
+#define MACHINE_GUN_BURST_TIME 0.075f
+#define MACHINE_GUN_BURST_PAUSE_TIME 2.0f
+
+#define ROCKET_SALVO_SIZE 5
+#define ROCKET_DELAY_TIME 1.5
+#define ROCKET_MIN_BURST_PAUSE_TIME 3
+#define ROCKET_MAX_BURST_PAUSE_TIME 4
+#define ROCKET_SPEED 800
+#define DEATH_VOLLEY_ROCKET_COUNT 4
+#define DEATH_VOLLEY_MIN_FIRE_TIME 0.333
+#define DEATH_VOLLEY_MAX_FIRE_TIME 0.166
+
+extern short g_sModelIndexFireball; // Echh...
+
+
+ConVar sk_apc_health( "sk_apc_health", "750" );
+
+
+#define APC_MAX_CHUNKS 3
+static const char *s_pChunkModelName[APC_MAX_CHUNKS] =
+{
+ "models/gibs/helicopter_brokenpiece_01.mdl",
+ "models/gibs/helicopter_brokenpiece_02.mdl",
+ "models/gibs/helicopter_brokenpiece_03.mdl",
+};
+
+#define APC_MAX_GIBS 6
+static const char *s_pGibModelName[APC_MAX_GIBS] =
+{
+ "models/combine_apc_destroyed_gib01.mdl",
+ "models/combine_apc_destroyed_gib02.mdl",
+ "models/combine_apc_destroyed_gib03.mdl",
+ "models/combine_apc_destroyed_gib04.mdl",
+ "models/combine_apc_destroyed_gib05.mdl",
+ "models/combine_apc_destroyed_gib06.mdl",
+};
+
+
+LINK_ENTITY_TO_CLASS( prop_vehicle_apc, CPropAPC );
+
+
+BEGIN_DATADESC( CPropAPC )
+
+ DEFINE_FIELD( m_flDangerSoundTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flHandbrakeTime, FIELD_TIME ),
+ DEFINE_FIELD( m_bInitialHandbrake, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_nSmokeTrailCount, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flMachineGunTime, FIELD_TIME ),
+ DEFINE_FIELD( m_iMachineGunBurstLeft, FIELD_INTEGER ),
+// DEFINE_FIELD( m_nMachineGunMuzzleAttachment, FIELD_INTEGER ),
+// DEFINE_FIELD( m_nMachineGunBaseAttachment, FIELD_INTEGER ),
+// DEFINE_FIELD( m_vecBarrelPos, FIELD_VECTOR ),
+ DEFINE_FIELD( m_bInFiringCone, FIELD_BOOLEAN ),
+// DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hRocketTarget, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_iRocketSalvoLeft, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flRocketTime, FIELD_TIME ),
+// DEFINE_FIELD( m_nRocketAttachment, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nRocketSide, FIELD_INTEGER ),
+ DEFINE_FIELD( m_hSpecificRocketTarget, FIELD_EHANDLE ),
+ DEFINE_KEYFIELD( m_strMissileHint, FIELD_STRING, "missilehint" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Destroy", InputDestroy ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "FireMissileAt", InputFireMissileAt ),
+
+ DEFINE_OUTPUT( m_OnDeath, "OnDeath" ),
+ DEFINE_OUTPUT( m_OnFiredMissile, "OnFiredMissile" ),
+ DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ),
+ DEFINE_OUTPUT( m_OnDamagedByPlayer, "OnDamagedByPlayer" ),
+
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::Precache( void )
+{
+ BaseClass::Precache();
+
+ int i;
+ for ( i = 0; i < APC_MAX_CHUNKS; ++i )
+ {
+ PrecacheModel( s_pChunkModelName[i] );
+ }
+
+ for ( i = 0; i < APC_MAX_GIBS; ++i )
+ {
+ PrecacheModel( s_pGibModelName[i] );
+ }
+
+ PrecacheScriptSound( "Weapon_AR2.Single" );
+ PrecacheScriptSound( "PropAPC.FireRocket" );
+ PrecacheScriptSound( "combine.door_lock" );
+}
+
+
+//------------------------------------------------
+// Spawn
+//------------------------------------------------
+void CPropAPC::Spawn( void )
+{
+ BaseClass::Spawn();
+ SetBlocksLOS( true );
+ m_iHealth = m_iMaxHealth = sk_apc_health.GetFloat();
+ SetCycle( 0 );
+ m_iMachineGunBurstLeft = MACHINE_GUN_BURST_SIZE;
+ m_iRocketSalvoLeft = ROCKET_SALVO_SIZE;
+ m_nRocketSide = 0;
+ m_lifeState = LIFE_ALIVE;
+ m_bInFiringCone = false;
+
+ m_flHandbrakeTime = gpGlobals->curtime + 0.1;
+ m_bInitialHandbrake = false;
+
+ // Reset the gun to a default pose.
+ SetPoseParameter( "vehicle_weapon_pitch", 0 );
+ SetPoseParameter( "vehicle_weapon_yaw", 90 );
+
+ CreateAPCLaserDot();
+
+ if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
+ {
+ AddFlag( FL_AIMTARGET );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a laser
+//-----------------------------------------------------------------------------
+void CPropAPC::CreateAPCLaserDot( void )
+{
+ // Create a laser if we don't have one
+ if ( m_hLaserDot == NULL )
+ {
+ m_hLaserDot = CreateLaserDot( GetAbsOrigin(), this, false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CPropAPC::ShouldAttractAutoAim( CBaseEntity *pAimingEnt )
+{
+ if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE && pAimingEnt->IsPlayer() && GetDriver() )
+ {
+ return true;
+ }
+
+ return BaseClass::ShouldAttractAutoAim( pAimingEnt );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::Activate()
+{
+ BaseClass::Activate();
+
+ m_nRocketAttachment = LookupAttachment( "cannon_muzzle" );
+ m_nMachineGunMuzzleAttachment = LookupAttachment( "muzzle" );
+ m_nMachineGunBaseAttachment = LookupAttachment( "gun_base" );
+
+ // NOTE: gun_ref must have the same position as gun_base, but rotates with the gun
+ int nMachineGunRefAttachment = LookupAttachment( "gun_def" );
+
+ Vector vecWorldBarrelPos;
+ matrix3x4_t matRefToWorld;
+ GetAttachment( m_nMachineGunMuzzleAttachment, vecWorldBarrelPos );
+ GetAttachment( nMachineGunRefAttachment, matRefToWorld );
+ VectorITransform( vecWorldBarrelPos, matRefToWorld, m_vecBarrelPos );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::UpdateOnRemove( void )
+{
+ if ( m_hLaserDot )
+ {
+ UTIL_Remove( m_hLaserDot );
+ m_hLaserDot = NULL;
+ }
+ BaseClass::UpdateOnRemove();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::CreateServerVehicle( void )
+{
+ // Create our armed server vehicle
+ m_pServerVehicle = new CAPCFourWheelServerVehicle();
+ m_pServerVehicle->SetVehicle( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pMoveData -
+//-----------------------------------------------------------------------------
+Class_T CPropAPC::ClassifyPassenger( CBaseCombatCharacter *pPassenger, Class_T defaultClassification )
+{
+ return CLASS_COMBINE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Damage events as modified for the passenger of the APC, not the APC itself
+//-----------------------------------------------------------------------------
+float CPropAPC::PassengerDamageModifier( const CTakeDamageInfo &info )
+{
+ CTakeDamageInfo DmgInfo = info;
+
+ // bullets, slashing and headbutts don't hurt us in the apc, neither do rockets
+ if( (DmgInfo.GetDamageType() & DMG_BULLET) || (DmgInfo.GetDamageType() & DMG_SLASH) ||
+ (DmgInfo.GetDamageType() & DMG_CLUB) || (DmgInfo.GetDamageType() & DMG_BLAST) )
+ return (0);
+
+ // Accept everything else by default
+ return 1.0;
+}
+
+
+//-----------------------------------------------------------------------------
+// position of eyes
+//-----------------------------------------------------------------------------
+Vector CPropAPC::EyePosition( )
+{
+ Vector vecEyePosition;
+ CollisionProp()->NormalizedToWorldSpace( Vector( 0.5, 0.5, 1.0 ), &vecEyePosition );
+ return vecEyePosition;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+Vector CPropAPC::BodyTarget( const Vector &posSrc, bool bNoisy )
+{
+ if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
+ {
+ return WorldSpaceCenter();
+ }
+
+ return BaseClass::BodyTarget( posSrc, bNoisy );
+}
+
+//-----------------------------------------------------------------------------
+// Add a smoke trail since we've taken more damage
+//-----------------------------------------------------------------------------
+void CPropAPC::AddSmokeTrail( const Vector &vecPos )
+{
+ // Start this trail out with a bang!
+ ExplosionCreate( vecPos, vec3_angle, this, 1000, 500.0f, SF_ENVEXPLOSION_NODAMAGE |
+ SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE |
+ SF_ENVEXPLOSION_NOFIREBALLSMOKE, 0 );
+ UTIL_ScreenShake( vecPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
+
+ if ( m_nSmokeTrailCount == MAX_SMOKE_TRAILS )
+ return;
+
+ SmokeTrail *pSmokeTrail = SmokeTrail::CreateSmokeTrail();
+ if( !pSmokeTrail )
+ return;
+
+ // See if there's an attachment for this smoke trail
+ char buf[32];
+ Q_snprintf( buf, 32, "damage%d", m_nSmokeTrailCount );
+ int nAttachment = LookupAttachment( buf );
+
+ ++m_nSmokeTrailCount;
+
+ pSmokeTrail->m_SpawnRate = 4;
+ pSmokeTrail->m_ParticleLifetime = 5.0f;
+ pSmokeTrail->m_StartColor.Init( 0.7f, 0.7f, 0.7f );
+ pSmokeTrail->m_EndColor.Init( 0.6, 0.6, 0.6 );
+ pSmokeTrail->m_StartSize = 32;
+ pSmokeTrail->m_EndSize = 64;
+ pSmokeTrail->m_SpawnRadius = 4;
+ pSmokeTrail->m_Opacity = 0.5f;
+ pSmokeTrail->m_MinSpeed = 16;
+ pSmokeTrail->m_MaxSpeed = 16;
+ pSmokeTrail->m_MinDirectedSpeed = 16.0f;
+ pSmokeTrail->m_MaxDirectedSpeed = 16.0f;
+ pSmokeTrail->SetLifetime( 5 );
+ pSmokeTrail->SetParent( this, nAttachment );
+
+ Vector vecForward( 0, 0, 1 );
+ QAngle angles;
+ VectorAngles( vecForward, angles );
+
+ if ( nAttachment == 0 )
+ {
+ pSmokeTrail->SetAbsOrigin( vecPos );
+ pSmokeTrail->SetAbsAngles( angles );
+ }
+ else
+ {
+ pSmokeTrail->SetLocalOrigin( vec3_origin );
+ pSmokeTrail->SetLocalAngles( angles );
+ }
+
+ pSmokeTrail->SetMoveType( MOVETYPE_NONE );
+}
+
+
+//------------------------------------------------------------------------------
+// Pow!
+//------------------------------------------------------------------------------
+void CPropAPC::ExplodeAndThrowChunk( const Vector &vecExplosionPos )
+{
+ ExplosionCreate( vecExplosionPos, vec3_angle, this, 1000, 500.0f,
+ SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS |
+ SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALLSMOKE, 0 );
+ UTIL_ScreenShake( vecExplosionPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
+
+ // Drop a flaming, smoking chunk.
+ CGib *pChunk = CREATE_ENTITY( CGib, "gib" );
+ pChunk->Spawn( "models/gibs/hgibs.mdl" );
+ pChunk->SetBloodColor( DONT_BLEED );
+
+ QAngle vecSpawnAngles;
+ vecSpawnAngles.Random( -90, 90 );
+ pChunk->SetAbsOrigin( vecExplosionPos );
+ pChunk->SetAbsAngles( vecSpawnAngles );
+
+ int nGib = random->RandomInt( 0, APC_MAX_CHUNKS - 1 );
+ pChunk->Spawn( s_pChunkModelName[nGib] );
+ pChunk->SetOwnerEntity( this );
+ pChunk->m_lifeTime = random->RandomFloat( 6.0f, 8.0f );
+ pChunk->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+ IPhysicsObject *pPhysicsObject = pChunk->VPhysicsInitNormal( SOLID_VPHYSICS, pChunk->GetSolidFlags(), false );
+
+ // Set the velocity
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->EnableMotion( true );
+ Vector vecVelocity;
+
+ QAngle angles;
+ angles.x = random->RandomFloat( -40, 0 );
+ angles.y = random->RandomFloat( 0, 360 );
+ angles.z = 0.0f;
+ AngleVectors( angles, &vecVelocity );
+
+ vecVelocity *= random->RandomFloat( 300, 900 );
+ vecVelocity += GetAbsVelocity();
+
+ AngularImpulse angImpulse;
+ angImpulse = RandomAngularImpulse( -180, 180 );
+
+ pChunk->SetAbsVelocity( vecVelocity );
+ pPhysicsObject->SetVelocity(&vecVelocity, &angImpulse );
+ }
+
+ CEntityFlame *pFlame = CEntityFlame::Create( pChunk, false );
+ if ( pFlame != NULL )
+ {
+ pFlame->SetLifetime( pChunk->m_lifeTime );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Should we trigger a damage effect?
+//-----------------------------------------------------------------------------
+inline bool CPropAPC::ShouldTriggerDamageEffect( int nPrevHealth, int nEffectCount ) const
+{
+ int nPrevRange = (int)( ((float)nPrevHealth / (float)GetMaxHealth()) * nEffectCount );
+ int nRange = (int)( ((float)GetHealth() / (float)GetMaxHealth()) * nEffectCount );
+ return ( nRange != nPrevRange );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::Event_Killed( const CTakeDamageInfo &info )
+{
+ m_OnDeath.FireOutput( info.GetAttacker(), this );
+
+ Vector vecAbsMins, vecAbsMaxs;
+ CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
+
+ Vector vecNormalizedMins, vecNormalizedMaxs;
+ CollisionProp()->WorldToNormalizedSpace( vecAbsMins, &vecNormalizedMins );
+ CollisionProp()->WorldToNormalizedSpace( vecAbsMaxs, &vecNormalizedMaxs );
+
+ Vector vecAbsPoint;
+ CPASFilter filter( GetAbsOrigin() );
+ for (int i = 0; i < 5; i++)
+ {
+ CollisionProp()->RandomPointInBounds( vecNormalizedMins, vecNormalizedMaxs, &vecAbsPoint );
+ te->Explosion( filter, random->RandomFloat( 0.0, 1.0 ), &vecAbsPoint,
+ g_sModelIndexFireball, random->RandomInt( 4, 10 ),
+ random->RandomInt( 8, 15 ),
+ ( i < 2 ) ? TE_EXPLFLAG_NODLIGHTS : TE_EXPLFLAG_NOPARTICLES | TE_EXPLFLAG_NOFIREBALLSMOKE | TE_EXPLFLAG_NODLIGHTS,
+ 100, 0 );
+ }
+
+ // TODO: make the gibs spawn in sync with the delayed explosions
+ int nGibs = random->RandomInt( 1, 4 );
+ for ( int i = 0; i < nGibs; i++)
+ {
+ // Throw a flaming, smoking chunk.
+ CGib *pChunk = CREATE_ENTITY( CGib, "gib" );
+ pChunk->Spawn( "models/gibs/hgibs.mdl" );
+ pChunk->SetBloodColor( DONT_BLEED );
+
+ QAngle vecSpawnAngles;
+ vecSpawnAngles.Random( -90, 90 );
+ pChunk->SetAbsOrigin( vecAbsPoint );
+ pChunk->SetAbsAngles( vecSpawnAngles );
+
+ int nGib = random->RandomInt( 0, APC_MAX_CHUNKS - 1 );
+ pChunk->Spawn( s_pChunkModelName[nGib] );
+ pChunk->SetOwnerEntity( this );
+ pChunk->m_lifeTime = random->RandomFloat( 6.0f, 8.0f );
+ pChunk->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+ IPhysicsObject *pPhysicsObject = pChunk->VPhysicsInitNormal( SOLID_VPHYSICS, pChunk->GetSolidFlags(), false );
+
+ // Set the velocity
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->EnableMotion( true );
+ Vector vecVelocity;
+
+ QAngle angles;
+ angles.x = random->RandomFloat( -20, 20 );
+ angles.y = random->RandomFloat( 0, 360 );
+ angles.z = 0.0f;
+ AngleVectors( angles, &vecVelocity );
+
+ vecVelocity *= random->RandomFloat( 300, 900 );
+ vecVelocity += GetAbsVelocity();
+
+ AngularImpulse angImpulse;
+ angImpulse = RandomAngularImpulse( -180, 180 );
+
+ pChunk->SetAbsVelocity( vecVelocity );
+ pPhysicsObject->SetVelocity(&vecVelocity, &angImpulse );
+ }
+
+ CEntityFlame *pFlame = CEntityFlame::Create( pChunk, false );
+ if ( pFlame != NULL )
+ {
+ pFlame->SetLifetime( pChunk->m_lifeTime );
+ }
+ }
+
+ UTIL_ScreenShake( vecAbsPoint, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
+
+ if( hl2_episodic.GetBool() )
+ {
+ // EP1 perf hit
+ Ignite( 6, false );
+ }
+ else
+ {
+ Ignite( 60, false );
+ }
+
+ m_lifeState = LIFE_DYING;
+
+ // Spawn a lesser amount if the player is close
+ m_iRocketSalvoLeft = DEATH_VOLLEY_ROCKET_COUNT;
+ m_flRocketTime = gpGlobals->curtime;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Blows it up!
+//-----------------------------------------------------------------------------
+void CPropAPC::InputDestroy( inputdata_t &inputdata )
+{
+ CTakeDamageInfo info( this, this, m_iHealth, DMG_BLAST );
+ info.SetDamagePosition( WorldSpaceCenter() );
+ info.SetDamageForce( Vector( 0, 0, 1 ) );
+ TakeDamage( info );
+}
+
+
+//-----------------------------------------------------------------------------
+// Aim the next rocket at a specific target
+//-----------------------------------------------------------------------------
+void CPropAPC::InputFireMissileAt( inputdata_t &inputdata )
+{
+ string_t strMissileTarget = MAKE_STRING( inputdata.value.String() );
+ CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, strMissileTarget, NULL, inputdata.pActivator, inputdata.pCaller );
+ if ( pTarget == NULL )
+ {
+ DevWarning( "%s: Could not find target '%s'!\n", GetClassname(), STRING( strMissileTarget ) );
+ return;
+ }
+
+ m_hSpecificRocketTarget = pTarget;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CPropAPC::OnTakeDamage( const CTakeDamageInfo &info )
+{
+ if ( m_iHealth == 0 )
+ return 0;
+
+ m_OnDamaged.FireOutput( info.GetAttacker(), this );
+
+ if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() )
+ {
+ m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this );
+ }
+
+ CTakeDamageInfo dmgInfo = info;
+ if ( dmgInfo.GetDamageType() & (DMG_BLAST | DMG_AIRBOAT) )
+ {
+ int nPrevHealth = GetHealth();
+
+ m_iHealth -= dmgInfo.GetDamage();
+ if ( m_iHealth <= 0 )
+ {
+ m_iHealth = 0;
+ Event_Killed( dmgInfo );
+ return 0;
+ }
+
+ // Chain
+// BaseClass::OnTakeDamage( dmgInfo );
+
+ // Spawn damage effects
+ if ( nPrevHealth != GetHealth() )
+ {
+ if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_SMOKE_TRAILS ) )
+ {
+ AddSmokeTrail( dmgInfo.GetDamagePosition() );
+ }
+
+ if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) )
+ {
+ ExplodeAndThrowChunk( dmgInfo.GetDamagePosition() );
+ }
+ }
+ }
+ return 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pMoveData -
+//-----------------------------------------------------------------------------
+void CPropAPC::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
+{
+ BaseClass::ProcessMovement( pPlayer, pMoveData );
+
+ if ( m_flDangerSoundTime > gpGlobals->curtime )
+ return;
+
+ QAngle vehicleAngles = GetLocalAngles();
+ Vector vecStart = GetAbsOrigin();
+ Vector vecDir;
+
+ GetVectors( &vecDir, NULL, NULL );
+
+ // Make danger sounds ahead of the APC
+ trace_t tr;
+ Vector vecSpot, vecLeftDir, vecRightDir;
+
+ // lay down sound path
+ vecSpot = vecStart + vecDir * 600;
+ CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this );
+
+ // put sounds a bit to left and right but slightly closer to APC to make a "cone" of sound
+ // in front of it
+ QAngle leftAngles = vehicleAngles;
+ leftAngles[YAW] += 20;
+ VehicleAngleVectors( leftAngles, &vecLeftDir, NULL, NULL );
+ vecSpot = vecStart + vecLeftDir * 400;
+ UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+ CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this );
+
+ QAngle rightAngles = vehicleAngles;
+ rightAngles[YAW] -= 20;
+ VehicleAngleVectors( rightAngles, &vecRightDir, NULL, NULL );
+ vecSpot = vecStart + vecRightDir * 400;
+ UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+ CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this);
+
+ m_flDangerSoundTime = gpGlobals->curtime + 0.3;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::Think( void )
+{
+ BaseClass::Think();
+
+ SetNextThink( gpGlobals->curtime );
+
+ if ( !m_bInitialHandbrake ) // after initial timer expires, set the handbrake
+ {
+ m_bInitialHandbrake = true;
+ m_VehiclePhysics.SetHandbrake( true );
+ m_VehiclePhysics.Think();
+ }
+
+ StudioFrameAdvance();
+
+ if ( IsSequenceFinished() )
+ {
+ int iSequence = SelectWeightedSequence( ACT_IDLE );
+ if ( iSequence > ACTIVITY_NOT_AVAILABLE )
+ {
+ SetCycle( 0 );
+ m_flAnimTime = gpGlobals->curtime;
+ ResetSequence( iSequence );
+ ResetClientsideFrame();
+ }
+ }
+
+ if (m_debugOverlays & OVERLAY_NPC_KILL_BIT)
+ {
+ CTakeDamageInfo info( this, this, m_iHealth, DMG_BLAST );
+ info.SetDamagePosition( WorldSpaceCenter() );
+ info.SetDamageForce( Vector( 0, 0, 1 ) );
+ TakeDamage( info );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Aims the secondary weapon at a target
+//-----------------------------------------------------------------------------
+void CPropAPC::AimSecondaryWeaponAt( CBaseEntity *pTarget )
+{
+ m_hRocketTarget = pTarget;
+
+ // Update the rocket target
+ CreateAPCLaserDot();
+
+ if ( m_hRocketTarget )
+ {
+ m_hLaserDot->SetAbsOrigin( m_hRocketTarget->BodyTarget( WorldSpaceCenter(), false ) );
+ }
+ SetLaserDotTarget( m_hLaserDot, m_hRocketTarget );
+ EnableLaserDot( m_hLaserDot, m_hRocketTarget != NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased )
+{
+ switch( m_lifeState )
+ {
+ case LIFE_ALIVE:
+ {
+ int iButtons = ucmd->buttons;
+ if ( iButtons & IN_ATTACK )
+ {
+ FireMachineGun();
+ }
+ else if ( iButtons & IN_ATTACK2 )
+ {
+ FireRocket();
+ }
+ }
+ break;
+
+ case LIFE_DYING:
+ FireDying( );
+ break;
+
+ case LIFE_DEAD:
+ return;
+ }
+
+ BaseClass::DriveVehicle( flFrameTime, ucmd, iButtonsDown, iButtonsReleased );
+}
+
+void CPropAPC::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ BaseClass::Use( pActivator, pCaller, useType, value );
+
+ if ( pActivator->IsPlayer() )
+ {
+ EmitSound ( "combine.door_lock" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Primary gun
+//-----------------------------------------------------------------------------
+void CPropAPC::AimPrimaryWeapon( const Vector &vecWorldTarget )
+{
+ EntityMatrix parentMatrix;
+ parentMatrix.InitFromEntity( this, m_nMachineGunBaseAttachment );
+ Vector target = parentMatrix.WorldToLocal( vecWorldTarget );
+
+ float quadTarget = target.LengthSqr();
+ float quadTargetXY = target.x*target.x + target.y*target.y;
+
+ // Target is too close! Can't aim at it
+ if ( quadTarget > m_vecBarrelPos.LengthSqr() )
+ {
+ // We're trying to aim the offset barrel at an arbitrary point.
+ // To calculate this, I think of the target as being on a sphere with
+ // it's center at the origin of the gun.
+ // The rotation we need is the opposite of the rotation that moves the target
+ // along the surface of that sphere to intersect with the gun's shooting direction
+ // To calculate that rotation, we simply calculate the intersection of the ray
+ // coming out of the barrel with the target sphere (that's the new target position)
+ // and use atan2() to get angles
+
+ // angles from target pos to center
+ float targetToCenterYaw = atan2( target.y, target.x );
+ float centerToGunYaw = atan2( m_vecBarrelPos.y, sqrt( quadTarget - (m_vecBarrelPos.y*m_vecBarrelPos.y) ) );
+
+ float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) );
+ float centerToGunPitch = atan2( -m_vecBarrelPos.z, sqrt( quadTarget - (m_vecBarrelPos.z*m_vecBarrelPos.z) ) );
+
+ QAngle angles;
+ angles.Init( -RAD2DEG(targetToCenterPitch+centerToGunPitch), RAD2DEG( targetToCenterYaw + centerToGunYaw ), 0 );
+
+ SetPoseParameter( "vehicle_weapon_yaw", angles.y );
+ SetPoseParameter( "vehicle_weapon_pitch", angles.x );
+ StudioFrameAdvance();
+
+ float curPitch = GetPoseParameter( "vehicle_weapon_pitch" );
+ float curYaw = GetPoseParameter( "vehicle_weapon_yaw" );
+ m_bInFiringCone = (fabs(curPitch - angles.x) < 1e-3) && (fabs(curYaw - angles.y) < 1e-3);
+ }
+ else
+ {
+ m_bInFiringCone = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CPropAPC::GetTracerType( void )
+{
+ return "HelicopterTracer";
+}
+
+
+//-----------------------------------------------------------------------------
+// Allows the shooter to change the impact effect of his bullets
+//-----------------------------------------------------------------------------
+void CPropAPC::DoImpactEffect( trace_t &tr, int nDamageType )
+{
+ UTIL_ImpactTrace( &tr, nDamageType, "HelicopterImpact" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::DoMuzzleFlash( void )
+{
+ CEffectData data;
+ data.m_nEntIndex = entindex();
+ data.m_nAttachmentIndex = m_nMachineGunMuzzleAttachment;
+ data.m_flScale = 1.0f;
+ DispatchEffect( "ChopperMuzzleFlash", data );
+
+ BaseClass::DoMuzzleFlash();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::FireMachineGun( void )
+{
+ if ( m_flMachineGunTime > gpGlobals->curtime )
+ return;
+
+ // If we're still firing the salvo, fire quickly
+ m_iMachineGunBurstLeft--;
+ if ( m_iMachineGunBurstLeft > 0 )
+ {
+ m_flMachineGunTime = gpGlobals->curtime + MACHINE_GUN_BURST_TIME;
+ }
+ else
+ {
+ // Reload the salvo
+ m_iMachineGunBurstLeft = MACHINE_GUN_BURST_SIZE;
+ m_flMachineGunTime = gpGlobals->curtime + MACHINE_GUN_BURST_PAUSE_TIME;
+ }
+
+ Vector vecMachineGunShootPos;
+ Vector vecMachineGunDir;
+ GetAttachment( m_nMachineGunMuzzleAttachment, vecMachineGunShootPos, &vecMachineGunDir );
+
+ // Fire the round
+ int bulletType = GetAmmoDef()->Index("AR2");
+ FireBullets( 1, vecMachineGunShootPos, vecMachineGunDir, VECTOR_CONE_8DEGREES, MAX_TRACE_LENGTH, bulletType, 1 );
+ DoMuzzleFlash();
+
+ EmitSound( "Weapon_AR2.Single" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::GetRocketShootPosition( Vector *pPosition )
+{
+ GetAttachment( m_nRocketAttachment, *pPosition );
+}
+
+
+//-----------------------------------------------------------------------------
+// Create a corpse
+//-----------------------------------------------------------------------------
+void CPropAPC::CreateCorpse( )
+{
+ m_lifeState = LIFE_DEAD;
+
+ for ( int i = 0; i < APC_MAX_GIBS; ++i )
+ {
+ CPhysicsProp *pGib = assert_cast<CPhysicsProp*>(CreateEntityByName( "prop_physics" ));
+ pGib->SetAbsOrigin( GetAbsOrigin() );
+ pGib->SetAbsAngles( GetAbsAngles() );
+ pGib->SetAbsVelocity( GetAbsVelocity() );
+ pGib->SetModel( s_pGibModelName[i] );
+ pGib->Spawn();
+ pGib->SetMoveType( MOVETYPE_VPHYSICS );
+
+ float flMass = pGib->GetMass();
+ if ( flMass < 200 )
+ {
+ Vector vecVelocity;
+ pGib->GetMassCenter( &vecVelocity );
+ vecVelocity -= WorldSpaceCenter();
+ vecVelocity.z = fabs(vecVelocity.z);
+ VectorNormalize( vecVelocity );
+
+ // Apply a force that would make a 100kg mass travel 150 - 300 m/s
+ float flRandomVel = random->RandomFloat( 150, 300 );
+ vecVelocity *= (100 * flRandomVel) / flMass;
+ vecVelocity.z += 100.0f;
+ AngularImpulse angImpulse = RandomAngularImpulse( -500, 500 );
+
+ IPhysicsObject *pObj = pGib->VPhysicsGetObject();
+ if ( pObj != NULL )
+ {
+ pObj->AddVelocity( &vecVelocity, &angImpulse );
+ }
+ pGib->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+ }
+ if( hl2_episodic.GetBool() )
+ {
+ // EP1 perf hit
+ pGib->Ignite( 6, false );
+ }
+ else
+ {
+ pGib->Ignite( 60, false );
+ }
+ }
+
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ AddEffects( EF_NODRAW );
+ UTIL_Remove( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Death volley
+//-----------------------------------------------------------------------------
+void CPropAPC::FireDying( )
+{
+ if ( m_flRocketTime > gpGlobals->curtime )
+ return;
+
+ Vector vecRocketOrigin;
+ GetRocketShootPosition( &vecRocketOrigin );
+
+ Vector vecDir;
+ vecDir.Random( -1.0f, 1.0f );
+ if ( vecDir.z < 0.0f )
+ {
+ vecDir.z *= -1.0f;
+ }
+
+ VectorNormalize( vecDir );
+
+ Vector vecVelocity;
+ VectorMultiply( vecDir, ROCKET_SPEED * random->RandomFloat( 0.75f, 1.25f ), vecVelocity );
+
+ QAngle angles;
+ VectorAngles( vecDir, angles );
+
+ CAPCMissile *pRocket = (CAPCMissile *) CAPCMissile::Create( vecRocketOrigin, angles, vecVelocity, this );
+ float flDeathTime = random->RandomFloat( 0.3f, 0.5f );
+ if ( random->RandomFloat( 0.0f, 1.0f ) < 0.3f )
+ {
+ pRocket->ExplodeDelay( flDeathTime );
+ }
+ else
+ {
+ pRocket->AugerDelay( flDeathTime );
+ }
+
+ // Make erratic firing
+ m_flRocketTime = gpGlobals->curtime + random->RandomFloat( DEATH_VOLLEY_MIN_FIRE_TIME, DEATH_VOLLEY_MAX_FIRE_TIME );
+ if ( --m_iRocketSalvoLeft <= 0 )
+ {
+ CreateCorpse();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::FireRocket( void )
+{
+ if ( m_flRocketTime > gpGlobals->curtime )
+ return;
+
+ // If we're still firing the salvo, fire quickly
+ m_iRocketSalvoLeft--;
+ if ( m_iRocketSalvoLeft > 0 )
+ {
+ m_flRocketTime = gpGlobals->curtime + ROCKET_DELAY_TIME;
+ }
+ else
+ {
+ // Reload the salvo
+ m_iRocketSalvoLeft = ROCKET_SALVO_SIZE;
+ m_flRocketTime = gpGlobals->curtime + random->RandomFloat( ROCKET_MIN_BURST_PAUSE_TIME, ROCKET_MAX_BURST_PAUSE_TIME );
+ }
+
+ Vector vecRocketOrigin;
+ GetRocketShootPosition( &vecRocketOrigin );
+
+ static float s_pSide[] = { 0.966, 0.866, 0.5, -0.5, -0.866, -0.966 };
+
+ Vector forward;
+ GetVectors( &forward, NULL, NULL );
+
+ Vector vecDir;
+ CrossProduct( Vector( 0, 0, 1 ), forward, vecDir );
+ vecDir.z = 1.0f;
+ vecDir.x *= s_pSide[m_nRocketSide];
+ vecDir.y *= s_pSide[m_nRocketSide];
+ if ( ++m_nRocketSide >= 6 )
+ {
+ m_nRocketSide = 0;
+ }
+
+ VectorNormalize( vecDir );
+
+ Vector vecVelocity;
+ VectorMultiply( vecDir, ROCKET_SPEED, vecVelocity );
+
+ QAngle angles;
+ VectorAngles( vecDir, angles );
+
+ CAPCMissile *pRocket = (CAPCMissile *)CAPCMissile::Create( vecRocketOrigin, angles, vecVelocity, this );
+ pRocket->IgniteDelay();
+
+ if ( m_hSpecificRocketTarget )
+ {
+ pRocket->AimAtSpecificTarget( m_hSpecificRocketTarget );
+ m_hSpecificRocketTarget = NULL;
+ }
+ else if ( m_strMissileHint != NULL_STRING )
+ {
+ pRocket->SetGuidanceHint( STRING( m_strMissileHint ) );
+ }
+
+ EmitSound( "PropAPC.FireRocket" );
+ m_OnFiredMissile.FireOutput( this, this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CPropAPC::MaxAttackRange() const
+{
+ return ROCKET_ATTACK_RANGE_MAX;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropAPC::OnRestore( void )
+{
+ IServerVehicle *pServerVehicle = GetServerVehicle();
+ if ( pServerVehicle != NULL )
+ {
+ // Restore the passenger information we're holding on to
+ pServerVehicle->RestorePassengerInfo();
+ }
+}
+
+//========================================================================================================================================
+// APC FOUR WHEEL PHYSICS VEHICLE SERVER VEHICLE
+//========================================================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAPCFourWheelServerVehicle::NPC_AimPrimaryWeapon( Vector vecTarget )
+{
+ CPropAPC *pAPC = ((CPropAPC*)m_pVehicle);
+ pAPC->AimPrimaryWeapon( vecTarget );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAPCFourWheelServerVehicle::NPC_AimSecondaryWeapon( Vector vecTarget )
+{
+ // Add some random noise
+// Vector vecOffset = vecTarget + RandomVector( -128, 128 );
+// ((CPropAPC*)m_pVehicle)->AimSecondaryWeaponAt( vecOffset );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAPCFourWheelServerVehicle::Weapon_PrimaryRanges( float *flMinRange, float *flMaxRange )
+{
+ *flMinRange = MACHINE_GUN_ATTACK_RANGE_MIN;
+ *flMaxRange = MACHINE_GUN_ATTACK_RANGE_MAX;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAPCFourWheelServerVehicle::Weapon_SecondaryRanges( float *flMinRange, float *flMaxRange )
+{
+ *flMinRange = ROCKET_ATTACK_RANGE_MIN;
+ *flMaxRange = ROCKET_ATTACK_RANGE_MAX;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the time at which this vehicle's primary weapon can fire again
+//-----------------------------------------------------------------------------
+float CAPCFourWheelServerVehicle::Weapon_PrimaryCanFireAt( void )
+{
+ return ((CPropAPC*)m_pVehicle)->PrimaryWeaponFireTime();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the time at which this vehicle's secondary weapon can fire again
+//-----------------------------------------------------------------------------
+float CAPCFourWheelServerVehicle::Weapon_SecondaryCanFireAt( void )
+{
+ return ((CPropAPC*)m_pVehicle)->SecondaryWeaponFireTime();
+}
+