|
|
//========= 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<EFBFBD>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 |
|
|
|
|
|
|