text: //========= 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;
  }
  
  