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

#include "cbase.h"
#include "in_buttons.h"
#include "takedamageinfo.h"
#include "weapon_dodbase.h"
#include "ammodef.h"
#include "dod_gamerules.h"

#ifdef CLIENT_DLL
extern IVModelInfoClient* modelinfo;
#else
extern IVModelInfo* modelinfo;
#include "ilagcompensationmanager.h"
#endif


#if defined( CLIENT_DLL )

	#include "vgui/ISurface.h"
	#include "vgui_controls/Controls.h"
	#include "c_dod_player.h"
	#include "hud_crosshair.h"
	#include "SoundEmitterSystem/isoundemittersystembase.h"

#else

	#include "dod_player.h"

#endif

#include "effect_dispatch_data.h"


// ----------------------------------------------------------------------------- //
// Global functions.
// ----------------------------------------------------------------------------- //

bool IsAmmoType( int iAmmoType, const char *pAmmoName )
{
	return GetAmmoDef()->Index( pAmmoName ) == iAmmoType;
}

//--------------------------------------------------------------------------------------------------------
//
// Given a weapon ID, return its alias
//
const char *WeaponIDToAlias( int id )
{
	if ( (id >= WEAPON_MAX) || (id < 0) )
		return NULL;

	return s_WeaponAliasInfo[id];
}

// ----------------------------------------------------------------------------- //
// CWeaponDODBase tables.
// ----------------------------------------------------------------------------- //

IMPLEMENT_NETWORKCLASS_ALIASED( WeaponDODBase, DT_WeaponDODBase )

BEGIN_NETWORK_TABLE( CWeaponDODBase, DT_WeaponDODBase )
#ifdef CLIENT_DLL
	RecvPropInt( RECVINFO(m_iReloadModelIndex) ),
	RecvPropVector( RECVINFO( m_vInitialDropVelocity ) ),
	RecvPropTime( RECVINFO( m_flSmackTime ) )
#else
	SendPropVector( SENDINFO( m_vInitialDropVelocity ), 
			20,		// nbits
			0,		// flags
			-3000,	// low value
			3000	// high value
			),
	SendPropModelIndex( SENDINFO(m_iReloadModelIndex) ),
	SendPropTime( SENDINFO( m_flSmackTime ) )
#endif
END_NETWORK_TABLE()

LINK_ENTITY_TO_CLASS( weapon_dod_base, CWeaponDODBase );


#ifdef GAME_DLL

	BEGIN_DATADESC( CWeaponDODBase )

		DEFINE_FUNCTION( FallThink ),
		DEFINE_FUNCTION( Die ),

		DEFINE_FUNCTION( Smack )

	END_DATADESC()

#else
	BEGIN_PREDICTION_DATA( CWeaponDODBase ) 
		DEFINE_PRED_FIELD( m_flSmackTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),	// for rifle melee attacks
		DEFINE_FIELD( m_bInAttack, FIELD_BOOLEAN )
	END_PREDICTION_DATA()
#endif

Vector head_hull_mins( -16, -16, -18 );
Vector head_hull_maxs( 16, 16, 18 );

void FindHullIntersection( const Vector &vecSrc, trace_t &tr, const Vector &mins, const Vector &maxs, CBaseEntity *pEntity )
{
	int			i, j, k;
	float		distance;
	Vector minmaxs[2] = {mins, maxs};
	trace_t tmpTrace;
	Vector		vecHullEnd = tr.endpos;
	Vector		vecEnd;

	CTraceFilterSimple filter( pEntity, COLLISION_GROUP_NONE );

	distance = 1e6f;

	vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
	UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SOLID, &filter, &tmpTrace );
	if ( tmpTrace.fraction < 1.0 )
	{
		tr = tmpTrace;
		return;
	}

	for ( i = 0; i < 2; i++ )
	{
		for ( j = 0; j < 2; j++ )
		{
			for ( k = 0; k < 2; k++ )
			{
				vecEnd.x = vecHullEnd.x + minmaxs[i][0];
				vecEnd.y = vecHullEnd.y + minmaxs[j][1];
				vecEnd.z = vecHullEnd.z + minmaxs[k][2];

				UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, &filter, &tmpTrace );
				if ( tmpTrace.fraction < 1.0 )
				{
					float thisDistance = (tmpTrace.endpos - vecSrc).Length();
					if ( thisDistance < distance )
					{
						tr = tmpTrace;
						distance = thisDistance;
					}
				}
			}
		}
	}
}

// ----------------------------------------------------------------------------- //
// CWeaponDODBase implementation. 
// ----------------------------------------------------------------------------- //
CWeaponDODBase::CWeaponDODBase()
{
	SetPredictionEligible( true );
	m_bInAttack = false;
	m_iAltFireHint = 0;
	AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches.

	m_flNextPrimaryAttack = 0;
}


bool CWeaponDODBase::IsPredicted() const
{ 
	return true;
}

bool CWeaponDODBase::PlayEmptySound()
{
	CPASAttenuationFilter filter( this );
	filter.UsePredictionRules();
	EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" );

	return false;
}


