Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.

280 lines
6.5 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "gasoline_blob.h"
#include "gasoline_shared.h"
#include "utllinkedlist.h"
#include "fire_damage_mgr.h"
#include "tf_gamerules.h"
// Flamethrower blobs wait a bit before they cause damage so they don't hurt the guy
// shooting them.
#define BLOB_DAMAGE_WAIT_TIME 1.0
// At what heat level does an unlit blob ignite?
#define IGNITION_HEAT 0.1
#define FIRE_DAMAGE_SEARCH_DISTANCE 200 // It searches within this sphere for entities to damage.
#define FIRE_DAMAGE_DISTANCE 90 // This is how far fire can damage an entity from.
ConVar fire_enable( "fire_enable", "1", 0, "Enable or disable fire." );
// ------------------------------------------------------------------------------------------ //
// CGasolineBlob implementation.
// ------------------------------------------------------------------------------------------ //
IMPLEMENT_SERVERCLASS_ST_NOBASE( CGasolineBlob, DT_GasolineBlob )
SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD ),
SendPropEHandle (SENDINFO_NAME(m_hMoveParent, moveparent)),
SendPropFloat( SENDINFO(m_flLitStartTime), -1, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flCreateTime), -1, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flMaxLifetime), -1, SPROP_NOSCALE ),
SendPropInt( SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0 ),
SendPropInt( SENDINFO( m_BlobFlags ), NUM_BLOB_FLAGS, SPROP_UNSIGNED ),
SendPropVector( SENDINFO( m_vSurfaceNormal ), 0, SPROP_NORMAL ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( gasoline_blob, CGasolineBlob );
CUtlLinkedList<CGasolineBlob*, int> g_GasolineBlobs;
CGasolineBlob* CGasolineBlob::Create(
CBaseEntity *pOwner,
const Vector &vOrigin,
const Vector &vStartVelocity,
bool bUseGravity,
float flAirLifetime,
float flLifetime )
{
CGasolineBlob *pBlob = (CGasolineBlob*)CreateEntityByName( "gasoline_blob" );
if ( !pBlob )
return NULL;
// The "constructor".
pBlob->SetLocalOrigin( vOrigin );
pBlob->SetAbsVelocity( vStartVelocity );
pBlob->SetThink( &CGasolineBlob::Think );
pBlob->SetNextThink( gpGlobals->curtime );
pBlob->SetCollisionBounds( Vector( -GASOLINE_BLOB_RADIUS, -GASOLINE_BLOB_RADIUS, -GASOLINE_BLOB_RADIUS ), Vector( GASOLINE_BLOB_RADIUS, GASOLINE_BLOB_RADIUS, GASOLINE_BLOB_RADIUS ) );
pBlob->SetMoveType( MOVETYPE_NONE );
pBlob->SetSolid( SOLID_BBOX );
pBlob->AddSolidFlags( FSOLID_NOT_SOLID );
pBlob->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
pBlob->m_BlobFlags = 0;
pBlob->m_HeatLevel = 0;
pBlob->m_hOwner = pOwner;
pBlob->m_flCreateTime = gpGlobals->curtime;
pBlob->m_flMaxLifetime = flLifetime;
pBlob->m_takedamage = DAMAGE_YES;
pBlob->m_flLitStartTime = 0;
pBlob->ChangeTeam( pOwner->GetTeamNumber() );
if ( bUseGravity )
pBlob->m_BlobFlags |= BLOBFLAG_USE_GRAVITY;
pBlob->m_flAirLifetime = flAirLifetime;
pBlob->m_flTimeInAir = 0;
pBlob->SetNextThink( gpGlobals->curtime );
g_GasolineBlobs.AddToTail( pBlob );
return pBlob;
}
CGasolineBlob::~CGasolineBlob()
{
g_GasolineBlobs.Remove( g_GasolineBlobs.Find( this ) );
}
void CGasolineBlob::AddAutoBurnBlob( CGasolineBlob *pBlob )
{
int index = m_AutoBurnBlobs.AddToTail();
m_AutoBurnBlobs[index] = pBlob;
}
int CGasolineBlob::OnTakeDamage( const CTakeDamageInfo &info )
{
m_HeatLevel += info.GetDamage();
if ( m_HeatLevel >= IGNITION_HEAT )
SetLit( true );
return 0;
}
void CGasolineBlob::SetLit( bool bLit )
{
if ( bLit != IsLit() )
{
if ( bLit )
{
m_BlobFlags |= BLOBFLAG_LIT;
m_flLitStartTime = gpGlobals->curtime;
}
else
{
m_BlobFlags &= ~BLOBFLAG_LIT;
}
}
}
bool CGasolineBlob::IsLit() const
{
return (m_BlobFlags & BLOBFLAG_LIT) != 0;
}
bool CGasolineBlob::IsStopped() const
{
return (m_BlobFlags & BLOBFLAG_STOPPED) != 0;
}
void CGasolineBlob::AutoBurn_R( CGasolineBlob *pParent )
{
SetLit( true );
for ( int i=0; i < m_AutoBurnBlobs.Count(); i++ )
{
CGasolineBlob *pTestBlob = m_AutoBurnBlobs[i];
if ( pTestBlob )
{
if ( pTestBlob != pParent )
pTestBlob->AutoBurn_R( this );
}
else
{
m_AutoBurnBlobs.Remove( i );
--i;
}
}
}
void CGasolineBlob::Think()
{
if ( !fire_enable.GetInt() )
{
UTIL_Remove( this );
return;
}
// Decay quickly while in the air.
if ( !IsStopped() )
{
m_flTimeInAir += gpGlobals->frametime;
if ( m_flTimeInAir >= m_flAirLifetime )
{
UTIL_Remove( this );
return;
}
}
float flLifetime = gpGlobals->curtime - m_flCreateTime;
if ( flLifetime >= m_flMaxLifetime )
{
UTIL_Remove( this );
return;
}
if ( IsLit() )
{
// Have we burnt out?
float litPercent = 1 - (flLifetime / m_flMaxLifetime);
if ( litPercent <= 0 )
{
UTIL_Remove( this );
return;
}
// Look for nearby entities to burn.
CBaseEntity *ents[512];
float dists[512];
int nEnts = FindBurnableEntsInSphere( ents, dists, ARRAYSIZE( ents ), GetAbsOrigin(), FIRE_DAMAGE_SEARCH_DISTANCE, m_hOwner );
for ( int i=0; i < nEnts; i++ )
{
float flDistFromBorder = MAX( 0, FIRE_DAMAGE_DISTANCE - dists[i] );
if ( flDistFromBorder <= 0 )
continue;
float flDamage = litPercent * flDistFromBorder / FIRE_DAMAGE_DISTANCE * FIRE_DAMAGE_PER_SEC;
GetFireDamageMgr()->AddDamage( ents[i], m_hOwner, flDamage, !IsGasolineBlob( ents[i] ) );
}
// Ignite our "auto burn" blobs.
AutoBurn_R( NULL );
}
// Figure out where we want to go.
if ( !IsStopped() )
{
// Apply gravity.
Vector vecNewVelocity = GetAbsVelocity();
if ( m_BlobFlags & BLOBFLAG_USE_GRAVITY )
{
vecNewVelocity.z -= 800 * gpGlobals->frametime;
SetAbsVelocity( vecNewVelocity );
}
Vector vNewPos = GetAbsOrigin() + vecNewVelocity * gpGlobals->frametime;
// Can we go there?
trace_t trace;
UTIL_TraceLine( GetAbsOrigin(), vNewPos, CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &trace );
bool bStopped = (trace.fraction != 1);
if ( !bStopped )
{
// Trace against shields.
if ( TFGameRules()->IsTraceBlockedByWorldOrShield( GetAbsOrigin(), vNewPos, m_hOwner, DMG_BURN, &trace ) )
{
// Blobs just fizzle out when they hit a shield.
UTIL_Remove( this );
}
}
if( bStopped )
{
SetLocalOrigin( trace.endpos + trace.plane.normal * 2 );
// Ok, we hit something. Stop moving.
m_BlobFlags |= BLOBFLAG_STOPPED;
m_vSurfaceNormal = trace.plane.normal;
}
else
{
SetLocalOrigin( vNewPos );
}
}
SetNextThink( gpGlobals->curtime );
}
bool IsGasolineBlob( CBaseEntity *pEnt )
{
return FClassnameIs( pEnt, "gasoline_blob" );
}