//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================

#include "cbase.h"
#include "tf_weapon_sword.h"

// Client specific.
#ifdef CLIENT_DLL
#include "c_tf_player.h"
#include "econ_entity.h"
// Server specific.
#else
#include "tf_player.h"
#endif

//=============================================================================
//
// Weapon Sword tables.
//

// CTFSword
IMPLEMENT_NETWORKCLASS_ALIASED( TFSword, DT_TFWeaponSword )

BEGIN_NETWORK_TABLE( CTFSword, DT_TFWeaponSword )
END_NETWORK_TABLE()

BEGIN_PREDICTION_DATA( CTFSword )
END_PREDICTION_DATA()

LINK_ENTITY_TO_CLASS( tf_weapon_sword, CTFSword );
PRECACHE_WEAPON_REGISTER( tf_weapon_sword );

// CTFKatana
IMPLEMENT_NETWORKCLASS_ALIASED( TFKatana, DT_TFWeaponKatana )

BEGIN_NETWORK_TABLE( CTFKatana, DT_TFWeaponKatana )
#ifdef CLIENT_DLL
	RecvPropBool( RECVINFO( m_bIsBloody ) ),
#else
	SendPropBool( SENDINFO( m_bIsBloody ) ),
#endif
END_NETWORK_TABLE()

BEGIN_PREDICTION_DATA( CTFKatana )
END_PREDICTION_DATA()

LINK_ENTITY_TO_CLASS( tf_weapon_katana, CTFKatana );
PRECACHE_WEAPON_REGISTER( tf_weapon_katana );

