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.
373 lines
8.9 KiB
373 lines
8.9 KiB
//========= 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 ) ); |
|
} |
|
} |
|
}
|
|
|