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.
 
 
 
 
 
 

283 lines
9.1 KiB

#include "cbase.h"
#include "asw_grenade_cluster.h"
#include "asw_gamerules.h"
#include "world.h"
#include "asw_util_shared.h"
#include "iasw_spawnable_npc.h"
#include "asw_marine.h"
#include "asw_marine_resource.h"
#include "asw_fx_shared.h"
#include "particle_parse.h"
#include "te_effect_dispatch.h"
#include "asw_achievements.h"
#include "asw_player.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define CLUSTER_GRENADE_MODEL "models/swarm/grenades/HandGrenadeProjectile.mdl"
extern ConVar sk_plr_dmg_asw_r_g;
extern ConVar asw_grenade_vindicator_radius;
extern ConVar asw_vindicator_grenade_friction;
extern ConVar asw_vindicator_grenade_gravity;
extern ConVar asw_vindicator_grenade_elasticity;
ConVar asw_cluster_grenade_min_detonation_time("asw_cluster_grenade_min_detonation_time", "0.9f", FCVAR_CHEAT, "Min. time before cluster grenade can detonate");
ConVar asw_cluster_grenade_fuse("asw_cluster_grenade_fuse", "4.0f", FCVAR_CHEAT, "Fuse length of cluster grenade");
ConVar asw_cluster_grenade_radius_check_interval("asw_cluster_grenade_radius_check_interval", "0.5f", FCVAR_CHEAT, "How often the cluster grenade checks for nearby drones to explode against");
ConVar asw_cluster_grenade_radius_check_scale("asw_cluster_grenade_radius_check_scale", "0.6f", FCVAR_CHEAT, "What fraction of the grenade's damage radius is used for the early detonation check");
ConVar asw_cluster_grenade_child_fuse_min("asw_cluster_grenade_child_fuse_min", "0.5", FCVAR_CHEAT, "Cluster grenade child cluster's minimum fuse length");
ConVar asw_cluster_grenade_child_fuse_max("asw_cluster_grenade_child_fuse_max", "1.0", FCVAR_CHEAT, "Cluster grenade child cluster's maximum fuse length");
LINK_ENTITY_TO_CLASS( asw_grenade_cluster, CASW_Grenade_Cluster );
PRECACHE_REGISTER( asw_grenade_cluster );
BEGIN_DATADESC( CASW_Grenade_Cluster )
DEFINE_THINKFUNC( CheckNearbyDrones ),
DEFINE_THINKFUNC( Detonate ),
DEFINE_FIELD( m_fDetonateTime, FIELD_TIME ),
DEFINE_FIELD( m_fEarliestAOEDetonationTime, FIELD_TIME ),
END_DATADESC()
extern int g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud
void CASW_Grenade_Cluster::Spawn( void )
{
Precache();
SetModel(CLUSTER_GRENADE_MODEL);
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
m_flDamage = sk_plr_dmg_asw_r_g.GetFloat();
m_DmgRadius = 220.0f;
m_takedamage = DAMAGE_YES;
m_iHealth = 1;
SetSize( -Vector(4,4,4), Vector(4,4,4) );
SetSolid( SOLID_BBOX );
SetGravity( asw_vindicator_grenade_gravity.GetFloat() );
SetFriction( asw_vindicator_grenade_friction.GetFloat() );
SetElasticity( asw_vindicator_grenade_elasticity.GetFloat() );
SetCollisionGroup( ASW_COLLISION_GROUP_GRENADES );
SetTouch( &CASW_Grenade_Vindicator::VGrenadeTouch );
CreateEffects();
m_hFirer = NULL;
// Tumble in air
QAngle vecAngVelocity( random->RandomFloat ( -100, -500 ), 0, 0 );
SetLocalAngularVelocity( vecAngVelocity );
m_fEarliestAOEDetonationTime = GetEarliestAOEDetonationTime();
m_fEarliestTouchDetonationTime = GetEarliestTouchDetonationTime();
m_iClusters = 0;
m_bMaster = true;
//EmitSound( "ASWGrenade.Alarm" );
SetFuseLength(asw_cluster_grenade_fuse.GetFloat());
if (m_fDetonateTime <= gpGlobals->curtime + asw_cluster_grenade_radius_check_interval.GetFloat())
{
SetThink( &CASW_Grenade_Cluster::Detonate );
SetNextThink( m_fDetonateTime );
}
else
{
SetThink( &CASW_Grenade_Cluster::CheckNearbyDrones );
SetNextThink( gpGlobals->curtime + asw_cluster_grenade_radius_check_interval.GetFloat() );
}
m_CreatorWeaponClass = (Class_T)CLASS_ASW_UNKNOWN;
}
void CASW_Grenade_Cluster::Precache()
{
BaseClass::Precache();
PrecacheModel(CLUSTER_GRENADE_MODEL);
PrecacheScriptSound( "ASWGrenade.Alarm" );
PrecacheParticleSystem( "explosion_grenade" );
}
void CASW_Grenade_Cluster::SetFuseLength(float fSeconds)
{
m_fDetonateTime = gpGlobals->curtime + fSeconds;
}
void CASW_Grenade_Cluster::CheckNearbyDrones()
{
// see if an alien is nearby
if (gpGlobals->curtime >= m_fEarliestAOEDetonationTime)
{
float flRadius = asw_cluster_grenade_radius_check_scale.GetFloat() * m_DmgRadius;
Vector vecSrc = GetAbsOrigin();
CBaseEntity *pEntity = NULL;
for ( CEntitySphereQuery sphere( vecSrc, flRadius ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
{
if (!pEntity || !pEntity->IsNPC())
continue;
IASW_Spawnable_NPC *pSpawnable = dynamic_cast<IASW_Spawnable_NPC*>(pEntity);
if (!pSpawnable)
continue;
Detonate();
return;
}
if (gpGlobals->curtime >= m_fDetonateTime)
{
Detonate();
return;
}
}
if (m_fDetonateTime <= gpGlobals->curtime + asw_cluster_grenade_radius_check_interval.GetFloat())
{
SetThink( &CASW_Grenade_Cluster::Detonate );
SetNextThink( m_fDetonateTime );
}
else
{
SetThink( &CASW_Grenade_Cluster::CheckNearbyDrones );
SetNextThink( gpGlobals->curtime + asw_cluster_grenade_radius_check_interval.GetFloat() );
}
}
CASW_Grenade_Cluster* CASW_Grenade_Cluster::Cluster_Grenade_Create( float flDamage, float fRadius, int iClusters, const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, CBaseEntity *pCreatorWeapon )
{
CASW_Grenade_Cluster *pGrenade = (CASW_Grenade_Cluster *)CreateEntityByName( "asw_grenade_cluster" );
pGrenade->SetAbsAngles( angles );
pGrenade->Spawn();
pGrenade->m_flDamage = flDamage;
pGrenade->m_DmgRadius = fRadius;
pGrenade->m_hFirer = pOwner;
pGrenade->SetOwnerEntity( pOwner );
UTIL_SetOrigin( pGrenade, position );
pGrenade->SetAbsVelocity( velocity );
pGrenade->SetClusters(iClusters, true);
pGrenade->m_hCreatorWeapon = pCreatorWeapon;
if( pCreatorWeapon )
pGrenade->m_CreatorWeaponClass = pCreatorWeapon->Classify();
// hack attack! for some reason, grenades refuse to be affect by damage forces until they're actually dead
// so we kill it immediately.
pGrenade->TakeDamage(CTakeDamageInfo(pGrenade, pGrenade, Vector(0, 0, 1), position, 10, DMG_SLASH));
return pGrenade;
}
void CASW_Grenade_Cluster::DoExplosion()
{
// scorch the ground
trace_t tr;
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -80 ), MASK_SHOT,
this, COLLISION_GROUP_NONE, &tr);
if (m_bMaster)
{
if ((tr.m_pEnt != GetWorldEntity()) || (tr.hitbox != 0))
{
// non-world needs smaller decals
if( tr.m_pEnt && !tr.m_pEnt->IsNPC() )
{
UTIL_DecalTrace( &tr, "SmallScorch" );
}
}
else
{
UTIL_DecalTrace( &tr, "Scorch" );
}
UTIL_ASW_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START );
}
UTIL_ASW_GrenadeExplosion( GetAbsOrigin(), m_DmgRadius );
EmitSound( "ASWGrenade.Explode" );
// damage to nearby things
CTakeDamageInfo info( this, m_hFirer.Get(), m_flDamage, DMG_BLAST );
info.SetWeapon( m_hCreatorWeapon );
ASWGameRules()->RadiusDamage( info, GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
}
extern ConVar asw_medal_explosive_kills;
void CASW_Grenade_Cluster::Detonate()
{
m_takedamage = DAMAGE_NO;
int iPreExplosionKills = 0;
CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(m_hFirer.Get());
if (pMarine && pMarine->GetMarineResource())
iPreExplosionKills = pMarine->GetMarineResource()->m_iAliensKilled;
DoExplosion();
if (pMarine && pMarine->GetMarineResource())
{
int iKilledByExplosion = pMarine->GetMarineResource()->m_iAliensKilled - iPreExplosionKills;
if (iKilledByExplosion > pMarine->GetMarineResource()->m_iSingleGrenadeKills)
{
pMarine->GetMarineResource()->m_iSingleGrenadeKills = iKilledByExplosion;
if ( iKilledByExplosion > asw_medal_explosive_kills.GetInt() && pMarine->GetCommander() && pMarine->IsInhabited() )
{
pMarine->GetCommander()->AwardAchievement( ACHIEVEMENT_ASW_GRENADE_MULTI_KILL );
}
}
if (m_bKicked)
pMarine->GetMarineResource()->m_iKickedGrenadeKills += iKilledByExplosion;
// primary cluster counts as a shot fired
if (m_iClusters > 0)
{
pMarine->GetMarineResource()->UsedWeapon(NULL, 1);
}
}
while (m_iClusters > 0)
{
float fYaw = random->RandomFloat(0, 360);
QAngle ang(0, fYaw, 0);
Vector newVel;
AngleVectors(ang, &newVel);
newVel *= random->RandomFloat(150, 200);
newVel.z = random->RandomFloat(200, 400);
CASW_Grenade_Cluster *pGrenade = CASW_Grenade_Cluster::Cluster_Grenade_Create(
m_flDamage,
m_DmgRadius,
0,
GetAbsOrigin(), ang, newVel, AngularImpulse(0,0,0), m_hFirer.Get() , m_hCreatorWeapon);
if (pGrenade)
{
pGrenade->m_fEarliestAOEDetonationTime = 0; // children can go whenever they want
pGrenade->m_fEarliestTouchDetonationTime = 0;
pGrenade->m_takedamage = DAMAGE_NO;
pGrenade->SetFuseLength(random->RandomFloat(asw_cluster_grenade_child_fuse_min.GetFloat(), asw_cluster_grenade_child_fuse_max.GetFloat()));
pGrenade->SetClusters(0, false);
pGrenade->m_bKicked = m_bKicked;
}
m_iClusters--;
}
UTIL_Remove( this );
}
float CASW_Grenade_Cluster::GetEarliestAOEDetonationTime()
{
return gpGlobals->curtime + asw_cluster_grenade_min_detonation_time.GetFloat();
}
void CASW_Grenade_Cluster::SetClusters(int iClusters, bool bMaster)
{
BaseClass::SetClusters(iClusters, bMaster);
if (!bMaster)
{
SetThink( &CASW_Grenade_Cluster::Detonate );
SetNextThink( m_fDetonateTime );
}
}