//=============================================================================
//
// Decapitation melee weapon base implementation.
//

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTFDecapitationMeleeWeaponBase::CTFDecapitationMeleeWeaponBase()
	: m_bHolstering( false )
{
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFDecapitationMeleeWeaponBase::Precache()
{
	BaseClass::Precache();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
float CTFDecapitationMeleeWeaponBase::GetMeleeDamage( CBaseEntity *pTarget, int* piDamageType, int* piCustomDamage )
{
	float flBaseDamage = BaseClass::GetMeleeDamage( pTarget, piDamageType, piCustomDamage );

	*piCustomDamage = TF_DMG_CUSTOM_DECAPITATION; 

	return flBaseDamage;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Activity CTFDecapitationMeleeWeaponBase::TranslateViewmodelHandActivityInternal( Activity actBase )
{
	CTFPlayer *pPlayer = GetTFPlayerOwner();
	if ( !pPlayer )
		return BaseClass::TranslateViewmodelHandActivityInternal( actBase );

	CEconItemView *pEconItemView = GetAttributeContainer()->GetItem();
	if ( pEconItemView )
	{
		if ( pEconItemView->GetAnimationSlot() == TF_WPN_TYPE_MELEE_ALLCLASS )
			return BaseClass::TranslateViewmodelHandActivityInternal( actBase );
	}

	// Alright, so we have some decapitation weapons (katana) that can be used
	// by both the soldier and the demoman, but the classes play totally different
	// animations using the same weapon.
	//
	// This logic is also responsible for playing the correct animations on the
	// demo when he's using non-shared weapons like the Eyelanders.
	if ( pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_DEMOMAN )
	{
		switch ( actBase )
		{
		case ACT_VM_DRAW:
			actBase = ACT_VM_DRAW_SPECIAL;
			break;
		case ACT_VM_HOLSTER:
			actBase = ACT_VM_HOLSTER_SPECIAL;
			break;
		case ACT_VM_IDLE:
			actBase = ACT_VM_IDLE_SPECIAL;
			break;
		case ACT_VM_PULLBACK:
			actBase = ACT_VM_PULLBACK_SPECIAL;
			break;
		case ACT_VM_PRIMARYATTACK:
			actBase = ACT_VM_PRIMARYATTACK_SPECIAL;
			break;
		case ACT_VM_SECONDARYATTACK:
			actBase = ACT_VM_PRIMARYATTACK_SPECIAL;
			break;
		case ACT_VM_HITCENTER:
			actBase = ACT_VM_HITCENTER_SPECIAL;
			break;
		case ACT_VM_SWINGHARD:
			actBase = ACT_VM_SWINGHARD_SPECIAL;
			break;
		case ACT_VM_IDLE_TO_LOWERED:
			actBase = ACT_VM_IDLE_TO_LOWERED_SPECIAL;
			break;
		case ACT_VM_IDLE_LOWERED:
			actBase = ACT_VM_IDLE_LOWERED_SPECIAL;
			break;
		case ACT_VM_LOWERED_TO_IDLE:
			actBase = ACT_VM_LOWERED_TO_IDLE_SPECIAL;
			break;
		default:
			break;
		}
	}

	return BaseClass::TranslateViewmodelHandActivityInternal( actBase );
}
/*
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFDecapitationMeleeWeaponBase::SendWeaponAnim( int iActivity )
{

}*/

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFDecapitationMeleeWeaponBase::CanDecapitate( void )
{
	CEconItemView *pScriptItem = GetAttributeContainer()->GetItem();
	if ( !pScriptItem )
		return false;

	CEconItemDefinition *pStaticData = pScriptItem->GetStaticData();
	if ( !pStaticData )
		return false;

	int iDecapitateType = 0;
	CALL_ATTRIB_HOOK_INT( iDecapitateType, decapitate_type );
	return iDecapitateType != 0;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFDecapitationMeleeWeaponBase::SetupGameEventListeners( void )
{
	ListenForGameEvent( "player_death" );
}

#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFDecapitationMeleeWeaponBase::UpdateAttachmentModels( void )
{
	BaseClass::UpdateAttachmentModels();

	CTFPlayer *pTFPlayer = GetTFPlayerOwner();
	if ( !pTFPlayer )
		return;

	if ( !pTFPlayer->IsLocalPlayer() )
		return;

	if ( !pTFPlayer->GetViewModel() )
		return;

	if ( !pTFPlayer->m_Shared.IsShieldEquipped() )
		return;

	CTFWearableDemoShield* pMyShield = dynamic_cast<CTFWearableDemoShield*>( m_hShield.Get() );

	if ( pMyShield )
	{
		pMyShield->UpdateAttachmentModels();
	}
	else
	{
		// Find a shield wearable...
		for ( int i=0; i<pTFPlayer->GetNumWearables(); ++i )
		{
			CEconWearable* pItem = pTFPlayer->GetWearable(i);
			if ( !pItem )
				continue;

			pMyShield = dynamic_cast<CTFWearableDemoShield*>( pItem );
			if ( !pMyShield )
				continue;

			m_hShield.Set( pMyShield );
		}

		if ( pMyShield )
		{
			pMyShield->UpdateAttachmentModels();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int	CTFDecapitationMeleeWeaponBase::DrawOverriddenViewmodel( C_BaseViewModel *pViewmodel, int flags )
{
	int iRes = BaseClass::DrawOverriddenViewmodel( pViewmodel, flags );

	CTFWearableDemoShield* pMyShield = dynamic_cast<CTFWearableDemoShield*>( m_hShield.Get() );

	if ( pMyShield )
	{
		pMyShield->DrawOverriddenViewmodel( pViewmodel, flags );
	}

	return iRes;
}
#endif // CLIENT_DLL

//=============================================================================
//
// Weapon Sword functions.
//

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFSword::WeaponReset( void )
{
	BaseClass::WeaponReset();

#ifdef CLIENT_DLL
	m_flNextIdleWavRoll = 0;
	m_iPrevWavDecap = 0;
#endif
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int	CTFSword::GetSwingRange( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( pOwner && pOwner->m_Shared.InCond( TF_COND_SHIELD_CHARGE ) )
	{
		return 128;
	}
	else
	{
		//int iRange = 0;
		//CALL_ATTRIB_HOOK_INT( iRange, is_a_sword )
		//return 72;
		return 72;
	}
}

//-----------------------------------------------------------------------------
// Purpose: A speed mod that is applied even if the weapon isn't in hand.
//-----------------------------------------------------------------------------
float CTFSword::GetSwordSpeedMod( void )
{
	if ( m_bHolstering )
		return 1.f;

	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return 0;

	if ( !CanDecapitate() )
		return 1.f;

	int iDecaps = MIN( MAX_DECAPITATIONS, pOwner->m_Shared.GetDecapitations() );
	return 1.f + (iDecaps * 0.08f);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CTFSword::GetSwordHealthMod( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return 0;

	if ( !CanDecapitate() )
		return 0.f;

	int iDecaps = MIN( MAX_DECAPITATIONS, pOwner->m_Shared.GetDecapitations() );
	return (iDecaps * 15);
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFSword::OnDecapitation( CTFPlayer *pDeadPlayer )
{
	BaseClass::OnDecapitation( pDeadPlayer );

#ifdef GAME_DLL
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	Assert( pOwner );

	// The pyro's scythe is actually a "sword" as far as the gameplay code is concerned,
	// but we don't want the demo's head-collecting silliness to apply.
	if ( pOwner->GetPlayerClass()->GetClassIndex() == TF_CLASS_DEMOMAN )
	{
		// We have chopped someone's bloody 'ead off!
		int iDecap = pOwner->m_Shared.GetDecapitations();

		// transfer decapitations
		if ( pDeadPlayer )
		{
			iDecap += pDeadPlayer->m_Shared.GetDecapitations();
		}
		pOwner->m_Shared.SetDecapitations( ++iDecap );
		pOwner->TeamFortress_SetSpeed();
		if ( pOwner->m_Shared.GetBestOverhealDecayMult() == -1.f )
		{
			pOwner->m_Shared.SetBestOverhealDecayMult( 0.25f );
		}
		if ( pOwner->GetHealth() < pOwner->m_Shared.GetMaxBuffedHealth() )
		{
			pOwner->TakeHealth( 15, DMG_IGNORE_MAXHEALTH );
		}
		if ( !pOwner->m_Shared.InCond( TF_COND_DEMO_BUFF ) )
		{
			pOwner->m_Shared.AddCond( TF_COND_DEMO_BUFF );
		}
	}
#endif // GAME_DLL
}

#ifdef GAME_DLL

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFDecapitationMeleeWeaponBase::Holster( CBaseCombatWeapon *pSwitchingTo )
{
	m_bHolstering = true;
	bool res = BaseClass::Holster( pSwitchingTo );
	m_bHolstering = false;
	if ( res )
	{
		StopListeningForAllEvents();
	}
	return res;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFDecapitationMeleeWeaponBase::FireGameEvent( IGameEvent *event )
{
	const char *pszEventName = event->GetName();
	if ( Q_strcmp( pszEventName, "player_death" ) != 0 )
		return;

	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return;

	int iAttackerID = event->GetInt( "attacker" );
	if ( iAttackerID != pOwner->GetUserID() )
		return;

	int iWeaponID = event->GetInt( "weaponid" );
	int iCustomKill = event->GetInt( "customkill" );
	if ( iWeaponID != TF_WEAPON_SWORD && iCustomKill != TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING )
		return;

	// Off with their heads!
	if ( !CanDecapitate() )
		return;
	
	OnDecapitation( ToTFPlayer( UTIL_PlayerByUserId( event->GetInt( "userid" ) ) ) );
}
#endif

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFSword::Deploy( void )
{
	bool res = BaseClass::Deploy();

	if ( !CanDecapitate() )
	{
		CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
		if ( !pOwner )
			return res;

		pOwner->m_Shared.SetDecapitations( 0 );

		return res;
	}

#ifdef GAME_DLL
	if ( res )
	{
		SetupGameEventListeners();
	}
#else
	// When we go active, there's a chance we immediately thirst for heads.
	if ( RandomInt( 1,4 ) == 1 )
	{
		m_flNextIdleWavRoll = gpGlobals->curtime + 3.0;
	}
	else
	{
		m_flNextIdleWavRoll = gpGlobals->curtime + RandomFloat( 5.0, 30.0 );
	}
#endif

	return res;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CTFSword::GetCount( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return 0;

	if ( !CanDecapitate() )
		return 0;

	return pOwner->m_Shared.GetDecapitations();
}

#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFSword::WeaponIdle( void )
{
	BaseClass::WeaponIdle();

	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return;

	if ( !CanDecapitate() )
		return;

	// If we've decapped someone recently, we roll shortly afterwards
	int iDecaps = pOwner->m_Shared.GetDecapitations();
	if ( m_iPrevWavDecap < iDecaps )
	{
		 m_flNextIdleWavRoll = MIN( m_flNextIdleWavRoll, gpGlobals->curtime + RandomFloat(3,5) );
	}
	m_iPrevWavDecap = iDecaps;

	// Randomly play sounds to the local player. The more decaps we have, the more chance it's a good wav.
	if ( m_flNextIdleWavRoll < gpGlobals->curtime )
	{
		float flChance = RemapValClamped( iDecaps, 0, 10, 0.25, 0.9 );
		if ( RandomFloat() <= flChance )
		{
			// Chance to get the more powerful wav: 
			float flChanceForGoodWav = RemapValClamped( iDecaps, 0, 10, 0.1, 0.75 );
			if ( RandomFloat() <= flChanceForGoodWav )
			{
				WeaponSound( SPECIAL2 );
			}
			else
			{
				WeaponSound( SPECIAL1 );
			}
		}

		m_flNextIdleWavRoll = gpGlobals->curtime + RandomFloat( 30.0, 60.0 );
	}
}

#endif


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFKatana::CTFKatana()
{
	m_bIsBloody = false;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFKatana::Deploy( void )
{
	bool res = BaseClass::Deploy();

	m_bIsBloody = false;

	if ( CanDecapitate() && res )
	{
#ifdef GAME_DLL
		SetupGameEventListeners();
#endif
	}

	return res;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CTFKatana::GetMeleeDamage( CBaseEntity *pTarget, int* piDamageType, int* piCustomDamage )
{
	// Start with our base damage. We use this to generate our custom damage flags,
	// if any. We may trash the damage amount.
	float fDamage = BaseClass::GetMeleeDamage( pTarget, piDamageType, piCustomDamage );

	// The katana is a weapon of honor!!!! (Hitting someone wielding a katana with
	// your katana results in a massive damage boost, a one-hit kill.)
	if ( IsHonorBound() )
	{
		CTFPlayer *pTFPlayerTarget = ToTFPlayer( pTarget );
		if ( pTFPlayerTarget )
		{
			// If our victim is wielding the weapon we're looking for, bump the damage way up.
			if ( pTFPlayerTarget->GetActiveTFWeapon() && pTFPlayerTarget->GetActiveTFWeapon()->IsHonorBound() )
			{
				fDamage = MAX( fDamage, pTFPlayerTarget->GetHealth() * 3 );
				*piDamageType |= DMG_DONT_COUNT_DAMAGE_TOWARDS_CRIT_RATE;
			}
		}
	}

	return fDamage;
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTFKatana::GetActivityWeaponRole() const
{
	CTFPlayer *pPlayer = GetTFPlayerOwner();
	if ( pPlayer && pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_DEMOMAN )
	{
		// demo should use act table item1
		return TF_WPN_TYPE_ITEM1;
	}

	return BaseClass::GetActivityWeaponRole();
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFKatana::OnDecapitation( CTFPlayer *pDeadPlayer )
{
	BaseClass::OnDecapitation( pDeadPlayer );

#ifndef CLIENT_DLL
	m_bIsBloody = true;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CTFKatana::GetSkinOverride() const
{
 	if ( m_bIsBloody && !UTIL_IsLowViolence() )
	{
		CTFPlayer *pPlayer = GetTFPlayerOwner();
		if ( pPlayer )
		{
			return ( ( pPlayer->GetTeamNumber() == TF_TEAM_RED ) ? 2 : 3 );
		}
	}
	
	return BaseClass::GetSkinOverride();
}