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.

407 lines
11 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_player.h"
#include "tf_basecombatweapon.h"
#include "basegrenade_shared.h"
#include "weapon_limpetmine.h"
#include "engine/IEngineSound.h"
#include "grenade_limpetmine.h"
#include "tf_shareddefs.h"
#include "IEffects.h"
#include "player.h"
#include "basetfvehicle.h"
#define LIMPET_LIVE_TIME 0.5 // Time it takes before a limpet can be detonated after placement
#define LIMPET_LIFETIME 120 // After this time, limpets fizzle naturally
#define LIMPET_FIZZLE_DURATION 2.0f
#define LIMPET_MINS Vector(-5, -5, 0)
#define LIMPET_MAXS Vector( 5, 5, 10)
// Damage CVars
ConVar weapon_limpetmine_grenade_damage( "weapon_limpetmine_grenade_damage","0", FCVAR_NONE, "Limpet Mine's grenade maximum damage" );
ConVar weapon_limpetmine_grenade_radius( "weapon_limpetmine_grenade_radius","0", FCVAR_NONE, "Limpet Mine's grenade splash radius" );
// Global Savedata for friction modifier
BEGIN_DATADESC( CLimpetMine )
// Function Pointers
DEFINE_THINKFUNC( LiveThink ),
DEFINE_ENTITYFUNC( StickyTouch ),
DEFINE_THINKFUNC( LimpetThink ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST(CLimpetMine, DT_LimpetMine)
SendPropInt(SENDINFO(m_bLive), 1, SPROP_UNSIGNED ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( grenade_limpetmine, CLimpetMine );
//-----------------------------------------------------------------------------
// Static initializers:
//-----------------------------------------------------------------------------
CLimpetMine* CLimpetMine::allLimpets = NULL;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CLimpetMine::CLimpetMine( void )
{
UseClientSideAnimation();
// ---------------------------------
// Add to linked list of limpets
// ---------------------------------
nextLimpet = CLimpetMine::allLimpets;
CLimpetMine::allLimpets = this;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CLimpetMine::~CLimpetMine( void )
{
// --------------------------------------
// Remove from linked list of limpets
// --------------------------------------
CLimpetMine *pLimpet = CLimpetMine::allLimpets;
if (pLimpet == this)
{
CLimpetMine::allLimpets = pLimpet->nextLimpet;
}
else
{
while (pLimpet)
{
if (pLimpet->nextLimpet == this)
{
pLimpet->nextLimpet = pLimpet->nextLimpet->nextLimpet;
break;
}
pLimpet = pLimpet->nextLimpet;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLimpetMine::Precache( void )
{
PrecacheModel( "models/projectiles/grenade_limpet.mdl" );
PrecacheScriptSound( "LimpetMine.Beep" );
PrecacheScriptSound( "LimpetMine.Fizzle" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLimpetMine::Spawn( void )
{
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
SetSolid( SOLID_BBOX );
SetGravity( 1.0 );
SetFriction( 1.25 );
SetModel( "models/projectiles/grenade_limpet.mdl");
UTIL_SetSize( this, LIMPET_MINS, LIMPET_MAXS );
m_bLive = false;
m_bFizzleInit = false;
m_bEMPed = false;
SetThink( LiveThink );
SetNextThink( gpGlobals->curtime + LIMPET_LIVE_TIME );
SetTouch( StickyTouch );
// Causes these to collide with everything but NPCs and players
SetCollisionGroup( TFCOLLISION_GROUP_GRENADE );
AddFlag( FL_OBJECT );
// Prevent sentry guns detecting these.
AddFlag( FL_NOTARGET );
// Set my damages to the cvar values
SetDamage( weapon_limpetmine_grenade_damage.GetFloat() );
SetDamageRadius( weapon_limpetmine_grenade_radius.GetFloat() );
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this limpet mine can be detonated yet
//-----------------------------------------------------------------------------
bool CLimpetMine::IsLive( void )
{
return m_bLive;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLimpetMine::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
Assert( pCaller );
// USE_SET Means we're being asked to fizzle out and dielf
if ( useType == USE_SET )
{
if ( !m_bFizzleInit )
{
// Set the defuse - fizzle think
m_flFizzleDuration = gpGlobals->curtime + 0.3;
SetThink( LimpetThink );
SetNextThink( gpGlobals->curtime + 0.1f );
m_bFizzleInit = true;
}
}
else if ( IsLive() )
{
// Get the TF2 player that owns this limpet:
CBaseTFPlayer *pPlayer = NULL;
if ( m_hLauncher )
{
pPlayer = ToBaseTFPlayer( m_hLauncher->GetOwner() );
}
// Get the TF2 player that is calling this object, if any:
CBaseTFPlayer *pCallerPlayer = NULL;
if ( pCaller )
{
pCallerPlayer = ToBaseTFPlayer( pCaller );
}
// If the owning player is directly using the limpet, then pick it up:
if( pPlayer && pCallerPlayer && pPlayer->IsSameClass( pCallerPlayer ) )
{
if ( m_hLauncher )
{
pPlayer->GiveAmmo( 1, m_hLauncher->m_iPrimaryAmmoType );
m_hLauncher->DecrementLimpets();
}
UTIL_Remove( this );
}
else if ( pActivator && !pActivator->InSameTeam( this ) )
{
// only the owning player can detonate his own limpets, so return if this isn't the owner.
return;
}
// We're being detonated
// If are EMPed then we cannot be detonated.
else if ( !IsEMPed() )
{
// Beep and detonate soon afterwards
EmitSound( "LimpetMine.Beep" );
SetThink( Detonate );
SetNextThink( gpGlobals->curtime + 0.5f );
// Pretend I'm not live anymore so I don't get exploded again
m_bLive = false;
if ( m_hLauncher )
{
m_hLauncher->DecrementLimpets();
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CLimpetMine::TakeEMPDamage( float duration )
{
if ( !m_bFizzleInit )
{
// Set the defuse - fizzle think
float flDuration = MIN( duration, LIMPET_FIZZLE_DURATION );
m_flFizzleDuration = gpGlobals->curtime + ( flDuration - 1.0f );
SetThink( LimpetThink );
SetNextThink( gpGlobals->curtime + 0.1f );
m_bFizzleInit = true;
m_bEMPed = true;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Create a limpet mine
//-----------------------------------------------------------------------------
CLimpetMine* CLimpetMine::Create( const Vector &vecOrigin, const Vector &vecForward, CBasePlayer *pOwner )
{
CLimpetMine *pGrenade = (CLimpetMine*)CreateEntityByName("grenade_limpetmine");
pGrenade->Teleport( &vecOrigin, NULL, NULL );
pGrenade->Spawn();
pGrenade->SetOwnerEntity( pOwner );
pGrenade->SetThrower( pOwner );
pGrenade->SetAbsVelocity( vecForward );
pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
return pGrenade;
}
//-----------------------------------------------------------------------------
// Purpose: Keep a pointer to the launcher (parent)
//-----------------------------------------------------------------------------
void CLimpetMine::SetLauncher( CWeaponLimpetmine *pLauncher )
{
m_hLauncher = pLauncher;
}
//-----------------------------------------------------------------------------
// Purpose: Go Live
//-----------------------------------------------------------------------------
void CLimpetMine::LiveThink( void )
{
m_bLive = true;
// Remove myself after a while
m_flFizzleDuration = gpGlobals->curtime + LIMPET_LIFETIME + 0.3;
SetNextThink( gpGlobals->curtime + 0.1f );
SetThink( LimpetThink );
}
//-----------------------------------------------------------------------------
// Purpose: Make the grenade stick to whatever it touches
//-----------------------------------------------------------------------------
void CLimpetMine::StickyTouch( CBaseEntity *pOther )
{
Assert( pOther );
if ( !pOther->IsSolid() )
return;
if ( !pOther->IsBSPModel() && !pOther->GetBaseAnimating() )
return;
BounceSound();
m_bStuckToTarget = false;
// Bounce off of shields
if ( pOther->GetCollisionGroup() == TFCOLLISION_GROUP_SHIELD )
{
// Move away from the shield...
// Fling it out a little extra along the plane normal
Vector vecNewVelocity;
Vector vecCenter;
AngleVectors( pOther->GetAbsAngles(), &vecCenter );
VectorMultiply( vecCenter, 400.0f, vecNewVelocity );
SetAbsVelocity( vecNewVelocity );
return;
}
// Only stick to non-moving entities
if ( !pOther->GetBaseAnimating() )
return;
// Don't stick to team members
if ( InSameTeam( pOther ) )
return;
// ROBIN: Removed stick to enemies for now
{
SetAbsVelocity( vec3_origin );
SetMoveType( MOVETYPE_NONE );
return;
}
m_bStuckToTarget = true;
// Orient to stick to the wall I just hit
trace_t tr;
Vector vecPrev = GetLocalOrigin() - (GetAbsVelocity() * 0.1);
UTIL_TraceLine( vecPrev, vecPrev + (GetAbsVelocity() * 2), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1 )
{
// Orient the *up* axis to be along the plane normal
Vector perp( 1, 0, 0 );
Vector forward, right;
CrossProduct( perp, tr.plane.normal, forward );
if (forward.LengthSqr() < 0.1f)
{
perp.Init( 0, 1, 0 );
CrossProduct( perp, tr.plane.normal, forward );
}
VectorNormalize( forward );
CrossProduct( tr.plane.normal, forward, right );
VMatrix orientation( forward, right, tr.plane.normal );
QAngle angles;
MatrixToAngles( orientation, angles );
SetAbsAngles( angles );
}
SetAbsVelocity( vec3_origin );
SetMoveType( MOVETYPE_NONE );
// At this point, it shouldn't affect player movement
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
BounceSound();
SetParent( pOther );
}
//-----------------------------------------------------------------------------
// Purpose: Once the limpet's active, it starts running this
//-----------------------------------------------------------------------------
void CLimpetMine::LimpetThink( void )
{
SetNextThink( gpGlobals->curtime + 0.1f );
// If I'm not ready to fizzle yet, make sure my parent's still there.
if ( m_bStuckToTarget )
{
// Lost our parent?
if ( !GetMoveParent() )
{
m_bStuckToTarget = false;
// Fall to the ground
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
SetTouch( StickyTouch );
}
}
float flDeltaTime = m_flFizzleDuration - gpGlobals->curtime;
// Not ready to fizzle yet?
if ( flDeltaTime > 0.3 )
return;
// Start fizzling
if ( flDeltaTime > 0.0f )
{
// Emit a fizzle sound
EmitSound( "LimpetMine.Fizzle" );
g_pEffects->Sparks( GetAbsOrigin() );
// Smoke.
UTIL_Smoke( GetAbsOrigin(), random->RandomInt( 1, 3), 10 );
}
else
{
// Done fizzling - no more sound.
StopSound( "LimpetMine.Fizzle" );
UTIL_Remove( this );
// Remove this limpet mine from the launcher deployment count.
if ( m_hLauncher )
{
m_hLauncher->DecrementLimpets();
}
return;
}
}