CBasePlayer* CWeaponDODBase::GetPlayerOwner() const
{
	return dynamic_cast< CBasePlayer* >( GetOwner() );
}

CDODPlayer* CWeaponDODBase::GetDODPlayerOwner() const
{
	return dynamic_cast< CDODPlayer* >( GetOwner() );
}

bool CWeaponDODBase::SendWeaponAnim( int iActivity )
{
	return BaseClass::SendWeaponAnim( iActivity );
}

bool CWeaponDODBase::CanAttack( void )
{
	CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() );

	if ( pPlayer )
	{
		return pPlayer->CanAttack();
	}

	return false;
}

bool CWeaponDODBase::ShouldAutoReload( void )
{
	CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() );

	if ( pPlayer )
	{
		return pPlayer->ShouldAutoReload();
	}

	return false;
}

void CWeaponDODBase::ItemPostFrame()
{
	if ( m_flSmackTime > 0 && gpGlobals->curtime > m_flSmackTime )
	{
		Smack();
		m_flSmackTime = -1;
	}

	CBasePlayer *pPlayer = GetPlayerOwner();

	if ( !pPlayer )
		return;

#ifdef _DEBUG
	CDODGameRules *mp = DODGameRules();
#endif

	assert( mp );

	if ((m_bInReload) && (pPlayer->m_flNextAttack <= gpGlobals->curtime))
	{
		// complete the reload. 
		int j = MIN( GetMaxClip1() - m_iClip1, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) );	

		// Add them to the clip
		m_iClip1 += j;
		pPlayer->RemoveAmmo( j, m_iPrimaryAmmoType );

		m_bInReload = false;

		FinishReload();
	}

	if ((pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime))
	{
		if ( m_iClip2 != -1 && !pPlayer->GetAmmoCount( GetSecondaryAmmoType() ) )
		{
			m_bFireOnEmpty = TRUE;
		}

		SecondaryAttack();

		pPlayer->m_nButtons &= ~IN_ATTACK2;
	}
	else if ((pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime ) && !m_bInAttack )
	{
		if ( (m_iClip1 == 0/* && pszAmmo1()*/) || (GetMaxClip1() == -1 && !pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) ) )
		{
			m_bFireOnEmpty = TRUE;
		}

		if( CanAttack() )
			PrimaryAttack();
	}
	else if ( pPlayer->m_nButtons & IN_RELOAD && GetMaxClip1() != WEAPON_NOCLIP && !m_bInReload && m_flNextPrimaryAttack < gpGlobals->curtime) 
	{
		// reload when reload is pressed, or if no buttons are down and weapon is empty.		
		Reload();	
	}
	else if ( !(pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2) ) )
	{
		// no fire buttons down

		m_bFireOnEmpty = false;

		m_bInAttack = false;	//reset semi-auto

		if ( !IsUseable() && m_flNextPrimaryAttack < gpGlobals->curtime ) 
		{
			// Intentionally blank -- used to switch weapons here
		}
		else if( ShouldAutoReload() )
		{
			// weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
			if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
			{
				Reload();
				return;
			}
		}

		WeaponIdle( );
		return;
	}
}

void CWeaponDODBase::WeaponIdle()
{
	if (m_flTimeWeaponIdle > gpGlobals->curtime)
		return;

	SendWeaponAnim( GetIdleActivity() );

	m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
}

Activity CWeaponDODBase::GetIdleActivity( void )
{
	return ACT_VM_IDLE;
}

