Team Fortress 2 Source Code as on 22/4/2020
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.
 
 
 
 
 
 

438 lines
11 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "weapon_csbase.h"
#include "gamerules.h"
#include "npcevent.h"
#include "engine/IEngineSound.h"
#include "weapon_basecsgrenade.h"
#include "in_buttons.h"
#include "datacache/imdlcache.h"
#ifdef CLIENT_DLL
#include "c_cs_player.h"
#else
#include "cs_player.h"
#include "items.h"
#include "../../server/cstrike/cs_gamestats.h"
#endif
#define GRENADE_TIMER 1.5f //Seconds
IMPLEMENT_NETWORKCLASS_ALIASED( BaseCSGrenade, DT_BaseCSGrenade )
BEGIN_NETWORK_TABLE(CBaseCSGrenade, DT_BaseCSGrenade)
#ifndef CLIENT_DLL
SendPropBool( SENDINFO(m_bRedraw) ),
SendPropBool( SENDINFO(m_bPinPulled) ),
SendPropFloat( SENDINFO(m_fThrowTime), 0, SPROP_NOSCALE ),
#else
RecvPropBool( RECVINFO(m_bRedraw) ),
RecvPropBool( RECVINFO(m_bPinPulled) ),
RecvPropFloat( RECVINFO(m_fThrowTime) ),
#endif
END_NETWORK_TABLE()
#if defined CLIENT_DLL
BEGIN_PREDICTION_DATA( CBaseCSGrenade )
DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
#endif
LINK_ENTITY_TO_CLASS( weapon_basecsgrenade, CBaseCSGrenade );
#ifndef CLIENT_DLL
ConVar sv_ignoregrenaderadio( "sv_ignoregrenaderadio", "0", 0, "Turn off Fire in the hole messages" );
#endif
CBaseCSGrenade::CBaseCSGrenade()
{
m_bRedraw = false;
m_bPinPulled = false;
m_fThrowTime = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseCSGrenade::Precache()
{
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseCSGrenade::Deploy()
{
m_bRedraw = false;
m_bPinPulled = false;
m_fThrowTime = 0;
#ifndef CLIENT_DLL
// if we're officially out of grenades, ditch this weapon
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
{
pPlayer->Weapon_Drop( this, NULL, NULL );
UTIL_Remove(this);
return false;
}
#endif
return BaseClass::Deploy();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseCSGrenade::Holster( CBaseCombatWeapon *pSwitchingTo )
{
m_bRedraw = false;
m_bPinPulled = false; // when this is holstered make sure the pin isn’t pulled.
m_fThrowTime = 0;
#ifndef CLIENT_DLL
// If they attempt to switch weapons before the throw animation is done,
// allow it, but kill the weapon if we have to.
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
{
CBaseCombatCharacter *pOwner = (CBaseCombatCharacter *)pPlayer;
pOwner->Weapon_Drop( this );
UTIL_Remove(this);
}
#endif
return BaseClass::Holster( pSwitchingTo );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseCSGrenade::PrimaryAttack()
{
if ( m_bRedraw || m_bPinPulled || m_fThrowTime > 0.0f )
return;
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer || pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
return;
// The pull pin animation has to finish, then we wait until they aren't holding the primary
// attack button, then throw the grenade.
SendWeaponAnim( ACT_VM_PULLPIN );
m_bPinPulled = true;
// Don't let weapon idle interfere in the middle of a throw!
MDLCACHE_CRITICAL_SECTION();
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseCSGrenade::SecondaryAttack()
{
if ( m_bRedraw )
return;
CCSPlayer *pPlayer = GetPlayerOwner();
if ( pPlayer == NULL )
return;
//See if we're ducking
if ( pPlayer->GetFlags() & FL_DUCKING )
{
//Send the weapon animation
SendWeaponAnim( ACT_VM_SECONDARYATTACK );
}
else
{
//Send the weapon animation
SendWeaponAnim( ACT_VM_HAULBACK );
}
// Don't let weapon idle interfere in the middle of a throw!
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseCSGrenade::Reload()
{
if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
{
//Redraw the weapon
SendWeaponAnim( ACT_VM_DRAW );
//Update our times
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
//Mark this as done
// m_bRedraw = false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseCSGrenade::ItemPostFrame()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return;
CBaseViewModel *vm = pPlayer->GetViewModel( m_nViewModelIndex );
if ( !vm )
return;
// If they let go of the fire button, they want to throw the grenade.
if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) )
{
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE );
StartGrenadeThrow();
MDLCACHE_CRITICAL_SECTION();
m_bPinPulled = false;
SendWeaponAnim( ACT_VM_THROW );
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); // we're still throwing, so reset our next primary attack
#ifndef CLIENT_DLL
IGameEvent * event = gameeventmanager->CreateEvent( "weapon_fire" );
if( event )
{
const char *weaponName = STRING( m_iClassname );
if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
{
weaponName += 7;
}
event->SetInt( "userid", pPlayer->GetUserID() );
event->SetString( "weapon", weaponName );
gameeventmanager->FireEvent( event );
}
#endif
}
else if ((m_fThrowTime > 0) && (m_fThrowTime < gpGlobals->curtime))
{
// only decrement our ammo when we actually create the projectile
DecrementAmmo( pPlayer );
ThrowGrenade();
}
else if( m_bRedraw )
{
// Has the throw animation finished playing
if( m_flTimeWeaponIdle < gpGlobals->curtime )
{
#ifdef GAME_DLL
// if we're officially out of grenades, ditch this weapon
if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
{
pPlayer->Weapon_Drop( this, NULL, NULL );
UTIL_Remove(this);
}
else
{
pPlayer->SwitchToNextBestWeapon( this );
}
#endif
return; //don't animate this grenade any more!
}
}
else if( !m_bRedraw )
{
BaseClass::ItemPostFrame();
}
}
#ifdef CLIENT_DLL
void CBaseCSGrenade::DecrementAmmo( CBaseCombatCharacter *pOwner )
{
}
void CBaseCSGrenade::DropGrenade()
{
m_bRedraw = true;
m_fThrowTime = 0.0f;
}
void CBaseCSGrenade::ThrowGrenade()
{
m_bRedraw = true;
m_fThrowTime = 0.0f;
}
void CBaseCSGrenade::StartGrenadeThrow()
{
m_fThrowTime = gpGlobals->curtime + 0.1f;
}
#else
BEGIN_DATADESC( CBaseCSGrenade )
DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ),
END_DATADESC()
int CBaseCSGrenade::CapabilitiesGet()
{
return bits_CAP_WEAPON_RANGE_ATTACK1;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOwner -
//-----------------------------------------------------------------------------
void CBaseCSGrenade::DecrementAmmo( CBaseCombatCharacter *pOwner )
{
pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
}
void CBaseCSGrenade::StartGrenadeThrow()
{
m_fThrowTime = gpGlobals->curtime + 0.1f;
}
void CBaseCSGrenade::ThrowGrenade()
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer )
{
Assert( false );
return;
}
QAngle angThrow = pPlayer->LocalEyeAngles();
Vector vForward, vRight, vUp;
if ( angThrow.x < 0 )
{
angThrow.x += 360; // make sure we have a positive angle from LocalEyeAngles()
}
if ( angThrow.x < 90 )
angThrow.x = -10 + angThrow.x * ((90 + 10) / 90.0);
else
{
angThrow.x = 360.0f - angThrow.x;
angThrow.x = -10 + angThrow.x * -((90 - 10) / 90.0);
}
float flVel = (90 - angThrow.x) * 6;
if (flVel > 750)
flVel = 750;
AngleVectors( angThrow, &vForward, &vRight, &vUp );
Vector vecSrc = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset();
// We want to throw the grenade from 16 units out. But that can cause problems if we're facing
// a thin wall. Do a hull trace to be safe.
trace_t trace;
Vector mins( -2, -2, -2 );
Vector maxs( 2, 2, 2 );
UTIL_TraceHull( vecSrc, vecSrc + vForward * 16, mins, maxs, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &trace );
vecSrc = trace.endpos;
Vector vecThrow = vForward * flVel + pPlayer->GetAbsVelocity();
EmitGrenade( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer );
m_bRedraw = true;
m_fThrowTime = 0.0f;
CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
if( pCSPlayer )
{
if ( !sv_ignoregrenaderadio.GetBool() )
{
pCSPlayer->Radio( "Radio.FireInTheHole", "#Cstrike_TitlesTXT_Fire_in_the_hole" );
}
CCS_GameStats.IncrementStat(pCSPlayer, CSSTAT_GRENADES_THROWN, 1);
}
}
void CBaseCSGrenade::DropGrenade()
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer )
{
Assert( false );
return;
}
Vector vForward;
pPlayer->EyeVectors( &vForward );
Vector vecSrc = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset() + vForward * 16;
Vector vecVel = pPlayer->GetAbsVelocity();
EmitGrenade( vecSrc, vec3_angle, vecVel, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer );
CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
if( pCSPlayer )
{
CCS_GameStats.IncrementStat(pCSPlayer, CSSTAT_GRENADES_THROWN, 1);
}
m_bRedraw = true;
m_fThrowTime = 0.0f;
}
void CBaseCSGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer )
{
Assert( 0 && "CBaseCSGrenade::EmitGrenade should not be called. Make sure to implement this in your subclass!\n" );
}
bool CBaseCSGrenade::AllowsAutoSwitchFrom( void ) const
{
return !m_bPinPulled;
}
#endif