You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
9.9 KiB
435 lines
9.9 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "basetfplayer_shared.h"
|
||
|
#include "in_buttons.h"
|
||
|
#include "weapon_combatshield.h"
|
||
|
#include "weapon_flame_thrower.h"
|
||
|
#include "gasoline_shared.h"
|
||
|
#include "ammodef.h"
|
||
|
|
||
|
|
||
|
#define FLAME_THROWER_FIRE_INTERVAL 0.3 // Eject a fire blob entity this often.
|
||
|
|
||
|
#define FLAMETHROWER_FLAME_DISTANCE 400.0
|
||
|
|
||
|
#define FLAMETHROWER_FLAME_SPEED 500.0 //
|
||
|
|
||
|
#define FLAMETHROWER_DAMAGE_PER_SEC 1000
|
||
|
|
||
|
// How far the flame particles will spread from the center.
|
||
|
#define FLAMETHROWER_SPREAD_ANGLE 15.0
|
||
|
|
||
|
|
||
|
// ------------------------------------------------------------------------------------------------ //
|
||
|
// Pretty little tables.
|
||
|
// ------------------------------------------------------------------------------------------------ //
|
||
|
|
||
|
#if defined( CLIENT_DLL )
|
||
|
|
||
|
#include "vstdlib/random.h"
|
||
|
#include "engine/IEngineSound.h"
|
||
|
|
||
|
#define FLAMETHROWER_PARTICLES_PER_SEC 100
|
||
|
|
||
|
#else
|
||
|
|
||
|
#include "gasoline_blob.h"
|
||
|
#include "fire_damage_mgr.h"
|
||
|
#include "tf_gamerules.h"
|
||
|
|
||
|
#define FLAMETHROWER_DAMAGE_INTERVAL 0.2
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
|
||
|
PRECACHE_WEAPON_REGISTER( weapon_flame_thrower );
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( weapon_flame_thrower, CWeaponFlameThrower );
|
||
|
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFlameThrower, DT_WeaponFlameThrower )
|
||
|
|
||
|
BEGIN_NETWORK_TABLE( CWeaponFlameThrower, DT_WeaponFlameThrower )
|
||
|
#if defined( CLIENT_DLL )
|
||
|
RecvPropInt( RECVINFO( m_bFiring ) )
|
||
|
#else
|
||
|
SendPropInt( SENDINFO( m_bFiring ), 1, SPROP_UNSIGNED )
|
||
|
#endif
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
BEGIN_PREDICTION_DATA( CWeaponFlameThrower )
|
||
|
|
||
|
DEFINE_PRED_FIELD( m_bFiring, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
|
||
|
|
||
|
END_PREDICTION_DATA()
|
||
|
|
||
|
|
||
|
static inline void GenerateRandomFlameThrowerVelocity( Vector &vOut, const Vector &vForward, const Vector &vRight, const Vector &vUp )
|
||
|
{
|
||
|
static float radians = DEG2RAD( 90 - FLAMETHROWER_SPREAD_ANGLE );
|
||
|
static float v = cos( radians ) / sin( radians );
|
||
|
|
||
|
vOut = vForward + vRight * RandomFloat( -v, v ) + vUp * RandomFloat( -v, v );
|
||
|
VectorNormalize( vOut );
|
||
|
}
|
||
|
|
||
|
template< class T >
|
||
|
int FindInArray( const T pTest, const T *pArray, int arrayLen )
|
||
|
{
|
||
|
for ( int i=0; i < arrayLen; i++ )
|
||
|
{
|
||
|
if ( pTest == pArray[i] )
|
||
|
return i;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
// ------------------------------------------------------------------------------------------------ //
|
||
|
// CWeaponFlameThrower implementation.
|
||
|
// ------------------------------------------------------------------------------------------------ //
|
||
|
|
||
|
CWeaponFlameThrower::CWeaponFlameThrower()
|
||
|
{
|
||
|
InternalConstructor( false );
|
||
|
}
|
||
|
|
||
|
|
||
|
CWeaponFlameThrower::CWeaponFlameThrower( bool bCanister )
|
||
|
{
|
||
|
InternalConstructor( bCanister );
|
||
|
}
|
||
|
|
||
|
void CWeaponFlameThrower::Precache()
|
||
|
{
|
||
|
BaseClass::Precache();
|
||
|
|
||
|
PrecacheScriptSound( "FlameThrower.Sound" );
|
||
|
}
|
||
|
|
||
|
void CWeaponFlameThrower::InternalConstructor( bool bCanister )
|
||
|
{
|
||
|
m_bCanister = bCanister;
|
||
|
|
||
|
m_bFiring = false;
|
||
|
m_flNextPrimaryAttack = -1;
|
||
|
|
||
|
#if defined( CLIENT_DLL )
|
||
|
{
|
||
|
m_hFlameEmitter = CSimpleEmitter::Create( "flamethrower" );
|
||
|
|
||
|
m_hFireMaterial = INVALID_MATERIAL_HANDLE;
|
||
|
if ( IsGasCanister() )
|
||
|
{
|
||
|
m_hFireMaterial = m_hFlameEmitter->GetPMaterial( "particle/particle_noisesphere" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_hFireMaterial = m_hFlameEmitter->GetPMaterial( "particle/fire" );
|
||
|
}
|
||
|
|
||
|
m_FlameEvent.Init( FLAMETHROWER_PARTICLES_PER_SEC );
|
||
|
|
||
|
m_bSoundOn = false;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
CWeaponFlameThrower::~CWeaponFlameThrower()
|
||
|
{
|
||
|
#if defined( CLIENT_DLL )
|
||
|
StopFlameSound();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
bool CWeaponFlameThrower::IsGasCanister() const
|
||
|
{
|
||
|
return m_bCanister;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool CWeaponFlameThrower::IsPredicted() const
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWeaponFlameThrower::ItemPostFrame()
|
||
|
{
|
||
|
CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
|
||
|
if ( !pOwner )
|
||
|
return;
|
||
|
|
||
|
if ( pOwner->IsAlive() &&
|
||
|
(pOwner->m_nButtons & IN_ATTACK) &&
|
||
|
GetShieldState() == SS_DOWN &&
|
||
|
GetPrimaryAmmo() > 2 )
|
||
|
{
|
||
|
PrimaryAttack();
|
||
|
m_bFiring = true;
|
||
|
|
||
|
// Prevent shield post frame if we're not ready to attack, or we're healing
|
||
|
AllowShieldPostFrame( false );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AllowShieldPostFrame( true );
|
||
|
m_flNextPrimaryAttack = -1;
|
||
|
m_bFiring = false;
|
||
|
|
||
|
#if defined( CLIENT_DLL )
|
||
|
#else
|
||
|
m_hPrevBlob = NULL;
|
||
|
|
||
|
// It's easy to lay down gasoline and forget to leave enough ammo to ignite it, so
|
||
|
// this allows the pyro to ignite any nearby gasoline blobs for free.
|
||
|
if ( !m_bCanister && GetPrimaryAmmo() <= 2 )
|
||
|
{
|
||
|
IgniteNearbyGasolineBlobs();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWeaponFlameThrower::PrimaryAttack()
|
||
|
{
|
||
|
#if defined( CLIENT_DLL )
|
||
|
|
||
|
#else
|
||
|
|
||
|
CBasePlayer *pOwner = ToBaseTFPlayer( GetOwner() );
|
||
|
if ( !pOwner )
|
||
|
return;
|
||
|
|
||
|
// Ok.. find eligible entities in a cone in front of us.
|
||
|
Vector vOrigin = pOwner->Weapon_ShootPosition( );
|
||
|
Vector vForward, vRight, vUp;
|
||
|
AngleVectors( pOwner->GetAbsAngles(), &vForward, &vRight, &vUp );
|
||
|
|
||
|
// Find some entities to burn.
|
||
|
CBaseEntity *pHitEnts[64];
|
||
|
int nHitEnts = 0;
|
||
|
|
||
|
#define NUM_TEST_VECTORS 30
|
||
|
for ( int iTest=0; iTest < NUM_TEST_VECTORS; iTest++ )
|
||
|
{
|
||
|
Vector vVel;
|
||
|
GenerateRandomFlameThrowerVelocity( vVel, vForward, vRight, vUp );
|
||
|
|
||
|
trace_t tr;
|
||
|
UTIL_TraceLine( vOrigin, vOrigin + vVel * FLAMETHROWER_FLAME_DISTANCE, MASK_SHOT & (~CONTENTS_HITBOX), NULL, COLLISION_GROUP_NONE, &tr );
|
||
|
if ( tr.m_pEnt )
|
||
|
{
|
||
|
if ( TFGameRules()->IsTraceBlockedByWorldOrShield( vOrigin, vOrigin + vVel * FLAMETHROWER_FLAME_DISTANCE, GetOwner(), DMG_BURN | DMG_PROBE, &tr ) == false )
|
||
|
{
|
||
|
CBaseEntity *pTestEnt = tr.m_pEnt;
|
||
|
if ( pTestEnt && IsBurnableEnt( pTestEnt, GetTeamNumber() ) )
|
||
|
{
|
||
|
if ( FindInArray( pTestEnt, pHitEnts, nHitEnts ) == -1 )
|
||
|
{
|
||
|
pHitEnts[nHitEnts++] = pTestEnt;
|
||
|
if ( nHitEnts >= ARRAYSIZE( pHitEnts ) )
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( int iHitEnt=0; iHitEnt < nHitEnts; iHitEnt++ )
|
||
|
{
|
||
|
CBaseEntity *pEnt = pHitEnts[iHitEnt];
|
||
|
|
||
|
float flDist = (pEnt->GetAbsOrigin() - vOrigin).Length();
|
||
|
float flPercent = 1.0 - flDist / FLAMETHROWER_FLAME_DISTANCE;
|
||
|
if ( flPercent < 0.1 )
|
||
|
flPercent = 0.1;
|
||
|
|
||
|
float flDamage = flPercent * FLAMETHROWER_DAMAGE_PER_SEC;
|
||
|
GetFireDamageMgr()->AddDamage( pEnt, GetOwner(), flDamage, !IsGasolineBlob( pEnt ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
// Drop a new petrol blob.
|
||
|
if ( gpGlobals->curtime >= m_flNextPrimaryAttack )
|
||
|
{
|
||
|
float flLifetime = MAX_LIT_GASOLINE_BLOB_LIFETIME;
|
||
|
if ( IsGasCanister() )
|
||
|
flLifetime = MAX_UNLIT_GASOLINE_BLOB_LIFETIME;
|
||
|
|
||
|
CGasolineBlob *pBlob = CGasolineBlob::Create( GetOwner(), vOrigin, vForward * FLAMETHROWER_FLAME_SPEED, false, FLAMETHROWER_FLAME_DISTANCE / FLAMETHROWER_FLAME_SPEED, flLifetime );
|
||
|
if ( pBlob )
|
||
|
{
|
||
|
if ( IsGasCanister() )
|
||
|
{
|
||
|
// Link the previous blob to this one.
|
||
|
pBlob->AddAutoBurnBlob( m_hPrevBlob );
|
||
|
if ( m_hPrevBlob.Get() )
|
||
|
m_hPrevBlob->AddAutoBurnBlob( pBlob );
|
||
|
|
||
|
m_hPrevBlob = pBlob;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pBlob->SetLit( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pOwner->RemoveAmmo( 2, m_iPrimaryAmmoType );
|
||
|
|
||
|
// Drop a blob every half second.
|
||
|
m_flNextPrimaryAttack = gpGlobals->curtime + FLAME_THROWER_FIRE_INTERVAL;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
#if defined( CLIENT_DLL )
|
||
|
|
||
|
bool CWeaponFlameThrower::ShouldPredict()
|
||
|
{
|
||
|
if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
|
||
|
return true;
|
||
|
|
||
|
return BaseClass::ShouldPredict();
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWeaponFlameThrower::NotifyShouldTransmit( ShouldTransmitState_t state )
|
||
|
{
|
||
|
BaseClass::NotifyShouldTransmit( state );
|
||
|
|
||
|
if ( state == SHOULDTRANSMIT_START )
|
||
|
{
|
||
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||
|
}
|
||
|
else if ( state == SHOULDTRANSMIT_END )
|
||
|
{
|
||
|
SetNextClientThink( CLIENT_THINK_NEVER );
|
||
|
StopFlameSound();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CWeaponFlameThrower::ClientThink()
|
||
|
{
|
||
|
// Spew some particles out.
|
||
|
if ( !IsDormant() && IsCarrierAlive() && m_bFiring && m_hFlameEmitter.IsValid() )
|
||
|
{
|
||
|
unsigned char color[4] = { 255, 128, 0, 255 };
|
||
|
if ( IsGasCanister() )
|
||
|
{
|
||
|
color[0] = color[1] = color[2] = 200;
|
||
|
color[3] = 255;
|
||
|
}
|
||
|
|
||
|
StartSound();
|
||
|
|
||
|
Vector vForward, vUp, vRight, vOrigin;
|
||
|
QAngle vAngles;
|
||
|
GetShootPosition( vOrigin, vAngles );
|
||
|
AngleVectors( vAngles, &vForward, &vRight, &vUp );
|
||
|
|
||
|
// Spew out flame particles.
|
||
|
float dt = gpGlobals->frametime;
|
||
|
while ( m_FlameEvent.NextEvent( dt ) )
|
||
|
{
|
||
|
SimpleParticle *p = m_hFlameEmitter->AddSimpleParticle(
|
||
|
m_hFireMaterial,
|
||
|
vOrigin + RandomVector( -3, 3 ),
|
||
|
FLAMETHROWER_FLAME_DISTANCE / FLAMETHROWER_FLAME_SPEED, // lifetime,
|
||
|
9 // size
|
||
|
);
|
||
|
|
||
|
if ( p )
|
||
|
{
|
||
|
p->m_uchColor[0] = color[0];
|
||
|
p->m_uchColor[1] = color[1];
|
||
|
p->m_uchColor[2] = color[2];
|
||
|
p->m_uchStartAlpha = color[3];
|
||
|
p->m_uchEndAlpha = 0;
|
||
|
GenerateRandomFlameThrowerVelocity( p->m_vecVelocity, vForward, vRight, vUp );
|
||
|
p->m_vecVelocity *= RandomFloat( FLAMETHROWER_FLAME_SPEED * 0.9, FLAMETHROWER_FLAME_SPEED * 1.1 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
StopFlameSound();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWeaponFlameThrower::StartSound()
|
||
|
{
|
||
|
if ( !m_bSoundOn )
|
||
|
{
|
||
|
CLocalPlayerFilter filter;
|
||
|
EmitSound( filter, entindex(), "FlameThrower.Sound" );
|
||
|
|
||
|
m_bSoundOn = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWeaponFlameThrower::StopFlameSound()
|
||
|
{
|
||
|
if ( m_bSoundOn )
|
||
|
{
|
||
|
StopSound( entindex(), "FlameThrower.Sound" );
|
||
|
m_bSoundOn = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#else
|
||
|
|
||
|
bool CWeaponFlameThrower::Holster( CBaseCombatWeapon *pSwitchingTo )
|
||
|
{
|
||
|
m_bFiring = false;
|
||
|
|
||
|
return BaseClass::Holster( pSwitchingTo );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWeaponFlameThrower::IgniteNearbyGasolineBlobs()
|
||
|
{
|
||
|
CBasePlayer *pOwner = ToBaseTFPlayer( GetOwner() );
|
||
|
if ( !pOwner )
|
||
|
return;
|
||
|
|
||
|
Vector vOrigin = pOwner->Weapon_ShootPosition( );
|
||
|
CBaseEntity *ents[128];
|
||
|
float dists[128];
|
||
|
int nEnts = FindBurnableEntsInSphere(
|
||
|
ents,
|
||
|
dists,
|
||
|
ARRAYSIZE( dists ),
|
||
|
vOrigin,
|
||
|
50,
|
||
|
pOwner );
|
||
|
|
||
|
for ( int i=0; i < nEnts; i++ )
|
||
|
{
|
||
|
CGasolineBlob *pBlob = dynamic_cast< CGasolineBlob* >( ents[i] );
|
||
|
if ( pBlob )
|
||
|
{
|
||
|
GetFireDamageMgr()->AddDamage( pBlob, pOwner, 500, false );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|