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

697 lines
19 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Grenade used by the city scanner
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "grenade_homer.h"
#include "weapon_ar2.h"
#include "soundent.h"
#include "decals.h"
#include "shake.h"
#include "smoke_trail.h"
#include "ar2_explosion.h"
#include "mathlib/mathlib.h"
#include "game.h"
#include "ndebugoverlay.h"
#include "hl2_shareddefs.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "movevars_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define HOMER_TRAIL0_LIFE 0.1
#define HOMER_TRAIL1_LIFE 0.2
#define HOMER_TRAIL2_LIFE 3.0// 1.0
extern short g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud
ConVar sk_dmg_homer_grenade( "sk_dmg_homer_grenade","0" );
ConVar sk_homer_grenade_radius( "sk_homer_grenade_radius","0" );
BEGIN_DATADESC( CGrenadeHomer )
DEFINE_ARRAY( m_hRocketTrail, FIELD_EHANDLE, 3 ),
DEFINE_FIELD( m_sFlySound, FIELD_STRING),
DEFINE_FIELD( m_flNextFlySoundTime, FIELD_TIME),
DEFINE_FIELD( m_flHomingStrength, FIELD_FLOAT),
DEFINE_FIELD( m_flHomingDelay, FIELD_FLOAT),
DEFINE_FIELD( m_flHomingRampUp, FIELD_FLOAT),
DEFINE_FIELD( m_flHomingDuration, FIELD_FLOAT),
DEFINE_FIELD( m_flHomingRampDown, FIELD_FLOAT),
DEFINE_FIELD( m_flHomingSpeed, FIELD_FLOAT),
DEFINE_FIELD( m_flSpinMagnitude, FIELD_FLOAT),
DEFINE_FIELD( m_flSpinSpeed, FIELD_FLOAT),
DEFINE_FIELD( m_nRocketTrailType, FIELD_INTEGER),
// DEFINE_FIELD( m_spriteTexture, FIELD_INTEGER),
DEFINE_FIELD( m_flHomingLaunchTime, FIELD_TIME),
DEFINE_FIELD( m_flHomingStartTime, FIELD_TIME ),
DEFINE_FIELD( m_flHomingEndTime, FIELD_TIME ),
DEFINE_FIELD( m_flSpinOffset, FIELD_FLOAT),
DEFINE_FIELD( m_hTarget, FIELD_EHANDLE),
// Function pointers
DEFINE_THINKFUNC( AimThink ),
DEFINE_ENTITYFUNC( GrenadeHomerTouch ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( grenade_homer, CGrenadeHomer );
///------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
CGrenadeHomer* CGrenadeHomer::CreateGrenadeHomer( string_t sModelName, string_t sFlySound, const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner )
{
CGrenadeHomer *pGrenade = (CGrenadeHomer*)CreateEntityByName( "grenade_homer" );
if ( !pGrenade )
{
Warning( "NULL Ent in Create!\n" );
return NULL;
}
if ( pGrenade->edict() )
{
pGrenade->m_sFlySound = sFlySound;
pGrenade->SetOwnerEntity( Instance( pentOwner ) );
pGrenade->SetLocalOrigin( vecOrigin );
pGrenade->SetLocalAngles( vecAngles );
pGrenade->SetModel( STRING(sModelName) );
}
return pGrenade;
}
void CGrenadeHomer::Precache( void )
{
m_spriteTexture = PrecacheModel( "sprites/lgtning.vmt" );
PrecacheScriptSound( "GrenadeHomer.StopSounds" );
if ( NULL_STRING != m_sFlySound )
{
PrecacheScriptSound( STRING(m_sFlySound) );
}
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CGrenadeHomer::Spawn( void )
{
Precache( );
SetSolid( SOLID_BBOX );
SetMoveType( MOVETYPE_FLY );
UTIL_SetSize(this, Vector(0, 0, 0), Vector(0, 0, 0));
m_flDamage = sk_dmg_homer_grenade.GetFloat();
m_DmgRadius = sk_homer_grenade_radius.GetFloat();
m_takedamage = DAMAGE_YES;
m_iHealth = 1;
SetGravity( 1.0 );
SetFriction( 0.8 );
SetSequence( 1 );
m_flHomingStrength = 0;
m_flHomingDelay = 0;
m_flHomingDuration = 0;
SetCollisionGroup( HL2COLLISION_GROUP_HOMING_MISSILE );
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CGrenadeHomer::SetSpin(float flSpinMagnitude, float flSpinSpeed)
{
m_flSpinMagnitude = flSpinMagnitude;
m_flSpinSpeed = flSpinSpeed;
m_flSpinOffset = random->RandomInt(-m_flSpinSpeed,m_flSpinSpeed);
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CGrenadeHomer::SetHoming(float flStrength, float flDelay, float flRampUp, float flDuration, float flRampDown)
{
m_flHomingStrength = flStrength;
m_flHomingDelay = flDelay;
m_flHomingRampUp = flRampUp;
m_flHomingDuration = flDuration;
m_flHomingRampDown = flRampDown;
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CGrenadeHomer::StartRocketTrail(void)
{
RocketTrail *pRocketTrail = RocketTrail::CreateRocketTrail();
if(pRocketTrail)
{
pRocketTrail->m_SpawnRate = 80;
pRocketTrail->m_ParticleLifetime = 2;
if ( m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN )
{
pRocketTrail->m_StartColor.Init(0.5, 0.0, 0.5);
}
else
{
pRocketTrail->m_StartColor.Init(0.75, 0.75, 0.75);
}
pRocketTrail->m_Opacity = 0.35f;
pRocketTrail->m_EndColor.Init(0.4,0.4,0.4);
pRocketTrail->m_StartSize = 8;
pRocketTrail->m_EndSize = 16;
pRocketTrail->m_SpawnRadius = 3;
pRocketTrail->m_MinSpeed = 2;
pRocketTrail->m_MaxSpeed = 10;
pRocketTrail->SetLifetime(120);
pRocketTrail->FollowEntity(this);
m_hRocketTrail[0] = pRocketTrail;
}
/*
pRocketTrail = RocketTrail::CreateRocketTrail();
if(pRocketTrail)
{
pRocketTrail->m_SpawnRate = 100;
pRocketTrail->m_ParticleLifetime = HOMER_TRAIL1_LIFE;
if ( m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN )
{
pRocketTrail->m_StartColor.Init(0.0, 0.0, 0.5);
}
else
{
pRocketTrail->m_StartColor.Init(0.5, 0.5, 0.0);
}
pRocketTrail->m_EndColor.Init(0.5,0.5,0.5);
pRocketTrail->m_StartSize = 3;
pRocketTrail->m_EndSize = 6;
pRocketTrail->m_SpawnRadius = 1;
pRocketTrail->m_MinSpeed = 15;
pRocketTrail->m_MaxSpeed = 25;
pRocketTrail->SetLifetime(120);
pRocketTrail->FollowEntity(this);
m_hRocketTrail[1] = pRocketTrail;
}
pRocketTrail = RocketTrail::CreateRocketTrail();
if(pRocketTrail)
{
pRocketTrail->m_SpawnRate = 50;
pRocketTrail->m_ParticleLifetime = HOMER_TRAIL2_LIFE;
if ( m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN )
{
pRocketTrail->m_StartColor.Init(0.1, 0.0, 0.1);
}
else
{
pRocketTrail->m_StartColor.Init(0.1, 0.1, 0.1);
}
pRocketTrail->m_EndColor.Init(0.5,0.5,0.5);
pRocketTrail->m_StartSize = 8;
pRocketTrail->m_EndSize = 20;
pRocketTrail->m_SpawnRadius = 1;
pRocketTrail->m_MinSpeed = 15;
pRocketTrail->m_MaxSpeed = 25;
pRocketTrail->SetLifetime(120);
pRocketTrail->FollowEntity(this);
m_hRocketTrail[2] = pRocketTrail;
}
*/
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CGrenadeHomer::UpdateRocketTrail(float fScale)
{
if (m_hRocketTrail[0] == NULL)
{
StartRocketTrail();
}
if (m_hRocketTrail[0])
{
m_hRocketTrail[0]->m_ParticleLifetime = fScale*HOMER_TRAIL0_LIFE;
}
if (m_hRocketTrail[1])
{
m_hRocketTrail[1]->m_ParticleLifetime = fScale*HOMER_TRAIL1_LIFE;
}
if (m_hRocketTrail[2])
{
m_hRocketTrail[2]->m_ParticleLifetime = fScale*HOMER_TRAIL2_LIFE;
}
}
void CGrenadeHomer::StopRocketTrail()
{
// Stop emitting smoke
for (int i=0;i<3;i++)
{
if(m_hRocketTrail[i])
{
m_hRocketTrail[i]->SetEmit(false);
UTIL_Remove( m_hRocketTrail[i] );
m_hRocketTrail[i] = NULL;
}
}
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CGrenadeHomer::Launch( CBaseEntity* pOwner,
CBaseEntity* pTarget,
const Vector& vInitVelocity,
float flHomingSpeed,
float flGravity,
int nRocketTrailType)
{
SetOwnerEntity( pOwner );
m_hTarget = pTarget;
SetAbsVelocity( vInitVelocity );
m_flHomingSpeed = flHomingSpeed;
SetGravity( flGravity );
m_nRocketTrailType = nRocketTrailType;
// ----------------------------
// Initialize homing parameters
// ----------------------------
m_flHomingLaunchTime = gpGlobals->curtime;
// -------------
// Smoke trail.
// -------------
if ( (m_nRocketTrailType == HOMER_SMOKE_TRAIL_ON) || (m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN) )
{
StartRocketTrail();
}
SetUse( &CGrenadeHomer::DetonateUse );
SetTouch( &CGrenadeHomer::GrenadeHomerTouch );
SetThink( &CGrenadeHomer::AimThink );
AimThink();
SetNextThink( gpGlobals->curtime );
// Issue danger!
if ( pTarget )
{
// Figure out how long it'll take for me to reach the target.
float flDist = ( pTarget->WorldSpaceCenter() - WorldSpaceCenter() ).Length();
float flTime = MAX( 0.5, flDist / GetAbsVelocity().Length() );
CSoundEnt::InsertSound ( SOUND_DANGER, m_hTarget->GetAbsOrigin(), 300, flTime, pOwner );
}
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CGrenadeHomer::Event_Killed( const CTakeDamageInfo &info )
{
Detonate( );
}
void CGrenadeHomer::GrenadeHomerTouch( CBaseEntity *pOther )
{
Assert( pOther );
// Don't take damage from other homing grenades so can shoot in vollies
if (FClassnameIs( pOther, "grenade_homer") || !pOther->IsSolid() )
{
return;
}
// ----------------------------------
// If I hit the sky, don't explode
// ----------------------------------
trace_t tr;
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity(), MASK_SOLID_BRUSHONLY,
this, COLLISION_GROUP_NONE, &tr);
if (tr.surface.flags & SURF_SKY)
{
StopRocketTrail();
UTIL_Remove( this );
}
else
{
Detonate();
}
}
void CGrenadeHomer::Detonate(void)
{
StopRocketTrail();
StopSound(entindex(), CHAN_BODY, STRING(m_sFlySound));
m_takedamage = DAMAGE_NO;
CPASFilter filter( GetAbsOrigin() );
te->Explosion( filter, 0.0,
&GetAbsOrigin(),
g_sModelIndexFireball,
2.0,
15,
TE_EXPLFLAG_NONE,
m_DmgRadius,
m_flDamage );
// int magnitude = 1.0;
// int colorRamp = random->RandomInt( 128, 255 );
if ( m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN )
{
// Add a shockring
CBroadcastRecipientFilter filter3;
te->BeamRingPoint( filter3, 0,
GetAbsOrigin(), //origin
16, //start radius
1000, //end radius
m_spriteTexture, //texture
0, //halo index
0, //start frame
2, //framerate
0.3f, //life
128, //width
16, //spread
0, //amplitude
100, //r
0, //g
200, //b
50, //a
128 //speed
);
// Add a shockring
CBroadcastRecipientFilter filter4;
te->BeamRingPoint( filter4, 0,
GetAbsOrigin(), //origin
16, //start radius
500, //end radius
m_spriteTexture, //texture
0, //halo index
0, //start frame
2, //framerate
0.3f, //life
128, //width
16, //spread
0, //amplitude
200, //r
0, //g
100, //b
50, //a
128 //speed
);
}
Vector vecForward = GetAbsVelocity();
VectorNormalize(vecForward);
trace_t tr;
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + 60*vecForward, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, & tr);
UTIL_DecalTrace( &tr, "Scorch" );
UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START );
RadiusDamage ( CTakeDamageInfo( this, GetOwnerEntity(), m_flDamage, DMG_BLAST ), GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
CPASAttenuationFilter filter2( this, "GrenadeHomer.StopSounds" );
EmitSound( filter2, entindex(), "GrenadeHomer.StopSounds" );
UTIL_Remove( this );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CGrenadeHomer::PlayFlySound(void)
{
if (gpGlobals->curtime > m_flNextFlySoundTime)
{
CPASAttenuationFilter filter( this, 0.8 );
EmitSound_t ep;
ep.m_nChannel = CHAN_BODY;
ep.m_pSoundName = STRING(m_sFlySound);
ep.m_flVolume = 1.0f;
ep.m_SoundLevel = SNDLVL_NORM;
ep.m_nPitch = 100;
EmitSound( filter, entindex(), ep );
m_flNextFlySoundTime = gpGlobals->curtime + 1.0;
}
}
//------------------------------------------------------------------------------
// Purpose : Move toward targetmap
// Input :
// Output :
//------------------------------------------------------------------------------
void CGrenadeHomer::AimThink( void )
{
// Blow up the missile if we have an explicit detonate time that
// has been reached
if (m_flDetonateTime != 0 &&
gpGlobals->curtime > m_flDetonateTime)
{
Detonate();
return;
}
PlayFlySound();
Vector vTargetPos = vec3_origin;
Vector vTargetDir;
float flCurHomingStrength = 0;
// ------------------------------------------------
// If I'm homing
// ------------------------------------------------
if (m_hTarget != NULL)
{
vTargetPos = m_hTarget->EyePosition();
vTargetDir = vTargetPos - GetAbsOrigin();
VectorNormalize(vTargetDir);
// --------------------------------------------------
// If my target is far away do some primitive
// obstacle avoidance
// --------------------------------------------------
if ((vTargetPos - GetAbsOrigin()).Length() > 200)
{
Vector vTravelDir = GetAbsVelocity();
VectorNormalize(vTravelDir);
vTravelDir *= 50;
trace_t tr;
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vTravelDir, MASK_SHOT, m_hTarget, COLLISION_GROUP_NONE, &tr );
if (tr.fraction != 1.0)
{
// Head off in normal
float dotPr = DotProduct(vTravelDir,tr.plane.normal);
Vector vBounce = -dotPr * tr.plane.normal;
vBounce.z = 0;
VectorNormalize(vBounce);
vTargetDir += vBounce;
VectorNormalize(vTargetDir);
// DEBUG TOOL
//NDebugOverlay::Line(GetOrigin(), GetOrigin()+vTravelDir, 255,0,0, true, 20);
//NDebugOverlay::Line(GetOrigin(), GetOrigin()+(12*tr.plane.normal), 0,0,255, true, 20);
//NDebugOverlay::Line(GetOrigin(), GetOrigin()+(vTargetDir), 0,255,0, true, 20);
}
}
float flTargetSpeed = GetAbsVelocity().Length();
float flHomingRampUpStartTime = m_flHomingLaunchTime + m_flHomingDelay;
float flHomingSustainStartTime = flHomingRampUpStartTime + m_flHomingRampUp;
float flHomingRampDownStartTime = flHomingSustainStartTime + m_flHomingDuration;
float flHomingEndHomingTime = flHomingRampDownStartTime + m_flHomingRampDown;
// ---------
// Delay
// ---------
if (gpGlobals->curtime < flHomingRampUpStartTime)
{
flCurHomingStrength = 0;
flTargetSpeed = 0;
}
// ----------
// Ramp Up
// ----------
else if (gpGlobals->curtime < flHomingSustainStartTime)
{
float flAge = gpGlobals->curtime - flHomingRampUpStartTime;
flCurHomingStrength = m_flHomingStrength * (flAge/m_flHomingRampUp);
flTargetSpeed = flCurHomingStrength * m_flHomingSpeed;
}
// ----------
// Sustain
// ----------
else if (gpGlobals->curtime < flHomingRampDownStartTime)
{
flCurHomingStrength = m_flHomingStrength;
flTargetSpeed = m_flHomingSpeed;
}
// -----------
// Ramp Down
// -----------
else if (gpGlobals->curtime < flHomingEndHomingTime)
{
float flAge = gpGlobals->curtime - flHomingRampDownStartTime;
flCurHomingStrength = m_flHomingStrength * (1-(flAge/m_flHomingRampDown));
flTargetSpeed = m_flHomingSpeed;
}
// ---------------
// Set Homing
// ---------------
if (flCurHomingStrength > 0)
{
// -------------
// Smoke trail.
// -------------
if (m_nRocketTrailType == HOMER_SMOKE_TRAIL_ON_HOMING)
{
UpdateRocketTrail(flCurHomingStrength);
}
// Extract speed and direction
Vector vCurDir = GetAbsVelocity();
float flCurSpeed = VectorNormalize(vCurDir);
flTargetSpeed = MAX(flTargetSpeed, flCurSpeed);
// Add in homing direction
Vector vecNewVelocity = GetAbsVelocity();
float flTimeToUse = gpGlobals->frametime;
while (flTimeToUse > 0)
{
vecNewVelocity = (flCurHomingStrength * vTargetDir) + ((1 - flCurHomingStrength) * vCurDir);
flTimeToUse = -0.1;
}
VectorNormalize(vecNewVelocity);
vecNewVelocity *= flTargetSpeed;
SetAbsVelocity( vecNewVelocity );
}
}
// ----------------------------------------------------------------------------------------
// Add time-coherent noise to the current velocity
// ----------------------------------------------------------------------------------------
Vector vecImpulse( 0, 0, 0 );
if (m_flSpinMagnitude > 0)
{
vecImpulse.x += m_flSpinMagnitude*sin(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
vecImpulse.y += m_flSpinMagnitude*cos(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
vecImpulse.z -= m_flSpinMagnitude*cos(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
}
// Add in gravity
vecImpulse.z -= GetGravity() * GetCurrentGravity() * gpGlobals->frametime;
ApplyAbsVelocityImpulse( vecImpulse );
QAngle angles;
VectorAngles( GetAbsVelocity(), angles );
SetLocalAngles( angles );
#if 0 // BUBBLE
if( gpGlobals->curtime > m_flNextWarnTime )
{
// Make a bubble of warning sound in front of me.
const float WARN_INTERVAL = 0.25f;
float flSpeed = GetAbsVelocity().Length();
Vector vecWarnLocation;
// warn a little bit ahead of us, please.
vecWarnLocation = GetAbsOrigin() + GetAbsVelocity() * 0.75;
// Make a bubble of warning ahead of the missile.
CSoundEnt::InsertSound ( SOUND_DANGER, vecWarnLocation, flSpeed * WARN_INTERVAL, 0.5 );
#if 0
Vector vecRight, vecForward;
AngleVectors( GetAbsAngles(), &vecForward, &vecRight, NULL );
NDebugOverlay::Line( vecWarnLocation, vecWarnLocation + vecForward * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
NDebugOverlay::Line( vecWarnLocation, vecWarnLocation - vecForward * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
NDebugOverlay::Line( vecWarnLocation, vecWarnLocation + vecRight * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
NDebugOverlay::Line( vecWarnLocation, vecWarnLocation - vecRight * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
#endif
m_flNextWarnTime = gpGlobals->curtime + WARN_INTERVAL;
}
#endif // BUBBLE
SetNextThink( gpGlobals->curtime + 0.1f );
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
int CGrenadeHomer::OnTakeDamage( const CTakeDamageInfo &info )
{
// Don't take damage from other homing grenades so can shoot in vollies
if (FClassnameIs( info.GetInflictor(), "grenade_homer"))
{
return 0;
}
return BaseClass::OnTakeDamage( info );
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
CGrenadeHomer::CGrenadeHomer(void)
{
for (int i=0;i<3;i++)
{
m_hRocketTrail[i] = NULL;
}
}