text: ﻿//========= Copyright Valve Corporation, All rights reserved. ============//
  //
  //
  //=============================================================================
  #include "cbase.h"
  #include "tf_weapon_revolver.h"
  #include "tf_fx_shared.h"
  #include "datamap.h"
  #include "tf_weaponbase_gun.h"
  
  // Client specific.
  #ifdef CLIENT_DLL
  #include "c_tf_player.h"
  // Server specific.
  #else
  #include "tf_player.h"
  #endif
  
  //=============================================================================
  //
  // Weapon Revolver tables.
  //
  IMPLEMENT_NETWORKCLASS_ALIASED( TFRevolver, DT_WeaponRevolver )
  
  BEGIN_NETWORK_TABLE( CTFRevolver, DT_WeaponRevolver )
  END_NETWORK_TABLE()
  
  #ifdef CLIENT_DLL
  BEGIN_PREDICTION_DATA( CTFRevolver )
  DEFINE_PRED_FIELD( m_flLastAccuracyCheck, FIELD_FLOAT, 0 ),
  END_PREDICTION_DATA()
  #endif
  
  LINK_ENTITY_TO_CLASS( tf_weapon_revolver, CTFRevolver );
  PRECACHE_WEAPON_REGISTER( tf_weapon_revolver );
  
  // Server specific.
  #ifndef CLIENT_DLL
  BEGIN_DATADESC( CTFRevolver )
  END_DATADESC()
  #endif
  
  #ifdef STAGING_ONLY
  CREATE_SIMPLE_WEAPON_TABLE( TFRevolver_Secondary, tf_weapon_revolver_secondary )
  #endif
  
  //=============================================================================
  //
  // Weapon Revolver functions.
  //
  
  CTFRevolver::CTFRevolver()
  {
  	m_flLastAccuracyCheck = 0.f;
  	m_flAccuracyCheckTime = 0.f;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  bool CTFRevolver::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
  {
  	// The the owning local player.
  	CTFPlayer *pPlayer = GetTFPlayerOwner();
  	if ( !pPlayer )
  		return false;
  
  	if ( pPlayer->IsPlayerClass( TF_CLASS_SPY ) )
  	{
  		if ( pPlayer->m_Shared.InCond( TF_COND_STEALTHED ) )
  		{
  			return false;
  		}
  	}
  
  	if ( pPlayer->m_Shared.IsFeignDeathReady() )
  		return false; // Can't reload if our feign death arm is up.
  
  	return BaseClass::DefaultReload( iClipSize1, iClipSize2, iActivity );
  
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  int	CTFRevolver::GetDamageType( void ) const
  {
  	if ( CanHeadshot() && (gpGlobals->curtime - m_flLastAccuracyCheck > 1.f) )
  	{
  		int iDamageType = BaseClass::GetDamageType() | DMG_USE_HITLOCATIONS;
  		return iDamageType;
  	}
  
  	return BaseClass::GetDamageType();
  }
  
  bool CTFRevolver::CanFireCriticalShot( bool bIsHeadshot )
  {
  	if ( !BaseClass::CanFireCriticalShot( bIsHeadshot ) )
  		return false;
  
  	CTFPlayer *pPlayer = GetTFPlayerOwner();
  	if ( pPlayer && pPlayer->m_Shared.IsCritBoosted() )
  		return true;
  
  	// can only fire a crit shot if this is a headshot, unless we're critboosted
  	if ( !bIsHeadshot )
  	{
  		// Base revolver still randomly crits. Ambassador doesn't.
  		return !CanHeadshot();
  	}
  
  	return true;
  }
  
  void CTFRevolver::PrimaryAttack( void )
  {
  	// Check for ammunition.
  	if ( m_iClip1 <= 0 && m_iClip1 != -1 )
  		return;
  
  	// Are we capable of firing again?
  	if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  		return;
  
  	// Get the player owning the weapon.
  	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  	if ( !pPlayer )
  		return;
  
  	if ( !CanAttack() )
  		return;
  
  	BaseClass::PrimaryAttack();
  
  	if ( HasLastShotCritical() )
  	{
  		pPlayer->m_Shared.AddCond( TF_COND_CRITBOOSTED );
  	}
  	else
  	{
  		int iAttr = 0;
  		CALL_ATTRIB_HOOK_INT( iAttr, last_shot_crits );
  		if ( iAttr )
  		{
  			pPlayer->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  		}
  	}
  
  	m_flLastAccuracyCheck = gpGlobals->curtime;
  
  	if ( SapperKillsCollectCrits() )
  	{
  		// Do this after the attack, so that we know if we are doing custom damage
  		CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  		if ( pOwner )
  		{
  			int iRevengeCrits = pOwner->m_Shared.GetRevengeCrits();
  			if ( iRevengeCrits > 0 )
  			{
  				pOwner->m_Shared.SetRevengeCrits( iRevengeCrits-1 );
  			}
  		}
  	}
  #ifdef GAME_DLL
  	// Lower bonus for each attack
  	int iExtraDamageOnHitPenalty = 0;
  	CALL_ATTRIB_HOOK_INT( iExtraDamageOnHitPenalty, extra_damage_on_hit_penalty );
  	if ( iExtraDamageOnHitPenalty )
  	{
  		int iDecaps = pPlayer->m_Shared.GetDecapitations();
  		pPlayer->m_Shared.SetDecapitations( Max( 0, iDecaps - iExtraDamageOnHitPenalty ) );
  	}
  #endif
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  float CTFRevolver::GetWeaponSpread( void )
  {
  	float fSpread = BaseClass::GetWeaponSpread();
  
  	if ( CanHeadshot() )
  	{
  		// We are highly accurate for our first shot.
  		float flTimeSinceCheck = gpGlobals->curtime - m_flLastAccuracyCheck;
  		fSpread = RemapValClamped( flTimeSinceCheck, 1.0f, 0.5f, 0.0f, fSpread+0.0f );
  	}
  
  	return fSpread;
  }
  
  #ifdef CLIENT_DLL
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CTFRevolver::GetWeaponCrosshairScale( float &flScale )
  {
  	C_TFPlayer* pTFPlayer = ToTFPlayer( GetOwner() );
  	if ( !pTFPlayer )
  		return;
  
  	if ( CanHeadshot() )
  	{
  		float curtime = pTFPlayer->GetFinalPredictedTime() + ( gpGlobals->interpolation_amount * TICK_INTERVAL );
  		float flTimeSinceCheck = curtime - m_flLastAccuracyCheck;
  		flScale = RemapValClamped( flTimeSinceCheck, 1.0f, 0.5f, 0.75f, 2.5f );
  	}
  	else
  	{
  		BaseClass::GetWeaponCrosshairScale( flScale );
  	}
  }
  #endif
  
  int CTFRevolver::GetCount( void )
  {
  	CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  	if ( !pOwner )
  		return 0;
  
  	if ( SapperKillsCollectCrits() )
  	{
  		return pOwner->m_Shared.GetRevengeCrits();
  	}
  
  	int iExtraDamageOnHit = 0;
  	CALL_ATTRIB_HOOK_INT( iExtraDamageOnHit, extra_damage_on_hit );
  	if ( iExtraDamageOnHit )
  	{
  		return Min( 200, pOwner->m_Shared.GetDecapitations() );
  	}
  
  	return 0;
  }
  
  //-----------------------------------------------------------------------------
  const char* CTFRevolver::GetEffectLabelText( void )
  {
  	int iExtraDamageOnHit = 0;
  	CALL_ATTRIB_HOOK_INT( iExtraDamageOnHit, extra_damage_on_hit );
  	if ( iExtraDamageOnHit )
  	{
  		return "#TF_BONUS";
  	}
  	return "#TF_CRITS";
  }
  bool CTFRevolver::Holster( CBaseCombatWeapon *pSwitchingTo )
  {
  #ifdef GAME_DLL
  	CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  	if ( pOwner )
  	{
  		if ( SapperKillsCollectCrits() )
  		{	
  			if ( pOwner->m_Shared.GetRevengeCrits() )
  			{
  				pOwner->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  			}
  		}
  
  		if ( HasLastShotCritical() )
  		{
  			pOwner->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  		}
  	}
  #endif
  
  	return BaseClass::Holster( pSwitchingTo );
  }
  
  bool CTFRevolver::Deploy( void )
  {
  #ifdef GAME_DLL
  	CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  	if ( pOwner )
  	{
  		if ( SapperKillsCollectCrits() )
  		{
  			if ( pOwner->m_Shared.GetRevengeCrits() )
  			{
  				pOwner->m_Shared.AddCond( TF_COND_CRITBOOSTED );
  			}
  		}
  
  		if ( HasLastShotCritical() )
  		{
  			pOwner->m_Shared.AddCond( TF_COND_CRITBOOSTED );
  		}
  	}
  #endif
  
  	return BaseClass::Deploy();
  }
  
  #ifdef GAME_DLL
  //-----------------------------------------------------------------------------
  // Purpose: Reset revenge crits when the revolver is changed
  //-----------------------------------------------------------------------------
  void CTFRevolver::Detach( void )
  {
  	if ( SapperKillsCollectCrits() )
  	{
  		CTFPlayer *pPlayer = GetTFPlayerOwner();
  		if ( pPlayer )
  		{
  			pPlayer->m_Shared.SetRevengeCrits( 0 );
  			pPlayer->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
  		}
  	}
  
  	BaseClass::Detach();
  }
  
  //-----------------------------------------------------------------------------
  float CTFRevolver::GetProjectileDamage( void )
  {
  	float flDamageMod = 1.0f;
  	int iExtraDamageOnHit = 0;
  	CALL_ATTRIB_HOOK_INT( iExtraDamageOnHit, extra_damage_on_hit );
  	if ( iExtraDamageOnHit )
  	{
  		CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  		if ( pOwner )
  		{
  			flDamageMod = 1.0f + ( Min( 200, pOwner->m_Shared.GetDecapitations() ) * 0.01f );
  		}
  	}
  
  	return BaseClass::GetProjectileDamage() * flDamageMod;
  }
  #endif
  