//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================
#include "cbase.h"
#include "basetfplayer_shared.h"
#include "basetfcombatweapon_shared.h"
#include "weapon_twohandedcontainer.h"

#if !defined( CLIENT_DLL )
#include "soundent.h"
#else
#include "FunctionProxy.h"
#include "ivrenderview.h"
#include "cl_animevent.h"
#include "fx.h"
#endif

CBaseTFCombatWeapon::CBaseTFCombatWeapon ( void )
{
	m_bReflectViewModelAnimations = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBaseTFCombatWeapon::Precache( void )
{
	BaseClass::Precache();

#if !defined( CLIENT_DLL )
	// Connect to my CVars
	m_pDamageCVar = cvar->FindVar( UTIL_VarArgs( "%s_damage", STRING(pev->classname) ) );
	m_pRangeCVar = cvar->FindVar( UTIL_VarArgs( "%s_range", STRING(pev->classname) ) );
#endif
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int	CBaseTFCombatWeapon::GetPrimaryAmmo( void )
{
	// Get the local player
	CBasePlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
	if ( pPlayer == NULL )
		return 0;

	return pPlayer->GetAmmoCount( m_iPrimaryAmmoType );
}


//-----------------------------------------------------------------------------
// Purpose: Checks if the owner is EMPed
//-----------------------------------------------------------------------------
bool CBaseTFCombatWeapon::IsOwnerEMPed()
{
	CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
	if ((!pPlayer) || (!pPlayer->GetPlayerClass()))
		return false;

	return ( pPlayer->HasPowerup(POWERUP_EMP) );
}

//-----------------------------------------------------------------------------
// Purpose: Temporarily remove disguise
//-----------------------------------------------------------------------------
void CBaseTFCombatWeapon::CheckRemoveDisguise( void )
{
#if !defined( CLIENT_DLL )
	CBaseTFPlayer *player = static_cast< CBaseTFPlayer * >( GetOwner());
	if ( player )
	{
		// Always remove camo
		player->ClearCamouflage();
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Factor in the player's anim speed to the sequence duration for weapons
//-----------------------------------------------------------------------------
float CBaseTFCombatWeapon::SequenceDuration( int iSequence )
{
	float flDuration = BaseClass::SequenceDuration( iSequence );
	CBaseTFPlayer *pOwner = (CBaseTFPlayer *)GetOwner();
	if ( pOwner )
	{
		flDuration /= pOwner->GetDefaultAnimSpeed();
	}

	return flDuration;
}


//-----------------------------------------------------------------------------
// Purpose: Override weapon sound. TF doesn't use ATTN_GUNFIRE for it's weapon attenuations.
//-----------------------------------------------------------------------------
void CBaseTFCombatWeapon::WeaponSound( WeaponSound_t shoot_type, float soundtime /*= 0.0f*/ )
{
#if !defined( CLIENT_DLL )
	// HACKHACK: Force a combat sound to alert NPCs, for antlion prototype
	CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 600, 0.1 );
#endif

	BaseClass::WeaponSound( shoot_type, soundtime );
}

//-----------------------------------------------------------------------------
// Purpose: Get the point from which to create the weapon's tracer
//-----------------------------------------------------------------------------
Vector CBaseTFCombatWeapon::GetTracerSrc( Vector &vecSrc, Vector &vecFireDir )
{
	QAngle vecAngles;
	VectorAngles( vecFireDir, vecAngles );
	Vector right;
	AngleVectors( vecAngles, NULL, &right, NULL );
	return (vecSrc + Vector ( 0,0,-4 ) + right * 2 + vecFireDir * 16);
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : activity - 
//-----------------------------------------------------------------------------
void CBaseTFCombatWeapon::PlayAttackAnimation( int activity )
{
	SendWeaponAnim( activity );
	CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
	if ( pPlayer )
	{
		pPlayer->SetAnimation( PLAYER_ATTACK1 );
		pPlayer->SetLastAttackTime( gpGlobals->curtime );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : sequence - 
//-----------------------------------------------------------------------------
void CBaseTFCombatWeapon::SendWeaponAnim( int iActivity )
{
	CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
	if ( !pPlayer )
		return;

	CBaseTFCombatWeapon *pOther = NULL;

	// See if we are wielding multiple weapons
	CWeaponTwoHandedContainer *pContainer = dynamic_cast< CWeaponTwoHandedContainer * >( pPlayer->GetActiveWeapon() );
	if ( pContainer )
	{
		// Make sure they exist
		CBaseTFCombatWeapon  *left = static_cast< CBaseTFCombatWeapon  * >( pContainer->GetLeftWeapon() );
		CBaseTFCombatWeapon  *right = static_cast< CBaseTFCombatWeapon  * >( pContainer->GetRightWeapon() );
		if ( !left || !right )
			return;

		// Make sure one of them is this!!!
		if ( left != this && right != this )
			return;

		// Get pointer to other one
		pOther = left;
		if ( left == this )
		{
			pOther = right;
		}

		Assert(pOther);

		// Now ask our other weapon if it would like to stomp my animation attempt
		iActivity = pOther->ReplaceOtherWeaponsActivity( iActivity );
		if ( iActivity == -1 )
			return;
	}

	// Always pass through to base
	BaseClass::SendWeaponAnim( iActivity );

	if ( !IsReflectingAnimations() )
		return;

	// See if we are wielding multiple weapons
	if ( !pContainer )
		return;

	Assert(pOther);
	// Send our other weapon the activity code
	iActivity = GetOtherWeaponsActivity( iActivity );
	if ( iActivity != -1 )
	{
		pOther->SendWeaponAnim( iActivity );

		// Remember it for weapon switching
		m_iLastReflectedActivity = iActivity;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : reflect - 
//-----------------------------------------------------------------------------
void CBaseTFCombatWeapon::SetReflectViewModelAnimations( bool reflect )
{
	m_bReflectViewModelAnimations = reflect;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseTFCombatWeapon::IsReflectingAnimations( void ) const
{
	return m_bReflectViewModelAnimations;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CBaseTFCombatWeapon::IsCamouflaged( void )
{
	CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)GetOwner();
	if ( pPlayer && pPlayer->IsCamouflaged() )
		return true;
		
	return false;
}

#if defined( CLIENT_DLL )
static ConVar	cl_bobcycle( "cl_bobcycle","0.8" );
static ConVar	cl_bob( "cl_bob","0.002" );
static ConVar	cl_bobup( "cl_bobup","0.5" );

// Register these cvars if needed for easy tweaking
static ConVar	v_iyaw_cycle( "v_iyaw_cycle", "2"/*, FCVAR_UNREGISTERED*/ );
static ConVar	v_iroll_cycle( "v_iroll_cycle", "0.5"/*, FCVAR_UNREGISTERED*/ );
static ConVar	v_ipitch_cycle( "v_ipitch_cycle", "1"/*, FCVAR_UNREGISTERED*/ );
static ConVar	v_iyaw_level( "v_iyaw_level", "0.3"/*, FCVAR_UNREGISTERED*/ );
static ConVar	v_iroll_level( "v_iroll_level", "0.1"/*, FCVAR_UNREGISTERED*/ );
static ConVar	v_ipitch_level( "v_ipitch_level", "0.3"/*, FCVAR_UNREGISTERED*/ );


//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
float CBaseTFCombatWeapon::CalcViewmodelBob( void )
{
	static	double	bobtime;
	static float	bob;
	float	cycle;
	
	CBasePlayer *player = ToBasePlayer( GetOwner() );
	if ( !player )
		return 0.0f;

	if ( ( player->GetGroundEntity() == NULL ) || !gpGlobals->frametime )
	{
		return bob;		// just use old value
	}

	bobtime += gpGlobals->frametime;
	
	cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat())*cl_bobcycle.GetFloat();
	cycle /= cl_bobcycle.GetFloat();

	if (cycle < cl_bobup.GetFloat())
	{
		cycle = M_PI * cycle / cl_bobup.GetFloat();
	}
	else
	{
		cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat());
	}

	// bob is proportional to simulated velocity in the xy plane
	// (don't count Z, or jumping messes it up)
	bob = player->GetAbsVelocity().Length2D() * cl_bob.GetFloat();
	
	bob = bob*0.3 + bob*0.7*sin(cycle);

	bob = min( 4.0, bob );
	bob = max( -7.0, bob );
	return bob;
	
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
void CBaseTFCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
{
	float fIdleScale = 2.0f;

	// Bias so view models aren't all synced to each other
	//float curtime = gpGlobals->curtime + ( viewmodelindex * 2 * M_PI / GetViewModelCount() );

	float curtime = gpGlobals->curtime + ( viewmodel->entindex() * 2 * M_PI );

	origin[ROLL]	-= fIdleScale * sin(curtime*v_iroll_cycle.GetFloat()) * v_iroll_level.GetFloat();
	origin[PITCH]	-= fIdleScale * sin(curtime*v_ipitch_cycle.GetFloat()) * (v_ipitch_level.GetFloat() * 0.5);
	origin[YAW]		-= fIdleScale * sin(curtime*v_iyaw_cycle.GetFloat()) * v_iyaw_level.GetFloat();

	Vector	forward;
	AngleVectors( angles, &forward, NULL, NULL );

	float	flBob = CalcViewmodelBob();

	// Apply bob, but scaled down to 40%
	VectorMA( origin, flBob * 0.4, forward, origin );

	// Z bob a bit more
	origin[2] += flBob;

	// throw in a little tilt.
	angles[ YAW ]	-= flBob * 0.6;
	angles[ ROLL ]	-= flBob * 0.5;
	angles[ PITCH ]	-= flBob * 0.4;
}

//-----------------------------------------------------------------------------
// Purpose: TF specific weapon anim events
//-----------------------------------------------------------------------------
bool CBaseTFCombatWeapon::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
{
	switch( event )
	{
	case CL_EVENT_MUZZLEFLASH0:
	case CL_EVENT_MUZZLEFLASH1:
	case CL_EVENT_MUZZLEFLASH2:
	case CL_EVENT_MUZZLEFLASH3:
		{
			int iAttachment = -1;
			Vector attachOrigin;
			QAngle attachAngles; 

			// First person muzzle flashes
			switch (event) 
			{
			case CL_EVENT_MUZZLEFLASH0:
				iAttachment = 0;
				break;

			case CL_EVENT_MUZZLEFLASH1:
				iAttachment = 1;
				break;

			case CL_EVENT_MUZZLEFLASH2:
				iAttachment = 2;
				break;

			case CL_EVENT_MUZZLEFLASH3:
				iAttachment = 3;
				break;
			}

			// Did we find it?
			if ( pViewModel->GetAttachment( iAttachment+1, attachOrigin, attachAngles ) )
			{
				//int iType = atoi( options );

				// Is our owner boosted?
				CBasePlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
				if ( pPlayer && pPlayer->HasPowerup(POWERUP_BOOST) )
				{
					unsigned char color[3];
					color[0] = 50;
					color[1] = 255;
					color[2] = 50;
					FX_MuzzleEffect( attachOrigin, attachAngles, 1.0, entindex(), &color[0] );
				}
				else
				{
					FX_MuzzleEffect( attachOrigin, attachAngles, 1.0, entindex() );
				}

				return true;
			}
		}
		break;

	default:
		break;
	}

	return false;
}

//============================================================================================================
// OWNER BOOSTED PROXY FOR WEAPONS / PLAYERS
//============================================================================================================
class CPlayerBoostedProxy : public CResultProxy
{
public:
	bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	void OnBind( void *pC_BaseEntity );

private:
	CFloatInput	m_Factor;
};

bool CPlayerBoostedProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	if (!CResultProxy::Init( pMaterial, pKeyValues ))
		return false;

	if (!m_Factor.Init( pMaterial, pKeyValues, "scale", 1 ))
		return false;

	return true;
}

void CPlayerBoostedProxy::OnBind( void *pRenderable )
{
	// Find the view angle between the player and this entity....
	IClientRenderable *pRend = (IClientRenderable *)pRenderable;
	C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
	CBaseTFPlayer *pPlayer = NULL;
	C_BaseViewModel *pViewModel = dynamic_cast<C_BaseViewModel*>(pEntity);
	if ( pViewModel )
	{
		pPlayer = C_BaseTFPlayer::GetLocalPlayer();
	}
	else
	{
		CBaseTFCombatWeapon *pWeapon = dynamic_cast<CBaseTFCombatWeapon*>(pEntity);
		if ( pWeapon )
		{
			pPlayer = ToBaseTFPlayer( pWeapon->GetOwner() );
		}
		else 
		{
			pPlayer = dynamic_cast<CBaseTFPlayer*>(pEntity);
		}
	}

	// Find him?
	if ( pPlayer )
	{
		float flBoosted = (int)pPlayer->HasPowerup( POWERUP_BOOST );
		SetFloatResult( flBoosted * m_Factor.GetFloat() );
	}
}

EXPOSE_INTERFACE( CPlayerBoostedProxy, IMaterialProxy, "PlayerBoosted" IMATERIAL_PROXY_INTERFACE_VERSION );


#else

//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
float CBaseTFCombatWeapon::CalcViewmodelBob( void )
{
	return 0.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
void CBaseTFCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
{
}

#endif

LINK_ENTITY_TO_CLASS( basetfcombatweapon, CBaseTFCombatWeapon );

IMPLEMENT_NETWORKCLASS_ALIASED( BaseTFCombatWeapon , DT_BaseTFCombatWeapon )

BEGIN_NETWORK_TABLE( CBaseTFCombatWeapon , DT_BaseTFCombatWeapon )
#if !defined( CLIENT_DLL )

	 // Don't network any animation stuff to client
 	SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ),
 	SendPropExclude( "DT_BaseAnimating", "m_flCycle" ),

	SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ),
#else
	RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ),
#endif
END_NETWORK_TABLE()

BEGIN_PREDICTION_DATA( CBaseTFCombatWeapon )

	// If true, reflect and weapon animations to all view models
	DEFINE_PRED_FIELD( CBaseTFCombatWeapon , m_bReflectViewModelAnimations, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
	DEFINE_FIELD( CBaseTFCombatWeapon , m_iLastReflectedActivity, FIELD_INTEGER )

END_PREDICTION_DATA()