const CDODWeaponInfo &CWeaponDODBase::GetDODWpnData() const
{
	const FileWeaponInfo_t *pWeaponInfo = &GetWpnData();
	const CDODWeaponInfo *pDODInfo;

	#ifdef _DEBUG
		pDODInfo = dynamic_cast< const CDODWeaponInfo* >( pWeaponInfo );
		Assert( pDODInfo );
	#else
		pDODInfo = static_cast< const CDODWeaponInfo* >( pWeaponInfo );
	#endif

	return *pDODInfo;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *CWeaponDODBase::GetViewModel( int /*viewmodelindex = 0 -- this is ignored in the base class here*/ ) const
{
	if ( GetPlayerOwner() == NULL )
	{
		 return BaseClass::GetViewModel();
	}
	
	return GetWpnData().szViewModel;
}

void CWeaponDODBase::Precache( void )
{
	// precache base first, it loads weapon scripts
	BaseClass::Precache();

	PrecacheScriptSound( "Default.ClipEmpty_Rifle" );

	PrecacheParticleSystem( "muzzle_pistols" );
	PrecacheParticleSystem( "muzzle_fullyautomatic" );
	PrecacheParticleSystem( "muzzle_rifles" );
	PrecacheParticleSystem( "muzzle_rockets" );
	PrecacheParticleSystem( "muzzle_mg42" );

	PrecacheParticleSystem( "view_muzzle_pistols" );
	PrecacheParticleSystem( "view_muzzle_fullyautomatic" );
	PrecacheParticleSystem( "view_muzzle_rifles" );
	PrecacheParticleSystem( "view_muzzle_rockets" );
	PrecacheParticleSystem( "view_muzzle_mg42" );

	const CDODWeaponInfo &info = GetDODWpnData();

	int iWpnNameLen = Q_strlen(info.m_szReloadModel);

#ifdef DEBUG
	// Make sure that if we declare an alt weapon, that we have criteria to show it
	// and vice-versa
	//Assert( ((info.m_iAltWpnCriteria & (ALTWPN_CRITERIA_RELOADING | ALTWPN_CRITERIA_FIRING)) > 0 ) ==
	//		(iWpnNameLen > 0) );  	
#endif

	if( iWpnNameLen > 0 )
		m_iReloadModelIndex = CBaseEntity::PrecacheModel( info.m_szReloadModel );	
}

bool CWeaponDODBase::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt )
{
	CBasePlayer *pOwner = GetPlayerOwner();
	if ( !pOwner )
	{
		return false;
	}

	pOwner->SetAnimationExtension( szAnimExt );

	SetViewModel();
	SendWeaponAnim( iActivity );

	pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() );
	m_flNextPrimaryAttack	= MAX( m_flNextPrimaryAttack, gpGlobals->curtime );
	m_flNextSecondaryAttack	= gpGlobals->curtime;

	SetWeaponVisible( true );
	SetWeaponModelIndex( szWeaponModel );

	CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex );

	Assert( vm );

	if( vm )
	{
		//set sleeves to proper team
		switch( pOwner->GetTeamNumber() )
		{
		case TEAM_ALLIES:
			vm->m_nSkin = SLEEVE_ALLIES;
			break;
		case TEAM_AXIS:
			vm->m_nSkin = SLEEVE_AXIS;
			break;
		default:
			Assert( !"TEAM_UNASSIGNED or spectator getting a view model assigned" );
			break;
		}
	}	

	return true;
}

void CWeaponDODBase::SetWeaponModelIndex( const char *pName )
{
 	 m_iWorldModelIndex = modelinfo->GetModelIndex( pName );
}

bool CWeaponDODBase::CanBeSelected( void )
{
	if ( !VisibleInWeaponSelection() )
		return false;

	return true;
}

bool CWeaponDODBase::CanDeploy( void )
{
	return BaseClass::CanDeploy();
}

bool CWeaponDODBase::CanHolster( void )
{
	return BaseClass::CanHolster();
}

void CWeaponDODBase::Drop( const Vector &vecVelocity )
{
#ifndef CLIENT_DLL
	if ( m_iAltFireHint )
	{
		CDODPlayer *pPlayer = GetDODPlayerOwner();
		if ( pPlayer )
		{
			pPlayer->StopHintTimer( m_iAltFireHint );
		}
	}
#endif

	// cancel any reload in progress
	m_bInReload = false;

	m_flSmackTime = -1;
	
	m_vInitialDropVelocity = vecVelocity;

	BaseClass::Drop( m_vInitialDropVelocity );
}

bool CWeaponDODBase::Holster( CBaseCombatWeapon *pSwitchingTo )
{
#ifndef CLIENT_DLL
	CDODPlayer *pPlayer = GetDODPlayerOwner();

	if ( pPlayer )
	{
		pPlayer->SetFOV( pPlayer, 0 ); // reset the default FOV.

		if ( m_iAltFireHint )
		{
			pPlayer->StopHintTimer( m_iAltFireHint );
		}
	}
#endif

	m_bInReload = false;

	m_flSmackTime = -1;

	return BaseClass::Holster( pSwitchingTo );
}

bool CWeaponDODBase::Deploy()
{
#ifndef CLIENT_DLL
	CDODPlayer *pPlayer = GetDODPlayerOwner();

	if ( pPlayer )
	{
		pPlayer->SetFOV( pPlayer, 0 );

		if ( m_iAltFireHint )
		{
			pPlayer->StartHintTimer( m_iAltFireHint );
		}
	}
#endif

	return BaseClass::Deploy();
}

