source-engine/game/server/hl2/weapon_immolator.cpp

374 lines
8.9 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "basehlcombatweapon.h"
#include "basecombatcharacter.h"
#include "player.h"
#include "soundent.h"
#include "te_particlesystem.h"
#include "ndebugoverlay.h"
#include "in_buttons.h"
#include "ai_basenpc.h"
#include "ai_memory.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define MAX_BURN_RADIUS 256
#define RADIUS_GROW_RATE 50.0 // units/sec
#define IMMOLATOR_TARGET_INVALID Vector( FLT_MAX, FLT_MAX, FLT_MAX )
class CWeaponImmolator : public CBaseHLCombatWeapon
{
public:
DECLARE_CLASS( CWeaponImmolator, CBaseHLCombatWeapon );
DECLARE_SERVERCLASS();
CWeaponImmolator( void );
void Precache( void );
void PrimaryAttack( void );
void ItemPostFrame( void );
int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
void ImmolationDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore );
virtual bool WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions );
virtual int WeaponRangeAttack1Condition( float flDot, float flDist );
void Update();
void UpdateThink();
void StartImmolating();
void StopImmolating();
bool IsImmolating() { return m_flBurnRadius != 0.0; }
DECLARE_ACTTABLE();
DECLARE_DATADESC();
int m_beamIndex;
float m_flBurnRadius;
float m_flTimeLastUpdatedRadius;
Vector m_vecImmolatorTarget;
};
IMPLEMENT_SERVERCLASS_ST(CWeaponImmolator, DT_WeaponImmolator)
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( info_target_immolator, CPointEntity );
LINK_ENTITY_TO_CLASS( weapon_immolator, CWeaponImmolator );
PRECACHE_WEAPON_REGISTER( weapon_immolator );
BEGIN_DATADESC( CWeaponImmolator )
DEFINE_FIELD( m_beamIndex, FIELD_INTEGER ),
DEFINE_FIELD( m_flBurnRadius, FIELD_FLOAT ),
DEFINE_FIELD( m_flTimeLastUpdatedRadius, FIELD_TIME ),
DEFINE_FIELD( m_vecImmolatorTarget, FIELD_VECTOR ),
DEFINE_ENTITYFUNC( UpdateThink ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Maps base activities to weapons-specific ones so our characters do the right things.
//-----------------------------------------------------------------------------
acttable_t CWeaponImmolator::m_acttable[] =
{
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SNIPER_RIFLE, true }
};
IMPLEMENT_ACTTABLE( CWeaponImmolator );
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CWeaponImmolator::CWeaponImmolator( void )
{
m_fMaxRange1 = 4096;
StopImmolating();
}
void CWeaponImmolator::StartImmolating()
{
// Start the radius really tiny because we use radius == 0.0 to
// determine whether the immolator is operating or not.
m_flBurnRadius = 0.1;
m_flTimeLastUpdatedRadius = gpGlobals->curtime;
SetThink( UpdateThink );
SetNextThink( gpGlobals->curtime );
CSoundEnt::InsertSound( SOUND_DANGER, m_vecImmolatorTarget, 256, 5.0, GetOwner() );
}
void CWeaponImmolator::StopImmolating()
{
m_flBurnRadius = 0.0;
SetThink( NULL );
m_vecImmolatorTarget= IMMOLATOR_TARGET_INVALID;
m_flNextPrimaryAttack = gpGlobals->curtime + 5.0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponImmolator::Precache( void )
{
m_beamIndex = PrecacheModel( "sprites/bluelaser1.vmt" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponImmolator::PrimaryAttack( void )
{
WeaponSound( SINGLE );
if( !IsImmolating() )
{
StartImmolating();
}
}
//-----------------------------------------------------------------------------
// This weapon is said to have Line of Sight when it CAN'T see the target, but
// can see a place near the target than can.
//-----------------------------------------------------------------------------
bool CWeaponImmolator::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions )
{
CAI_BaseNPC* npcOwner = GetOwner()->MyNPCPointer();
if( !npcOwner )
{
return false;
}
if( IsImmolating() )
{
// Don't update while Immolating. This is a committed attack.
return false;
}
// Assume we won't find a target.
m_vecImmolatorTarget = targetPos;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Weapon firing conditions
//-----------------------------------------------------------------------------
int CWeaponImmolator::WeaponRangeAttack1Condition( float flDot, float flDist )
{
if( m_flNextPrimaryAttack > gpGlobals->curtime )
{
// Too soon to attack!
return COND_NONE;
}
if( IsImmolating() )
{
// Once is enough!
return COND_NONE;
}
if( m_vecImmolatorTarget == IMMOLATOR_TARGET_INVALID )
{
// No target!
return COND_NONE;
}
if ( flDist > m_fMaxRange1 )
{
return COND_TOO_FAR_TO_ATTACK;
}
else if ( flDot < 0.5f ) // UNDONE: Why check this here? Isn't the AI checking this already?
{
return COND_NOT_FACING_ATTACK;
}
return COND_CAN_RANGE_ATTACK1;
}
void CWeaponImmolator::UpdateThink( void )
{
CBaseCombatCharacter *pOwner = GetOwner();
if( pOwner && !pOwner->IsAlive() )
{
StopImmolating();
return;
}
Update();
SetNextThink( gpGlobals->curtime + 0.05 );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CWeaponImmolator::Update()
{
float flDuration = gpGlobals->curtime - m_flTimeLastUpdatedRadius;
if( flDuration != 0.0 )
{
m_flBurnRadius += RADIUS_GROW_RATE * flDuration;
}
// Clamp
m_flBurnRadius = MIN( m_flBurnRadius, MAX_BURN_RADIUS );
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
Vector vecSrc;
Vector vecAiming;
if( pOwner )
{
vecSrc = pOwner->Weapon_ShootPosition( );
vecAiming = pOwner->GetAutoaimVector(AUTOAIM_2DEGREES);
}
else
{
CBaseCombatCharacter *pOwner = GetOwner();
vecSrc = pOwner->Weapon_ShootPosition( );
vecAiming = m_vecImmolatorTarget - vecSrc;
VectorNormalize( vecAiming );
}
trace_t tr;
UTIL_TraceLine( vecSrc, vecSrc + vecAiming * MAX_TRACE_LENGTH, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
int brightness;
brightness = 255 * (m_flBurnRadius/MAX_BURN_RADIUS);
UTIL_Beam( vecSrc,
tr.endpos,
m_beamIndex,
0, //halo index
0, //frame start
2.0f, //framerate
0.1f, //life
20, // width
1, // endwidth
0, // fadelength,
1, // noise
0, // red
255, // green
0, // blue,
brightness, // bright
100 // speed
);
if( tr.DidHitWorld() )
{
int beams;
for( beams = 0 ; beams < 5 ; beams++ )
{
Vector vecDest;
// Random unit vector
vecDest.x = random->RandomFloat( -1, 1 );
vecDest.y = random->RandomFloat( -1, 1 );
vecDest.z = random->RandomFloat( 0, 1 );
// Push out to radius dist.
vecDest = tr.endpos + vecDest * m_flBurnRadius;
UTIL_Beam( tr.endpos,
vecDest,
m_beamIndex,
0, //halo index
0, //frame start
2.0f, //framerate
0.15f, //life
20, // width
1.75, // endwidth
0.75, // fadelength,
15, // noise
0, // red
255, // green
0, // blue,
128, // bright
100 // speed
);
}
// Immolator starts to hurt a few seconds after the effect is seen
if( m_flBurnRadius > 64.0 )
{
ImmolationDamage( CTakeDamageInfo( this, this, 1, DMG_BURN ), tr.endpos, m_flBurnRadius, CLASS_NONE );
}
}
else
{
// The attack beam struck some kind of entity directly.
}
m_flTimeLastUpdatedRadius = gpGlobals->curtime;
if( m_flBurnRadius >= MAX_BURN_RADIUS )
{
StopImmolating();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponImmolator::ItemPostFrame( void )
{
BaseClass::ItemPostFrame();
}
void CWeaponImmolator::ImmolationDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore )
{
CBaseEntity *pEntity = NULL;
trace_t tr;
Vector vecSpot;
Vector vecSrc = vecSrcIn;
// iterate on all entities in the vicinity.
for ( CEntitySphereQuery sphere( vecSrc, flRadius ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
{
CBaseCombatCharacter *pBCC;
pBCC = pEntity->MyCombatCharacterPointer();
if ( pBCC && !pBCC->IsOnFire() )
{
// UNDONE: this should check a damage mask, not an ignore
if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
{
continue;
}
if( pEntity == GetOwner() )
{
continue;
}
pBCC->Ignite( random->RandomFloat( 15, 20 ) );
}
}
}