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.
 
 
 
 
 
 

508 lines
14 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "basehlcombatweapon.h"
#include "player.h"
#include "gamerules.h"
#include "grenade_frag.h"
#include "npcevent.h"
#include "engine/IEngineSound.h"
#include "items.h"
#include "in_buttons.h"
#include "soundent.h"
#include "grenade_hopwire.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define GRENADE_TIMER 2.0f //Seconds
#define GRENADE_PAUSED_NO 0
#define GRENADE_PAUSED_PRIMARY 1
#define GRENADE_PAUSED_SECONDARY 2
#define GRENADE_RADIUS 4.0f // inches
//-----------------------------------------------------------------------------
// Fragmentation grenades
//-----------------------------------------------------------------------------
class CWeaponHopwire: public CBaseHLCombatWeapon
{
DECLARE_CLASS( CWeaponHopwire, CBaseHLCombatWeapon );
public:
DECLARE_SERVERCLASS();
void Precache( void );
void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
void PrimaryAttack( void );
void SecondaryAttack( void );
void DecrementAmmo( CBaseCombatCharacter *pOwner );
void ItemPostFrame( void );
void HandleFireOnEmpty( void );
bool HasAnyAmmo( void );
bool Deploy( void );
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
bool Reload( void );
private:
void ThrowGrenade( CBasePlayer *pPlayer );
void RollGrenade( CBasePlayer *pPlayer );
void LobGrenade( CBasePlayer *pPlayer );
// check a throw from vecSrc. If not valid, move the position back along the line to vecEye
void CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc );
bool m_bRedraw; //Draw the weapon again after throwing a grenade
int m_AttackPaused;
bool m_fDrawbackFinished;
CHandle<CGrenadeHopwire> m_hActiveHopWire;
DECLARE_ACTTABLE();
DECLARE_DATADESC();
};
BEGIN_DATADESC( CWeaponHopwire )
DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ),
DEFINE_FIELD( m_AttackPaused, FIELD_INTEGER ),
DEFINE_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hActiveHopWire, FIELD_EHANDLE ),
END_DATADESC()
acttable_t CWeaponHopwire::m_acttable[] =
{
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true },
};
IMPLEMENT_ACTTABLE(CWeaponHopwire);
IMPLEMENT_SERVERCLASS_ST(CWeaponHopwire, DT_WeaponHopwire)
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( weapon_hopwire, CWeaponHopwire );
PRECACHE_WEAPON_REGISTER(weapon_hopwire);
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponHopwire::Precache( void )
{
BaseClass::Precache();
UTIL_PrecacheOther( "npc_grenade_hopwire" );
PrecacheScriptSound( "WeaponFrag.Throw" );
PrecacheScriptSound( "WeaponFrag.Roll" );
m_bRedraw = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponHopwire::Deploy( void )
{
m_bRedraw = false;
m_fDrawbackFinished = false;
return BaseClass::Deploy();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponHopwire::Holster( CBaseCombatWeapon *pSwitchingTo )
{
if ( m_hActiveHopWire != NULL )
return false;
m_bRedraw = false;
m_fDrawbackFinished = false;
return BaseClass::Holster( pSwitchingTo );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEvent -
// *pOperator -
//-----------------------------------------------------------------------------
void CWeaponHopwire::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
{
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
bool fThrewGrenade = false;
switch( pEvent->event )
{
case EVENT_WEAPON_SEQUENCE_FINISHED:
m_fDrawbackFinished = true;
break;
case EVENT_WEAPON_THROW:
ThrowGrenade( pOwner );
DecrementAmmo( pOwner );
fThrewGrenade = true;
break;
case EVENT_WEAPON_THROW2:
RollGrenade( pOwner );
DecrementAmmo( pOwner );
fThrewGrenade = true;
break;
case EVENT_WEAPON_THROW3:
LobGrenade( pOwner );
DecrementAmmo( pOwner );
fThrewGrenade = true;
break;
default:
BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
break;
}
#define RETHROW_DELAY 0.5
if( fThrewGrenade )
{
m_flNextPrimaryAttack = gpGlobals->curtime + RETHROW_DELAY;
m_flNextSecondaryAttack = gpGlobals->curtime + RETHROW_DELAY;
m_flTimeWeaponIdle = FLT_MAX; //NOTE: This is set once the animation has finished up!
// Make a sound designed to scare snipers back into their holes!
CBaseCombatCharacter *pOwner = GetOwner();
if( pOwner )
{
Vector vecSrc = pOwner->Weapon_ShootPosition();
Vector vecDir;
AngleVectors( pOwner->EyeAngles(), &vecDir );
trace_t tr;
UTIL_TraceLine( vecSrc, vecSrc + vecDir * 1024, MASK_SOLID_BRUSHONLY, pOwner, COLLISION_GROUP_NONE, &tr );
CSoundEnt::InsertSound( SOUND_DANGER_SNIPERONLY, tr.endpos, 384, 0.2, pOwner );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Override the ammo behavior so we never disallow pulling the weapon out
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponHopwire::HasAnyAmmo( void )
{
if ( m_hActiveHopWire != NULL )
return true;
return BaseClass::HasAnyAmmo();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponHopwire::Reload( void )
{
if ( !HasPrimaryAmmo() )
return false;
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();
m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
//Mark this as done
m_bRedraw = false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponHopwire::SecondaryAttack( void )
{
/*
if ( m_bRedraw )
return;
if ( !HasPrimaryAmmo() )
return;
CBaseCombatCharacter *pOwner = GetOwner();
if ( pOwner == NULL )
return;
CBasePlayer *pPlayer = ToBasePlayer( pOwner );
if ( pPlayer == NULL )
return;
// Note that this is a secondary attack and prepare the grenade attack to pause.
m_AttackPaused = GRENADE_PAUSED_SECONDARY;
SendWeaponAnim( ACT_VM_PULLBACK_LOW );
// Don't let weapon idle interfere in the middle of a throw!
m_flTimeWeaponIdle = FLT_MAX;
m_flNextSecondaryAttack = FLT_MAX;
// If I'm now out of ammo, switch away
if ( !HasPrimaryAmmo() )
{
pPlayer->SwitchToNextBestWeapon( this );
}
*/
}
//-----------------------------------------------------------------------------
// Purpose: Allow activation even if this is our last piece of ammo
//-----------------------------------------------------------------------------
void CWeaponHopwire::HandleFireOnEmpty( void )
{
if ( m_hActiveHopWire!= NULL )
{
// FIXME: This toggle is hokey
m_bRedraw = false;
PrimaryAttack();
m_bRedraw = true;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponHopwire::PrimaryAttack( void )
{
if ( m_bRedraw )
return;
CBaseCombatCharacter *pOwner = GetOwner();
if ( pOwner == NULL )
return;
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );;
if ( !pPlayer )
return;
// See if we're in trap mode
if ( hopwire_trap.GetBool() && ( m_hActiveHopWire != NULL ) )
{
// Spring the trap
m_hActiveHopWire->Detonate();
m_hActiveHopWire = NULL;
// Don't allow another throw for awhile
m_flTimeWeaponIdle = m_flNextPrimaryAttack = gpGlobals->curtime + 2.0f;
return;
}
// Note that this is a primary attack and prepare the grenade attack to pause.
/*
m_AttackPaused = GRENADE_PAUSED_PRIMARY;
SendWeaponAnim( ACT_VM_PULLBACK_HIGH );
*/
m_AttackPaused = GRENADE_PAUSED_SECONDARY;
SendWeaponAnim( ACT_VM_PULLBACK_LOW );
// Put both of these off indefinitely. We do not know how long
// the player will hold the grenade.
m_flTimeWeaponIdle = FLT_MAX;
m_flNextPrimaryAttack = FLT_MAX;
// If I'm now out of ammo, switch away
/*
if ( !HasPrimaryAmmo() )
{
pPlayer->SwitchToNextBestWeapon( this );
}
*/
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOwner -
//-----------------------------------------------------------------------------
void CWeaponHopwire::DecrementAmmo( CBaseCombatCharacter *pOwner )
{
pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponHopwire::ItemPostFrame( void )
{
if( m_fDrawbackFinished )
{
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if (pOwner)
{
switch( m_AttackPaused )
{
case GRENADE_PAUSED_PRIMARY:
if( !(pOwner->m_nButtons & IN_ATTACK) )
{
SendWeaponAnim( ACT_VM_THROW );
m_fDrawbackFinished = false;
}
break;
case GRENADE_PAUSED_SECONDARY:
if( !(pOwner->m_nButtons & (IN_ATTACK|IN_ATTACK2)) )
{
//See if we're ducking
if ( pOwner->m_nButtons & IN_DUCK )
{
//Send the weapon animation
SendWeaponAnim( ACT_VM_SECONDARYATTACK );
}
else
{
//Send the weapon animation
SendWeaponAnim( ACT_VM_HAULBACK );
}
m_fDrawbackFinished = false;
}
break;
default:
break;
}
}
}
BaseClass::ItemPostFrame();
if ( m_bRedraw )
{
if ( IsViewModelSequenceFinished() )
{
Reload();
}
}
}
// check a throw from vecSrc. If not valid, move the position back along the line to vecEye
void CWeaponHopwire::CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc )
{
trace_t tr;
UTIL_TraceHull( vecEye, vecSrc, -Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2), Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2),
pPlayer->PhysicsSolidMaskForEntity(), pPlayer, pPlayer->GetCollisionGroup(), &tr );
if ( tr.DidHit() )
{
vecSrc = tr.endpos;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
//-----------------------------------------------------------------------------
void CWeaponHopwire::ThrowGrenade( CBasePlayer *pPlayer )
{
Vector vecEye = pPlayer->EyePosition();
Vector vForward, vRight;
pPlayer->EyeVectors( &vForward, &vRight, NULL );
Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f;
CheckThrowPosition( pPlayer, vecEye, vecSrc );
vForward[2] += 0.1f;
Vector vecThrow;
pPlayer->GetVelocity( &vecThrow, NULL );
vecThrow += vForward * 1200;
m_hActiveHopWire = static_cast<CGrenadeHopwire *> (HopWire_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, GRENADE_TIMER ));
m_bRedraw = true;
WeaponSound( SINGLE );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
//-----------------------------------------------------------------------------
void CWeaponHopwire::LobGrenade( CBasePlayer *pPlayer )
{
Vector vecEye = pPlayer->EyePosition();
Vector vForward, vRight;
pPlayer->EyeVectors( &vForward, &vRight, NULL );
Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f + Vector( 0, 0, -8 );
CheckThrowPosition( pPlayer, vecEye, vecSrc );
Vector vecThrow;
pPlayer->GetVelocity( &vecThrow, NULL );
vecThrow += vForward * 350 + Vector( 0, 0, 50 );
m_hActiveHopWire = static_cast<CGrenadeHopwire *> (HopWire_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(200,random->RandomInt(-600,600),0), pPlayer, GRENADE_TIMER ));
WeaponSound( WPN_DOUBLE );
m_bRedraw = true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
//-----------------------------------------------------------------------------
void CWeaponHopwire::RollGrenade( CBasePlayer *pPlayer )
{
// BUGBUG: Hardcoded grenade width of 4 - better not change the model :)
Vector vecSrc;
pPlayer->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecSrc );
vecSrc.z += GRENADE_RADIUS;
Vector vecFacing = pPlayer->BodyDirection2D( );
// no up/down direction
vecFacing.z = 0;
VectorNormalize( vecFacing );
trace_t tr;
UTIL_TraceLine( vecSrc, vecSrc - Vector(0,0,16), MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0 )
{
// compute forward vec parallel to floor plane and roll grenade along that
Vector tangent;
CrossProduct( vecFacing, tr.plane.normal, tangent );
CrossProduct( tr.plane.normal, tangent, vecFacing );
}
vecSrc += (vecFacing * 18.0);
CheckThrowPosition( pPlayer, pPlayer->WorldSpaceCenter(), vecSrc );
Vector vecThrow;
pPlayer->GetVelocity( &vecThrow, NULL );
vecThrow += vecFacing * 700;
// put it on its side
QAngle orientation(0,pPlayer->GetLocalAngles().y,-90);
// roll it
AngularImpulse rotSpeed(0,0,720);
m_hActiveHopWire = static_cast<CGrenadeHopwire *> (HopWire_Create( vecSrc, orientation, vecThrow, rotSpeed, pPlayer, GRENADE_TIMER ));
WeaponSound( SPECIAL1 );
m_bRedraw = true;
}