#ifdef CLIENT_DLL
	
	void CWeaponDODBase::PostDataUpdate( DataUpdateType_t updateType )
	{
		// We need to do this before the C_BaseAnimating code starts to drive
		// clientside animation sequences on this model, which will be using bad sequences for the world model.
		int iDesiredModelIndex = 0;
		C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();
		if ( localplayer && localplayer == GetOwner() && !C_BasePlayer::ShouldDrawLocalPlayer() )		// FIXME: use localplayer->ShouldDrawThisPlayer() instead.
		{
			iDesiredModelIndex = m_iViewModelIndex;
		}
		else
		{
			iDesiredModelIndex = GetWorldModelIndex();

			// Our world models never animate
			SetSequence( 0 );
		}

		if ( GetModelIndex() != iDesiredModelIndex )
		{
			SetModelIndex( iDesiredModelIndex );
		}

		BaseClass::PostDataUpdate( updateType );
	}

	void CWeaponDODBase::OnDataChanged( DataUpdateType_t type )
	{
		if ( m_iState == WEAPON_NOT_CARRIED && m_iOldState != WEAPON_NOT_CARRIED ) 
		{
			// we are being notified of the weapon being dropped
			// add an interpolation history so the movement is smoother

			// Now stick our initial velocity into the interpolation history 
			CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator();

			interpolator.ClearHistory();
			float changeTime = GetLastChangeTime( LATCH_SIMULATION_VAR );

			// Add a sample 1 second back.
			Vector vCurOrigin = GetLocalOrigin() - m_vInitialDropVelocity;
			interpolator.AddToHead( changeTime - 1.0, &vCurOrigin, false );

			// Add the current sample.
			vCurOrigin = GetLocalOrigin();
			interpolator.AddToHead( changeTime, &vCurOrigin, false );

			Vector estVel;
			EstimateAbsVelocity( estVel );

			/*Msg( "estimated velocity ( %.1f %.1f %.1f )  initial velocity ( %.1f %.1f %.1f )\n",
				estVel.x,
				estVel.y,
				estVel.z,
				m_vInitialDropVelocity.m_Value.x,
				m_vInitialDropVelocity.m_Value.y,
				m_vInitialDropVelocity.m_Value.z );*/

			OnWeaponDropped();
		}

		BaseClass::OnDataChanged( type );

		if ( GetPredictable() && !ShouldPredict() )
			ShutdownPredictable();
	}

	int CWeaponDODBase::GetWorldModelIndex( void )
	{
		if( m_bUseAltWeaponModel && GetOwner() != NULL )
			return m_iReloadModelIndex;
		else
            return m_iWorldModelIndex;
	}

	bool CWeaponDODBase::ShouldPredict()
	{
		if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() )
			return true;

		return BaseClass::ShouldPredict();
	}

	void CWeaponDODBase::ProcessMuzzleFlashEvent()
	{
		CDODPlayer *pPlayer = GetDODPlayerOwner();
		if ( pPlayer )
			pPlayer->ProcessMuzzleFlashEvent();

		BaseClass::ProcessMuzzleFlashEvent();
	}
#else
		

	//-----------------------------------------------------------------------------
	// Purpose: Get the accuracy derived from weapon and player, and return it
	//-----------------------------------------------------------------------------
	const Vector& CWeaponDODBase::GetBulletSpread()
	{
		static Vector cone = VECTOR_CONE_8DEGREES;
		return cone;
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void CWeaponDODBase::ItemBusyFrame()
	{
		if( ShouldAutoReload() && !m_bInReload )
		{
			// weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
			if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
			{
				Reload();
			}
		}

		BaseClass::ItemBusyFrame();
	}

	//-----------------------------------------------------------------------------
	// Purpose: Match the anim speed to the weapon speed while crouching
	//-----------------------------------------------------------------------------
	float CWeaponDODBase::GetDefaultAnimSpeed()
	{
		return 1.0;
	}

	bool CWeaponDODBase::ShouldRemoveOnRoundRestart()
	{
		if ( GetPlayerOwner() )
			return false;
		else
			return true;
	}


	//=========================================================
	// Materialize - make a CWeaponDODBase visible and tangible
	//=========================================================
	void CWeaponDODBase::Materialize()
	{
		if ( IsEffectActive( EF_NODRAW ) )
		{
			RemoveEffects( EF_NODRAW );
			DoMuzzleFlash();
		}

		AddSolidFlags( FSOLID_TRIGGER );

		SetThink (&CWeaponDODBase::SUB_Remove);
		SetNextThink( gpGlobals->curtime + 1 );
	}

	//=========================================================
	// AttemptToMaterialize - the item is trying to rematerialize,
	// should it do so now or wait longer?
	//=========================================================
	void CWeaponDODBase::AttemptToMaterialize()
	{
		float time = g_pGameRules->FlWeaponTryRespawn( this );

		if ( time == 0 )
		{
			Materialize();
			return;
		}

		SetNextThink( gpGlobals->curtime + time );
	}

	//=========================================================
	// CheckRespawn - a player is taking this weapon, should 
	// it respawn?
	//=========================================================
	void CWeaponDODBase::CheckRespawn()
	{
		//GOOSEMAN : Do not respawn weapons!
		return;
	}

		
	//=========================================================
	// Respawn- this item is already in the world, but it is
	// invisible and intangible. Make it visible and tangible.
	//=========================================================
	CBaseEntity* CWeaponDODBase::Respawn()
	{
		// make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
		// will decide when to make the weapon visible and touchable.
		CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetAbsAngles(), GetOwner() );

		if ( pNewWeapon )
		{
			pNewWeapon->AddEffects( EF_NODRAW );// invisible for now
			pNewWeapon->SetTouch( NULL );// no touch
			pNewWeapon->SetThink( &CWeaponDODBase::AttemptToMaterialize );

			UTIL_DropToFloor( this, MASK_SOLID );

			// not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement,
			// but when it should respawn is based on conditions belonging to the weapon that was taken.
			pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) );
		}
		else
		{
			Msg( "Respawn failed to create %s!\n", GetClassname() );
		}

		return pNewWeapon;
	}

	bool CWeaponDODBase::Reload()
	{
		return BaseClass::Reload();
	}

	void CWeaponDODBase::Spawn()
	{
		BaseClass::Spawn();

		// Set this here to allow players to shoot dropped weapons
		SetCollisionGroup( COLLISION_GROUP_WEAPON );
		
		SetExtraAmmoCount(0);	//Start with no additional ammo

		CollisionProp()->UseTriggerBounds( true, 10.0f );
	}
	
	void CWeaponDODBase::SetDieThink( bool bDie )
	{
		if( bDie )
			SetContextThink( &CWeaponDODBase::Die, gpGlobals->curtime + 45.0f, "DieContext" );
		else
			SetContextThink( NULL, gpGlobals->curtime, "DieContext" );
	}

	void CWeaponDODBase::Die( void )
	{
		UTIL_Remove( this );
	}

