|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "npcevent.h"
#include "in_buttons.h"
#ifdef CLIENT_DLL
#include "c_hl2mp_player.h"
#include "c_te_effect_dispatch.h"
#else
#include "hl2mp_player.h"
#include "te_effect_dispatch.h"
#include "grenade_frag.h"
#endif
#include "weapon_ar2.h"
#include "effect_dispatch_data.h"
#include "weapon_hl2mpbasehlmpcombatweapon.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define GRENADE_TIMER 2.5f //Seconds
#define GRENADE_PAUSED_NO 0
#define GRENADE_PAUSED_PRIMARY 1
#define GRENADE_PAUSED_SECONDARY 2
#define GRENADE_RADIUS 4.0f // inches
#define GRENADE_DAMAGE_RADIUS 250.0f
#ifdef CLIENT_DLL
#define CWeaponFrag C_WeaponFrag
#endif
//-----------------------------------------------------------------------------
// Fragmentation grenades
//-----------------------------------------------------------------------------
class CWeaponFrag: public CBaseHL2MPCombatWeapon { DECLARE_CLASS( CWeaponFrag, CBaseHL2MPCombatWeapon ); public:
DECLARE_NETWORKCLASS(); DECLARE_PREDICTABLE();
CWeaponFrag();
void Precache( void ); void PrimaryAttack( void ); void SecondaryAttack( void ); void DecrementAmmo( CBaseCombatCharacter *pOwner ); void ItemPostFrame( void );
bool Deploy( void ); bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); bool Reload( void );
#ifndef CLIENT_DLL
void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); #endif
void ThrowGrenade( CBasePlayer *pPlayer ); bool IsPrimed( bool ) { return ( m_AttackPaused != 0 ); } private:
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 );
CNetworkVar( bool, m_bRedraw ); //Draw the weapon again after throwing a grenade
CNetworkVar( int, m_AttackPaused ); CNetworkVar( bool, m_fDrawbackFinished );
CWeaponFrag( const CWeaponFrag & );
#ifndef CLIENT_DLL
DECLARE_ACTTABLE(); #endif
};
#ifndef CLIENT_DLL
acttable_t CWeaponFrag::m_acttable[] = { { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_GRENADE, false }, { ACT_HL2MP_RUN, ACT_HL2MP_RUN_GRENADE, false }, { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_GRENADE, false }, { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_GRENADE, false }, { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false }, { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false }, { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_GRENADE, false }, };
IMPLEMENT_ACTTABLE(CWeaponFrag);
#endif
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFrag, DT_WeaponFrag )
BEGIN_NETWORK_TABLE( CWeaponFrag, DT_WeaponFrag )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bRedraw ) ), RecvPropBool( RECVINFO( m_fDrawbackFinished ) ), RecvPropInt( RECVINFO( m_AttackPaused ) ), #else
SendPropBool( SENDINFO( m_bRedraw ) ), SendPropBool( SENDINFO( m_fDrawbackFinished ) ), SendPropInt( SENDINFO( m_AttackPaused ) ), #endif
END_NETWORK_TABLE()
#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CWeaponFrag ) DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_AttackPaused, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), END_PREDICTION_DATA() #endif
LINK_ENTITY_TO_CLASS( weapon_frag, CWeaponFrag ); PRECACHE_WEAPON_REGISTER(weapon_frag);
CWeaponFrag::CWeaponFrag( void ) : CBaseHL2MPCombatWeapon() { m_bRedraw = false; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponFrag::Precache( void ) { BaseClass::Precache();
#ifndef CLIENT_DLL
UTIL_PrecacheOther( "npc_grenade_frag" ); #endif
PrecacheScriptSound( "WeaponFrag.Throw" ); PrecacheScriptSound( "WeaponFrag.Roll" ); }
#ifndef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEvent -
// *pOperator -
//-----------------------------------------------------------------------------
void CWeaponFrag::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!
} }
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponFrag::Deploy( void ) { m_bRedraw = false; m_fDrawbackFinished = false;
return BaseClass::Deploy(); }
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponFrag::Holster( CBaseCombatWeapon *pSwitchingTo ) { m_bRedraw = false; m_fDrawbackFinished = false;
return BaseClass::Holster( pSwitchingTo ); }
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponFrag::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 CWeaponFrag::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:
//-----------------------------------------------------------------------------
void CWeaponFrag::PrimaryAttack( void ) { if ( m_bRedraw ) return;
CBaseCombatCharacter *pOwner = GetOwner(); if ( pOwner == NULL ) { return; }
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );;
if ( !pPlayer ) 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 ); // 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 CWeaponFrag::DecrementAmmo( CBaseCombatCharacter *pOwner ) { pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponFrag::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_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 CWeaponFrag::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; } }
void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade ) { CWeaponFrag *pWeaponFrag = dynamic_cast<CWeaponFrag*>( pGrenade );
if ( pWeaponFrag ) { pWeaponFrag->ThrowGrenade( pPlayer ); pWeaponFrag->DecrementAmmo( pPlayer ); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
//-----------------------------------------------------------------------------
void CWeaponFrag::ThrowGrenade( CBasePlayer *pPlayer ) { #ifndef CLIENT_DLL
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[0] += 0.1f;
vForward[2] += 0.1f;
Vector vecThrow; pPlayer->GetVelocity( &vecThrow, NULL ); vecThrow += vForward * 1200; CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, GRENADE_TIMER, false );
if ( pGrenade ) { if ( pPlayer && pPlayer->m_lifeState != LIFE_ALIVE ) { pPlayer->GetVelocity( &vecThrow, NULL );
IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject(); if ( pPhysicsObject ) { pPhysicsObject->SetVelocity( &vecThrow, NULL ); } } pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage ); pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS ); } #endif
m_bRedraw = true;
WeaponSound( SINGLE ); // player "shoot" animation
pPlayer->SetAnimation( PLAYER_ATTACK1 ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
//-----------------------------------------------------------------------------
void CWeaponFrag::LobGrenade( CBasePlayer *pPlayer ) { #ifndef CLIENT_DLL
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 ); CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(200,random->RandomInt(-600,600),0), pPlayer, GRENADE_TIMER, false );
if ( pGrenade ) { pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage ); pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS ); } #endif
WeaponSound( WPN_DOUBLE );
// player "shoot" animation
pPlayer->SetAnimation( PLAYER_ATTACK1 );
m_bRedraw = true; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
//-----------------------------------------------------------------------------
void CWeaponFrag::RollGrenade( CBasePlayer *pPlayer ) { #ifndef CLIENT_DLL
// 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); CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, orientation, vecThrow, rotSpeed, pPlayer, GRENADE_TIMER, false );
if ( pGrenade ) { pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage ); pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS ); }
#endif
WeaponSound( SPECIAL1 );
// player "shoot" animation
pPlayer->SetAnimation( PLAYER_ATTACK1 );
m_bRedraw = true; }
|