#endif

void CWeaponDODBase::OnPickedUp( CBaseCombatCharacter *pNewOwner )
{
	BaseClass::OnPickedUp( pNewOwner );

#if !defined( CLIENT_DLL )
	SetDieThink( false );
#endif
}

bool CWeaponDODBase::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
{
	CBaseCombatCharacter *pOwner = GetOwner();
	if (!pOwner)
		return false;

	// If I don't have any spare ammo, I can't reload
	if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
		return false;

	bool bReload = false;

	// If you don't have clips, then don't try to reload them.
	if ( UsesClipsForAmmo1() )
	{
		// need to reload primary clip?
		int primary	= min(iClipSize1 - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
		if ( primary != 0 )
		{
			bReload = true;
		}
	}

	if ( UsesClipsForAmmo2() )
	{
		// need to reload secondary clip?
		int secondary = min(iClipSize2 - m_iClip2, pOwner->GetAmmoCount(m_iSecondaryAmmoType));
		if ( secondary != 0 )
		{
			bReload = true;
		}
	}

	if ( !bReload )
		return false;

	CDODPlayer *pPlayer = GetDODPlayerOwner();
	if ( pPlayer )
	{
#ifdef CLIENT_DLL
		PlayWorldReloadSound( pPlayer );
#else
		pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD );
#endif
	}

	SendWeaponAnim( iActivity );

	// Play the player's reload animation
	if ( pOwner->IsPlayer() )
	{
		( ( CBasePlayer * )pOwner)->SetAnimation( PLAYER_RELOAD );
	}

	float flSequenceEndTime = gpGlobals->curtime + SequenceDuration();
	pOwner->SetNextAttack( flSequenceEndTime );
	m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime;

	m_bInReload = true;

	return true;
}

#ifdef CLIENT_DLL
	void CWeaponDODBase::PlayWorldReloadSound( CDODPlayer *pPlayer )
	{
		Assert( pPlayer );

		const char *shootsound = GetShootSound( RELOAD );
		if ( !shootsound || !shootsound[0] )
			return;

		CSoundParameters params;

		if ( !GetParametersForSound( shootsound, params, NULL ) )
			return;

		// Play weapon sound from the owner
		CPASAttenuationFilter filter( pPlayer, params.soundlevel );
		filter.RemoveRecipient( pPlayer );	// no local player, that is done in the model

		EmitSound( filter, pPlayer->entindex(), shootsound, NULL, 0.0 ); 
	}
#endif

bool CWeaponDODBase::IsUseable()
{
	CBasePlayer *pPlayer = GetPlayerOwner();

	if ( Clip1() <= 0 )
	{
		if ( pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 && GetMaxClip1() != -1 )			
		{
			// clip is empty (or nonexistant) and the player has no more ammo of this type. 
			return false;
		}
	}

	return true;
}

#ifndef CLIENT_DLL
ConVar dod_meleeattackforcescale( "dod_meleeattackforcescale", "8.0", FCVAR_CHEAT | FCVAR_GAMEDLL );
#endif

void CWeaponDODBase::RifleButt( void )
{
	//MeleeAttack( 60, MELEE_DMG_BUTTSTOCK | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.9f );
}

void CWeaponDODBase::Bayonet( void )
{
	//MeleeAttack( 60, MELEE_DMG_BAYONET | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.9f );
}

void CWeaponDODBase::Punch( void )
{
	MeleeAttack( 60, MELEE_DMG_FIST | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.4f );
}

//--------------------------------------------
// iDamageAmount - how much damage to give
// iDamageType - DMG_ bits 
// flDmgDelay - delay between attack and the giving of damage, usually timed to animation
// flAttackDelay - time until we can next attack 
//--------------------------------------------
CBaseEntity *CWeaponDODBase::MeleeAttack( int iDamageAmount, int iDamageType, float flDmgDelay, float flAttackDelay )
{
	if ( !CanAttack() )
		return NULL;

	CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() );

#if !defined (CLIENT_DLL)
	// Move other players back to history positions based on local player's lag
	lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
#endif

	Vector vForward, vRight, vUp;
	AngleVectors( pPlayer->EyeAngles(), &vForward, &vRight, &vUp );
	Vector vecSrc	= pPlayer->Weapon_ShootPosition();
	Vector vecEnd	= vecSrc + vForward * 48;

	CTraceFilterSimple filter( pPlayer, COLLISION_GROUP_NONE );

	int iTraceMask = MASK_SOLID | CONTENTS_HITBOX | CONTENTS_DEBRIS;

	trace_t tr;
	UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &filter, &tr );

	const float rayExtension = 40.0f;
	UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vForward * rayExtension, iTraceMask, &filter, &tr );

	// If the exact forward trace did not hit, try a larger swept box 
	if ( tr.fraction >= 1.0 )
	{
		Vector head_hull_mins( -16, -16, -18 );
		Vector head_hull_maxs( 16, 16, 18 );

		UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, &filter, &tr );
		if ( tr.fraction < 1.0 )
		{
			// Calculate the point of intersection of the line (or hull) and the object we hit
			// This is and approximation of the "best" intersection
			CBaseEntity *pHit = tr.m_pEnt;
			if ( !pHit || pHit->IsBSPModel() )
				FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer );
			vecEnd = tr.endpos;	// This is the point on the actual surface (the hull could have hit space)

			// Make sure it is in front of us
			Vector vecToEnd = vecEnd - vecSrc;
			VectorNormalize( vecToEnd );

			// if zero length, always hit
			if ( vecToEnd.Length() > 0 )
			{
				float dot = DotProduct( vForward, vecToEnd );

				// sanity that our hit is within range
				if ( abs(dot) < 0.95 )
				{
					// fake that we actually missed
					tr.fraction = 1.0;
				}
			}			
		}
	}

	WeaponSound( MELEE_MISS );

	bool bDidHit = ( tr.fraction < 1.0f );

	if ( bDidHit )	//if the swing hit 
	{	
		// delay the decal a bit
		m_trHit = tr;

		// Store the ent in an EHANDLE, just in case it goes away by the time we get into our think function.
		m_pTraceHitEnt = tr.m_pEnt; 

		m_iSmackDamage = iDamageAmount;
		m_iSmackDamageType = iDamageType;

		m_flSmackTime = gpGlobals->curtime + flDmgDelay;
	}

	SendWeaponAnim( GetMeleeActivity() );

	// player animation
	pPlayer->DoAnimationEvent( PLAYERANIMEVENT_SECONDARY_ATTACK );

	m_flNextPrimaryAttack = gpGlobals->curtime + flAttackDelay;
	m_flNextSecondaryAttack = gpGlobals->curtime + flAttackDelay;
	m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();

#ifndef CLIENT_DLL
	IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" );
	if ( event )
	{
		event->SetInt( "attacker", pPlayer->GetUserID() );
		event->SetInt( "weapon", GetAltWeaponID() );

		gameeventmanager->FireEvent( event );
	}

	lagcompensation->FinishLagCompensation( pPlayer );
#endif	//CLIENT_DLL

	return tr.m_pEnt;
}

//Think function to delay the impact decal until the animation is finished playing
void CWeaponDODBase::Smack()
{
	Assert( GetPlayerOwner() );

	if ( !GetPlayerOwner() )
		return;

	CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() );

	if ( !pPlayer )
		return;

	// Check that we are still facing the victim
	Vector vForward, vRight, vUp;
	AngleVectors( pPlayer->EyeAngles(), &vForward, &vRight, &vUp );
	Vector vecSrc	= pPlayer->Weapon_ShootPosition();
	Vector vecEnd	= vecSrc + vForward * 48;

	CTraceFilterSimple filter( pPlayer, COLLISION_GROUP_NONE );

	int iTraceMask = MASK_SOLID | CONTENTS_HITBOX | CONTENTS_DEBRIS;

	trace_t tr;
	UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &filter, &tr );

	const float rayExtension = 40.0f;
	UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vForward * rayExtension, iTraceMask, &filter, &tr );

	if ( tr.fraction >= 1.0 )
	{
		Vector head_hull_mins( -16, -16, -18 );
		Vector head_hull_maxs( 16, 16, 18 );

		UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, &filter, &tr );
		if ( tr.fraction < 1.0 )
		{
			// Calculate the point of intersection of the line (or hull) and the object we hit
			// This is and approximation of the "best" intersection
			CBaseEntity *pHit = tr.m_pEnt;
			if ( !pHit || pHit->IsBSPModel() )
				FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer );
			vecEnd = tr.endpos;	// This is the point on the actual surface (the hull could have hit space)
		}
	}

	m_trHit = tr;

	if ( !m_trHit.m_pEnt || (m_trHit.surface.flags & SURF_SKY) )
		return;

	if ( m_trHit.fraction == 1.0 )
		return;

	CPASAttenuationFilter attenuationFilter( this );
	attenuationFilter.UsePredictionRules();

	if( m_trHit.m_pEnt->IsPlayer() )
	{
		if ( m_iSmackDamageType & MELEE_DMG_STRONGATTACK )
			WeaponSound( SPECIAL1 );
		else
            WeaponSound( MELEE_HIT );
	}
	else
		WeaponSound( MELEE_HIT_WORLD );

	int iDamageType = DMG_CLUB | DMG_NEVERGIB;

#ifndef CLIENT_DLL
	//if they hit the bounding box, just assume a chest hit
	if( m_trHit.hitgroup == HITGROUP_GENERIC )
		m_trHit.hitgroup = HITGROUP_CHEST;

	float flDamage = (float)m_iSmackDamage;

	CTakeDamageInfo info( pPlayer, pPlayer, flDamage, iDamageType );

	if ( m_iSmackDamageType & MELEE_DMG_SECONDARYATTACK )
		info.SetDamageCustom( MELEE_DMG_SECONDARYATTACK );

	float flScale = (1.0f / flDamage) * dod_meleeattackforcescale.GetFloat();

	Vector vecForceDir = vForward;

	CalculateMeleeDamageForce( &info, vecForceDir, m_trHit.endpos, flScale );

	Assert( m_trHit.m_pEnt != GetPlayerOwner() );

	m_trHit.m_pEnt->DispatchTraceAttack( info, vForward, &m_trHit ); 
	ApplyMultiDamage();
#endif

	// We've gotten minidumps where this happened.
	if ( !GetPlayerOwner() )
		return;

	CEffectData data;
	data.m_vOrigin = m_trHit.endpos;
	data.m_vStart = m_trHit.startpos;
	data.m_nSurfaceProp = m_trHit.surface.surfaceProps;
	data.m_nHitBox = m_trHit.hitbox;
#ifdef CLIENT_DLL
	data.m_hEntity = m_trHit.m_pEnt->GetRefEHandle();
#else
	data.m_nEntIndex = m_trHit.m_pEnt->entindex();
#endif

	CPASFilter effectfilter( data.m_vOrigin );

#ifndef CLIENT_DLL
	effectfilter.RemoveRecipient( GetPlayerOwner() );
#endif

	data.m_vAngles = GetPlayerOwner()->GetAbsAngles();
	data.m_fFlags = 0x1;	//IMPACT_NODECAL;
	data.m_nDamageType = iDamageType;

	bool bHitPlayer = m_trHit.m_pEnt && m_trHit.m_pEnt->IsPlayer();

	// don't do any impacts if we hit a teammate and ff is off
	if ( bHitPlayer && 
		m_trHit.m_pEnt->GetTeamNumber() == GetPlayerOwner()->GetTeamNumber() && 
		!friendlyfire.GetBool() )
		return;

	if ( bHitPlayer )
	{
		te->DispatchEffect( effectfilter, 0.0, data.m_vOrigin, "Impact", data );
	}
	else if ( m_iSmackDamageType & MELEE_DMG_EDGE )
	{
		data.m_nDamageType = DMG_SLASH;
		te->DispatchEffect( effectfilter, 0.0, data.m_vOrigin, "KnifeSlash", data );
	}
}

#if defined( CLIENT_DLL )

	float	g_lateralBob = 0;
	float	g_verticalBob = 0;

	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 CWeaponDODBase::CalcViewmodelBob( void )
	{
		static	float bobtime;
		static	float lastbobtime;
		static  float lastspeed;
		float	cycle;
		
		CBasePlayer *player = ToBasePlayer( GetOwner() );
		//Assert( player );

		//NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it

		if ( ( !gpGlobals->frametime ) || ( player == NULL ) )
		{
			//NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
			return 0.0f;// just use old value
		}

		//Find the speed of the player
		float speed = player->GetLocalVelocity().Length2D();
		float flmaxSpeedDelta = MAX( 0, (gpGlobals->curtime - lastbobtime) * 320.0f );

		// don't allow too big speed changes
		speed = clamp( speed, lastspeed-flmaxSpeedDelta, lastspeed+flmaxSpeedDelta );
		speed = clamp( speed, -320, 320 );

		lastspeed = speed;

		//FIXME: This maximum speed value must come from the server.
		//		 MaxSpeed() is not sufficient for dealing with sprinting - jdw

		float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f );
		
		bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset;
		lastbobtime = gpGlobals->curtime;

		//Calculate the vertical bob
		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());
		}
		
		g_verticalBob = speed*0.005f;
		g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle);

		g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f );

		//Calculate the lateral bob
		cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat()*2)*cl_bobcycle.GetFloat()*2;
		cycle /= cl_bobcycle.GetFloat()*2;

		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());
		}

		g_lateralBob = speed*0.005f;
		g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle);
		g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f );
		
		//NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
		return 0.0f;

	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	// Input  : &origin - 
	//			&angles - 
	//			viewmodelindex - 
	//-----------------------------------------------------------------------------
	void CWeaponDODBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
	{
		Vector	forward, right;
		AngleVectors( angles, &forward, &right, NULL );

		CalcViewmodelBob();

		// Apply bob, but scaled down to 40%
		VectorMA( origin, g_verticalBob * 0.4f, forward, origin );
		
		// Z bob a bit more
		origin[2] += g_verticalBob * 0.1f;
		
		// bob the angles
		angles[ ROLL ]	+= g_verticalBob * 0.5f;
		angles[ PITCH ]	-= g_verticalBob * 0.4f;

		angles[ YAW ]	-= g_lateralBob  * 0.3f;

	//	VectorMA( origin, g_lateralBob * 0.2f, right, origin );
	}
	
	#include "c_te_effect_dispatch.h"

	#define NUM_MUZZLE_FLASH_TYPES 4

	bool CWeaponDODBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
	{
		if( event == 5001 )
		{
			if ( ShouldDrawMuzzleFlash() )
			{
				Assert( GetOwnerEntity() == C_BasePlayer::GetLocalPlayer() );

				const char *pszMuzzleFlashEffect;

				switch( GetDODWpnData().m_iMuzzleFlashType )
				{
				case DOD_MUZZLEFLASH_PISTOL:
					pszMuzzleFlashEffect = "view_muzzle_pistols";
					break;
				case DOD_MUZZLEFLASH_AUTO:
					pszMuzzleFlashEffect = "view_muzzle_fullyautomatic";
					break;
				case DOD_MUZZLEFLASH_RIFLE:
					pszMuzzleFlashEffect = "view_muzzle_rifles";
					break;
				case DOD_MUZZLEFLASH_MG:
					pszMuzzleFlashEffect = "view_muzzle_miniguns";
					break;
				case DOD_MUZZLEFLASH_ROCKET:
					pszMuzzleFlashEffect = "view_muzzle_rockets";
					break;
				case DOD_MUZZLEFLASH_MG42:
					pszMuzzleFlashEffect = "view_muzzle_mg42";
					break;
				default:
					pszMuzzleFlashEffect = NULL;
					break;
				}

				if ( pszMuzzleFlashEffect )
				{
					pViewModel->ParticleProp()->Create( pszMuzzleFlashEffect, PATTACH_POINT_FOLLOW, 1 );
				}				
			}
			return true;
		}
		else if( event == 6002 )
		{
			CEffectData data;
			data.m_nHitBox = atoi( options );
			data.m_hEntity = GetPlayerOwner() ? GetPlayerOwner()->GetRefEHandle() : INVALID_EHANDLE_INDEX;
			pViewModel->GetAttachment( 2, data.m_vOrigin, data.m_vAngles );

			DispatchEffect( "DOD_EjectBrass", data );
			return true;
		}

		return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
	}

	bool CWeaponDODBase::ShouldAutoEjectBrass( void )
	{
		// Don't eject brass if further than N units from the local player
		C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
		if ( !pLocalPlayer )
			return true;

		float flMaxDistSqr = 250;
		flMaxDistSqr *= flMaxDistSqr;

		float flDistSqr = pLocalPlayer->EyePosition().DistToSqr( GetAbsOrigin() );
		return ( flDistSqr < flMaxDistSqr );
	}

	bool CWeaponDODBase::GetEjectBrassShellType( void )
	{
		return 1;
	}

	void CWeaponDODBase::SetUseAltModel( bool bUseAltModel )
	{
		m_bUseAltWeaponModel = bUseAltModel;
	}

	void CWeaponDODBase::CheckForAltWeapon( int iCurrentState )
	{
		int iCriteria = GetDODWpnData().m_iAltWpnCriteria;

		bool bUseAltModel = false;

		if( ( iCriteria & iCurrentState ) != 0 )
			bUseAltModel = true;

		SetUseAltModel( bUseAltModel );
	}

	Vector CWeaponDODBase::GetDesiredViewModelOffset( C_DODPlayer *pOwner )
	{
		Vector viewOffset = pOwner->GetViewOffset();

		float flPercent = ( viewOffset.z - VEC_PRONE_VIEW_SCALED( pOwner ).z ) / ( VEC_VIEW_SCALED( pOwner ).z - VEC_PRONE_VIEW_SCALED( pOwner ).z );

		return ( flPercent * GetDODWpnData().m_vecViewNormalOffset +
			( 1.0 - flPercent ) * GetDODWpnData().m_vecViewProneOffset );
	}

	bool CWeaponDODBase::ShouldDraw( void )
	{
		if ( GetModel() == NULL )
		{
			// XXX(johns): Removed, doesn't seem to be the proper spot for this warning given that weapons can call
			//             ShouldDraw before their properties are filled.

			// C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer();

			// Warning( "BADNESS! Tell Matt that the weapon '%s' tried to draw with a null model ( %d, %d, %s ) \n",
			// 	GetDODWpnData().szClassName,
			// 	m_iWorldModelIndex.Get(), m_iReloadModelIndex.Get(), m_bUseAltWeaponModel ? "alt" : "not alt" );

			return false;
		}

		return BaseClass::ShouldDraw();
	}

#else

	void CWeaponDODBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
	{

	}

	float CWeaponDODBase::CalcViewmodelBob( void )
	{
		return 0.0f;
	}

	void CWeaponDODBase::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
	{
		if ( CanDrop() == false )
			return;

		CDODPlayer *pPlayer = ToDODPlayer( pActivator );

		if ( pPlayer )
		{
			pPlayer->PickUpWeapon( this );
		}         
	}

#endif