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

#include "cbase.h"
#include "cs_player.h"
#include "cs_gamerules.h"
#include "dt_send.h"
#include "trains.h"
#include "vcollide_parse.h"
#include "in_buttons.h"
#include "igamemovement.h"
#include "ai_hull.h"
#include "ndebugoverlay.h"
#include "weapon_csbase.h"
#include "decals.h"
#include "cs_ammodef.h"
#include "IEffects.h"
#include "cs_client.h"
#include "client.h"
#include "cs_shareddefs.h"
#include "shake.h"
#include "team.h"
#include "weapon_c4.h"
#include "weapon_parse.h"
#include "weapon_knife.h"
#include "movehelper_server.h"
#include "tier0/vprof.h"
#include "te_effect_dispatch.h"
#include "vphysics/player_controller.h"
#include "weapon_hegrenade.h"
#include "weapon_flashbang.h"
#include "weapon_csbasegun.h"
#include "weapon_smokegrenade.h"
#include <KeyValues.h>
#include "engine/IEngineSound.h"
#include "bot.h"
#include "studio.h"
#include <coordsize.h>
#include "predicted_viewmodel.h"
#include "props_shared.h"
#include "tier0/icommandline.h"
#include "info_camera_link.h"
#include "hintmessage.h"
#include "obstacle_pushaway.h"
#include "movevars_shared.h"
#include "death_pose.h"
#include "basecsgrenade_projectile.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "CRagdollMagnet.h"
#include "datacache/imdlcache.h"
#include "npcevent.h"
#include "cs_gamestats.h"
#include "gamestats.h"
#include "holiday_gift.h"
#include "../../shared/cstrike/cs_achievement_constants.h"
#include "ilagcompensationmanager.h"

//=============================================================================
// HPE_BEGIN
//=============================================================================

// [dwenger] Needed for global hostage list
#include "cs_simple_hostage.h"

// [dwenger] Needed for weapon type used tracking
#include "../../shared/cstrike/cs_weapon_parse.h"

#define REPORT_PLAYER_DAMAGE 0

//=============================================================================
// HPE_END
//=============================================================================

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

#pragma optimize( "", off )

#pragma warning( disable : 4355 )

// Minimum interval between rate-limited commands that players can run.
#define CS_COMMAND_MAX_RATE 0.3

const float CycleLatchInterval = 0.2f;

#define CS_PUSHAWAY_THINK_CONTEXT	"CSPushawayThink"

ConVar cs_ShowStateTransitions( "cs_ShowStateTransitions", "-2", FCVAR_CHEAT, "cs_ShowStateTransitions <ent index or -1 for all>. Show player state transitions." );
ConVar sv_max_usercmd_future_ticks( "sv_max_usercmd_future_ticks", "8", 0, "Prevents clients from running usercmds too far in the future. Prevents speed hacks." );
ConVar sv_motd_unload_on_dismissal( "sv_motd_unload_on_dismissal", "0", 0, "If enabled, the MOTD contents will be unloaded when the player closes the MOTD." );
//=============================================================================
// HPE_BEGIN:
// [Forrest] Allow MVP to be turned off for a server
// [Forrest] Allow freezecam to be turned off for a server
// [Forrest] Allow win panel to be turned off for a server
//=============================================================================
static void SvNoMVPChangeCallback( IConVar *pConVar, const char *pOldValue, float flOldValue )
{
	ConVarRef var( pConVar );
	if ( var.IsValid() && var.GetBool() )
	{
		// Clear the MVPs of all players when MVP is turned off.
		for ( int i = 1; i <= MAX_PLAYERS; i++ )
		{
			CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );

			if ( pPlayer )
			{
				pPlayer->SetNumMVPs( 0 );
			}
		}
	}
}
ConVar sv_nomvp( "sv_nomvp", "0", 0, "Disable MVP awards.", SvNoMVPChangeCallback );
ConVar sv_disablefreezecam( "sv_disablefreezecam", "0", FCVAR_REPLICATED, "Turn on/off freezecam on server" );
ConVar sv_nowinpanel( "sv_nowinpanel", "0", FCVAR_REPLICATED, "Turn on/off win panel on server" );
//=============================================================================
// HPE_END
//=============================================================================


// ConVar bot_mimic( "bot_mimic", "0", FCVAR_CHEAT );
ConVar bot_freeze( "bot_freeze", "0", FCVAR_CHEAT );
ConVar bot_crouch( "bot_crouch", "0", FCVAR_CHEAT );
ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "180", FCVAR_CHEAT );

ConVar sv_legacy_grenade_damage( "sv_legacy_grenade_damage", "0", FCVAR_REPLICATED, "Enable to replicate grenade damage behavior of the original Counter-Strike Source game." );

extern ConVar mp_autokick;
extern ConVar mp_holiday_nogifts;
extern ConVar sv_turbophysics;
//=============================================================================
// HPE_BEGIN:
// [menglish] Added in convars for freeze cam time length
//=============================================================================
 
extern ConVar spec_freeze_time;
extern ConVar spec_freeze_traveltime;
 
//=============================================================================
// HPE_END
//=============================================================================

extern ConVar ammo_hegrenade_max;
extern ConVar ammo_flashbang_max;
extern ConVar ammo_smokegrenade_max;


#define THROWGRENADE_COUNTER_BITS 3


EHANDLE g_pLastCTSpawn;
EHANDLE g_pLastTerroristSpawn;

void TE_RadioIcon( IRecipientFilter& filter, float delay, CBaseEntity *pPlayer );


// -------------------------------------------------------------------------------- //
// Classes
// -------------------------------------------------------------------------------- //

class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent
{
public:
	int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position )
	{
		CCSPlayer *pPlayer = (CCSPlayer *)pObject->GetGameData();
		if ( pPlayer )
		{
			if ( pPlayer->TouchedPhysics() )
			{
				return 0;
			}
		}
		return 1;
	}
};

static CPhysicsPlayerCallback playerCallback;


// -------------------------------------------------------------------------------- //
// Ragdoll entities.
// -------------------------------------------------------------------------------- //

class CCSRagdoll : public CBaseAnimatingOverlay
{
public:
	DECLARE_CLASS( CCSRagdoll, CBaseAnimatingOverlay );
	DECLARE_SERVERCLASS();

	// Transmit ragdolls to everyone.
	virtual int UpdateTransmitState()
	{
		return SetTransmitState( FL_EDICT_ALWAYS );
	}

	void Init( void )
	{
		SetSolid( SOLID_BBOX );
		SetMoveType( MOVETYPE_STEP );
		SetFriction( 1.0f );
		SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
		m_takedamage = DAMAGE_NO;
		SetCollisionGroup( COLLISION_GROUP_DEBRIS );
		SetAbsOrigin( m_hPlayer->GetAbsOrigin() );
		SetAbsVelocity( m_hPlayer->GetAbsVelocity() );
		AddSolidFlags( FSOLID_NOT_SOLID );
		ChangeTeam( m_hPlayer->GetTeamNumber() );
	}

public:
	// In case the client has the player entity, we transmit the player index.
	// In case the client doesn't have it, we transmit the player's model index, origin, and angles
	// so they can create a ragdoll in the right place.
	CNetworkHandle( CBaseEntity, m_hPlayer );	// networked entity handle
	CNetworkVector( m_vecRagdollVelocity );
	CNetworkVector( m_vecRagdollOrigin );
	CNetworkVar(int, m_iDeathPose );
	CNetworkVar(int, m_iDeathFrame );
};

LINK_ENTITY_TO_CLASS( cs_ragdoll, CCSRagdoll );

IMPLEMENT_SERVERCLASS_ST_NOBASE( CCSRagdoll, DT_CSRagdoll )
	SendPropVector	(SENDINFO(m_vecOrigin), -1,  SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
	SendPropVector( SENDINFO(m_vecRagdollOrigin), -1,  SPROP_COORD ),
	SendPropEHandle( SENDINFO( m_hPlayer ) ),
	SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
	SendPropInt		( SENDINFO(m_nForceBone), 8, 0 ),
	SendPropVector	( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ),
	SendPropVector( SENDINFO( m_vecRagdollVelocity ) ),
	SendPropInt( SENDINFO( m_iDeathPose ), ANIMATION_SEQUENCE_BITS, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_iDeathFrame ), 5 ),
	SendPropInt( SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0),
	SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ),
END_SEND_TABLE()


// -------------------------------------------------------------------------------- //
// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
// -------------------------------------------------------------------------------- //

class CTEPlayerAnimEvent : public CBaseTempEntity
{
public:
	DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
	DECLARE_SERVERCLASS();

					CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
					{
					}

	CNetworkHandle( CBasePlayer, m_hPlayer );
	CNetworkVar( int, m_iEvent );
	CNetworkVar( int, m_nData );
};

IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
	SendPropEHandle( SENDINFO( m_hPlayer ) ),
	SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_nData ), 32 )
END_SEND_TABLE()

static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );

void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
{
	CPVSFilter filter( (const Vector&)pPlayer->EyePosition() );

	g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
	g_TEPlayerAnimEvent.m_iEvent = event;
	g_TEPlayerAnimEvent.m_nData = nData;
	g_TEPlayerAnimEvent.Create( filter, 0 );
}


//-----------------------------------------------------------------------------
// Purpose: Filters updates to a variable so that only non-local players see
// the changes.  This is so we can send a low-res origin to non-local players
// while sending a hi-res one to the local player.
// Input  : *pVarData -
//			*pOut -
//			objectID -
//-----------------------------------------------------------------------------

void* SendProxy_SendNonLocalDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
{
	pRecipients->SetAllRecipients();
	pRecipients->ClearRecipient( objectID - 1 );
	return ( void * )pVarData;
}
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendNonLocalDataTable );


// -------------------------------------------------------------------------------- //
// Tables.
// -------------------------------------------------------------------------------- //

LINK_ENTITY_TO_CLASS( player, CCSPlayer );
PRECACHE_REGISTER(player);

BEGIN_SEND_TABLE_NOBASE( CCSPlayer, DT_CSLocalPlayerExclusive )
	SendPropFloat( SENDINFO( m_flStamina ) ),
	SendPropInt( SENDINFO( m_iDirection ), 1, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_iShotsFired ), 8, SPROP_UNSIGNED ),
	SendPropFloat( SENDINFO( m_flVelocityModifier ), 8, 0, 0, 1  ),

	//=============================================================================
	// HPE_BEGIN:
	// [tj]Set up the send table for per-client domination data
	//=============================================================================
 
	SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominated ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominated ) ) ),
	SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominatingMe ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominatingMe ) ) ),
 
	//=============================================================================
	// HPE_END
	//=============================================================================

END_SEND_TABLE()

IMPLEMENT_SERVERCLASS_ST( CCSPlayer, DT_CSPlayer )
	// Data that only gets sent to the local player.
	SendPropDataTable( "cslocaldata", 0, &REFERENCE_SEND_TABLE(DT_CSLocalPlayerExclusive), SendProxy_SendLocalDataTable ),

	SendPropInt( SENDINFO( m_iThrowGrenadeCounter ), THROWGRENADE_COUNTER_BITS, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_iAddonBits ), NUM_ADDON_BITS, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_iPrimaryAddon ), 8, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_iSecondaryAddon ), 8, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_iPlayerState ), Q_log2( NUM_PLAYER_STATES )+1, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_iAccount ), 16, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_bInBombZone ), 1, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_bInBuyZone ), 1, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_iClass ), Q_log2( CS_NUM_CLASSES )+1, SPROP_UNSIGNED ),
	SendPropInt( SENDINFO( m_ArmorValue ), 8 ),
	SendPropQAngles(SENDINFO(m_angEyeAngles)),
	SendPropBool( SENDINFO( m_bHasDefuser ) ),
	SendPropBool( SENDINFO( m_bNightVisionOn ) ),	//send as int so we can use a RecvProxy on the client
	SendPropBool( SENDINFO( m_bHasNightVision ) ),

	//=============================================================================
	// HPE_BEGIN:
	// [dwenger] Added for fun-fact support
	//=============================================================================

	//SendPropBool( SENDINFO( m_bPickedUpDefuser ) ),
	//SendPropBool( SENDINFO( m_bDefusedWithPickedUpKit) ),

	//=============================================================================
	// HPE_END
	//=============================================================================

	SendPropBool( SENDINFO( m_bInHostageRescueZone ) ),
	SendPropBool( SENDINFO( m_bIsDefusing ) ),

	SendPropBool( SENDINFO( m_bResumeZoom ) ),
	SendPropInt( SENDINFO( m_iLastZoom ), 8, SPROP_UNSIGNED ),

#ifdef CS_SHIELD_ENABLED
	SendPropBool( SENDINFO( m_bHasShield ) ),
	SendPropBool( SENDINFO( m_bShieldDrawn ) ),
#endif
	SendPropBool( SENDINFO( m_bHasHelmet ) ),
	SendPropFloat	(SENDINFO(m_flFlashDuration), 0, SPROP_NOSCALE ),
	SendPropFloat( SENDINFO(m_flFlashMaxAlpha), 0, SPROP_NOSCALE ),
	SendPropInt( SENDINFO( m_iProgressBarDuration ), 4, SPROP_UNSIGNED ),
	SendPropFloat( SENDINFO( m_flProgressBarStartTime ), 0, SPROP_NOSCALE ),
	SendPropEHandle( SENDINFO( m_hRagdoll ) )


END_SEND_TABLE()


BEGIN_DATADESC( CCSPlayer )

	DEFINE_INPUTFUNC( FIELD_VOID, "OnRescueZoneTouch", RescueZoneTouch ),
	DEFINE_THINKFUNC( PushawayThink )

END_DATADESC()


// has to be included after above macros
#include "cs_bot.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"



// -------------------------------------------------------------------------------- //

void cc_CreatePredictionError_f( const CCommand &args )
{
	float distance = 32;

	if ( args.ArgC() >= 2 )
	{
		distance = atof(args[1]);
	}

	CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
	pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( distance, 0, 0 ) );
}

ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );


// -------------------------------------------------------------------------------- //
// CCSPlayer implementation.
// -------------------------------------------------------------------------------- //

CCSPlayer::CCSPlayer()
{
	m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true );

	m_iLastWeaponFireUsercmd = 0;
	m_iAddonBits = 0;
	m_bEscaped = false;
	m_iAccount = 0;

	m_bIsVIP = false;
	m_iClass = (int)CS_CLASS_NONE;
	m_angEyeAngles.Init();

	SetViewOffset( VEC_VIEW_SCALED( this ) );

	m_pCurStateInfo = NULL;	// no state yet
	m_iThrowGrenadeCounter = 0;

	m_lifeState = LIFE_DEAD; // Start "dead".
	m_bInBombZone = false;
	m_bInBuyZone = false;
	m_bInHostageRescueZone = false;
	m_flDeathTime = 0.0f;
	m_iHostagesKilled = 0;
	iRadioMenu = -1;
	m_bTeamChanged = false;
	m_iShotsFired = 0;
	m_iDirection = 0;
	m_receivesMoneyNextRound = true;
	m_bIsBeingGivenItem = false;
	m_isVIP = false;

	m_bJustKilledTeammate = false;
	m_bPunishedForTK = false;
	m_iTeamKills = 0;
	m_flLastMovement = gpGlobals->curtime;
	m_iNextTimeCheck = 0;

	m_szNewName[0] = 0;
	m_szClanTag[0] = 0;

	for ( int i=0; i<NAME_CHANGE_HISTORY_SIZE; i++ )
	{
		m_flNameChangeHistory[i] = -NAME_CHANGE_HISTORY_INTERVAL;
	}

	m_iIgnoreGlobalChat = 0;
	m_bIgnoreRadio = false;

	m_pHintMessageQueue = new CHintMessageQueue(this);
	m_iDisplayHistoryBits = 0;
	m_bShowHints = true;
	m_flNextMouseoverUpdate = gpGlobals->curtime;

	m_lastDamageHealth = 0;
	m_lastDamageArmor = 0;

    m_applyDeafnessTime = 0.0f;

	m_iShouldHaveCash = 0;

	m_lastNavArea = NULL;

	//=============================================================================
	// HPE_BEGIN:
	// [menglish] Init achievement variables
	// [menglish] Init bullet collision variables
	//=============================================================================

	m_NumEnemiesKilledThisRound = 0;
	m_NumEnemiesAtRoundStart = 0;
	m_KillingSpreeStartTime = -1;
	m_firstKillBlindStartTime = -1;
	m_killsWhileBlind = 0;
	m_bSurvivedHeadshotDueToHelmet = false;
	m_pGooseChaseDistractingPlayer = NULL;
	m_gooseChaseStep = GC_NONE;
	m_defuseDefenseStep = DD_NONE;
	m_lastRoundResult = Invalid_Round_End_Reason;
	m_bMadeFootstepNoise = false;
	m_bombPickupTime = -1;
	m_bMadePurchseThisRound = false;
	m_roundsWonWithoutPurchase = 0;
	m_iDeathFlags = 0;
	m_lastFlashBangAttacker = NULL;
	m_iMVPs = 0;
	m_bKilledDefuser = false;
	m_bKilledRescuer = false;
	m_maxGrenadeKills = 0;
	m_grenadeDamageTakenThisRound = 0;

	m_vLastHitLocationObjectSpace = Vector(0,0,0);

	m_wasNotKilledNaturally = false;
	//=============================================================================
	// HPE_END
	//=============================================================================
}


CCSPlayer::~CCSPlayer()
{
	delete m_pHintMessageQueue;
	m_pHintMessageQueue = NULL;

	// delete the records of damage taken and given
	ResetDamageCounters();
	m_PlayerAnimState->Release();
}


CCSPlayer *CCSPlayer::CreatePlayer( const char *className, edict_t *ed )
{
	CCSPlayer::s_PlayerEdict = ed;
	return (CCSPlayer*)CreateEntityByName( className );
}


void CCSPlayer::Precache()
{
	Vector mins( -13, -13, -10 );
	Vector maxs( 13, 13, 75 );

	int i;
	for ( i=0; i<CTPlayerModels.Count(); ++i )
	{
		PrecacheModel( CTPlayerModels[i] );
		engine->ForceModelBounds( CTPlayerModels[i], mins, maxs );
	}
	for ( i=0; i<TerroristPlayerModels.Count(); ++i )
	{
		PrecacheModel( TerroristPlayerModels[i] );
		engine->ForceModelBounds( TerroristPlayerModels[i], mins, maxs );
	}

	// Sigh - have to force identical VMTs for the player models.  I'm just going to hard-code these
	// strings here, rather than have char***'s or the CUtlVector<CUtlVector<>> equivalent.
	engine->ForceSimpleMaterial( "materials/models/player/ct_urban/ct_urban.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/ct_urban/ct_urban_glass.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/ct_sas/ct_sas.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/ct_sas/ct_sas_glass.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/ct_gsg9/ct_gsg9.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/ct_gign/ct_gign.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/ct_gign/ct_gign_glass.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/t_phoenix/t_phoenix.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/t_guerilla/t_guerilla.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/t_leet/t_leet.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/t_leet/t_leet_glass.vmt" );
	engine->ForceSimpleMaterial( "materials/models/player/t_arctic/t_arctic.vmt" );

#ifdef CS_SHIELD_ENABLED
	PrecacheModel( SHIELD_VIEW_MODEL );
#endif

	PrecacheScriptSound( "Player.DeathHeadShot" );
	PrecacheScriptSound( "Player.Death" );
	PrecacheScriptSound( "Player.DamageHelmet" );
	PrecacheScriptSound( "Player.DamageHeadShot" );
	PrecacheScriptSound( "Flesh.BulletImpact" );
	PrecacheScriptSound( "Player.DamageKevlar" );
	PrecacheScriptSound( "Player.PickupWeapon" );
	PrecacheScriptSound( "Player.NightVisionOff" );
	PrecacheScriptSound( "Player.NightVisionOn" );
	PrecacheScriptSound( "Player.FlashlightOn" );
	PrecacheScriptSound( "Player.FlashlightOff" );

	// CS Bot sounds
	PrecacheScriptSound( "Bot.StuckSound" );
	PrecacheScriptSound( "Bot.StuckStart" );
	PrecacheScriptSound( "Bot.FellOff" );

	UTIL_PrecacheOther( "item_kevlar" );
	UTIL_PrecacheOther( "item_assaultsuit" );
	UTIL_PrecacheOther( "item_defuser" );

	PrecacheModel ( "sprites/glow01.vmt" );
	PrecacheModel ( "models/items/cs_gift.mdl" );

	BaseClass::Precache();
}


//-----------------------------------------------------------------------------
// Purpose: Allow pre-frame adjustments on the player
//-----------------------------------------------------------------------------
ConVar sv_runcmds( "sv_runcmds", "1" );
void CCSPlayer::PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper )
{
	VPROF( "CCSPlayer::PlayerRunCommand" );

	if ( !sv_runcmds.GetInt() )
		return;

	// don't run commands in the future
    if (!IsEngineThreaded()
        && (ucmd->tick_count > (gpGlobals->tickcount + sv_max_usercmd_future_ticks.GetInt())) && !IsBot())
	{
		DevMsg( "Client cmd out of sync (delta: %i, client: %i != server: %i).\n", ucmd->tick_count - gpGlobals->tickcount, ucmd->tick_count, gpGlobals->tickcount);
		return;
	}

	// If they use a negative bot_mimic value, then don't process their usercmds, but have
	// bots process them instead (so they can stay still and have the bot move around).
	CUserCmd tempCmd;
	if ( -bot_mimic.GetInt() == entindex() )
	{
		tempCmd = *ucmd;
		ucmd = &tempCmd;

		ucmd->forwardmove = ucmd->sidemove = ucmd->upmove = 0;
		ucmd->buttons = 0;
		ucmd->impulse = 0;
	}

	if ( IsBot() && bot_crouch.GetInt() )
		ucmd->buttons |= IN_DUCK;

	BaseClass::PlayerRunCommand( ucmd, moveHelper );
}


bool CCSPlayer::RunMimicCommand( CUserCmd& cmd )
{
	if ( !IsBot() )
		return false;

	int iMimic = abs( bot_mimic.GetInt() );
	if ( iMimic > gpGlobals->maxClients )
		return false;

	CBasePlayer *pPlayer = UTIL_PlayerByIndex( iMimic );
	if ( !pPlayer )
		return false;

	if ( !pPlayer->GetLastUserCommand() )
		return false;

	cmd = *pPlayer->GetLastUserCommand();
	cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();

	pl.fixangle = FIXANGLE_NONE;

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Simulates a single frame of movement for a player
//-----------------------------------------------------------------------------
void CCSPlayer::RunPlayerMove( const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
{
	CUserCmd cmd;

	// Store off the globals.. they're gonna get whacked
	float flOldFrametime = gpGlobals->frametime;
	float flOldCurtime = gpGlobals->curtime;

	float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
	this->SetTimeBase( flTimeBase );

	CUserCmd lastUserCmd = *GetLastUserCommand();
	Q_memset( &cmd, 0, sizeof( cmd ) );

	if ( !RunMimicCommand( cmd ) )
	{
		cmd.forwardmove = forwardmove;
		cmd.sidemove = sidemove;
		cmd.upmove = upmove;
		cmd.buttons = buttons;
		cmd.impulse = impulse;

		VectorCopy( viewangles, cmd.viewangles );
		cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
	}

	MoveHelperServer()->SetHost( this );
	PlayerRunCommand( &cmd, MoveHelperServer() );

	// save off the last good usercmd
	if ( -bot_mimic.GetInt() == entindex() )
	{
		CUserCmd lastCmd = *GetLastUserCommand();
		lastCmd.command_number = cmd.command_number;
		lastCmd.tick_count = cmd.tick_count;
		SetLastUserCommand( lastCmd );
	}
	else
	{
		SetLastUserCommand( cmd );
	}

	// Clear out any fixangle that has been set
	pl.fixangle = FIXANGLE_NONE;

	// Restore the globals..
	gpGlobals->frametime = flOldFrametime;
	gpGlobals->curtime = flOldCurtime;

	MoveHelperServer()->SetHost( NULL );
}


void CCSPlayer::InitialSpawn( void )
{
	BaseClass::InitialSpawn();

	// we're going to give the bots money here instead of FinishClientPutInServer()
	// because of the bots' timing for purchasing weapons/items.
	if ( IsBot() )
	{
		m_iAccount = CSGameRules()->GetStartMoney();
	}

	if ( !engine->IsDedicatedServer() && TheNavMesh->IsOutOfDate() && this == UTIL_GetListenServerHost() )
	{
		ClientPrint( this, HUD_PRINTCENTER, "The Navigation Mesh was built using a different version of this map." );
	}

	State_Enter( STATE_WELCOME );

	//=============================================================================
	// HPE_BEGIN:
	// [tj] We reset the stats at the beginning of the map (including domination tracking)
	//=============================================================================
	 
	CCS_GameStats.ResetPlayerStats(this);
	RemoveNemesisRelationships();
	 
	//=============================================================================
	// HPE_END
	//=============================================================================
	
}

void CCSPlayer::SetModelFromClass( void )
{
	if ( GetTeamNumber() == TEAM_TERRORIST )
	{
		int index = m_iClass - FIRST_T_CLASS;
		if ( index < 0 || index >= TerroristPlayerModels.Count() )
		{
			index = RandomInt( 0, TerroristPlayerModels.Count() - 1 );
			m_iClass = index + FIRST_T_CLASS; // clean up players who selected a higher class than we support yet
		}
		SetModel( TerroristPlayerModels[index] );
	}
	else if ( GetTeamNumber() == TEAM_CT )
	{
		int index = m_iClass - FIRST_CT_CLASS;
		if ( index < 0 || index >= CTPlayerModels.Count() )
		{
			index = RandomInt( 0, CTPlayerModels.Count() - 1 );
			m_iClass = index + FIRST_CT_CLASS; // clean up players who selected a higher class than we support yet
		}
		SetModel( CTPlayerModels[index] );
	}
	else
	{
		SetModel( CTPlayerModels[0] );
	}
}

void CCSPlayer::Spawn()
{
	m_RateLimitLastCommandTimes.Purge();

	// Get rid of the progress bar...
	SetProgressBarTime( 0 );

	CreateViewModel( 1 );

	// Set their player model.
	SetModelFromClass();

	BaseClass::Spawn();

	//=============================================================================
	// HPE_BEGIN:
	// [pfreese] Clear the last known nav area (used to be done by CBasePlayer)
	//=============================================================================
	
	m_lastNavArea = NULL;
	
	//=============================================================================
	// HPE_END
	//=============================================================================

	AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round.

	// Override what CBasePlayer set for the view offset.
	SetViewOffset( VEC_VIEW_SCALED( this ) );

	//
	// Our player movement speed is set once here. This will override the cl_xxxx
	// cvars unless they are set to be lower than this.
	//
	SetMaxSpeed( CS_PLAYER_SPEED_RUN );

	SetFOV( this, 0 );

	m_bIsDefusing = false;

	//=============================================================================
	// HPE_BEGIN
	// [dwenger] Reset hostage-related variables
	//=============================================================================

	m_bIsRescuing = false;
	m_bInjuredAHostage = false;
	m_iNumFollowers = 0;
	
	// [tj] Reset this flag if the player is not in observer mode (as happens when a player spawns late)
	if (m_iPlayerState != STATE_OBSERVER_MODE)
	{
		m_wasNotKilledNaturally = false;
	}

	//=============================================================================
	// HPE_END
	//=============================================================================

	m_iShotsFired = 0;
	m_iDirection = 0;

	if ( m_pHintMessageQueue )
	{
		m_pHintMessageQueue->Reset();
	}
	m_iDisplayHistoryBits &= ~DHM_ROUND_CLEAR;

	// Special-case here. A bunch of things happen in CBasePlayer::Spawn(), and we really want the
	// player states to control these things, so give whatever player state we're in a chance
	// to reinitialize itself.
	State_Transition( m_iPlayerState );

	ClearFlashbangScreenFade();

	m_flVelocityModifier = 1.0f;

	ResetStamina();

	m_flLastRadarUpdateTime = 0.0f;

	m_iNumSpawns++;

	if ( !engine->IsDedicatedServer() && CSGameRules()->m_iTotalRoundsPlayed < 2 && TheNavMesh->IsOutOfDate() && this == UTIL_GetListenServerHost() )
	{
		ClientPrint( this, HUD_PRINTCENTER, "The Navigation Mesh was built using a different version of this map." );
	}

	m_bTeamChanged	= false;
	m_iOldTeam = TEAM_UNASSIGNED;

	m_iRadioMessages = 60;
	m_flRadioTime = gpGlobals->curtime;

	if ( m_hRagdoll )
	{
		UTIL_Remove( m_hRagdoll );
	}

	m_hRagdoll = NULL;

	// did we change our name while we were dead?
	if ( m_szNewName[0] != 0 )
	{
		ChangeName( m_szNewName );
		m_szNewName[0] = 0;
	}

	if ( m_bIsVIP )
	{
		HintMessage( "#Hint_you_are_the_vip", true, true );
	}

	m_bIsInAutoBuy = false;
	m_bIsInRebuy = false;
	m_bAutoReload = false;

	SetContextThink( &CCSPlayer::PushawayThink, gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, CS_PUSHAWAY_THINK_CONTEXT );

	if ( GetActiveWeapon() && !IsObserver() )
	{
		GetActiveWeapon()->Deploy();
		m_flNextAttack = gpGlobals->curtime; // Allow reloads to finish, since we're playing the deploy anim instead.  This mimics goldsrc behavior, anyway.
	}

    m_applyDeafnessTime = 0.0f;

	StockPlayerAmmo();
	}

void CCSPlayer::ShowViewPortPanel( const char * name, bool bShow, KeyValues *data )
{
	if ( CSGameRules()->IsLogoMap() )
		return;

	if ( CommandLine()->FindParm("-makedevshots") )
		return;

	BaseClass::ShowViewPortPanel( name, bShow, data );
}

void CCSPlayer::ClearFlashbangScreenFade( void )
{
	if( IsBlind() )
	{
		color32 clr = { 0, 0, 0, 0 };
		UTIL_ScreenFade( this, clr, 0.01, 0.0, FFADE_OUT | FFADE_PURGE );

		m_flFlashDuration = 0.0f;
		m_flFlashMaxAlpha = 255.0f;
	}

	// clear blind time (after screen fades are canceled)
	m_blindUntilTime = 0.0f;
	m_blindStartTime = 0.0f;
}

void CCSPlayer::GiveDefaultItems()
{
	// Always give the player the knife.
	CBaseCombatWeapon *pistol = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
	if ( pistol )
	{
		return;
	}

	m_bUsingDefaultPistol = true;

	if ( GetTeamNumber() == TEAM_CT )
	{
		GiveNamedItem( "weapon_knife" );
		GiveNamedItem( "weapon_usp" );
		GiveAmmo( 24, BULLET_PLAYER_45ACP );
	}
	else if ( GetTeamNumber() == TEAM_TERRORIST )
	{
		GiveNamedItem( "weapon_knife" );
		GiveNamedItem( "weapon_glock" );
		GiveAmmo( 40, BULLET_PLAYER_9MM );
	}
}

void CCSPlayer::SetClanTag( const char *pTag )
{
	if ( pTag )
	{
		Q_strncpy( m_szClanTag, pTag, sizeof( m_szClanTag ) );
	}
}
void CCSPlayer::CreateRagdollEntity()
{
	// If we already have a ragdoll, don't make another one.
	CCSRagdoll *pRagdoll = dynamic_cast< CCSRagdoll* >( m_hRagdoll.Get() );

	if ( !pRagdoll )
	{
		// create a new one
		pRagdoll = dynamic_cast< CCSRagdoll* >( CreateEntityByName( "cs_ragdoll" ) );
	}

	if ( pRagdoll )
	{
		pRagdoll->m_hPlayer = this;
		pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
		pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
		pRagdoll->m_nModelIndex = m_nModelIndex;
		pRagdoll->m_nForceBone = m_nForceBone;
		pRagdoll->m_vecForce = m_vecTotalBulletForce;
		pRagdoll->m_iDeathPose = m_iDeathPose;
		pRagdoll->m_iDeathFrame = m_iDeathFrame;
		pRagdoll->Init();
	}

	// ragdolls will be removed on round restart automatically
	m_hRagdoll = pRagdoll;
}

int CCSPlayer::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
	if ( GetFlags() & FL_GODMODE )
		return 0;

	// set damage type sustained
	m_bitsDamageType |= info.GetDamageType();

	if ( !CBaseCombatCharacter::OnTakeDamage_Alive( info ) )
		return 0;

	// don't apply damage forces in CS

	// fire global game event

	IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );

	if ( event )
	{
		event->SetInt("userid", GetUserID() );
		event->SetInt("health", MAX(0, m_iHealth) );
		event->SetInt("armor", MAX(0, ArmorValue()) );

		event->SetInt( "dmg_health", m_lastDamageHealth );
		event->SetInt( "dmg_armor", m_lastDamageArmor );

		if ( info.GetDamageType() & DMG_BLAST )
		{
			event->SetInt( "hitgroup", HITGROUP_GENERIC );
		}
		else
		{
			event->SetInt( "hitgroup", m_LastHitGroup );
		}

		CBaseEntity * attacker = info.GetAttacker();
		const char *weaponName = "";

		if ( attacker->IsPlayer() )
		{
			CBasePlayer *player = ToBasePlayer( attacker );
			event->SetInt("attacker", player->GetUserID() ); // hurt by other player

			CBaseEntity *pInflictor = info.GetInflictor();
			if ( pInflictor )
			{
				if ( pInflictor == player )
				{
					// If the inflictor is the killer,  then it must be their current weapon doing the damage
					if ( player->GetActiveWeapon() )
					{
						weaponName = player->GetActiveWeapon()->GetClassname();
					}
				}
				else
				{
					weaponName = STRING( pInflictor->m_iClassname );  // it's just that easy
				}
			}
		}
		else
		{
			event->SetInt("attacker", 0 ); // hurt by "world"
		}

		if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
		{
			weaponName += 7;
		}
		else if( strncmp( weaponName, "hegrenade", 9 ) == 0 )	//"hegrenade_projectile"
		{
			//=============================================================================
			// HPE_BEGIN:
			// [tj] Handle grenade-surviving achievement
			//=============================================================================
			if (info.GetAttacker()->GetTeamNumber() != GetTeamNumber())
			{
				m_grenadeDamageTakenThisRound += info.GetDamage();
			}
			//=============================================================================
			// HPE_END
			//=============================================================================

			weaponName = "hegrenade";
		}
		else if( strncmp( weaponName, "flashbang", 9 ) == 0 )	//"flashbang_projectile"
		{
			weaponName = "flashbang";
		}
		else if( strncmp( weaponName, "smokegrenade", 12 ) == 0 )	//"smokegrenade_projectile"
		{
			weaponName = "smokegrenade";
		}

		event->SetString( "weapon", weaponName );
		event->SetInt( "priority", 5 );

		gameeventmanager->FireEvent( event );
	}

	return 1;
}

//=============================================================================
// HPE_BEGIN:
// [dwenger] Supports fun-fact
//=============================================================================

// Returns the % of the enemies this player killed in the round
int CCSPlayer::GetPercentageOfEnemyTeamKilled()
{
	if ( m_NumEnemiesAtRoundStart > 0 )
	{
		return (int)( ( (float)m_NumEnemiesKilledThisRound / (float)m_NumEnemiesAtRoundStart ) * 100.0f );
	}

	return 0;
}

//=============================================================================
// HPE_END
//=============================================================================

void CCSPlayer::Event_Killed( const CTakeDamageInfo &info )
{
	//=============================================================================
	// HPE_BEGIN:
	// [pfreese] Process on-death achievements
	//=============================================================================
	
	ProcessPlayerDeathAchievements(ToCSPlayer(info.GetAttacker()), this, info);

	//=============================================================================
	// HPE_END
	//=============================================================================

	SetArmorValue( 0 );

	//=============================================================================
	// HPE_BEGIN:
	// [tj] Added a parameter so we know if it was death that caused the drop
	// [menglish] Keep track of what the player has dropped for the freeze panel callouts
	//=============================================================================
	 
	CBaseEntity* pAttacker = info.GetAttacker();
	bool friendlyFire = pAttacker && pAttacker->GetTeamNumber() == GetTeamNumber();

	//Only count the drop if it was not friendly fire
	DropWeapons(true, !friendlyFire);
	 
	//=============================================================================
	// HPE_END
	//=============================================================================
	

	// Just in case the progress bar is on screen, kill it.
	SetProgressBarTime( 0 );

	m_bIsDefusing = false;

	m_bHasNightVision = false;
	m_bNightVisionOn = false;

	//=============================================================================
	// HPE_BEGIN:
	// [dwenger] Added for fun-fact support
	//=============================================================================

	m_bPickedUpDefuser = false;
	m_bDefusedWithPickedUpKit = false;

	//=============================================================================
	// HPE_END
	//=============================================================================

	m_bHasHelmet = false;

	m_flFlashDuration = 0.0f;

	FlashlightTurnOff();

	// show killer in death cam mode
	if( IsValidObserverTarget( info.GetAttacker() ) )
	{
		SetObserverTarget( info.GetAttacker() );
	}
	else
	{
		ResetObserverMode();
	}

	//update damage info with our accumulated physics force
	CTakeDamageInfo subinfo = info;

	// HACK[pfreese]: scale impulse up for visual effect
	const float kImpulseBonusScale = 2.0f;
	subinfo.SetDamageForce( m_vecTotalBulletForce * kImpulseBonusScale);

	//Adrian: Select a death pose to extrapolate the ragdoll's velocity.
	SelectDeathPose( info );

	// See if there's a ragdoll magnet that should influence our force.
	CRagdollMagnet *pMagnet = CRagdollMagnet::FindBestMagnet( this );
	if( pMagnet )
	{
		m_vecTotalBulletForce += pMagnet->GetForceVector( this );
	}

	// Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
	// because we still want to transmit to the clients in our PVS.
	CreateRagdollEntity();

	// Special code to drop holiday gifts for the holiday achievement
	if ( ( mp_holiday_nogifts.GetBool() == false ) && UTIL_IsHolidayActive( 3 /*kHoliday_Christmas*/ ) )
	{
		if ( RandomInt( 0, 100 ) < 20 )
		{
			CHolidayGift::Create( WorldSpaceCenter(), GetAbsAngles(), EyeAngles(), GetAbsVelocity(), this );
		}
	}

	State_Transition( STATE_DEATH_ANIM );	// Transition into the dying state.
	BaseClass::Event_Killed( subinfo );

	//=============================================================================
	// HPE_BEGIN:
	// [pfreese] If this kill ended the round, award the MVP to someone on the
	// winning team.
	// TODO - move this code somewhere else more MVP related
	//=============================================================================	 

	bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
	bool roundIsWonNow = CSGameRules()->CheckWinConditions();

	if ( !roundWasAlreadyWon && roundIsWonNow )
	{
		CCSPlayer* pMVP = NULL;
		int maxKills = 0;
		int maxDamage = 0;

		for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
			if ( pPlayer )
			{
				// only consider players on the winning team
				if ( pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus )
					continue;

				int nKills = CCS_GameStats.FindPlayerStats( pPlayer ).statsCurrentRound[CSSTAT_KILLS];
				int nDamage = CCS_GameStats.FindPlayerStats( pPlayer ).statsCurrentRound[CSSTAT_DAMAGE];

				if ( nKills > maxKills || ( nKills == maxKills && nDamage > maxDamage ) )
				{
					pMVP = pPlayer;
					maxKills = nKills;
					maxDamage = nDamage;
				}
			}
		}

		if ( pMVP )
		{
			pMVP->IncrementNumMVPs( CSMVP_ELIMINATION );
		}
	}

	//=============================================================================
	// HPE_END
	//=============================================================================

	OutputDamageGiven();
	OutputDamageTaken();
	ResetDamageCounters();

	if ( m_bPunishedForTK )
	{
		m_bPunishedForTK = false;
		HintMessage( "#Hint_cannot_play_because_tk", true, true );
	}

	if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) )
	{
		m_iDisplayHistoryBits |= DHF_SPEC_DUCK;
		HintMessage( "#Spec_Duck", true, true );
	}
}

//=============================================================================
// HPE_BEGIN:
// [menglish, tj] Update and check any one-off achievements based on the kill
//=============================================================================

// Notify that I've killed some other entity. (called from Victim's Event_Killed).
void CCSPlayer::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info )
{
	BaseClass::Event_KilledOther(pVictim, info);
}

//=============================================================================
// HPE_END
//=============================================================================

void CCSPlayer::DeathSound( const CTakeDamageInfo &info )
{
	if( m_LastHitGroup == HITGROUP_HEAD )
	{
		EmitSound( "Player.DeathHeadShot" );
	}
	else
	{
		EmitSound( "Player.Death" );
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCSPlayer::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity )
{
	BaseClass::InitVCollision( vecAbsOrigin, vecAbsVelocity );

	if ( sv_turbophysics.GetBool() )
		return;

	// Setup the HL2 specific callback.
	GetPhysicsController()->SetEventHandler( &playerCallback );
}

void CCSPlayer::VPhysicsShadowUpdate( IPhysicsObject *pPhysics )
{
	if ( !CanMove() )
		return;

	BaseClass::VPhysicsShadowUpdate( pPhysics );
}

bool CCSPlayer::HasShield() const
{
#ifdef CS_SHIELD_ENABLED
	return m_bHasShield;
#else
	return false;
#endif
}


bool CCSPlayer::IsShieldDrawn() const
{
#ifdef CS_SHIELD_ENABLED
	return m_bShieldDrawn;
#else
	return false;
#endif
}


void CCSPlayer::CheatImpulseCommands( int iImpulse )
{
	switch( iImpulse )
	{
		case 101:
		{
			if( sv_cheats->GetBool() )
			{
				extern int gEvilImpulse101;
				gEvilImpulse101 = true;

				AddAccount( 16000 );

				GiveAmmo( 250, BULLET_PLAYER_50AE );
				GiveAmmo( 250, BULLET_PLAYER_762MM );
				GiveAmmo( 250, BULLET_PLAYER_338MAG );
				GiveAmmo( 250, BULLET_PLAYER_556MM );
				GiveAmmo( 250, BULLET_PLAYER_556MM_BOX );
				GiveAmmo( 250, BULLET_PLAYER_9MM );
				GiveAmmo( 250, BULLET_PLAYER_BUCKSHOT );
				GiveAmmo( 250, BULLET_PLAYER_45ACP );
				GiveAmmo( 250, BULLET_PLAYER_357SIG );
				GiveAmmo( 250, BULLET_PLAYER_57MM );

				gEvilImpulse101 = false;
			}
		}
		break;

		default:
		{
			BaseClass::CheatImpulseCommands( iImpulse );
		}
	}
}

void CCSPlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
{
	BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );

	int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
	PointCameraSetupVisibility( this, area, pvs, pvssize );
}

void CCSPlayer::UpdateAddonBits()
{
	int iNewBits = 0;

	int nFlashbang = GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_FLASHBANG ) );
	if ( dynamic_cast< CFlashbang* >( GetActiveWeapon() ) )
	{
		--nFlashbang;
	}

	if ( nFlashbang >= 1 )
		iNewBits |= ADDON_FLASHBANG_1;

	if ( nFlashbang >= 2 )
		iNewBits |= ADDON_FLASHBANG_2;

	if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_HEGRENADE ) ) &&
		!dynamic_cast< CHEGrenade* >( GetActiveWeapon() ) )
	{
		iNewBits |= ADDON_HE_GRENADE;
	}

	if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_SMOKEGRENADE ) ) &&
		!dynamic_cast< CSmokeGrenade* >( GetActiveWeapon() ) )
	{
		iNewBits |= ADDON_SMOKE_GRENADE;
	}

	if ( HasC4() && !dynamic_cast< CC4* >( GetActiveWeapon() ) )
		iNewBits |= ADDON_C4;

	if ( HasDefuser() )
		iNewBits |= ADDON_DEFUSEKIT;

	CWeaponCSBase *weapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ));
	if ( weapon && weapon != GetActiveWeapon() )
	{
		iNewBits |= ADDON_PRIMARY;
		m_iPrimaryAddon = weapon->GetWeaponID();
	}
	else
	{
		m_iPrimaryAddon = WEAPON_NONE;
	}

	weapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL ));
	if ( weapon && weapon != GetActiveWeapon() )
	{
		iNewBits |= ADDON_PISTOL;
		if ( weapon->GetWeaponID() == WEAPON_ELITE )
		{
			iNewBits |= ADDON_PISTOL2;
		}
		m_iSecondaryAddon = weapon->GetWeaponID();
	}
	else if ( weapon && weapon->GetWeaponID() == WEAPON_ELITE )
	{
		// The active weapon is weapon_elite.  Set ADDON_PISTOL2 without ADDON_PISTOL, so we know
		// to display the empty holster.
		iNewBits |= ADDON_PISTOL2;
		m_iSecondaryAddon = weapon->GetWeaponID();
	}
	else
	{
		m_iSecondaryAddon = WEAPON_NONE;
	}

	m_iAddonBits = iNewBits;
}

void CCSPlayer::UpdateRadar()
{
	// update once a second
	if ( (m_flLastRadarUpdateTime + 1.0) > gpGlobals->curtime )
		return;

	m_flLastRadarUpdateTime = gpGlobals->curtime;

	// update positions of all players outside of my PVS
	CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits;
	engine->Message_DetermineMulticastRecipients( false, EyePosition(), playerbits );

	CSingleUserRecipientFilter user( this );
	UserMessageBegin( user, "UpdateRadar" );

	for ( int i=0; i < MAX_PLAYERS; i++ )
	{
		CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i+1 ) );

		if ( !pPlayer )
			continue; // nothing there

		bool bSameTeam = pPlayer->GetTeamNumber() == GetTeamNumber();

		if ( playerbits.Get(i) && bSameTeam == true )
			continue; // this player is in my PVS and not in my team, don't update radar pos

		if ( pPlayer == this )
			continue;

		if ( !pPlayer->IsAlive() || pPlayer->IsObserver() || !pPlayer->IsConnected() )
			continue; // don't update specattors or dead players

		WRITE_BYTE( i+1 ); // player index as entity
		WRITE_SBITLONG( pPlayer->GetAbsOrigin().x/4, COORD_INTEGER_BITS-1 );
		WRITE_SBITLONG( pPlayer->GetAbsOrigin().y/4, COORD_INTEGER_BITS-1 );
		WRITE_SBITLONG( pPlayer->GetAbsOrigin().z/4, COORD_INTEGER_BITS-1 );
		WRITE_SBITLONG(  AngleNormalize( pPlayer->GetAbsAngles().y ), 9 );
	}

	WRITE_BYTE( 0 ); // end marker

	MessageEnd();
}

void CCSPlayer::UpdateMouseoverHints()
{
	if ( IsBlind() || IsObserver() )
		return;

	Vector forward, up;
	EyeVectors( &forward, NULL, &up );

	trace_t tr;
	// Search for objects in a sphere (tests for entities that are not solid, yet still useable)
	Vector searchStart = EyePosition();
	Vector searchEnd = searchStart + forward * 2048;

	int useableContents = MASK_NPCSOLID_BRUSHONLY | MASK_VISIBLE_AND_NPCS;

	UTIL_TraceLine( searchStart, searchEnd, useableContents, this, COLLISION_GROUP_NONE, &tr );

	if ( tr.fraction != 1.0f )
	{
		if (tr.DidHitNonWorldEntity() && tr.m_pEnt)
		{
			CBaseEntity *pObject = tr.m_pEnt;
			switch ( pObject->Classify() )
			{
			case CLASS_PLAYER:
				{
					const float grenadeBloat = 1.2f; // Be conservative in estimating what a player can distinguish
					if ( !TheBots->IsLineBlockedBySmoke( EyePosition(), pObject->EyePosition(), grenadeBloat ) )
					{
						if ( g_pGameRules->PlayerRelationship( this, pObject ) == GR_TEAMMATE )
						{
							if ( !(m_iDisplayHistoryBits & DHF_FRIEND_SEEN) )
							{
								m_iDisplayHistoryBits |= DHF_FRIEND_SEEN;
								HintMessage( "#Hint_spotted_a_friend", true );
							}
						}
						else
						{
							if ( !(m_iDisplayHistoryBits & DHF_ENEMY_SEEN) )
							{
								m_iDisplayHistoryBits |= DHF_ENEMY_SEEN;
								HintMessage( "#Hint_spotted_an_enemy", true );
							}
						}
					}
				}
				break;
			case CLASS_PLAYER_ALLY:
				switch ( GetTeamNumber() )
				{
				case TEAM_CT:
					if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR) && tr.fraction > 0.1f )
					{
						m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
						HintMessage( "#Hint_rescue_the_hostages", true );
					}
					else if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_NEAR) && tr.fraction <= 0.1f )
					{
						m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
						m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_NEAR;
						HintMessage( "#Hint_press_use_so_hostage_will_follow", false );
					}
					break;
				case TEAM_TERRORIST:
					if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR) )
					{
						m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR;
						HintMessage( "#Hint_prevent_hostage_rescue", true );
					}
					break;
				}
				break;
			}
		}
	}
}

void CCSPlayer::PostThink()
{
	BaseClass::PostThink();

	UpdateAddonBits();

	UpdateRadar();

	if ( !(m_iDisplayHistoryBits & DHF_ROUND_STARTED) && CanPlayerBuy(false) )
	{
		HintMessage( "#Hint_press_buy_to_purchase", false );
		m_iDisplayHistoryBits |= DHF_ROUND_STARTED;
	}
	if ( m_flNextMouseoverUpdate < gpGlobals->curtime )
	{
		m_flNextMouseoverUpdate = gpGlobals->curtime + 0.2f;
		if ( m_bShowHints )
		{
			UpdateMouseoverHints();
		}
	}
	if ( GetActiveWeapon() && !(m_iDisplayHistoryBits & DHF_AMMO_EXHAUSTED) )
	{
		CBaseCombatWeapon *pWeapon = GetActiveWeapon();
		if ( !pWeapon->HasAnyAmmo() && !(pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE) )
		{
			m_iDisplayHistoryBits |= DHF_AMMO_EXHAUSTED;
			HintMessage( "#Hint_out_of_ammo", false );
		}
	}

	// Store the eye angles pitch so the client can compute its animation state correctly.
	m_angEyeAngles = EyeAngles();

	m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );

	// check if we need to apply a deafness DSP effect.
	if ((m_applyDeafnessTime != 0.0f) && (m_applyDeafnessTime <= gpGlobals->curtime))
	{
		ApplyDeafnessEffect();
	}

	if ( IsPlayerUnderwater() && GetWaterLevel() < 3 )
	{
		StopSound( "Player.AmbientUnderWater" );
		SetPlayerUnderwater( false );
    }
}


void CCSPlayer::PushawayThink()
{
	// Push physics props out of our way.
	PerformObstaclePushaway( this );
	SetNextThink( gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, CS_PUSHAWAY_THINK_CONTEXT );
}


//-----------------------------------------------------------------------------
// Purpose: Returns whether or not we can switch to the given weapon.
// Input  : pWeapon -
//-----------------------------------------------------------------------------
bool CCSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
{
	if ( !pWeapon->CanDeploy() )
		return false;

	if ( GetActiveWeapon() )
	{
		if ( !GetActiveWeapon()->CanHolster() )
			return false;
	}

	return true;
}

bool CCSPlayer::ShouldDoLargeFlinch( int nHitGroup, CBaseEntity *pAttacker )
{
	if ( FBitSet( GetFlags(), FL_DUCKING ) )
		return FALSE;

	if ( nHitGroup == HITGROUP_LEFTLEG )
		return FALSE;

	if ( nHitGroup == HITGROUP_RIGHTLEG )
		return FALSE;

	CCSPlayer *pPlayer = ToCSPlayer( pAttacker );

	if ( pPlayer == NULL || !pPlayer->IsPlayer() )
		 pPlayer = NULL;

	if ( pPlayer )
	{
		CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon();

		if ( pWeapon )
		{
			if ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_RIFLE ||
				 pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SHOTGUN ||
				 pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SNIPER_RIFLE )
				 return true;
		}
		else
			return false;
	}

	return false;
}

bool CCSPlayer::IsArmored( int nHitGroup )
{
	bool bApplyArmor = false;

	if ( ArmorValue() > 0 )
	{
		switch ( nHitGroup )
		{
		case HITGROUP_GENERIC:
		case HITGROUP_CHEST:
		case HITGROUP_STOMACH:
		case HITGROUP_LEFTARM:
		case HITGROUP_RIGHTARM:
			bApplyArmor = true;
			break;
		case HITGROUP_HEAD:
			if ( m_bHasHelmet )
			{
				bApplyArmor = true;
			}
			break;
		default:
			break;
		}
	}

	return bApplyArmor;
}

void CCSPlayer::Pain( bool bHasArmour )
{
	switch (m_LastHitGroup)
	{
		case HITGROUP_HEAD:
			if (m_bHasHelmet)  // He's wearing a helmet
			{
				EmitSound( "Player.DamageHelmet" );
			}
			else  // He's not wearing a helmet
			{
				EmitSound( "Player.DamageHeadShot" );
			}
			break;
		default:
			if ( bHasArmour == false )
			{
				EmitSound( "Flesh.BulletImpact" );
			}
			else
			{
				EmitSound( "Player.DamageKevlar" );
			}
			break;
	}
}

int CCSPlayer::OnTakeDamage( const CTakeDamageInfo &inputInfo )
{
	CTakeDamageInfo info = inputInfo;

	CBaseEntity *pInflictor = info.GetInflictor();

	if ( !pInflictor )
		return 0;

	if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER )
		return 0;

	const float flArmorBonus = 0.5f;
	float flArmorRatio = 0.5f;
	float flDamage = info.GetDamage();

	bool bFriendlyFire = CSGameRules()->IsFriendlyFireOn();

	//=============================================================================
	// HPE_BEGIN:
	// [tj] Added properties for goose chase achievement
	//=============================================================================
 
	CSGameRules()->PlayerTookDamage(this, inputInfo);

	//Check "Goose Chase" achievement
	CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker());
	if (m_bIsDefusing && m_gooseChaseStep == GC_NONE && pAttacker && pAttacker->GetTeamNumber() != GetTeamNumber() )
	{

		//Count enemies
		int livingEnemies = 0;
		CTeam *pAttackerTeam = GetGlobalTeam( pAttacker->GetTeamNumber() );
		for ( int iPlayer=0; iPlayer < pAttackerTeam->GetNumPlayers(); iPlayer++ )
		{
			CCSPlayer *pPlayer = ToCSPlayer( pAttackerTeam->GetPlayer( iPlayer ) );
			Assert( pPlayer );
			if ( !pPlayer )
				continue;

			Assert( pPlayer->GetTeamNumber() == pAttackerTeam->GetTeamNumber() );
			
			if ( pPlayer->m_lifeState == LIFE_ALIVE )
			{
				livingEnemies++;
			}
		}

		//Must be last enemy alive;
		if (livingEnemies == 1)
		{
			m_gooseChaseStep = GC_SHOT_DURING_DEFUSE;
			m_pGooseChaseDistractingPlayer = pAttacker;
		}
	}
 
	//=============================================================================
	// HPE_END
	//=============================================================================


	// warn about team attacks
	if ( bFriendlyFire && pInflictor->GetTeamNumber() == GetTeamNumber() && pInflictor != this && info.GetAttacker() != this )
	{
		CCSPlayer *pCSAttacker = ToCSPlayer( pInflictor );
		if ( !pCSAttacker )
			pCSAttacker = ToCSPlayer( info.GetAttacker() );

		if ( pCSAttacker )
		{
			if ( !(pCSAttacker->m_iDisplayHistoryBits & DHF_FRIEND_INJURED) )
			{
				pCSAttacker->HintMessage( "#Hint_try_not_to_injure_teammates", false );
				pCSAttacker->m_iDisplayHistoryBits |= DHF_FRIEND_INJURED;
			}

			if ( (pCSAttacker->m_flLastAttackedTeammate + 0.6f) < gpGlobals->curtime )
			{
				pCSAttacker->m_flLastAttackedTeammate = gpGlobals->curtime;

				// tell the rest of this player's team
				Msg( "%s attacked a teammate\n", pCSAttacker->GetPlayerName() );
				for ( int i=1; i<=gpGlobals->maxClients; ++i )
				{
					CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
					if ( pPlayer && pPlayer->GetTeamNumber() == GetTeamNumber()	)
					{
						ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_teammate_attack", pCSAttacker->GetPlayerName() );
					}
				}
			}
		}
	}

	if ( bFriendlyFire ||
		pInflictor->GetTeamNumber() != GetTeamNumber() ||
		pInflictor == this ||
		info.GetAttacker() == this )
	{
		if ( bFriendlyFire && (info.GetDamageType() & DMG_BLAST) == 0 )
		{
			if ( pInflictor->GetTeamNumber() == GetTeamNumber() )
			{
				flDamage *= 0.35; // bullets hurt teammates less
			}
		}

		if ( ShouldDoLargeFlinch( m_LastHitGroup, info.GetAttacker() ) )
		{
			if ( GetAbsVelocity().Length() < 300 )
			{
				m_flVelocityModifier = 0.65;
			}
		}
		else
		{
			m_flVelocityModifier = 0.5;
		}

//=============================================================================
// HPE_BEGIN:
//=============================================================================
		// [menglish] Store whether or not the knife did this damage as knives do bullet damage,
		// so we need to specifically check the weapon here
		bool bKnifeDamage = false;
		CCSPlayer *pPlayer = ToCSPlayer( info.GetAttacker() );

		if ( pPlayer )
		{

			// [paquin. forest] if  this is blast damage, and we haven't opted out with a cvar,
			// we need to get the armor ratio out of the inflictor

			if ( (info.GetDamageType() & DMG_BLAST) && !sv_legacy_grenade_damage.GetBool() )
			{

				// [paquin] if we know this is a grenade, use it's armor ratio, otherwise
				// use the he grenade armor ratio

				CBaseCSGrenadeProjectile *pGrenade = dynamic_cast< CBaseCSGrenadeProjectile * >( pInflictor );
				CCSWeaponInfo* pWeaponInfo;

				if ( pGrenade && pGrenade->m_pWeaponInfo )
				{
					pWeaponInfo = pGrenade->m_pWeaponInfo;
				}
				else
				{
					pWeaponInfo = GetWeaponInfo( WEAPON_HEGRENADE );
				}

				if ( pWeaponInfo )
				{
					flArmorRatio *= pWeaponInfo->m_flArmorRatio;
				}
			}
			else
			{
				CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon();

				if ( pWeapon )
				{
					flArmorRatio *= pWeapon->GetCSWpnData().m_flArmorRatio;
					//Knives do bullet damage, so we need to specifically check the weapon here
					bKnifeDamage = pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_KNIFE;

					if (  info.GetDamageType() & DMG_BULLET && !bKnifeDamage && pPlayer->GetTeam() != GetTeam() )
					{
						CCS_GameStats.Event_ShotHit( pPlayer, info );
					}
				}
			}
		}
 
//=============================================================================
// HPE_END
//=============================================================================

		// keep track of amount of damage last sustained
		m_lastDamageAmount = flDamage;

		// Deal with Armour
		if ( ArmorValue() && !( info.GetDamageType() & (DMG_FALL | DMG_DROWN)) && IsArmored( m_LastHitGroup ) )
		{
			float fDamageToHealth = flDamage * flArmorRatio;
			float fDamageToArmor = (flDamage - fDamageToHealth) * flArmorBonus;

			int armorValue = ArmorValue();

			// Does this use more armor than we have?
			if (fDamageToArmor > armorValue )
			{
				fDamageToHealth = flDamage - armorValue / flArmorBonus;
				fDamageToArmor = armorValue;
				armorValue = 0;
			}
			else
			{
				if ( fDamageToArmor < 0 )
					 fDamageToArmor = 1;

				armorValue -= fDamageToArmor;
			}
			m_lastDamageArmor = (int)fDamageToArmor;
			SetArmorValue(armorValue);

			//=============================================================================
			// HPE_BEGIN:
			// [tj] Handle headshot-surviving achievement
			//=============================================================================
			if (m_LastHitGroup == HITGROUP_HEAD && flDamage > m_iHealth && fDamageToHealth < m_iHealth)
			{
				m_bSurvivedHeadshotDueToHelmet = true;
			}			
			//=============================================================================
			// HPE_END			
			//=============================================================================

			flDamage = fDamageToHealth;

			info.SetDamage( flDamage );

			if ( ArmorValue() <= 0.0)
				m_bHasHelmet = false;

			if( !(info.GetDamageType() & DMG_FALL) )
				Pain( true /*has armor*/ );
		}
		else
		{
			m_lastDamageArmor = 0;
			if( !(info.GetDamageType() & DMG_FALL) )
				Pain( false /*no armor*/ );
		}

		// round damage to integer
		m_lastDamageHealth = (int)flDamage;
		info.SetDamage( m_lastDamageHealth );

		if ( info.GetDamage() <= 0 )
			return 0;

		CSingleUserRecipientFilter user( this );
		user.MakeReliable();
		UserMessageBegin( user, "Damage" );
			WRITE_BYTE( (int)info.GetDamage() );
			WRITE_VEC3COORD( info.GetInflictor()->WorldSpaceCenter() );
//=============================================================================
// HPE_BEGIN:
// [menglish] Send the info about where the player was hit
//=============================================================================
			if ( !( info.GetDamageType() & DMG_BULLET ) || bKnifeDamage )
			{
				WRITE_LONG( -1 );
			}
			else
			{
				WRITE_LONG( m_LastHitBox );
			}
			WRITE_VEC3COORD( m_vLastHitLocationObjectSpace );
			 
//=============================================================================
// HPE_END
//=============================================================================
		MessageEnd();

		// Do special explosion damage effect
		if ( info.GetDamageType() & DMG_BLAST )
		{
			OnDamagedByExplosion( info );
		}

//=============================================================================
// HPE_BEGIN:
// [menglish] Achievement award for kill stealing i.e. killing an enemy who was very damaged from other players
// [Forrest] Moved this check before RecordDamageTaken so that the damage currently being dealt by this player
//           won't disqualify them from getting the achievement.
//=============================================================================

		if(m_iHealth - info.GetDamage() <= 0 && m_iHealth <= AchievementConsts::KillLowDamage_MaxHealthLeft)
		{
			bool onlyDamage = true;
			CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker());
			if(pAttacker && pAttacker->GetTeamNumber() != GetTeamNumber())
			{
				//Verify that the killer has not done damage to this player beforehand
				FOR_EACH_LL( m_DamageTakenList, i )
				{
					if( Q_strncmp( pAttacker->GetPlayerName(), m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 )
					{
						onlyDamage = false;
						break;
					}
				}
				if(onlyDamage)
				{
					pAttacker->AwardAchievement(CSKillLowDamage);
				}
			}
		}

//=============================================================================
// HPE_END
//=============================================================================

#if REPORT_PLAYER_DAMAGE
		// damage output spew
		char dmgtype[64];
		CTakeDamageInfo::DebugGetDamageTypeString( info.GetDamageType(), dmgtype, sizeof(dmgtype) );

		if ( info.GetDamageType() & DMG_HEADSHOT )
			Q_strncat(dmgtype, "HEADSHOT", sizeof(dmgtype));

		char outputString[256];
		Q_snprintf( outputString, sizeof(outputString), "%f: Player %s incoming %f damage from %s, type %s; applied %d health and %d armor\n", 
			gpGlobals->curtime, GetPlayerName(),
			inputInfo.GetDamage(), info.GetInflictor()->GetDebugName(), dmgtype,
			m_lastDamageHealth, m_lastDamageArmor);

		Msg(outputString);
#endif


		if ( pPlayer )
		{		
			// Record for the shooter
			pPlayer->RecordDamageGiven( GetPlayerName(), info.GetDamage() );

			// And for the victim
			RecordDamageTaken( pPlayer->GetPlayerName(), info.GetDamage() );
		}
		else
		{
			RecordDamageTaken( "world", info.GetDamage() );
		}

		m_vecTotalBulletForce += info.GetDamageForce();

		gamestats->Event_PlayerDamage( this, info );

		return CBaseCombatCharacter::OnTakeDamage( info );
	}
	else
	{
		return 0;
	}
}


//MIKETODO: this probably should let the shield model catch the trace attacks.
bool CCSPlayer::IsHittingShield( const Vector &vecDirection, trace_t *ptr )
{
	if ( HasShield() == false )
		 return false;

	if ( IsShieldDrawn() == false )
		 return false;

	float		flDot;
	Vector		vForward;
	Vector2D	vec2LOS = vecDirection.AsVector2D();
	AngleVectors( GetLocalAngles(), &vForward );

	Vector2DNormalize( vForward.AsVector2D() );
	Vector2DNormalize( vec2LOS );

	flDot = DotProduct2D ( vec2LOS , vForward.AsVector2D() );

	if ( flDot < -0.87f )
		 return true;

	return false;
}


void CCSPlayer::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
{
	bool bShouldBleed = true;
	bool bShouldSpark = false;
	bool bHitShield = IsHittingShield( vecDir, ptr );

	CBasePlayer *pAttacker = (CBasePlayer*)ToBasePlayer( info.GetAttacker() );

	// show blood for firendly fire only if FF is on
	if ( pAttacker && ( GetTeamNumber() == pAttacker->GetTeamNumber() ) )
		 bShouldBleed = CSGameRules()->IsFriendlyFireOn();

	if ( m_takedamage != DAMAGE_YES )
		return;

	m_LastHitGroup = ptr->hitgroup;
//=============================================================================
// HPE_BEGIN:
// [menglish] Used when calculating the position this player was hit at in the bone space
//=============================================================================
	m_LastHitBox = ptr->hitbox;
//=============================================================================
// HPE_END
//=============================================================================

	m_nForceBone = ptr->physicsbone;	//Save this bone for ragdoll

	float flDamage = info.GetDamage();

	bool bHeadShot = false;

	if ( bHitShield )
	{
		flDamage = 0;
		bShouldBleed = false;
		bShouldSpark = true;
	}
	else if( info.GetDamageType() & DMG_BLAST )
	{
		if ( ArmorValue() > 0 )
			 bShouldBleed = false;

		if ( bShouldBleed == true )
		{
			// punch view if we have no armor
			QAngle punchAngle = GetPunchAngle();
			punchAngle.x = flDamage * -0.1;

			if ( punchAngle.x < -4 )
				punchAngle.x = -4;

			SetPunchAngle( punchAngle );
		}
	}
	else
	{
//=============================================================================
// HPE_BEGIN:
// [menglish] Calculate the position this player was hit at in the bone space
//=============================================================================
		 
		matrix3x4_t boneTransformToWorld, boneTransformToObject;
		GetBoneTransform(GetHitboxBone(ptr->hitbox), boneTransformToWorld);
		MatrixInvert(boneTransformToWorld, boneTransformToObject);
		VectorTransform(ptr->endpos, boneTransformToObject, m_vLastHitLocationObjectSpace);
		 
//=============================================================================
// HPE_END
//=============================================================================

		switch ( ptr->hitgroup )
		{
		case HITGROUP_GENERIC:
			break;

		case HITGROUP_HEAD:

			if ( m_bHasHelmet )
			{
//				bShouldBleed = false;
				bShouldSpark = true;
			}

			flDamage *= 4;

			if ( !m_bHasHelmet )
			{
				QAngle punchAngle = GetPunchAngle();
				punchAngle.x = flDamage * -0.5;

				if ( punchAngle.x < -12 )
					punchAngle.x = -12;

				punchAngle.z = flDamage * random->RandomFloat(-1,1);

				if ( punchAngle.z < -9 )
					punchAngle.z = -9;

				else if ( punchAngle.z > 9 )
					punchAngle.z = 9;

				SetPunchAngle( punchAngle );
			}

			bHeadShot = true;

			break;

		case HITGROUP_CHEST:

			flDamage *= 1.0;

			if ( ArmorValue() <= 0 )
			{
				QAngle punchAngle = GetPunchAngle();
				punchAngle.x = flDamage * -0.1;

				if ( punchAngle.x < -4 )
					punchAngle.x = -4;

				SetPunchAngle( punchAngle );
			}
			break;

		case HITGROUP_STOMACH:

			flDamage *= 1.25;

			if ( ArmorValue() <= 0 )
			{
				QAngle punchAngle = GetPunchAngle();
				punchAngle.x = flDamage * -0.1;

				if ( punchAngle.x < -4 )
					punchAngle.x = -4;

				SetPunchAngle( punchAngle );
			}

			break;

		case HITGROUP_LEFTARM:
		case HITGROUP_RIGHTARM:
			flDamage *= 1.0;
			break;

		case HITGROUP_LEFTLEG:
		case HITGROUP_RIGHTLEG:
			flDamage *= 0.75;
			break;

		default:
			break;
		}
	}

	// Since this code only runs on the server, make sure it shows the tempents it creates.
	CDisablePredictionFiltering disabler;

	if ( bShouldBleed )
	{
		// This does smaller splotches on the guy and splats blood on the world.
		TraceBleed( flDamage, vecDir, ptr, info.GetDamageType() );

		CEffectData	data;
		data.m_vOrigin = ptr->endpos;
		data.m_vNormal = vecDir * -1;
		data.m_nEntIndex = ptr->m_pEnt ?  ptr->m_pEnt->entindex() : 0;
		data.m_flMagnitude = flDamage;

		// reduce blood effect if target has armor
		if ( ArmorValue() > 0 )
			data.m_flMagnitude *= 0.5f;

		// reduce blood effect if target is hit in the helmet
		if ( ptr->hitgroup == HITGROUP_HEAD && bShouldSpark )
			data.m_flMagnitude *= 0.5;

		DispatchEffect( "csblood", data );
	}
	if ( ( ptr->hitgroup == HITGROUP_HEAD || bHitShield ) && bShouldSpark ) // they hit a helmet
	{
		// show metal spark effect
		g_pEffects->Sparks( ptr->endpos, 1, 1, &ptr->plane.normal );
	}

	if ( !bHitShield )
	{
		CTakeDamageInfo subInfo = info;

		subInfo.SetDamage( flDamage );

		if( bHeadShot )
			subInfo.AddDamageType( DMG_HEADSHOT );

		AddMultiDamage( subInfo, this );
	}
}


void CCSPlayer::Reset()
{
	ResetFragCount();
	ResetDeathCount();
	m_iAccount = 0;
	AddAccount( -16000, false );

	//remove any weapons they bought before the round started
	RemoveAllItems( true );

	//RemoveShield();

	AddAccount( CSGameRules()->GetStartMoney(), true );
}

//-----------------------------------------------------------------------------
// Purpose: Displays a hint message to the player
// Input  : *pMessage -
//			bDisplayIfDead -
//			bOverrideClientSettings -
//-----------------------------------------------------------------------------
void CCSPlayer::HintMessage( const char *pMessage, bool bDisplayIfDead, bool bOverrideClientSettings )
{
	if ( ( !bDisplayIfDead && !IsAlive() ) || !IsNetClient() || !m_pHintMessageQueue )
		return;

	if ( bOverrideClientSettings || m_bShowHints )
		m_pHintMessageQueue->AddMessage( pMessage );
}

void CCSPlayer::AddAccount( int amount, bool bTrackChange, bool bItemBought, const char *pItemName )
{
	m_iAccount += amount;

	//=============================================================================
	// HPE_BEGIN:
	// [menglish] Description of reason for change
	//=============================================================================

	if(amount > 0)
	{
		CCS_GameStats.Event_MoneyEarned( this, amount );
	}
	else if( amount < 0 && bItemBought)
	{
		CCS_GameStats.Event_MoneySpent( this, ABS(amount), pItemName );
	}

	//=============================================================================
	// HPE_END
	//=============================================================================

	if ( m_iAccount < 0 )
		m_iAccount = 0;
	else if ( m_iAccount > 16000 )
		m_iAccount = 16000;
}

void CCSPlayer::MarkAsNotReceivingMoneyNextRound()
{
	m_receivesMoneyNextRound = false;
}

bool CCSPlayer::DoesPlayerGetRoundStartMoney()
{
	return m_receivesMoneyNextRound;
}

CCSPlayer* CCSPlayer::Instance( int iEnt )
{
	return dynamic_cast< CCSPlayer* >( CBaseEntity::Instance( INDEXENT( iEnt ) ) );
}


void CCSPlayer::DropC4()
{
}


bool CCSPlayer::HasDefuser()
{
	return m_bHasDefuser;
}

void CCSPlayer::RemoveDefuser()
{
	m_bHasDefuser = false;
}

void CCSPlayer::GiveDefuser(bool bPickedUp /* = false */)
{
	if ( !m_bHasDefuser )
	{
		IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
		if( event )
		{
			event->SetInt( "userid", GetUserID() );
			event->SetString( "item", "defuser" );
			gameeventmanager->FireEvent( event );
		}
	}

	m_bHasDefuser = true;

	//=============================================================================
	// HPE_BEGIN:
	// [dwenger] Added for fun-fact support
	//=============================================================================

	m_bPickedUpDefuser = bPickedUp;

	//=============================================================================
	// HPE_END
	//=============================================================================
}

// player blinded by a flashbang
void CCSPlayer::Blind( float holdTime, float fadeTime, float startingAlpha )
{
	// Don't flash a spectator.
	color32 clr = {255, 255, 255, 255};

	clr.a = startingAlpha;

	// estimate when we can see again
	float oldBlindUntilTime = m_blindUntilTime;
	float oldBlindStartTime = m_blindStartTime;
	m_blindUntilTime = MAX( m_blindUntilTime, gpGlobals->curtime + holdTime + 0.5f * fadeTime );
	m_blindStartTime = gpGlobals->curtime;

	// Spectators get a lessened flash.
	if ( (GetObserverMode() != OBS_MODE_NONE)  &&  (GetObserverMode() != OBS_MODE_IN_EYE) )
	{
		if ( !mp_fadetoblack.GetBool() )
		{
			clr.a = 150;

			fadeTime = MIN(fadeTime, 0.5f); // make sure the spectator flashbang time is 1/2 second or less.
			holdTime = MIN(holdTime, fadeTime * 0.5f); // adjust the hold time to match the fade time.
			UTIL_ScreenFade( this, clr, fadeTime, holdTime, FFADE_IN );
		}
	}
	else
	{
		fadeTime /= 1.4;

		if ( gpGlobals->curtime > oldBlindUntilTime )
		{
			// The previous flashbang is wearing off, or completely gone
			m_flFlashDuration = fadeTime;
			m_flFlashMaxAlpha = startingAlpha;
		}
		else
		{
			// The previous flashbang is still going strong - only extend the duration
			float remainingDuration = oldBlindStartTime + m_flFlashDuration - gpGlobals->curtime;

			m_flFlashDuration = MAX( remainingDuration, fadeTime );
			m_flFlashMaxAlpha = MAX( m_flFlashMaxAlpha, startingAlpha );
		}

		// allow bots to react
		IGameEvent * event = gameeventmanager->CreateEvent( "player_blind" );
		if ( event )
		{
			event->SetInt( "userid", GetUserID() );
			gameeventmanager->FireEvent( event );
		}
	}
}

void CCSPlayer::Deafen( float flDistance )
{
	// Spectators don't get deafened
	if ( (GetObserverMode() == OBS_MODE_NONE)  ||  (GetObserverMode() == OBS_MODE_IN_EYE) )
	{
		// dsp presets are defined in hl2/scripts/dsp_presets.txt

		int effect;

		if( flDistance < 600 )
		{
			effect = 134;
		}
		else if( flDistance < 800 )
		{
			effect = 135;
		}
		else if( flDistance < 1000 )
		{
			effect = 136;
		}
		else
		{
			// too far for us to get an effect
			return;
		}

		CSingleUserRecipientFilter user( this );
		enginesound->SetPlayerDSP( user, effect, false );

		//TODO: bots can't hear sound for a while?
	}
}

void CCSPlayer::GiveShield( void )
{
#ifdef CS_SHIELD_ENABLED
	m_bHasShield = true;
	m_bShieldDrawn = false;

	if ( HasSecondaryWeapon() )
	{
		CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
		pWeapon->SetModel( pWeapon->GetViewModel() );
		pWeapon->Deploy();
	}

	CBaseViewModel *pVM = GetViewModel( 1 );

	if ( pVM )
	{
		ShowViewModel( true );
		pVM->RemoveEffects( EF_NODRAW );
		pVM->SetWeaponModel( SHIELD_VIEW_MODEL, GetActiveWeapon() );
		pVM->SendViewModelMatchingSequence( 1 );
	}
#endif
}

void CCSPlayer::RemoveShield( void )
{
#ifdef CS_SHIELD_ENABLED
	m_bHasShield = false;

	CBaseViewModel *pVM = GetViewModel( 1 );

	if ( pVM )
	{
		pVM->AddEffects( EF_NODRAW );
	}
#endif
}

void CCSPlayer::RemoveAllItems( bool removeSuit )
{
	if( HasDefuser() )
	{
		RemoveDefuser();
	}

	if ( HasShield() )
	{
		RemoveShield();
	}

	m_bHasNightVision = false;
	m_bNightVisionOn = false;

	//=============================================================================
	// HPE_BEGIN:
	// [dwenger] Added for fun-fact support
	//=============================================================================

	m_bPickedUpDefuser = false;
	m_bDefusedWithPickedUpKit = false;

	//=============================================================================
	// HPE_END
	//=============================================================================

	if ( removeSuit )
	{
		m_bHasHelmet = false;
		SetArmorValue( 0 );
	}

	BaseClass::RemoveAllItems( removeSuit );
}

void CCSPlayer::ObserverRoundRespawn()
{
	ClearFlashbangScreenFade();

	// did we change our name last round?
	if ( m_szNewName[0] != 0 )
	{
		// ... and force the name change now.  After this happens, the gamerules will get
		// a ClientSettingsChanged callback from the above ClientCommand, but the name
		// matches what we're setting here, so it will do nothing.
		ChangeName( m_szNewName );
		m_szNewName[0] = 0;
	}
}

void CCSPlayer::RoundRespawn()
{
	//MIKETODO: menus
	//if ( m_iMenu != Menu_ChooseAppearance )
	{
		// Put them back into the game.
		StopObserverMode();
		State_Transition( STATE_ACTIVE );
		respawn( this, false );
		m_nButtons = 0;
		SetNextThink( TICK_NEVER_THINK );
	}

	m_receivesMoneyNextRound = true; // reset this variable so they can receive their cash next round.

	//If they didn't die, this will print out their damage info
	OutputDamageGiven();
	OutputDamageTaken();
	ResetDamageCounters();
}

void CCSPlayer::CheckTKPunishment( void )
{
	// teamkill punishment..
	if ( (m_bJustKilledTeammate == true) && mp_tkpunish.GetInt() )
	{
		m_bJustKilledTeammate = false;
		m_bPunishedForTK = true;
		CommitSuicide();
	}
}

CWeaponCSBase* CCSPlayer::GetActiveCSWeapon() const
{
	return dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() );
}

void CCSPlayer::PreThink()
{
	BaseClass::PreThink();
	if ( m_bAutoReload )
	{
		m_bAutoReload = false;
		m_nButtons |= IN_RELOAD;
	}

	if ( m_afButtonLast != m_nButtons )
		m_flLastMovement = gpGlobals->curtime;

	if ( g_fGameOver )
		return;

	State_PreThink();

	if ( m_pHintMessageQueue )
		m_pHintMessageQueue->Update();

	//Reset bullet force accumulator, only lasts one frame
	m_vecTotalBulletForce = vec3_origin;

	if ( mp_autokick.GetBool() && !IsBot() && !IsHLTV() && !IsAutoKickDisabled() )
	{
		if ( m_flLastMovement + CSGameRules()->GetRoundLength()*2 < gpGlobals->curtime )
		{
			UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_idle_kick", GetPlayerName() );
			engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", GetUserID() ) );
			m_flLastMovement = gpGlobals->curtime;
		}
	}
#ifndef _XBOX
	// CS would like their players to continue to update their LastArea since it is displayed in the hud voice chat UI
	// But we won't do the population tracking while dead.
	CNavArea *area = TheNavMesh->GetNavArea( GetAbsOrigin(), 1000 );
	if (area && area != m_lastNavArea)
	{
		m_lastNavArea = area;
		if ( area->GetPlace() != UNDEFINED_PLACE )
		{
			const char *placeName = TheNavMesh->PlaceToName( area->GetPlace() );
			if ( placeName && *placeName )
			{
				Q_strncpy( m_szLastPlaceName.GetForModify(), placeName, MAX_PLACE_NAME_LENGTH );
			}
		}
	}
#endif
}

void CCSPlayer::MoveToNextIntroCamera()
{
	m_pIntroCamera = gEntList.FindEntityByClassname( m_pIntroCamera, "point_viewcontrol" );

	// if m_pIntroCamera is NULL we just were at end of list, start searching from start again
	if(!m_pIntroCamera)
		m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "point_viewcontrol");

	// find the target
	CBaseEntity *Target = NULL;

	if( m_pIntroCamera )
	{
		Target = gEntList.FindEntityByName( NULL, STRING(m_pIntroCamera->m_target.Get()) );
	}

	// if we still couldn't find a camera, goto T spawn
	if(!m_pIntroCamera)
		m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "info_player_terrorist");

	SetViewOffset( vec3_origin );	// no view offset
	UTIL_SetSize( this, vec3_origin, vec3_origin ); // no bbox

	if( !Target ) //if there are no cameras(or the camera has no target, find a spawn point and black out the screen
	{
		if ( m_pIntroCamera.IsValid() )
			SetAbsOrigin( m_pIntroCamera->GetAbsOrigin() + VEC_VIEW );

		SetAbsAngles( QAngle( 0, 0, 0 ) );

		m_pIntroCamera = NULL;  // never update again
		return;
	}


	Vector vCamera = Target->GetAbsOrigin() - m_pIntroCamera->GetAbsOrigin();
	Vector vIntroCamera = m_pIntroCamera->GetAbsOrigin();

	VectorNormalize( vCamera );

	QAngle CamAngles;
	VectorAngles( vCamera, CamAngles );

	SetAbsOrigin( vIntroCamera );
	SetAbsAngles( CamAngles );
	SnapEyeAngles( CamAngles );
	m_fIntroCamTime = gpGlobals->curtime + 6;
}

class NotVIP
{
public:
	bool operator()( CBasePlayer *player )
	{
		CCSPlayer *csPlayer = static_cast< CCSPlayer * >(player);
		csPlayer->MakeVIP( false );

		return true;
	}
};

// Expose the VIP selection to plugins, since we don't have an official VIP mode.  This
// allows plugins to access the (limited) VIP functionality already present (scoreboard
// identification and radar color).
CON_COMMAND( cs_make_vip, "Marks a player as the VIP" )
{
	if ( !UTIL_IsCommandIssuedByServerAdmin() )
		return;

	if ( args.ArgC() != 2 )
	{
		return;
	}

	CCSPlayer *player = static_cast< CCSPlayer * >(UTIL_PlayerByIndex( atoi( args[1] ) ));
	if ( !player )
	{
		// Invalid value clears out VIP
		NotVIP notVIP;
		ForEachPlayer( notVIP );
		return;
	}

	player->MakeVIP( true );
}

void CCSPlayer::MakeVIP( bool isVIP )
{
	if ( isVIP )
	{
		NotVIP notVIP;
		ForEachPlayer( notVIP );
	}
	m_isVIP = isVIP;
}

bool CCSPlayer::IsVIP() const
{
	return m_isVIP;
}

void CCSPlayer::DropShield( void )
{
#ifdef CS_SHIELD_ENABLED
	//Drop an item_defuser
	Vector vForward, vRight;
	AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );

	RemoveShield();

	CBaseAnimating *pShield = (CBaseAnimating *)CBaseEntity::Create( "item_shield", WorldSpaceCenter(), GetLocalAngles() );
	pShield->ApplyAbsVelocityImpulse( vForward * 200 + vRight * random->RandomFloat( -50, 50 ) );

	CBaseCombatWeapon *pActive = GetActiveWeapon();

	if ( pActive )
	{
		pActive->Deploy();
	}
#endif
}

void CCSPlayer::SetShieldDrawnState( bool bState )
{
#ifdef CS_SHIELD_ENABLED
	m_bShieldDrawn = bState;
#endif
}

bool CCSPlayer::CSWeaponDrop( CBaseCombatWeapon *pWeapon, bool bDropShield, bool bThrowForward )
{
	bool bSuccess = false;

	if ( HasShield() && bDropShield == true )
	{
		DropShield();
		return true;
	}

	if ( pWeapon )
	{
		Vector vForward;

		AngleVectors( EyeAngles(), &vForward, NULL, NULL );
		//GetVectors( &vForward, NULL, NULL );
		Vector vTossPos = WorldSpaceCenter();

		if( bThrowForward )
			vTossPos = vTossPos + vForward * 64;

		Weapon_Drop( pWeapon, &vTossPos, NULL );

		pWeapon->SetSolidFlags( FSOLID_NOT_STANDABLE | FSOLID_TRIGGER | FSOLID_USE_TRIGGER_BOUNDS );
		pWeapon->SetMoveCollide( MOVECOLLIDE_FLY_BOUNCE );

		CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon );

		if( pCSWeapon )
		{
			pCSWeapon->SetWeaponModelIndex( pCSWeapon->GetCSWpnData().szWorldModel );

			//Find out the index of the ammo type
			int iAmmoIndex = pCSWeapon->GetPrimaryAmmoType();

			//If it has an ammo type, find out how much the player has
			if( iAmmoIndex != -1 )
			{
				// Check to make sure we don't have other weapons using this ammo type
				bool bAmmoTypeInUse = false;
				if ( IsAlive() && GetHealth() > 0 )
				{
					for ( int i=0; i<MAX_WEAPONS; ++i )
					{
						CBaseCombatWeapon *pOtherWeapon = GetWeapon(i);
						if ( pOtherWeapon && pOtherWeapon != pWeapon && pOtherWeapon->GetPrimaryAmmoType() == iAmmoIndex )
						{
							bAmmoTypeInUse = true;
							break;
						}
					}
				}

				if ( !bAmmoTypeInUse )
				{
					int iAmmoToDrop = GetAmmoCount( iAmmoIndex );

					//Add this much to the dropped weapon
					pCSWeapon->SetExtraAmmoCount( iAmmoToDrop );

					//Remove all ammo of this type from the player
					SetAmmoCount( 0, iAmmoIndex );
				}
			}
		}

		//=========================================
		// Teleport the weapon to the player's hand
		//=========================================
		int iBIndex = -1;
		int iWeaponBoneIndex = -1;

		MDLCACHE_CRITICAL_SECTION();
		CStudioHdr *hdr = pWeapon->GetModelPtr();
		// If I have a hand, set the weapon position to my hand bone position.
		if ( hdr && hdr->numbones() > 0 )
		{
			// Assume bone zero is the root
			for ( iWeaponBoneIndex = 0; iWeaponBoneIndex < hdr->numbones(); ++iWeaponBoneIndex )
			{
				iBIndex = LookupBone( hdr->pBone( iWeaponBoneIndex )->pszName() );
				// Found one!
				if ( iBIndex != -1 )
				{
					break;
				}
			}

			if ( iWeaponBoneIndex == hdr->numbones() )
				 return true;

			if ( iBIndex == -1 )
			{
				iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" );
			}
		}
		else
		{
			iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" );
		}

		if ( iBIndex != -1)
		{
			Vector origin;
			QAngle angles;
			matrix3x4_t transform;

			// Get the transform for the weapon bonetoworldspace in the NPC
			GetBoneTransform( iBIndex, transform );

			// find offset of root bone from origin in local space
			// Make sure we're detached from hierarchy before doing this!!!
			pWeapon->StopFollowingEntity();
			pWeapon->SetAbsOrigin( Vector( 0, 0, 0 ) );
			pWeapon->SetAbsAngles( QAngle( 0, 0, 0 ) );
			pWeapon->InvalidateBoneCache();
			matrix3x4_t rootLocal;
			pWeapon->GetBoneTransform( iWeaponBoneIndex, rootLocal );

			// invert it
			matrix3x4_t rootInvLocal;
			MatrixInvert( rootLocal, rootInvLocal );

			matrix3x4_t weaponMatrix;
			ConcatTransforms( transform, rootInvLocal, weaponMatrix );
			MatrixAngles( weaponMatrix, angles, origin );

			pWeapon->Teleport( &origin, &angles, NULL );

			//Have to teleport the physics object as well

			IPhysicsObject *pWeaponPhys = pWeapon->VPhysicsGetObject();

			if( pWeaponPhys )
			{
				Vector vPos;
				QAngle vAngles;
				pWeaponPhys->GetPosition( &vPos, &vAngles );
				pWeaponPhys->SetPosition( vPos, angles, true );

				AngularImpulse	angImp(0,0,0);
				Vector vecAdd = GetAbsVelocity();
				pWeaponPhys->AddVelocity( &vecAdd, &angImp );
			}
		}

		bSuccess = true;
	}

	return bSuccess;
}


bool CCSPlayer::DropRifle( bool fromDeath )
{
	bool bSuccess = false;

	CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
	if ( pWeapon )
	{
		bSuccess = CSWeaponDrop( pWeapon, false );
	}

	//=============================================================================
	// HPE_BEGIN:
	// [menglish] Add the dropped weapon to the dropped equipment list
	//=============================================================================
	if( fromDeath && bSuccess )
	{
		m_hDroppedEquipment[DROPPED_WEAPON] = static_cast<CBaseEntity *>(pWeapon);
	}
	//=============================================================================
	// HPE_END
	//=============================================================================

	return bSuccess;
}


bool CCSPlayer::DropPistol( bool fromDeath )
{
	bool bSuccess = false;

	CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
	if ( pWeapon )
	{
		bSuccess = CSWeaponDrop( pWeapon, false );
		m_bUsingDefaultPistol = false;
	}
	//=============================================================================
	// HPE_BEGIN:
	// [menglish] Add the dropped weapon to the dropped equipment list
	//=============================================================================
	if( fromDeath && bSuccess )
	{
		m_hDroppedEquipment[DROPPED_WEAPON] = static_cast<CBaseEntity *>(pWeapon);
	}
	//=============================================================================
	// HPE_END
	//=============================================================================

	return bSuccess;
}

bool CCSPlayer::HasPrimaryWeapon( void )
{
	bool bSuccess = false;

	CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE );

	if ( pWeapon )
	{
		bSuccess = true;
	}

	return bSuccess;
}


bool CCSPlayer::HasSecondaryWeapon( void )
{
	bool bSuccess = false;

	CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
	if ( pWeapon )
	{
		bSuccess = true;
	}

	return bSuccess;
}

bool CCSPlayer::IsInBuyZone()
{
	return m_bInBuyZone && !IsVIP();
}

bool CCSPlayer::CanPlayerBuy( bool display )
{
	// is the player in a buy zone?
	if ( !IsInBuyZone() )
	{
		return false;
	}

	CCSGameRules* mp = CSGameRules();

	// is the player alive?
	if ( m_lifeState != LIFE_ALIVE )
	{
		return false;
	}

	int buyTime = (int)(mp_buytime.GetFloat() * 60);

	if ( mp->IsBuyTimeElapsed() )
	{
		if ( display == true )
		{
			char strBuyTime[16];
			Q_snprintf( strBuyTime, sizeof( strBuyTime ), "%d", buyTime );
			ClientPrint( this, HUD_PRINTCENTER, "#Cant_buy", strBuyTime );
		}

		return false;
	}

	if ( m_bIsVIP )
	{
		if ( display == true )
			ClientPrint( this, HUD_PRINTCENTER, "#VIP_cant_buy" );

		return false;
	}

	if ( mp->m_bCTCantBuy && ( GetTeamNumber() == TEAM_CT ) )
	{
		if ( display == true )
			ClientPrint( this, HUD_PRINTCENTER, "#CT_cant_buy" );

		return false;
	}

	if ( mp->m_bTCantBuy && ( GetTeamNumber() == TEAM_TERRORIST ) )
	{
		if ( display == true )
			ClientPrint( this, HUD_PRINTCENTER, "#Terrorist_cant_buy" );

		return false;
	}

	return true;
}


BuyResult_e CCSPlayer::AttemptToBuyVest( void )
{
	int iKevlarPrice = KEVLAR_PRICE;

	if ( CSGameRules()->IsBlackMarket() )
	{
		iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR );
	}

	if ( ArmorValue() >= 100 )
	{
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar" );
		return BUY_ALREADY_HAVE;
	}
	else if ( m_iAccount < iKevlarPrice )
	{
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
		return BUY_CANT_AFFORD;
	}
	else
	{
		if ( m_bHasHelmet )
		{
			if( !m_bIsInAutoBuy && !m_bIsInRebuy )
				ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" );
		}

		IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
		if( event )
		{
			event->SetInt( "userid", GetUserID() );
			event->SetString( "item", "vest" );
			gameeventmanager->FireEvent( event );
		}

		GiveNamedItem( "item_kevlar" );
		AddAccount( -iKevlarPrice, true, true, "item_kevlar" );
		BlackMarketAddWeapon( "item_kevlar", this );
		return BUY_BOUGHT;
	}
}


BuyResult_e CCSPlayer::AttemptToBuyAssaultSuit( void )
{
	// WARNING: This price logic also exists in C_CSPlayer::GetCurrentAssaultSuitPrice
	// and must be kept in sync if changes are made.

	int fullArmor = ArmorValue() >= 100 ? 1 : 0;

	int price = 0, enoughMoney = 0;

	int iHelmetPrice = HELMET_PRICE;
	int iKevlarPrice = KEVLAR_PRICE;
	int iAssaultSuitPrice = ASSAULTSUIT_PRICE;

	if ( CSGameRules()->IsBlackMarket() )
	{
		iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR );
		iAssaultSuitPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_ASSAULTSUIT );

		iHelmetPrice = iAssaultSuitPrice - iKevlarPrice;
	}

	if ( fullArmor && m_bHasHelmet )
	{
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Helmet" );
		return BUY_ALREADY_HAVE;
	}
	else if ( fullArmor && !m_bHasHelmet && m_iAccount >= iHelmetPrice )
	{
		enoughMoney = 1;
		price = iHelmetPrice;
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Bought_Helmet" );
	}
	else if ( !fullArmor && m_bHasHelmet && m_iAccount >= iKevlarPrice )
	{
		enoughMoney = 1;
		price = iKevlarPrice;
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" );
	}
	else if ( m_iAccount >= iAssaultSuitPrice )
	{
		enoughMoney = 1;
		price = iAssaultSuitPrice;
	}

	// process the result
	if ( !enoughMoney )
	{
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
		return BUY_CANT_AFFORD;
	}
	else
	{
		IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
		if( event )
		{
			event->SetInt( "userid", GetUserID() );
			event->SetString( "item", "vesthelm" );
			gameeventmanager->FireEvent( event );
		}

		GiveNamedItem( "item_assaultsuit" );
		AddAccount( -price, true, true, "item_assaultsuit" );
		BlackMarketAddWeapon( "item_assaultsuit", this );
		return BUY_BOUGHT;
	}
}

BuyResult_e CCSPlayer::AttemptToBuyShield( void )
{
#ifdef CS_SHIELD_ENABLED
	if ( HasShield() )		// prevent this guy from buying more than 1 Defuse Kit
	{
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" );
		return BUY_ALREADY_HAVE;
	}
	else if ( m_iAccount < SHIELD_PRICE )
	{
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
		return BUY_CANT_AFFORD;
	}
	else
	{
		if ( HasSecondaryWeapon() )
		{
			CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
			CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon );

			if ( pCSWeapon && pCSWeapon->GetCSWpnData().m_bCanUseWithShield == false )
				 return;
		}

		if ( HasPrimaryWeapon() )
			 DropRifle();

		GiveShield();

		CPASAttenuationFilter filter( this, "Player.PickupWeapon" );
		EmitSound( filter, entindex(), "Player.PickupWeapon" );

		m_bAnythingBought = true;
		AddAccount( -SHIELD_PRICE, true, true, "item_shield" );
		return BUY_BOUGHT;
	}
#else
	ClientPrint( this, HUD_PRINTCENTER, "Tactical shield disabled" );
	return BUY_NOT_ALLOWED;
#endif
}

BuyResult_e CCSPlayer::AttemptToBuyDefuser( void )
{
	CCSGameRules *MPRules = CSGameRules();

	if( ( GetTeamNumber() == TEAM_CT ) && MPRules->IsBombDefuseMap() )
	{
		if ( HasDefuser() )		// prevent this guy from buying more than 1 Defuse Kit
		{
			if( !m_bIsInAutoBuy && !m_bIsInRebuy )
				ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" );
			return BUY_ALREADY_HAVE;
		}
		else if ( m_iAccount < DEFUSEKIT_PRICE )
		{
			if( !m_bIsInAutoBuy && !m_bIsInRebuy )
				ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
			return BUY_CANT_AFFORD;
		}
		else
		{
			GiveDefuser();

			CPASAttenuationFilter filter( this, "Player.PickupWeapon" );
			EmitSound( filter, entindex(), "Player.PickupWeapon" );

			AddAccount( -DEFUSEKIT_PRICE, true, true, "item_defuser" );
			return BUY_BOUGHT;
		}
	}

	return BUY_NOT_ALLOWED;
}

BuyResult_e CCSPlayer::AttemptToBuyNightVision( void )
{
	int iNVGPrice = NVG_PRICE;

	if ( CSGameRules()->IsBlackMarket() )
	{
		iNVGPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_NVG );
	}

	if ( m_bHasNightVision == TRUE )
	{
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" );
		return BUY_ALREADY_HAVE;
	}
	else if ( m_iAccount < iNVGPrice )
	{
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
			ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
		return BUY_CANT_AFFORD;
	}
	else
	{
		IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
		if( event )
		{
			event->SetInt( "userid", GetUserID() );
			event->SetString( "item", "nvgs" );
			gameeventmanager->FireEvent( event );
		}

		GiveNamedItem( "item_nvgs" );
		AddAccount( -iNVGPrice, true, true );
		BlackMarketAddWeapon( "nightvision", this );

		if ( !(m_iDisplayHistoryBits & DHF_NIGHTVISION) )
		{
			HintMessage( "#Hint_use_nightvision", false );
			m_iDisplayHistoryBits |= DHF_NIGHTVISION;
		}
		return BUY_BOUGHT;
	}
}


// Handles the special "buy" alias commands we're creating to accommodate the buy
// scripts players use (now that we've rearranged the buy menus and broken the scripts)
//=============================================================================
// HPE_BEGIN:
//[tj]  This is essentially a shim so I can easily check the return
//      value without adding new code to all the return points.
//=============================================================================

BuyResult_e CCSPlayer::HandleCommand_Buy( const char *item )
{    
	BuyResult_e result = HandleCommand_Buy_Internal(item);
	if (result == BUY_BOUGHT)
	{
		m_bMadePurchseThisRound = true;
		CCS_GameStats.IncrementStat(this, CSSTAT_ITEMS_PURCHASED, 1);
	}
	return result;
}

BuyResult_e CCSPlayer::HandleCommand_Buy_Internal( const char* wpnName ) 
//=============================================================================
// HPE_END
//=============================================================================
{
	BuyResult_e result = CanPlayerBuy( false ) ? BUY_PLAYER_CANT_BUY : BUY_INVALID_ITEM; // set some defaults

	// translate the new weapon names to the old ones that are actually being used.
	wpnName = GetTranslatedWeaponAlias(wpnName);

	CCSWeaponInfo *pWeaponInfo = GetWeaponInfo( AliasToWeaponID( wpnName ) );
	if ( pWeaponInfo == NULL )
	{
		if ( Q_stricmp( wpnName, "primammo" ) == 0 )
		{
			result = AttemptToBuyAmmo( 0 );
		}
		else if ( Q_stricmp( wpnName, "secammo" ) == 0 )
		{
			result = AttemptToBuyAmmo( 1 );
		}
		else if ( Q_stristr( wpnName, "defuser" )  )
		{
			if( CanPlayerBuy( true ) )
			{
				result = AttemptToBuyDefuser();
			}
		}
	}
	else
	{

		if( !CanPlayerBuy( true ) )
		{
			return BUY_PLAYER_CANT_BUY;
		}

		BuyResult_e equipResult = BUY_INVALID_ITEM;

		if ( Q_stristr( wpnName, "kevlar" ) )
		{
			equipResult = AttemptToBuyVest();
		}
		else if ( Q_stristr( wpnName, "assaultsuit" ) )
		{
			equipResult = AttemptToBuyAssaultSuit();
		}
		else if ( Q_stristr( wpnName, "shield" ) )
		{
			equipResult = AttemptToBuyShield();
		}
		else if ( Q_stristr( wpnName, "nightvision" )  )
		{
			equipResult = AttemptToBuyNightVision();
		}

		if ( equipResult != BUY_INVALID_ITEM )
		{
			if ( equipResult == BUY_BOUGHT )
			{
				BuildRebuyStruct();
			}
			return equipResult; // intentional early return here
		}

		bool bPurchase = false;

		// MIKETODO: assasination maps have a specific set of weapons that can be used in them.
		if ( pWeaponInfo->m_iTeam != TEAM_UNASSIGNED && GetTeamNumber() != pWeaponInfo->m_iTeam )
		{
			result = BUY_NOT_ALLOWED;
			if ( pWeaponInfo->m_WrongTeamMsg[0] != 0 )
			{
				ClientPrint( this, HUD_PRINTCENTER, "#Alias_Not_Avail", pWeaponInfo->m_WrongTeamMsg );
			}
		}
		else if ( pWeaponInfo->GetWeaponPrice() <= 0 )
		{
			// ClientPrint( this, HUD_PRINTCENTER, "#Cant_buy_this_item", pWeaponInfo->m_WrongTeamMsg );
		}
		else if( pWeaponInfo->m_WeaponType == WEAPONTYPE_GRENADE )
		{
			// make sure the player can afford this item.
			if ( m_iAccount >= pWeaponInfo->GetWeaponPrice() )
			{
				bPurchase = true;

				const char *szWeaponName = NULL;
				int ammoMax = 0;
				if ( Q_strstr( pWeaponInfo->szClassName, "flashbang" ) )
				{
					szWeaponName = "weapon_flashbang";
					ammoMax = ammo_flashbang_max.GetInt();
				}
				else if ( Q_strstr( pWeaponInfo->szClassName, "hegrenade" ) )
				{
					szWeaponName = "weapon_hegrenade";
					ammoMax = ammo_hegrenade_max.GetInt();
				}
				else if ( Q_strstr( pWeaponInfo->szClassName, "smokegrenade" ) )
				{
					szWeaponName = "weapon_smokegrenade";
					ammoMax = ammo_smokegrenade_max.GetInt();
				}

				if ( szWeaponName != NULL )
				{
					CBaseCombatWeapon* pGrenadeWeapon = Weapon_OwnsThisType( szWeaponName );
					{
						if ( pGrenadeWeapon != NULL )
						{
							int nAmmoType = pGrenadeWeapon->GetPrimaryAmmoType();

							if( nAmmoType != -1 )
							{
								if( GetAmmoCount(nAmmoType) >= ammoMax )
								{
									result = BUY_ALREADY_HAVE;
									if( !m_bIsInAutoBuy && !m_bIsInRebuy )
										ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Carry_Anymore" );
									bPurchase = false;
								}
							}
						}
					}
				}
			}
		}
		else if ( !Weapon_OwnsThisType( pWeaponInfo->szClassName ) )	//don't buy duplicate weapons
		{
			// do they have enough money?
			if ( m_iAccount >= pWeaponInfo->GetWeaponPrice() )
			{
				if ( m_lifeState != LIFE_DEAD )
				{
					if ( pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL )
					{
						DropPistol();
					}
					else if ( pWeaponInfo->iSlot == WEAPON_SLOT_RIFLE )
					{
						DropRifle();
					}
				}

				bPurchase = true;
			}
			else
			{
				result = BUY_CANT_AFFORD;
				if( !m_bIsInAutoBuy && !m_bIsInRebuy )
					ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
			}
		}
		else
		{
			result = BUY_ALREADY_HAVE;
		}

		if ( HasShield() )
		{
			if ( pWeaponInfo->m_bCanUseWithShield == false )
			{
				result = BUY_NOT_ALLOWED;
				bPurchase = false;
			}
		}

		if( bPurchase )
		{
			result = BUY_BOUGHT;

			if ( bPurchase && pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL )
				m_bUsingDefaultPistol = false;

			GiveNamedItem( pWeaponInfo->szClassName );
            AddAccount( -pWeaponInfo->GetWeaponPrice(), true, true, pWeaponInfo->szClassName );
			BlackMarketAddWeapon( wpnName, this );
		}
	}

	if ( result == BUY_BOUGHT )
	{
		BuildRebuyStruct();
	}

	return result;
}


BuyResult_e CCSPlayer::BuyGunAmmo( CBaseCombatWeapon *pWeapon, bool bBlinkMoney )
{
	if ( !CanPlayerBuy( false ) )
	{
		return BUY_PLAYER_CANT_BUY;
	}

	// Ensure that the weapon uses ammo
	int nAmmo = pWeapon->GetPrimaryAmmoType();
	if ( nAmmo == -1 )
	{
		return BUY_ALREADY_HAVE;
	}

	// Can only buy if the player does not already have full ammo
	if ( GetAmmoCount( nAmmo ) >= GetAmmoDef()->MaxCarry( nAmmo ) )
	{
		return BUY_ALREADY_HAVE;
	}

	// Purchase the ammo if the player has enough money
	if ( m_iAccount >= GetCSAmmoDef()->GetCost( nAmmo ) )
	{
		GiveAmmo( GetCSAmmoDef()->GetBuySize( nAmmo ), nAmmo, true );
		AddAccount( -GetCSAmmoDef()->GetCost( nAmmo ), true, true, GetCSAmmoDef()->GetAmmoOfIndex( nAmmo )->pName  );
		return BUY_BOUGHT;
	}

	if ( bBlinkMoney )
	{
		// Not enough money.. let the player know
		if( !m_bIsInAutoBuy && !m_bIsInRebuy )
					ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" );
	}

	return BUY_CANT_AFFORD;
}


BuyResult_e CCSPlayer::BuyAmmo( int nSlot, bool bBlinkMoney )
{
	if ( !CanPlayerBuy( false ) )
	{
		return BUY_PLAYER_CANT_BUY;
	}

	if ( nSlot < 0 || nSlot > 1 )
	{
		return BUY_INVALID_ITEM;
	}

	// Buy one ammo clip for all weapons in the given slot
	//
	//  nSlot == 1 : Primary weapons
	//  nSlot == 2 : Secondary weapons

	CBaseCombatWeapon *pSlot = Weapon_GetSlot( nSlot );
	if ( !pSlot )
		return BUY_INVALID_ITEM;

	//MIKETODO: shield.
	//if ( player->HasShield() && player->m_rgpPlayerItems[2] )
	//	 pItem = player->m_rgpPlayerItems[2];

	return BuyGunAmmo( pSlot, bBlinkMoney );
}


BuyResult_e CCSPlayer::AttemptToBuyAmmo( int iAmmoType )
{
	Assert( iAmmoType == 0 || iAmmoType == 1 );

	BuyResult_e result = BuyAmmo( iAmmoType, true );

	if ( result == BUY_BOUGHT )
	{
		while ( BuyAmmo( iAmmoType, false ) == BUY_BOUGHT )
		{
			// empty loop - keep buying
		}

		return BUY_BOUGHT;
	}

	return result;
}

BuyResult_e CCSPlayer::AttemptToBuyAmmoSingle( int iAmmoType )
{
	Assert( iAmmoType == 0 || iAmmoType == 1 );

	BuyResult_e result = BuyAmmo( iAmmoType, true );

	if ( result == BUY_BOUGHT )
	{
		BuildRebuyStruct();
	}

	return result;
}

const char *RadioEventName[ RADIO_NUM_EVENTS+1 ] =
{
	"RADIO_INVALID",

	"EVENT_START_RADIO_1",

	"EVENT_RADIO_COVER_ME",
	"EVENT_RADIO_YOU_TAKE_THE_POINT",
	"EVENT_RADIO_HOLD_THIS_POSITION",
	"EVENT_RADIO_REGROUP_TEAM",
	"EVENT_RADIO_FOLLOW_ME",
	"EVENT_RADIO_TAKING_FIRE",

	"EVENT_START_RADIO_2",

	"EVENT_RADIO_GO_GO_GO",
	"EVENT_RADIO_TEAM_FALL_BACK",
	"EVENT_RADIO_STICK_TOGETHER_TEAM",
	"EVENT_RADIO_GET_IN_POSITION_AND_WAIT",
	"EVENT_RADIO_STORM_THE_FRONT",
	"EVENT_RADIO_REPORT_IN_TEAM",

	"EVENT_START_RADIO_3",

	"EVENT_RADIO_AFFIRMATIVE",
	"EVENT_RADIO_ENEMY_SPOTTED",
	"EVENT_RADIO_NEED_BACKUP",
	"EVENT_RADIO_SECTOR_CLEAR",
	"EVENT_RADIO_IN_POSITION",
	"EVENT_RADIO_REPORTING_IN",
	"EVENT_RADIO_GET_OUT_OF_THERE",
	"EVENT_RADIO_NEGATIVE",
	"EVENT_RADIO_ENEMY_DOWN",

	"EVENT_RADIO_END",

	NULL		// must be NULL-terminated
};


/**
 * Convert name to RadioType
 */
RadioType NameToRadioEvent( const char *name )
{
	for( int i=0; RadioEventName[i]; ++i )
		if (!stricmp( RadioEventName[i], name ))
			return static_cast<RadioType>( i );

	return RADIO_INVALID;
}


void CCSPlayer::HandleMenu_Radio1( int slot )
{
	if( m_iRadioMessages < 0 )
		return;

	if( m_flRadioTime > gpGlobals->curtime )
		return;

	m_iRadioMessages--;
	m_flRadioTime = gpGlobals->curtime + 1.5;

	switch ( slot )
	{
		case 1 :
			Radio( "Radio.CoverMe",   "#Cstrike_TitlesTXT_Cover_me" );
			break;

		case 2 :
			Radio( "Radio.YouTakeThePoint", "#Cstrike_TitlesTXT_You_take_the_point" );
			break;

		case 3 :
			Radio( "Radio.HoldPosition",  "#Cstrike_TitlesTXT_Hold_this_position" );
			break;

		case 4 :
			Radio( "Radio.Regroup",   "#Cstrike_TitlesTXT_Regroup_team" );
			break;

		case 5 :
			Radio( "Radio.FollowMe",  "#Cstrike_TitlesTXT_Follow_me" );
			break;

		case 6 :
			Radio( "Radio.TakingFire", "#Cstrike_TitlesTXT_Taking_fire" );
			break;
	}

	// tell bots about radio message
	IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
	if ( event )
	{
		event->SetInt("userid", GetUserID() );
		event->SetInt("slot", RADIO_START_1 + slot );
		gameeventmanager->FireEvent( event );
	}
}

void CCSPlayer::HandleMenu_Radio2( int slot )
{
	if( m_iRadioMessages < 0 )
		return;

	if( m_flRadioTime > gpGlobals->curtime )
		return;

	m_iRadioMessages--;
	m_flRadioTime = gpGlobals->curtime + 1.5;

	switch ( slot )
	{
		case 1 :
			Radio( "Radio.GoGoGo",			"#Cstrike_TitlesTXT_Go_go_go" );
			break;

		case 2 :
			Radio( "Radio.TeamFallBack",	"#Cstrike_TitlesTXT_Team_fall_back" );
			break;

		case 3 :
			Radio( "Radio.StickTogether",	"#Cstrike_TitlesTXT_Stick_together_team" );
			break;

		case 4 :
			Radio( "Radio.GetInPosition",   "#Cstrike_TitlesTXT_Get_in_position_and_wait" );
			break;

		case 5 :
			Radio( "Radio.StormFront",		"#Cstrike_TitlesTXT_Storm_the_front" );
			break;

		case 6 :
			Radio( "Radio.ReportInTeam",	"#Cstrike_TitlesTXT_Report_in_team" );
			break;
	}

	// tell bots about radio message
	IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
	if ( event )
	{
		event->SetInt("userid", GetUserID() );
		event->SetInt("slot", RADIO_START_2 + slot );
		gameeventmanager->FireEvent( event );
	}
}

void CCSPlayer::HandleMenu_Radio3( int slot )
{
	if( m_iRadioMessages < 0 )
		return;

	if( m_flRadioTime > gpGlobals->curtime )
		return;

	m_iRadioMessages--;
	m_flRadioTime = gpGlobals->curtime + 1.5;

	switch ( slot )
	{
		case 1 :
			if ( random->RandomInt( 0,1 ) )
				Radio( "Radio.Affirmitive",	"#Cstrike_TitlesTXT_Affirmative" );
			else
				Radio( "Radio.Roger",		"#Cstrike_TitlesTXT_Roger_that" );

			break;

		case 2 :
			Radio( "Radio.EnemySpotted",	"#Cstrike_TitlesTXT_Enemy_spotted" );
			break;

		case 3 :
			Radio( "Radio.NeedBackup",		"#Cstrike_TitlesTXT_Need_backup" );
			break;

		case 4 :
			Radio( "Radio.SectorClear",		"#Cstrike_TitlesTXT_Sector_clear" );
			break;

		case 5 :
			Radio( "Radio.InPosition",		"#Cstrike_TitlesTXT_In_position" );
			break;

		case 6 :
			Radio( "Radio.ReportingIn",		"#Cstrike_TitlesTXT_Reporting_in" );
			break;

		case 7 :
			Radio( "Radio.GetOutOfThere",	"#Cstrike_TitlesTXT_Get_out_of_there" );
			break;

		case 8 :
			Radio( "Radio.Negative",		"#Cstrike_TitlesTXT_Negative" );
			break;

		case 9 :
			Radio( "Radio.EnemyDown",		"#Cstrike_TitlesTXT_Enemy_down" );
			break;
	}

	// tell bots about radio message
	IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" );
	if ( event )
	{
		event->SetInt("userid", GetUserID() );
		event->SetInt("slot", RADIO_START_3 + slot );
		gameeventmanager->FireEvent( event );
	}
}

void UTIL_CSRadioMessage( IRecipientFilter& filter, int iClient, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL )
{
	UserMessageBegin( filter, "RadioText" );
		WRITE_BYTE( msg_dest );
		WRITE_BYTE( iClient );
		WRITE_STRING( msg_name );

		if ( param1 )
			WRITE_STRING( param1 );
		else
			WRITE_STRING( "" );

		if ( param2 )
			WRITE_STRING( param2 );
		else
			WRITE_STRING( "" );

		if ( param3 )
			WRITE_STRING( param3 );
		else
			WRITE_STRING( "" );

		if ( param4 )
			WRITE_STRING( param4 );
		else
			WRITE_STRING( "" );

	MessageEnd();
}

void CCSPlayer::ConstructRadioFilter( CRecipientFilter& filter )
{
	filter.MakeReliable();

	int localTeam = GetTeamNumber();

	int i;
	for ( i = 1; i <= gpGlobals->maxClients; ++i )
	{
		CCSPlayer *player = static_cast<CCSPlayer *>( UTIL_PlayerByIndex( i ) );
		if ( !player )
			continue;

		// Skip players ignoring the radio
		if ( player->m_bIgnoreRadio )
			continue;

		if( player->GetTeamNumber() == TEAM_SPECTATOR )
		{
			// add spectators
			if( player->m_iObserverMode == OBS_MODE_IN_EYE || player->m_iObserverMode == OBS_MODE_CHASE )
			{
				filter.AddRecipient( player );
			}
		}
		else if( player->GetTeamNumber() == localTeam )
		{
			// add teammates
			filter.AddRecipient( player );
		}
	}
}

void CCSPlayer::Radio( const char *pszRadioSound, const char *pszRadioText )
{
	if( !IsAlive() )
		return;

	if ( IsObserver() )
		return;

	CRecipientFilter filter;
	ConstructRadioFilter( filter );

	if( pszRadioText )
	{
		const char *pszLocationText = CSGameRules()->GetChatLocation( true, this );
		if ( pszLocationText && *pszLocationText )
		{
			UTIL_CSRadioMessage( filter, entindex(), HUD_PRINTTALK, "#Game_radio_location", GetPlayerName(), pszLocationText, pszRadioText );
		}
		else
		{
			UTIL_CSRadioMessage( filter, entindex(), HUD_PRINTTALK, "#Game_radio", GetPlayerName(), pszRadioText );
		}
	}

	UserMessageBegin ( filter, "SendAudio" );
		WRITE_STRING( pszRadioSound );
	MessageEnd();

	//icon over the head for teammates
	TE_RadioIcon( filter, 0.0, this );
}

//-----------------------------------------------------------------------------
// Purpose: Outputs currently connected players to the console
//-----------------------------------------------------------------------------
void CCSPlayer::ListPlayers()
{
	char buf[64];
	for ( int i=1; i <= gpGlobals->maxClients; i++ )
	{
		CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) );
		if ( pPlayer && !pPlayer->IsDormant() )
		{
			if ( pPlayer->IsBot() )
			{
				Q_snprintf( buf, sizeof(buf), "B %d : %s", pPlayer->GetUserID(), pPlayer->GetPlayerName() );
			}
			else
			{
				Q_snprintf( buf, sizeof(buf), "  %d : %s", pPlayer->GetUserID(), pPlayer->GetPlayerName() );
			}
			ClientPrint( this, HUD_PRINTCONSOLE, buf );
		}
	}
	ClientPrint( this, HUD_PRINTCONSOLE, "\n" );
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  : &info -
//-----------------------------------------------------------------------------
void CCSPlayer::OnDamagedByExplosion( const CTakeDamageInfo &info )
{
	float lastDamage = info.GetDamage();

	//Adrian - This is hacky since we might have been damaged by something else
	//but since the round is ending, who cares.
	if ( CSGameRules()->m_bTargetBombed == true )
		 return;

	float distanceFromPlayer = 9999.0f;

	CBaseEntity *inflictor = info.GetInflictor();
	if ( inflictor )
	{
		Vector delta = GetAbsOrigin() - inflictor->GetAbsOrigin();
		distanceFromPlayer = delta.Length();
	}

	bool shock = lastDamage >= 30.0f;

	if ( !shock )
		return;

	m_applyDeafnessTime = gpGlobals->curtime + 0.3;
	m_currentDeafnessFilter = 0;
}

void CCSPlayer::ApplyDeafnessEffect()
{
	// what's happening here is that the low-pass filter and the oscillator frequency effects need
	// to fade in and out slowly.  So we have several filters that we switch between to achieve this
	// effect.  The first 3rd of the total effect will be the "fade in" of the effect.  Which means going
	// from filter to filter from the first to the last.  Then we keep on the "last" filter for another
	// third of the total effect time.  Then the last third of the time we go back from the last filter
	// to the first.  Clear as mud?

	// glossary:
	//  filter: an individual filter as defined in dsp_presets.txt
	//  section: one of the sections for the total effect, fade in, full, fade out are the possible sections
	//  effect: the total effect of combining all the sections, the whole of what the player hears from start to finish.

	const int firstGrenadeFilterIndex = 137;
	const int lastGrenadeFilterIndex = 139;
	const float grenadeEffectLengthInSecs = 4.5f; // time of the total effect
	const float fadeInSectionTime = 0.1f;
	const float fadeOutSectionTime = 1.5f;

	const float timeForEachFilterInFadeIn = fadeInSectionTime / (lastGrenadeFilterIndex - firstGrenadeFilterIndex);
	const float timeForEachFilterInFadeOut = fadeOutSectionTime / (lastGrenadeFilterIndex - firstGrenadeFilterIndex);

	float timeIntoEffect = gpGlobals->curtime - m_applyDeafnessTime;

	if (timeIntoEffect >= grenadeEffectLengthInSecs)
	{
		// the effect is done, so reset the deafness variables.
		m_applyDeafnessTime = 0.0f;
		m_currentDeafnessFilter = 0;
		return;
	}

	int section = 0;

	if (timeIntoEffect < fadeInSectionTime)
	{
		section = 0;
	}
	else if (timeIntoEffect < (grenadeEffectLengthInSecs - fadeOutSectionTime))
	{
		section = 1;
	}
	else
	{
		section = 2;
	}

	int filterToUse = 0;

	if (section == 0)
	{
		// fade into the effect.
		int filterIndex = (int)(timeIntoEffect / timeForEachFilterInFadeIn);
		filterToUse = filterIndex += firstGrenadeFilterIndex;
	}
	else if (section == 1)
	{
		// in full effect.
		filterToUse = lastGrenadeFilterIndex;
	}
	else if (section == 2)
	{
		// fade out of the effect
		float timeIntoSection = timeIntoEffect - (grenadeEffectLengthInSecs - fadeOutSectionTime);
		int filterIndex = (int)(timeIntoSection / timeForEachFilterInFadeOut);
		filterToUse = lastGrenadeFilterIndex - filterIndex - 1;
	}

	if (filterToUse != m_currentDeafnessFilter)
	{
		m_currentDeafnessFilter = filterToUse;

		CSingleUserRecipientFilter user( this );
		enginesound->SetPlayerDSP( user, m_currentDeafnessFilter, false );
	}
}


void CCSPlayer::NoteWeaponFired()
{
	Assert( m_pCurrentCommand );
	if( m_pCurrentCommand )
	{
		m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number;
	}
}


bool CCSPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits )
{
	// No need to check for IN_ATTACK etc since it is called in firebullet
	return BaseClass::WantsLagCompensationOnEntity( pPlayer, pCmd, pEntityTransmitBits );
}

// Handles the special "radio" alias commands we're creating to accommodate the scripts players use
// ** Returns true if we've handled the command **
bool HandleRadioAliasCommands( CCSPlayer *pPlayer, const char *pszCommand )
{
	bool bRetVal = false;

	// don't execute them if we are not alive or are an observer
	if( !pPlayer->IsAlive() || pPlayer->IsObserver() )
		return false;

	// Radio1 commands
	if ( FStrEq( pszCommand, "coverme" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio1( 1 );
	}
	else if ( FStrEq( pszCommand, "takepoint" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio1( 2 );
	}
	else if ( FStrEq( pszCommand, "holdpos" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio1( 3 );
	}
	else if ( FStrEq( pszCommand, "regroup" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio1( 4 );
	}
	else if ( FStrEq( pszCommand, "followme" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio1( 5 );
	}
	else if ( FStrEq( pszCommand, "takingfire" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio1( 6 );
	}
	// Radio2 commands
	else if ( FStrEq( pszCommand, "go" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio2( 1 );
	}
	else if ( FStrEq( pszCommand, "fallback" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio2( 2 );
	}
	else if ( FStrEq( pszCommand, "sticktog" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio2( 3 );
	}
	else if ( FStrEq( pszCommand, "getinpos" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio2( 4 );
	}
	else if ( FStrEq( pszCommand, "stormfront" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio2( 5 );
	}
	else if ( FStrEq( pszCommand, "report" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio2( 6 );
	}
	// Radio3 commands
	else if ( FStrEq( pszCommand, "roger" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 1 );
	}
	else if ( FStrEq( pszCommand, "enemyspot" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 2 );
	}
	else if ( FStrEq( pszCommand, "needbackup" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 3 );
	}
	else if ( FStrEq( pszCommand, "sectorclear" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 4 );
	}
	else if ( FStrEq( pszCommand, "inposition" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 5 );
	}
	else if ( FStrEq( pszCommand, "reportingin" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 6 );
	}
	else if ( FStrEq( pszCommand, "getout" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 7 );
	}
	else if ( FStrEq( pszCommand, "negative" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 8 );
	}
	else if ( FStrEq( pszCommand, "enemydown" ) )
	{
		bRetVal = true;
		pPlayer->HandleMenu_Radio3( 9 );
	}

	return bRetVal;
}

bool CCSPlayer::ShouldRunRateLimitedCommand( const CCommand &args )
{
	const char *pcmd = args[0];

	int i = m_RateLimitLastCommandTimes.Find( pcmd );
	if ( i == m_RateLimitLastCommandTimes.InvalidIndex() )
	{
		m_RateLimitLastCommandTimes.Insert( pcmd, gpGlobals->curtime );
		return true;
	}
	else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i]) < CS_COMMAND_MAX_RATE )
	{
		// Too fast.
		return false;
	}
	else
	{
		m_RateLimitLastCommandTimes[i] = gpGlobals->curtime;
		return true;
	}
}

bool CCSPlayer::ClientCommand( const CCommand &args )
{
	const char *pcmd = args[0];

	// Bots mimic our client commands.
/*
	if ( bot_mimic.GetInt() && !( GetFlags() & FL_FAKECLIENT ) )
	{
		for ( int i=1; i <= gpGlobals->maxClients; i++ )
		{
			CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) );
			if ( pPlayer && pPlayer != this && ( pPlayer->GetFlags() & FL_FAKECLIENT ) )
			{
				pPlayer->ClientCommand( pcmd );
			}
		}
	}
*/

#if defined ( DEBUG )

	if ( FStrEq( pcmd, "bot_cmd" ) )
	{
		CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( atoi( args[1] ) ) );
		if ( pPlayer && pPlayer != this && ( pPlayer->GetFlags() & FL_FAKECLIENT ) )
		{
			CCommand botArgs( args.ArgC() - 2, &args.ArgV()[2] );
			pPlayer->ClientCommand( botArgs );
			pPlayer->RemoveEffects( EF_NODRAW );
		}
		return true;
	}

	if ( FStrEq( pcmd, "blind" ) )
	{
		if ( ShouldRunRateLimitedCommand( args ) )
		{
			if ( args.ArgC() == 3 )
			{
				Blind( atof( args[1] ), atof( args[2] ) );
			}
			else
			{
				ClientPrint( this, HUD_PRINTCONSOLE, "usage: blind holdtime fadetime\n" );
			}
		}
		return true;
	}

	if ( FStrEq( pcmd, "deafen" ) )
	{
		Deafen( 0.0f );
		return true;
	}

	if ( FStrEq( pcmd, "he_deafen" ) )
	{
		m_applyDeafnessTime = gpGlobals->curtime + 0.3;
		m_currentDeafnessFilter = 0;
		return true;
	}

	if ( FStrEq( pcmd, "hint_reset" ) )
	{
		m_iDisplayHistoryBits = 0;
		return true;
	}

	if ( FStrEq( pcmd, "punch" ) )
	{
		float flDamage = 100;

		QAngle punchAngle = GetPunchAngle();

		punchAngle.x = flDamage * random->RandomFloat ( -0.15, 0.15 );
		punchAngle.y = flDamage * random->RandomFloat ( -0.15, 0.15 );
		punchAngle.z = flDamage * random->RandomFloat ( -0.15, 0.15 );

		clamp( punchAngle.x, -4, punchAngle.x );
		clamp( punchAngle.y, -5, 5 );
		clamp( punchAngle.z, -5, 5 );

		// +y == down
		// +x == left
		// +z == roll clockwise
		if ( args.ArgC() == 4 )
		{
			punchAngle.x = atof(args[1]);
			punchAngle.y = atof(args[2]);
			punchAngle.z = atof(args[3]);
		}

		SetPunchAngle( punchAngle );

		return true;
	}

#endif //DEBUG

	if ( FStrEq( pcmd, "jointeam" ) )
	{
		if ( args.ArgC() < 2 )
		{
			Warning( "Player sent bad jointeam syntax\n" );
		}

		if ( ShouldRunRateLimitedCommand( args ) )
		{
			int iTeam = atoi( args[1] );
			HandleCommand_JoinTeam( iTeam );
		}
		return true;
	}
	else if ( FStrEq( pcmd, "spectate" ) )
	{
		if ( ShouldRunRateLimitedCommand( args ) )
		{
			// instantly join spectators
			HandleCommand_JoinTeam( TEAM_SPECTATOR );
		}
		return true;
	}
	else if ( FStrEq( pcmd, "joingame" ) )
	{
		// player just closed MOTD dialog
		if ( m_iPlayerState == STATE_WELCOME )
		{
			State_Transition( STATE_PICKINGTEAM );
		}

		return true;
	}
	else if ( FStrEq( pcmd, "joinclass" ) )
	{
		if ( args.ArgC() < 2 )
		{
			Warning( "Player sent bad joinclass syntax\n" );
		}

		if ( ShouldRunRateLimitedCommand( args ) )
		{
			int iClass = atoi( args[1] );
			HandleCommand_JoinClass( iClass );
		}
		return true;
	}
	else if ( FStrEq( pcmd, "drop" ) )
	{
		CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() );

		if( pWeapon )
		{
			//=============================================================================
			// HPE_BEGIN:
			// [dwenger] Determine value of dropped item.
			//=============================================================================

			if ( !pWeapon->IsAPriorOwner( this ) )
			{
				pWeapon->AddToPriorOwnerList( this );

				CCS_GameStats.IncrementStat(this, CSTAT_ITEMS_DROPPED_VALUE, pWeapon->GetCSWpnData().GetWeaponPrice());
			}

			//=============================================================================
			// HPE_END
			//=============================================================================

			CSWeaponType type = pWeapon->GetCSWpnData().m_WeaponType;

			if( type != WEAPONTYPE_KNIFE && type != WEAPONTYPE_GRENADE )
			{
				if (CSGameRules()->GetCanDonateWeapon() && !pWeapon->GetDonated())
				{
					pWeapon->SetDonated(true);
					pWeapon->SetDonor(this);
				}
				CSWeaponDrop( pWeapon, true, true );

			}
		}

		return true;
	}
	else if ( FStrEq( pcmd, "buy" ) )
	{
		BuyResult_e result = BUY_INVALID_ITEM;
		if ( args.ArgC() == 2 )
		{
			result = HandleCommand_Buy( args[1] );
		}
		if ( result == BUY_INVALID_ITEM )
		{
			// Print out a message on the console
			int msg_dest = HUD_PRINTCONSOLE;

			ClientPrint( this, msg_dest, "usage: buy <item>\n" );
			ClientPrint( this, msg_dest, "  primammo\n" );
			ClientPrint( this, msg_dest, "  secammo\n" );
			ClientPrint( this, msg_dest, "  vest\n" );
			ClientPrint( this, msg_dest, "  vesthelm\n" );
			ClientPrint( this, msg_dest, "  defuser\n" );
			//ClientPrint( this, msg_dest, "  shield\n" );
			ClientPrint( this, msg_dest, "  nvgs\n" );
			ClientPrint( this, msg_dest, "  flashbang\n" );
			ClientPrint( this, msg_dest, "  hegrenade\n" );
			ClientPrint( this, msg_dest, "  smokegrenade\n" );
			ClientPrint( this, msg_dest, "  galil\n" );
			ClientPrint( this, msg_dest, "  ak47\n" );
			ClientPrint( this, msg_dest, "  scout\n" );
			ClientPrint( this, msg_dest, "  sg552\n" );
			ClientPrint( this, msg_dest, "  awp\n" );
			ClientPrint( this, msg_dest, "  g3sg1\n" );
			ClientPrint( this, msg_dest, "  famas\n" );
			ClientPrint( this, msg_dest, "  m4a1\n" );
			ClientPrint( this, msg_dest, "  aug\n" );
			ClientPrint( this, msg_dest, "  sg550\n" );
			ClientPrint( this, msg_dest, "  glock\n" );
			ClientPrint( this, msg_dest, "  usp\n" );
			ClientPrint( this, msg_dest, "  p228\n" );
			ClientPrint( this, msg_dest, "  deagle\n" );
			ClientPrint( this, msg_dest, "  elite\n" );
			ClientPrint( this, msg_dest, "  fiveseven\n" );
			ClientPrint( this, msg_dest, "  m3\n" );
			ClientPrint( this, msg_dest, "  xm1014\n" );
			ClientPrint( this, msg_dest, "  mac10\n" );
			ClientPrint( this, msg_dest, "  tmp\n" );
			ClientPrint( this, msg_dest, "  mp5navy\n" );
			ClientPrint( this, msg_dest, "  ump45\n" );
			ClientPrint( this, msg_dest, "  p90\n" );
			ClientPrint( this, msg_dest, "  m249\n" );
		}

		return true;
	}
	else if ( FStrEq( pcmd, "buyammo1" ) )
	{
		AttemptToBuyAmmoSingle(0);
		return true;
	}
	else if ( FStrEq( pcmd, "buyammo2" ) )
	{
		AttemptToBuyAmmoSingle(1);
		return true;
	}
	else if ( FStrEq( pcmd, "nightvision" ) )
	{
		if ( ShouldRunRateLimitedCommand( args ) )
		{
			if( m_bHasNightVision )
			{
				if( m_bNightVisionOn )
				{
					CPASAttenuationFilter filter( this );
					EmitSound( filter, entindex(), "Player.NightVisionOff" );
				}
				else
				{
					CPASAttenuationFilter filter( this );
					EmitSound( filter, entindex(), "Player.NightVisionOn" );
				}

				m_bNightVisionOn = !m_bNightVisionOn;
			}
		}
		return true;
	}
	else if ( FStrEq( pcmd, "menuselect" ) )
	{
		return true;
	}
	else if ( HandleRadioAliasCommands( this, pcmd ) )
	{
		return true;
	}
	else if ( FStrEq( pcmd, "listplayers" ) )
	{
		ListPlayers();
		return true;
	}

	else if ( FStrEq( pcmd, "ignorerad" ) )
	{
		m_bIgnoreRadio = !m_bIgnoreRadio;
		if ( m_bIgnoreRadio )
		{
			ClientPrint( this, HUD_PRINTTALK, "#Ignore_Radio" );
		}
		else
		{
			ClientPrint( this, HUD_PRINTTALK, "#Accept_Radio" );
		}
		return true;
	}
	else if ( FStrEq( pcmd, "become_vip" ) )
	{
		//MIKETODO: VIP mode
		/*
		if ( ( CSGameRules()->m_iMapHasVIPSafetyZone == 1 ) && ( m_iTeam == TEAM_CT ) )
		{
			mp->AddToVIPQueue( this );
		}
		*/
		return true;
	}

	return BaseClass::ClientCommand( args );
}


// returns true if the selection has been handled and the player's menu
// can be closed...false if the menu should be displayed again
bool CCSPlayer::HandleCommand_JoinTeam( int team )
{
	CCSGameRules *mp = CSGameRules();

	if ( !GetGlobalTeam( team ) )
	{
		DevWarning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team );
		return false;
	}

	// If this player is a VIP, don't allow him to switch teams/appearances unless the following conditions are met :
	// a) There is another TEAM_CT player who is in the queue to be a VIP
	// b) This player is dead

	//MIKETODO: handle this when doing VIP mode
	/*
	if ( m_bIsVIP == true )
	{
		if ( !IsDead() )
		{
			ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Switch_From_VIP" );
			MenuReset();
			return true;
		}
		else if ( mp->IsVIPQueueEmpty() == true )
		{
			ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Switch_From_VIP" );
			MenuReset();
			return true;
		}
	}

	//MIKETODO: VIP mode

	case 3:
		if ( ( mp->m_iMapHasVIPSafetyZone == 1 ) && ( m_iTeam == TEAM_CT ) )
		{
			mp->AddToVIPQueue( player );
			MenuReset();
			return true;
		}
		else
		{
			return false;
		}
		break;
	*/

	// If we already died and changed teams once, deny
	if( m_bTeamChanged && team != m_iOldTeam && team != TEAM_SPECTATOR )
	{
		ClientPrint( this, HUD_PRINTCENTER, "#Only_1_Team_Change" );
		return true;
	}

	// check if we're limited in our team selection
	if ( team == TEAM_UNASSIGNED && !IsBot() )
	{
		team = mp->GetHumanTeam(); // returns TEAM_UNASSIGNED if we're unrestricted
	}

	if ( team == TEAM_UNASSIGNED )
	{
		// Attempt to auto-select a team, may set team to T, CT or SPEC
		team = mp->SelectDefaultTeam( !IsBot() );

		if ( team == TEAM_UNASSIGNED )
		{
			// still team unassigned, try to kick a bot if possible

			// kick a bot to allow human to join
			if (cv_bot_auto_vacate.GetBool() && !IsBot())
			{
				team = (random->RandomInt( 0, 1 ) == 0) ? TEAM_TERRORIST : TEAM_CT;
				if (UTIL_KickBotFromTeam( team ) == false)
				{
					// no bots on that team, try the other
					team = (team == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT;
					if (UTIL_KickBotFromTeam( team ) == false)
					{
						// couldn't kick any bots, fail
						team = TEAM_UNASSIGNED;
					}
				}
			}

			if (team == TEAM_UNASSIGNED)
			{
				ClientPrint( this, HUD_PRINTCENTER, "#All_Teams_Full" );
				ShowViewPortPanel( PANEL_TEAM );
				return false;
			}
		}
	}

	if ( team == GetTeamNumber() )
	{
		// Let people change class (skin) by re-joining the same team
		if ( GetTeamNumber() == TEAM_TERRORIST && TerroristPlayerModels.Count() > 1 )
		{
			ShowViewPortPanel( PANEL_CLASS_TER );
		}
		else if ( GetTeamNumber() == TEAM_CT && CTPlayerModels.Count() > 1 )
		{
			ShowViewPortPanel( PANEL_CLASS_CT );
		}
		return true;	// we wouldn't change the team
	}

	if ( mp->TeamFull( team ) )
	{
		// attempt to kick a bot to make room for this player
		bool madeRoom = false;
		if (cv_bot_auto_vacate.GetBool() && !IsBot())
		{
			if (UTIL_KickBotFromTeam( team ))
				madeRoom = true;
		}

		if (!madeRoom)
		{
			if ( team == TEAM_TERRORIST )
			{
				ClientPrint( this, HUD_PRINTCENTER, "#Terrorists_Full" );
			}
			else if ( team == TEAM_CT )
			{
				ClientPrint( this, HUD_PRINTCENTER, "#CTs_Full" );
			}

			ShowViewPortPanel( PANEL_TEAM );
			return false;
		}
	}

	// check if humans are restricted to a single team (Tour of Duty, etc)
	if ( !IsBot() && team != TEAM_SPECTATOR)
	{
		int humanTeam = mp->GetHumanTeam();
		if ( humanTeam != TEAM_UNASSIGNED && humanTeam != team )
		{
			if ( humanTeam == TEAM_TERRORIST )
			{
				ClientPrint( this, HUD_PRINTCENTER, "#Humans_Join_Team_T" );
			}
			else if ( humanTeam == TEAM_CT )
			{
				ClientPrint( this, HUD_PRINTCENTER, "#Humans_Join_Team_CT" );
			}

			ShowViewPortPanel( PANEL_TEAM );
			return false;
		}
	}

	if ( team == TEAM_SPECTATOR )
	{
		// Prevent this is the cvar is set
		if ( !mp_allowspectators.GetInt() && !IsHLTV() )
		{
			ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" );
			return false;
		}

		if ( GetTeamNumber() != TEAM_UNASSIGNED && State_Get() == STATE_ACTIVE )
		{
			m_fNextSuicideTime = gpGlobals->curtime;	// allow the suicide to work

			CommitSuicide();

			// add 1 to frags to balance out the 1 subtracted for killing yourself
			IncrementFragCount( 1 );
		}

		ChangeTeam( TEAM_SPECTATOR );
		m_iClass = (int)CS_CLASS_NONE;

		if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) )
		{
			m_iDisplayHistoryBits |= DHF_SPEC_DUCK;
			HintMessage( "#Spec_Duck", true, true );
		}

		// do we have fadetoblack on? (need to fade their screen back in)
		if ( mp_fadetoblack.GetBool() )
		{
			color32_s clr = { 0,0,0,255 };
			UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE );
		}

		return true;
	}

	// If the code gets this far, the team is not TEAM_UNASSIGNED


	if (mp->TeamStacked( team, GetTeamNumber() ))//players are allowed to change to their own team so they can just change their model
	{
		// attempt to kick a bot to make room for this player
		bool madeRoom = false;
		if (cv_bot_auto_vacate.GetBool() && !IsBot())
		{
			if (UTIL_KickBotFromTeam( team ))
				madeRoom = true;
		}

		if (!madeRoom)
		{
			// The specified team is full
			ClientPrint(
				this,
				HUD_PRINTCENTER,
				( team == TEAM_TERRORIST ) ?	"#Too_Many_Terrorists" : "#Too_Many_CTs" );

			ShowViewPortPanel( PANEL_TEAM );
			return false;
		}
	}

	// Show the appropriate Choose Appearance menu
	// This must come before ClientKill() for CheckWinConditions() to function properly

	// Switch their actual team...
	ChangeTeam( team );

	return true;
}


bool CCSPlayer::HandleCommand_JoinClass( int iClass )
{
	if( iClass == CS_CLASS_NONE )
	{
		// User choosed random class
		switch ( GetTeamNumber() )
		{
			case TEAM_TERRORIST :	iClass = RandomInt(FIRST_T_CLASS, LAST_T_CLASS);
									break;

			case TEAM_CT :			iClass = RandomInt(FIRST_CT_CLASS, LAST_CT_CLASS);
									break;

			default	:				iClass = CS_CLASS_NONE;
									break;
		}
	}

	// clamp to valid classes
	switch ( GetTeamNumber() )
	{
		case TEAM_TERRORIST:
			iClass = clamp( iClass, FIRST_T_CLASS, LAST_T_CLASS );
			break;
		case TEAM_CT:
			iClass = clamp( iClass, FIRST_CT_CLASS, LAST_CT_CLASS );
			break;
		default:
			iClass = CS_CLASS_NONE;
	}

	// Reset the player's state
	if ( State_Get() == STATE_ACTIVE )
	{
		CSGameRules()->CheckWinConditions();
	}

	if ( !IsBot() && State_Get() == STATE_ACTIVE ) // Bots are responsible about only switching classes when they join.
	{
		// Kill player if switching classes while alive.
		// This mimics goldsrc CS 1.6, and prevents a player from hiding, and switching classes to
		// make the opposing team think there are more enemies than there really are.
		CommitSuicide();
	}

	m_iClass = iClass;

	if (State_Get() == STATE_PICKINGCLASS)
	{
// 		SetModelFromClass();
		GetIntoGame();
	}

	return true;
}


/*
void CheckStartMoney( void )
{
	if ( mp_startmoney.GetInt() > 16000 )
	{
		mp_startmoney.SetInt( 16000 );
	}
	else if ( mp_startmoney.GetInt() < 800 )
	{
		mp_startmoney.SetInt( 800 );
	}
}
*/

void CCSPlayer::GetIntoGame()
{
	// Set their model and if they're allowed to spawn right now, put them into the world.
	//SetPlayerModel( iClass );

	SetFOV( this, 0 );
	m_flLastMovement = gpGlobals->curtime;

	CCSGameRules *MPRules = CSGameRules();

/*	//MIKETODO: Escape gameplay ?
	if ( ( MPRules->m_bMapHasEscapeZone == true ) && ( m_iTeam == TEAM_CT ) )
	{
		m_iAccount = 0;

		CheckStartMoney();
		AddAccount( (int)startmoney.value, true );
	}
	*/


	//****************New Code by SupraFiend************
	if ( !MPRules->FPlayerCanRespawn( this ) )
	{
		// This player is joining in the middle of a round or is an observer. Put them directly into observer mode.
		//pev->deadflag		= DEAD_RESPAWNABLE;
		//pev->classname		= MAKE_STRING("player");
		//pev->flags		   &= ( FL_PROXY | FL_FAKECLIENT );	// clear flags, but keep proxy and bot flags that might already be set
		//pev->flags		   |= FL_CLIENT | FL_SPECTATOR;
		//SetThink(PlayerDeathThink);
		if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) )
		{
			m_iDisplayHistoryBits |= DHF_SPEC_DUCK;
			HintMessage( "#Spec_Duck", true, true );
		}

		State_Transition( STATE_OBSERVER_MODE );

		m_wasNotKilledNaturally = true;

		MPRules->CheckWinConditions();
	}
	else// else spawn them right in
	{
		State_Transition( STATE_ACTIVE );

		Spawn();

		MPRules->CheckWinConditions();

		//=============================================================================
		// HPE_BEGIN:
		// [menglish] Have the rules update anything related to a player spawning in late
		//=============================================================================

		MPRules->SpawningLatePlayer(this);

		//=============================================================================
		// HPE_END
		//=============================================================================

		if( MPRules->m_flRestartRoundTime == 0.0f )
		{
			//Bomb target, no bomber and no bomb lying around.
			if( MPRules->IsBombDefuseMap() && !MPRules->IsThereABomber() && !MPRules->IsThereABomb() )
				MPRules->GiveC4(); //Checks for terrorists.
		}

		// If a new terrorist is entering the fray, then up the # of potential escapers.
		if ( GetTeamNumber() == TEAM_TERRORIST )
			MPRules->m_iNumEscapers++;

		//=============================================================================
		// HPE_BEGIN:
		// [menglish] Reset Round Based Achievement Variables
		//=============================================================================

		ResetRoundBasedAchievementVariables();

		//=============================================================================
		// HPE_END
		//=============================================================================

	}
}


int CCSPlayer::PlayerClass() const
{
	return m_iClass;
}



bool CCSPlayer::SelectSpawnSpot( const char *pEntClassName, CBaseEntity* &pSpot )
{
	// Find the next spawn spot.
	pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );

	if ( pSpot == NULL ) // skip over the null point
		pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );

	CBaseEntity *pFirstSpot = pSpot;
	do
	{
		if ( pSpot )
		{
			// check if pSpot is valid
			if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) )
			{
				if ( pSpot->GetAbsOrigin() == Vector( 0, 0, 0 ) )
				{
					pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
					continue;
				}

				// if so, go to pSpot
				return true;
			}
		}
		// increment pSpot
		pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName );
	} while ( pSpot != pFirstSpot ); // loop if we're not back to the start

	DevMsg("CCSPlayer::SelectSpawnSpot: couldn't find valid spawn point.\n");

	return true;
}


CBaseEntity* CCSPlayer::EntSelectSpawnPoint()
{
	CBaseEntity *pSpot;

	/* MIKETODO: VIP
		// VIP spawn point *************
		if ( ( g_pGameRules->IsDeathmatch() ) && ( ((CBasePlayer*)pPlayer)->m_bIsVIP == TRUE) )
		{
			//ALERT (at_console,"Looking for a VIP spawn point\n");
			// Randomize the start spot
			//for ( int i = RANDOM_LONG(1,5); i > 0; i-- )
			pSpot = UTIL_FindEntityByClassname( NULL, "info_vip_start" );
			if ( !FNullEnt( pSpot ) )  // skip over the null point
				goto ReturnSpot;
			else
				goto CTSpawn;
		}

		//
		// the counter-terrorist spawns at "info_player_start"
		else
	*/

	pSpot = NULL;
	if ( CSGameRules()->IsLogoMap() )
	{
		// This is a logo map. Don't allow movement or logos or menus.
		SelectSpawnSpot( "info_player_logo", pSpot );
		LockPlayerInPlace();
		goto ReturnSpot;
	}
	else
	{
		if ( GetTeamNumber() == TEAM_CT )
		{
			pSpot = g_pLastCTSpawn;
			if ( SelectSpawnSpot( "info_player_counterterrorist", pSpot ))
			{

				g_pLastCTSpawn = pSpot;
				goto ReturnSpot;
			}
		}

		/*********************************************************/
		// The terrorist spawn points
		else if ( GetTeamNumber() == TEAM_TERRORIST )
		{
			pSpot = g_pLastTerroristSpawn;

			if ( SelectSpawnSpot( "info_player_terrorist", pSpot ) )
			{
				g_pLastTerroristSpawn = pSpot;
				goto ReturnSpot;
			}
		}
	}


	// If startspot is set, (re)spawn there.
	if ( !gpGlobals->startspot || !strlen(STRING(gpGlobals->startspot)))
	{
		pSpot = gEntList.FindEntityByClassname(NULL, "info_player_terrorist");
		if ( pSpot )
			goto ReturnSpot;
	}
	else
	{
		pSpot = gEntList.FindEntityByTarget( NULL, STRING(gpGlobals->startspot) );
		if ( pSpot )
			goto ReturnSpot;
	}

ReturnSpot:
	if ( !pSpot )
	{
		if( CSGameRules()->IsLogoMap() )
			Warning( "PutClientInServer: no info_player_logo on level\n" );
		else
			Warning( "PutClientInServer: no info_player_start on level\n" );

		return CBaseEntity::Instance( INDEXENT(0) );
	}

	return pSpot;
}


void CCSPlayer::SetProgressBarTime( int barTime )
{
	m_iProgressBarDuration = barTime;
	m_flProgressBarStartTime = this->m_flSimulationTime;
}


void CCSPlayer::PlayerDeathThink()
{
}


void CCSPlayer::State_Transition( CSPlayerState newState )
{
	State_Leave();
	State_Enter( newState );
}


void CCSPlayer::State_Enter( CSPlayerState newState )
{
	m_iPlayerState = newState;
	m_pCurStateInfo = State_LookupInfo( newState );

	if ( cs_ShowStateTransitions.GetInt() == -1 || cs_ShowStateTransitions.GetInt() == entindex() )
	{
		if ( m_pCurStateInfo )
			Msg( "ShowStateTransitions: entering '%s'\n", m_pCurStateInfo->m_pStateName );
		else
			Msg( "ShowStateTransitions: entering #%d\n", newState );
	}

	// Initialize the new state.
	if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
		(this->*m_pCurStateInfo->pfnEnterState)();
}


void CCSPlayer::State_Leave()
{
	if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
	{
		(this->*m_pCurStateInfo->pfnLeaveState)();
	}
}


void CCSPlayer::State_PreThink()
{
	if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink )
	{
		(this->*m_pCurStateInfo->pfnPreThink)();
	}
}


CCSPlayerStateInfo* CCSPlayer::State_LookupInfo( CSPlayerState state )
{
	// This table MUST match the
	static CCSPlayerStateInfo playerStateInfos[] =
	{
		{ STATE_ACTIVE,			"STATE_ACTIVE",			&CCSPlayer::State_Enter_ACTIVE, NULL, &CCSPlayer::State_PreThink_ACTIVE },
		{ STATE_WELCOME,		"STATE_WELCOME",		&CCSPlayer::State_Enter_WELCOME, NULL, &CCSPlayer::State_PreThink_WELCOME },
		{ STATE_PICKINGTEAM,	"STATE_PICKINGTEAM",	&CCSPlayer::State_Enter_PICKINGTEAM, NULL,	&CCSPlayer::State_PreThink_OBSERVER_MODE },
		{ STATE_PICKINGCLASS,	"STATE_PICKINGCLASS",	&CCSPlayer::State_Enter_PICKINGCLASS, NULL,	&CCSPlayer::State_PreThink_OBSERVER_MODE },
		{ STATE_DEATH_ANIM,		"STATE_DEATH_ANIM",		&CCSPlayer::State_Enter_DEATH_ANIM,	NULL, &CCSPlayer::State_PreThink_DEATH_ANIM },
		{ STATE_DEATH_WAIT_FOR_KEY,	"STATE_DEATH_WAIT_FOR_KEY",	&CCSPlayer::State_Enter_DEATH_WAIT_FOR_KEY,	NULL, &CCSPlayer::State_PreThink_DEATH_WAIT_FOR_KEY },
		{ STATE_OBSERVER_MODE,	"STATE_OBSERVER_MODE",	&CCSPlayer::State_Enter_OBSERVER_MODE,	NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE }
	};

	for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
	{
		if ( playerStateInfos[i].m_iPlayerState == state )
			return &playerStateInfos[i];
	}

	return NULL;
}


void CCSPlayer::PhysObjectSleep()
{
	IPhysicsObject *pObj = VPhysicsGetObject();
	if ( pObj )
		pObj->Sleep();
}


void CCSPlayer::PhysObjectWake()
{
	IPhysicsObject *pObj = VPhysicsGetObject();
	if ( pObj )
		pObj->Wake();
}


void CCSPlayer::State_Enter_WELCOME()
{
	StartObserverMode( OBS_MODE_ROAMING );

	// Important to set MOVETYPE_NONE or our physics object will fall while we're sitting at one of the intro cameras.
	SetMoveType( MOVETYPE_NONE );
	AddSolidFlags( FSOLID_NOT_SOLID );

	PhysObjectSleep();

	const ConVar *hostname = cvar->FindVar( "hostname" );
	const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY";

	// Show info panel (if it's not a simple demo map).
	if ( !CSGameRules()->IsLogoMap() )
	{
		if ( CommandLine()->FindParm( "-makereslists" ) ) // don't show the MOTD when making reslists
		{
			engine->ClientCommand( edict(), "jointeam 3\n" );
		}
		else
		{
			KeyValues *data = new KeyValues("data");
			data->SetString( "title", title );		// info panel title
			data->SetString( "type", "1" );			// show userdata from stringtable entry
			data->SetString( "msg",	"motd" );		// use this stringtable entry
			data->SetInt( "cmd", TEXTWINDOW_CMD_JOINGAME );	// exec this command if panel closed
			data->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() );

			ShowViewPortPanel( PANEL_INFO, true, data );

			data->deleteThis();
		}
	}
}


void CCSPlayer::State_PreThink_WELCOME()
{
	// Verify some state.
	Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
	Assert( GetAbsVelocity().Length() == 0 );

	// Update whatever intro camera it's at.
	if( m_pIntroCamera && (gpGlobals->curtime >= m_fIntroCamTime) )
	{
		MoveToNextIntroCamera();
	}
}


void CCSPlayer::State_Enter_PICKINGTEAM()
{
	ShowViewPortPanel( "team" ); // show the team menu
}


void CCSPlayer::State_Enter_DEATH_ANIM()
{
	if ( HasWeapons() )
	{
		// we drop the guns here because weapons that have an area effect and can kill their user
		// will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
		// player class sometimes is freed. It's safer to manipulate the weapons once we know
		// we aren't calling into any of their code anymore through the player pointer.
		PackDeadPlayerItems();
	}

	// Used for a timer.
	m_flDeathTime = gpGlobals->curtime;

	m_bAbortFreezeCam = false;

	StartObserverMode( OBS_MODE_DEATHCAM );	// go to observer mode
	RemoveEffects( EF_NODRAW );	// still draw player body

	if ( mp_fadetoblack.GetBool() )
	{
		color32_s clr = {0,0,0,255};
		UTIL_ScreenFade( this, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT );
		//Don't perform any freezecam stuff if we are fading to black
		State_Transition( STATE_DEATH_WAIT_FOR_KEY );
	}
}


//=============================================================================
// HPE_BEGIN:
// [menglish, pfreese] Added freeze cam logic
//=============================================================================
 
void CCSPlayer::State_PreThink_DEATH_ANIM()
{
	// If the anim is done playing, go to the next state (waiting for a keypress to
	// either respawn the guy or put him into observer mode).
	if ( GetFlags() & FL_ONGROUND )
	{
		float flForward = GetAbsVelocity().Length() - 20;
		if (flForward <= 0)
		{
			SetAbsVelocity( vec3_origin );
		}
		else
		{
			Vector vAbsVel = GetAbsVelocity();
			VectorNormalize( vAbsVel );
			vAbsVel *= flForward;
			SetAbsVelocity( vAbsVel );
		}
	}

	float fDeathEnd = m_flDeathTime + CS_DEATH_ANIMATION_TIME;
	float fFreezeEnd = fDeathEnd + spec_freeze_traveltime.GetFloat() + spec_freeze_time.GetFloat();

	// transition to Freezecam mode once the death animation is complete
	if ( gpGlobals->curtime >= fDeathEnd )
	{
		if ( GetObserverTarget() && GetObserverTarget() != this &&
			!m_bAbortFreezeCam && gpGlobals->curtime < fFreezeEnd && GetObserverMode() != OBS_MODE_FREEZECAM)
		{
			StartObserverMode( OBS_MODE_FREEZECAM );
		}
		else if(GetObserverMode() == OBS_MODE_FREEZECAM)
		{
			if ( m_bAbortFreezeCam && !mp_fadetoblack.GetBool() )
			{
				State_Transition( STATE_OBSERVER_MODE );
			}
		}
	}

	// Don't transfer to observer state until the freeze cam is done
	if ( gpGlobals->curtime < fFreezeEnd )
		return;

	State_Transition( STATE_OBSERVER_MODE );
}
 
//=============================================================================
// HPE_END
//=============================================================================


void CCSPlayer::State_Enter_DEATH_WAIT_FOR_KEY()
{
	// Remember when we died, so we can automatically put them into observer mode
	// if they don't hit a key soon enough.

	m_lifeState = LIFE_DEAD;

	StopAnimation();

	// Don't do this.  The ragdoll system expects to be able to read from this player on 
	// the next update and will read it at the new origin if this is set.
	// Since it is more complicated to redesign the ragdoll system to not need that data
	// it is easier to cause a less obvious bug than popping ragdolls
	//AddEffects( EF_NOINTERP );
}


void CCSPlayer::State_PreThink_DEATH_WAIT_FOR_KEY()
{
	// once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore
	// this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn
	if ( GetMoveType() != MOVETYPE_NONE && (GetFlags() & FL_ONGROUND) )
		SetMoveType( MOVETYPE_NONE );

	// if the player has been dead for one second longer than allowed by forcerespawn,
	// forcerespawn isn't on. Send the player off to an intermission camera until they
	// choose to respawn.

	bool fAnyButtonDown = (m_nButtons & ~IN_SCORE) != 0;
	if ( mp_fadetoblack.GetBool() )
		fAnyButtonDown = false;

	// after a certain amount of time switch to observer mode even if they don't press a key.
	if (gpGlobals->curtime >= (m_flDeathTime + DEATH_ANIMATION_TIME + 3.0))
	{
		fAnyButtonDown = true;
	}

	if ( fAnyButtonDown )
	{
		if ( GetObserverTarget() )
		{
			StartReplayMode( 8, 8, GetObserverTarget()->entindex() );
		}

		State_Transition( STATE_OBSERVER_MODE );
	}
}

void CCSPlayer::State_Enter_OBSERVER_MODE()
{
	// do we have fadetoblack on? (need to fade their screen back in)
	if ( mp_fadetoblack.GetBool() && mp_forcecamera.GetInt() != OBS_ALLOW_NONE)
	{
		color32_s clr = { 0,0,0,255 };
		UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE );
	}

	int observerMode = m_iObserverLastMode;
	if ( IsNetClient() )
	{
		const char *pIdealMode = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_spec_mode" );
		if ( pIdealMode )
		{
			int nIdealMode = atoi( pIdealMode );

			if ( nIdealMode < OBS_MODE_IN_EYE )
			{
				nIdealMode = OBS_MODE_IN_EYE;
			}
			else if ( nIdealMode > OBS_MODE_ROAMING )
			{
				nIdealMode = OBS_MODE_ROAMING;
			}

			observerMode = nIdealMode;
		}
	}

	StartObserverMode( observerMode );

	PhysObjectSleep();
}

void CCSPlayer::State_PreThink_OBSERVER_MODE()
{
	// Make sure nobody has changed any of our state.
//	Assert( GetMoveType() == MOVETYPE_FLY );
	Assert( m_takedamage == DAMAGE_NO );
	Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
//	Assert( IsEffectActive( EF_NODRAW ) );

	// Must be dead.
	Assert( m_lifeState == LIFE_DEAD );
	Assert( pl.deadflag );
}


void CCSPlayer::State_Enter_PICKINGCLASS()
{
	if ( CommandLine()->FindParm( "-makereslists" ) ) // don't show the menu when making reslists
	{
		engine->ClientCommand( edict(), "joinclass 0\n" );
		return;
	}

	// go to spec mode, if dying keep deathcam
	if ( GetObserverMode() == OBS_MODE_DEATHCAM )
	{
		StartObserverMode( OBS_MODE_DEATHCAM );
	}
	else
	{
		StartObserverMode( OBS_MODE_FIXED );
	}

	m_iClass = (int)CS_CLASS_NONE;

	PhysObjectSleep();

	// show the class menu:
	if ( GetTeamNumber() == TEAM_TERRORIST && TerroristPlayerModels.Count() > 1 )
	{
		ShowViewPortPanel( PANEL_CLASS_TER );
	}
	else if ( GetTeamNumber() == TEAM_CT && CTPlayerModels.Count() > 1 )
	{
		ShowViewPortPanel( PANEL_CLASS_CT );
	}
	else
	{
		HandleCommand_JoinClass( 0 );
	}
}

void CCSPlayer::State_Enter_ACTIVE()
{
	SetMoveType( MOVETYPE_WALK );
	RemoveSolidFlags( FSOLID_NOT_SOLID );
	m_Local.m_iHideHUD = 0;
	PhysObjectWake();
}


void CCSPlayer::State_PreThink_ACTIVE()
{
	// We only allow noclip here only because noclip is useful for debugging.
	// It would be nice if the noclip command set some flag so we could tell that they
	// did it intentionally.
	if ( IsEFlagSet( EFL_NOCLIP_ACTIVE ) )
	{
//		Assert( GetMoveType() == MOVETYPE_NOCLIP );
	}
	else
	{
//		Assert( GetMoveType() == MOVETYPE_WALK );
	}

	Assert( !IsSolidFlagSet( FSOLID_NOT_SOLID ) );
}


void CCSPlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon )
{
	CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon );
	if ( pCSWeapon )
	{
		// For rifles, pistols, or the knife, drop our old weapon in this slot.
		if ( pCSWeapon->GetSlot() == WEAPON_SLOT_RIFLE ||
			pCSWeapon->GetSlot() == WEAPON_SLOT_PISTOL ||
			pCSWeapon->GetSlot() == WEAPON_SLOT_KNIFE )
		{
			CBaseCombatWeapon *pDropWeapon = Weapon_GetSlot( pCSWeapon->GetSlot() );
			if ( pDropWeapon )
			{
				CSWeaponDrop( pDropWeapon, false, true );
			}
		}
		else if( pCSWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_GRENADE )
		{
			//if we already have this weapon, just add the ammo and destroy it
			if( Weapon_OwnsThisType( pCSWeapon->GetClassname() ) )
			{
				Weapon_EquipAmmoOnly( pWeapon );
				UTIL_Remove( pCSWeapon );
				return;
			}
		}

		pCSWeapon->SetSolidFlags( FSOLID_NOT_SOLID );
		pCSWeapon->SetOwnerEntity( this );
	}

	BaseClass::Weapon_Equip( pWeapon );
}

bool CCSPlayer::Weapon_CanUse( CBaseCombatWeapon *pBaseWeapon )
{
	CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );

	if ( pWeapon )
	{
		// Don't give weapon_c4 to non-terrorists
		if( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_C4 && GetTeamNumber() != TEAM_TERRORIST )
		{
			return false;
		}
	}

	return true;
}

bool CCSPlayer::BumpWeapon( CBaseCombatWeapon *pBaseWeapon )
{
	CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );
	if ( !pWeapon )
	{
		Assert( !pWeapon );
		pBaseWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
		pBaseWeapon->AddEffects( EF_NODRAW );
		Weapon_Equip( pBaseWeapon );
		return true;
	}

	CBaseCombatCharacter *pOwner = pWeapon->GetOwner();

	// Can I have this weapon type?
	if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
	{
		extern int gEvilImpulse101;
		if ( gEvilImpulse101 )
		{
			UTIL_Remove( pWeapon );
		}
		return false;
	}

	// Even if we already have a grenade in this slot, we can pickup another one if we don't already
	// own this type of grenade.
	bool bPickupGrenade =  ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_GRENADE );


	/*
	// ----------------------------------------
	// If I already have it just take the ammo
	// ----------------------------------------
	if ( !bPickupGrenade && Weapon_SlotOccupied( pWeapon ) )
	{
		Weapon_EquipAmmoOnly( pWeapon );
		// Only remove me if I have no ammo left
		// Can't just check HasAnyAmmo because if I don't use clips, I want to be removed,
		if ( pWeapon->UsesClipsForAmmo1() && pWeapon->HasPrimaryAmmo() )
			return false;

		UTIL_Remove( pWeapon );
		return false;
	}
	*/

	if ( HasShield() && pWeapon->GetCSWpnData().m_bCanUseWithShield == false )
		 return false;

	// Check ammo counts for grenades, and don't try to pick up more grenades than we can carry
	if ( bPickupGrenade )
	{
		CBaseCombatWeapon *pOwnedGrenade = Weapon_OwnsThisType( pWeapon->GetClassname() );

		if( pOwnedGrenade )
		{
			int numGrenades = 0;
			int maxGrenades = 0;

			int ammoIndex = pOwnedGrenade->GetPrimaryAmmoType();
			if( ammoIndex != -1 )
			{
				numGrenades = GetAmmoCount( ammoIndex );
			}
			maxGrenades = GetAmmoDef()->MaxCarry(ammoIndex);

			if( numGrenades >= maxGrenades )
			{
				return false;
			}
		}
	}

	if( bPickupGrenade || !Weapon_SlotOccupied( pWeapon ) )
	{
		pWeapon->CheckRespawn();

		pWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
		pWeapon->AddEffects( EF_NODRAW );

		CCSPlayer* pDonor = pWeapon->GetDonor();
		if ( pDonor && pDonor != this && pWeapon->GetCSWpnData().GetWeaponPrice() > m_iAccount )
		{
			CCS_GameStats.Event_PlayerDonatedWeapon( pDonor );
		}
		pWeapon->SetDonor(NULL);

		Weapon_Equip( pWeapon );

		int iExtraAmmo = pWeapon->GetExtraAmmoCount();

		if( iExtraAmmo && !bPickupGrenade )
		{
			//Find out the index of the ammo
			int iAmmoIndex = pWeapon->GetPrimaryAmmoType();

			if( iAmmoIndex != -1 )
			{
				//Remove the extra ammo from the weapon
				pWeapon->SetExtraAmmoCount(0);

				//Give it to the player
				SetAmmoCount( iExtraAmmo, iAmmoIndex );
			}
		}

		IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" );
		if( event )
		{
			const char *weaponName = pWeapon->GetClassname();
			if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
			{
				weaponName += 7;
			}
			event->SetInt( "userid", GetUserID() );
			event->SetString( "item", weaponName );
			gameeventmanager->FireEvent( event );
		}

		return true;
	}

	return false;
}


void CCSPlayer::ResetStamina( void )
{
	m_flStamina = 0.0f;
}

void CCSPlayer::RescueZoneTouch( inputdata_t &inputdata )
{
	m_bInHostageRescueZone = true;
	if ( GetTeamNumber() == TEAM_CT && !(m_iDisplayHistoryBits & DHF_IN_RESCUE_ZONE) )
	{
		HintMessage( "#Hint_hostage_rescue_zone", false );
		m_iDisplayHistoryBits |= DHF_IN_RESCUE_ZONE;
	}
}

//------------------------------------------------------------------------------------------
CON_COMMAND( timeleft, "prints the time remaining in the match" )
{
	CCSPlayer *pPlayer = ToCSPlayer( UTIL_GetCommandClient() );
	if ( pPlayer && pPlayer->m_iNextTimeCheck >= gpGlobals->curtime )
	{
		return; // rate limiting
	}

	int iTimeRemaining = (int)CSGameRules()->GetMapRemainingTime();

	if ( iTimeRemaining < 0 )
	{
		if ( pPlayer )
		{
			ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_no_timelimit" );
		}
		else
		{
			Msg( "* No Time Limit *\n" );
		}
	}
	else if ( iTimeRemaining == 0 )
	{
		if ( pPlayer )
		{
			ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_last_round" );
		}
		else
		{
			Msg( "* Last Round *\n" );
		}
	}
	else
	{
		int iMinutes, iSeconds;
		iMinutes = iTimeRemaining / 60;
		iSeconds = iTimeRemaining % 60;

		char minutes[8];
		char seconds[8];

		Q_snprintf( minutes, sizeof(minutes), "%d", iMinutes );
		Q_snprintf( seconds, sizeof(seconds), "%2.2d", iSeconds );

		if ( pPlayer )
		{
			ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_timelimit", minutes, seconds );
		}
		else
		{
			Msg( "Time Remaining:  %s:%s\n", minutes, seconds );
		}
	}

	if ( pPlayer )
	{
		pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1;
	}
}

//------------------------------------------------------------------------------------------
/**
 * Emit given sound that only we can hear
 */
void CCSPlayer::EmitPrivateSound( const char *soundName )
{
	CSoundParameters params;
	if (!GetParametersForSound( soundName, params, NULL ))
		return;

	CSingleUserRecipientFilter filter( this );
	EmitSound( filter, entindex(), soundName );
}


//=====================
//Autobuy
//=====================
static void AutoBuy( void )
{
	CCSPlayer *player = ToCSPlayer( UTIL_GetCommandClient() );

	if ( player )
		player->AutoBuy();
}
static ConCommand autobuy( "autobuy", AutoBuy, "Attempt to purchase items with the order listed in cl_autobuy" );

//==============================================
//AutoBuy - do the work of deciding what to buy
//==============================================
void CCSPlayer::AutoBuy()
{
	if ( !IsInBuyZone() )
	{
		EmitPrivateSound( "BuyPreset.CantBuy" );
		return;
	}

	const char *autobuyString = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_autobuy" );
	if ( !autobuyString || !*autobuyString )
	{
		EmitPrivateSound( "BuyPreset.AlreadyBought" );
		return;
	}

	bool boughtPrimary = false, boughtSecondary = false;

	m_bIsInAutoBuy = true;
	ParseAutoBuyString(autobuyString, boughtPrimary, boughtSecondary);
	m_bIsInAutoBuy = false;

	m_bAutoReload = true;

	//TODO ?: stripped out all the attempts to buy career weapons.
	// as we're not porting cs:cz, these were skipped
}

void CCSPlayer::ParseAutoBuyString(const char *string, bool &boughtPrimary, bool &boughtSecondary)
{
	char command[32];
	int nBuffSize = sizeof(command) - 1; // -1 to leave space for the NULL at the end of the string
	const char *c = string;

	if (c == NULL)
	{
		EmitPrivateSound( "BuyPreset.AlreadyBought" );
		return;
	}

	BuyResult_e overallResult = BUY_ALREADY_HAVE;

	// loop through the string of commands, trying each one in turn.
	while (*c != 0)
	{
		int i = 0;
		// copy the next word into the command buffer.
		while ((*c != 0) && (*c != ' ') && (i < nBuffSize))
		{
			command[i] = *(c);
			++c;
			++i;
		}
		if (*c == ' ')
		{
			++c; // skip the space.
		}

		command[i] = 0; // terminate the string.

		// clear out any spaces.
		i = 0;
		while (command[i] != 0)
		{
			if (command[i] == ' ')
			{
				command[i] = 0;
				break;
			}
			++i;
		}

		// make sure we actually have a command.
		if (strlen(command) == 0)
		{
			continue;
		}

		AutoBuyInfoStruct * commandInfo = GetAutoBuyCommandInfo(command);

		if (ShouldExecuteAutoBuyCommand(commandInfo, boughtPrimary, boughtSecondary))
		{
			BuyResult_e result = HandleCommand_Buy( command );

			overallResult = CombineBuyResults( overallResult, result );

			// check to see if we actually bought a primary or secondary weapon this time.
			PostAutoBuyCommandProcessing(commandInfo, boughtPrimary, boughtSecondary);
		}
	}

	if ( overallResult == BUY_CANT_AFFORD )
	{
		EmitPrivateSound( "BuyPreset.CantBuy" );
	}
	else if ( overallResult == BUY_ALREADY_HAVE )
	{
		EmitPrivateSound( "BuyPreset.AlreadyBought" );
	}
	else if ( overallResult == BUY_BOUGHT )
	{
		g_iAutoBuyPurchases++;
	}
}

BuyResult_e CCSPlayer::CombineBuyResults( BuyResult_e prevResult, BuyResult_e newResult )
{
	if ( newResult == BUY_BOUGHT )
	{
		prevResult = BUY_BOUGHT;
	}
	else if ( prevResult != BUY_BOUGHT &&
		(newResult == BUY_CANT_AFFORD || newResult == BUY_INVALID_ITEM || newResult == BUY_PLAYER_CANT_BUY ) )
	{
		prevResult = BUY_CANT_AFFORD;
	}

	return prevResult;
}

//==============================================
//PostAutoBuyCommandProcessing
//==============================================
void CCSPlayer::PostAutoBuyCommandProcessing(const AutoBuyInfoStruct *commandInfo, bool &boughtPrimary, bool &boughtSecondary)
{
	if (commandInfo == NULL)
	{
		return;
	}

	CBaseCombatWeapon *pPrimary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
	CBaseCombatWeapon *pSecondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL );

	if ((pPrimary != NULL) && (stricmp(pPrimary->GetClassname(), commandInfo->m_classname) == 0))
	{
		// I just bought the gun I was trying to buy.
		boughtPrimary = true;
	}
	else if ((pPrimary == NULL) && ((commandInfo->m_class & AUTOBUYCLASS_SHIELD) == AUTOBUYCLASS_SHIELD) && HasShield())
	{
		// the shield is a primary weapon even though it isn't a "real" weapon.
		boughtPrimary = true;
	}
	else if ((pSecondary != NULL) && (stricmp(pSecondary->GetClassname(), commandInfo->m_classname) == 0))
	{
		// I just bought the pistol I was trying to buy.
		boughtSecondary = true;
	}
}

bool CCSPlayer::ShouldExecuteAutoBuyCommand(const AutoBuyInfoStruct *commandInfo, bool boughtPrimary, bool boughtSecondary)
{
	if (commandInfo == NULL)
	{
		return false;
	}

	if ((boughtPrimary) && ((commandInfo->m_class & AUTOBUYCLASS_PRIMARY) != 0) && ((commandInfo->m_class & AUTOBUYCLASS_AMMO) == 0))
	{
		// this is a primary weapon and we already have one.
		return false;
	}

	if ((boughtSecondary) && ((commandInfo->m_class & AUTOBUYCLASS_SECONDARY) != 0) && ((commandInfo->m_class & AUTOBUYCLASS_AMMO) == 0))
	{
		// this is a secondary weapon and we already have one.
		return false;
	}

	if( commandInfo->m_class & AUTOBUYCLASS_ARMOR && ArmorValue() >= 100 )
	{
		return false;
	}

	return true;
}

AutoBuyInfoStruct *CCSPlayer::GetAutoBuyCommandInfo(const char *command)
{
	int i = 0;
	AutoBuyInfoStruct *ret = NULL;
	AutoBuyInfoStruct *temp = &(g_autoBuyInfo[i]);

	// loop through all the commands till we find the one that matches.
	while ((ret == NULL) && (temp->m_class != (AutoBuyClassType)0))
	{
		temp = &(g_autoBuyInfo[i]);
		++i;

		if (stricmp(temp->m_command, command) == 0)
		{
			ret = temp;
		}
	}

	return ret;
}

//==============================================
//PostAutoBuyCommandProcessing
//- reorders the tokens in autobuyString based on the order of tokens in the priorityString.
//==============================================
void CCSPlayer::PrioritizeAutoBuyString(char *autobuyString, const char *priorityString)
{
	char newString[256];
	int newStringPos = 0;
	char priorityToken[32];

	if ((priorityString == NULL) || (autobuyString == NULL))
	{
		return;
	}

	const char *priorityChar = priorityString;

	while (*priorityChar != 0)
	{
		int i = 0;

		// get the next token from the priority string.
		while ((*priorityChar != 0) && (*priorityChar != ' '))
		{
			priorityToken[i] = *priorityChar;
			++i;
			++priorityChar;
		}
		priorityToken[i] = 0;

		// skip spaces
		while (*priorityChar == ' ')
		{
			++priorityChar;
		}

		if (strlen(priorityToken) == 0)
		{
			continue;
		}

		// see if the priority token is in the autobuy string.
		// if  it is, copy that token to the new string and blank out
		// that token in the autobuy string.
		char *autoBuyPosition = strstr(autobuyString, priorityToken);
		if (autoBuyPosition != NULL)
		{
			while ((*autoBuyPosition != 0) && (*autoBuyPosition != ' '))
			{
				newString[newStringPos] = *autoBuyPosition;
				*autoBuyPosition = ' ';
				++newStringPos;
				++autoBuyPosition;
			}

			newString[newStringPos++] = ' ';
		}
	}

	// now just copy anything left in the autobuyString to the new string in the order it's in already.
	char *autobuyPosition = autobuyString;
	while (*autobuyPosition != 0)
	{
		// skip spaces
		while (*autobuyPosition == ' ')
		{
			++autobuyPosition;
		}

		// copy the token over to the new string.
		while ((*autobuyPosition != 0) && (*autobuyPosition != ' '))
		{
			newString[newStringPos] = *autobuyPosition;
			++newStringPos;
			++autobuyPosition;
		}

		// add a space at the end.
		newString[newStringPos++] = ' ';
	}

	// terminate the string.  Trailing spaces shouldn't matter.
	newString[newStringPos] = 0;

	Q_snprintf(autobuyString, sizeof(autobuyString), "%s", newString);
}


//==============================================================
// ReBuy
// system for attempting to buy the weapons you had last round
//==============================================================
static void Rebuy( void )
{
	CCSPlayer *player = ToCSPlayer( UTIL_GetCommandClient() );

	if ( player )
		player->Rebuy();
}
static ConCommand rebuy( "rebuy", Rebuy, "Attempt to repurchase items with the order listed in cl_rebuy" );

void CCSPlayer::BuildRebuyStruct()
{
	if (m_bIsInRebuy)
	{
		// if we are in the middle of a rebuy, we don't want to update the buy struct.
		return;
	}

	CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
	CBaseCombatWeapon *secondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL );

	// do the primary weapon/ammo stuff.
	if (primary == NULL)
	{
		// count a shieldgun as a primary.
		if (HasShield())
		{
			//m_rebuyStruct.m_primaryWeapon = WEAPON_SHIELDGUN;
			Q_strncpy( m_rebuyStruct.m_szPrimaryWeapon, "shield", sizeof(m_rebuyStruct.m_szPrimaryWeapon) );
			m_rebuyStruct.m_primaryAmmo = 0; // shields don't have ammo.
		}
		else
		{

			m_rebuyStruct.m_szPrimaryWeapon[0] = 0;	// if we don't have a shield and we don't have a primary weapon, we got nuthin.
			m_rebuyStruct.m_primaryAmmo = 0;		// can't have ammo if we don't have a gun right?
		}
	}
	else
	{
		//strip off the "weapon_"

		const char *wpnName = primary->GetClassname();

		Q_strncpy( m_rebuyStruct.m_szPrimaryWeapon, wpnName + 7, sizeof(m_rebuyStruct.m_szPrimaryWeapon) );

		if( primary->GetPrimaryAmmoType() != -1 )
		{
			m_rebuyStruct.m_primaryAmmo = GetAmmoCount( primary->GetPrimaryAmmoType() );
		}
	}

	// do the secondary weapon/ammo stuff.
	if (secondary == NULL)
	{
		m_rebuyStruct.m_szSecondaryWeapon[0] = 0;
		m_rebuyStruct.m_secondaryAmmo = 0; // can't have ammo if we don't have a gun right?
	}
	else
	{
		const char *wpnName = secondary->GetClassname();

		Q_strncpy( m_rebuyStruct.m_szSecondaryWeapon, wpnName + 7, sizeof(m_rebuyStruct.m_szSecondaryWeapon) );

		if( secondary->GetPrimaryAmmoType() != -1 )
		{
			m_rebuyStruct.m_secondaryAmmo = GetAmmoCount( secondary->GetPrimaryAmmoType() );
		}
	}

	CBaseCombatWeapon *pGrenade;

	//MATTTODO: right now you can't buy more than one grenade. make it so you can
	//buy more and query the number you have.
	// HE Grenade
	pGrenade = Weapon_OwnsThisType( "weapon_hegrenade" );
	if ( pGrenade && pGrenade->GetPrimaryAmmoType() != -1 )
	{
		m_rebuyStruct.m_heGrenade = GetAmmoCount(pGrenade->GetPrimaryAmmoType());
	}
	else
		m_rebuyStruct.m_heGrenade = 0;


	// flashbang
	pGrenade = Weapon_OwnsThisType( "weapon_flashbang" );
	if ( pGrenade && pGrenade->GetPrimaryAmmoType() != -1 )
	{
		m_rebuyStruct.m_flashbang = GetAmmoCount(pGrenade->GetPrimaryAmmoType());
	}
	else
		m_rebuyStruct.m_flashbang = 0;

	// smokegrenade
	pGrenade = Weapon_OwnsThisType( "weapon_smokegrenade" );
	if ( pGrenade /*&& pGrenade->GetPrimaryAmmoType() != -1*/ )
	{
		m_rebuyStruct.m_smokeGrenade = 1; //GetAmmoCount(pGrenade->GetPrimaryAmmoType());
	}
	else
		m_rebuyStruct.m_smokeGrenade = 0;

	// defuser
	m_rebuyStruct.m_defuser = HasDefuser();

	// night vision
	m_rebuyStruct.m_nightVision = m_bHasNightVision.Get();	//cast to avoid strange compiler warning

	// check for armor.
	m_rebuyStruct.m_armor = ( m_bHasHelmet ? 2 : ( ArmorValue() > 0 ? 1 : 0 ) );
}

void CCSPlayer::Rebuy( void )
{
	if ( !IsInBuyZone() )
	{
		EmitPrivateSound( "BuyPreset.CantBuy" );
		return;
	}

	const char *rebuyString = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_rebuy" );
	if ( !rebuyString || !*rebuyString )
	{
		EmitPrivateSound( "BuyPreset.AlreadyBought" );
		return;
	}

	m_bIsInRebuy = true;
	BuyResult_e overallResult = BUY_ALREADY_HAVE;

	char token[256];
	rebuyString = engine->ParseFile( rebuyString, token, sizeof( token ) );

	while (rebuyString != NULL)
	{
		BuyResult_e result = BUY_ALREADY_HAVE;

		if (!Q_strncmp(token, "PrimaryWeapon", 14))
		{
			result = RebuyPrimaryWeapon();
		}
		else if (!Q_strncmp(token, "PrimaryAmmo", 12))
		{
			result = RebuyPrimaryAmmo();
		}
		else if (!Q_strncmp(token, "SecondaryWeapon", 16))
		{
			result = RebuySecondaryWeapon();
		}
		else if (!Q_strncmp(token, "SecondaryAmmo", 14))
		{
			result = RebuySecondaryAmmo();
		}
		else if (!Q_strncmp(token, "HEGrenade", 10))
		{
			result = RebuyHEGrenade();
		}
		else if (!Q_strncmp(token, "Flashbang", 10))
		{
			result = RebuyFlashbang();
		}
		else if (!Q_strncmp(token, "SmokeGrenade", 13))
		{
			result = RebuySmokeGrenade();
		}
		else if (!Q_strncmp(token, "Defuser", 8))
		{
			result = RebuyDefuser();
		}
		else if (!Q_strncmp(token, "NightVision", 12))
		{
			result = RebuyNightVision();
		}
		else if (!Q_strncmp(token, "Armor", 6))
		{
			result = RebuyArmor();
		}

		overallResult = CombineBuyResults( overallResult, result );

		rebuyString = engine->ParseFile( rebuyString, token, sizeof( token ) );
	}

	m_bIsInRebuy = false;

	// after we're done buying, the user is done with their equipment purchasing experience.
	// so we are effectively out of the buy zone.
//	if (TheTutor != NULL)
//	{
//		TheTutor->OnEvent(EVENT_PLAYER_LEFT_BUY_ZONE);
//	}

	m_bAutoReload = true;

	if ( overallResult == BUY_CANT_AFFORD )
	{
		EmitPrivateSound( "BuyPreset.CantBuy" );
	}
	else if ( overallResult == BUY_ALREADY_HAVE )
	{
		EmitPrivateSound( "BuyPreset.AlreadyBought" );
	}
	else if ( overallResult == BUY_BOUGHT )
	{
		g_iReBuyPurchases++;
	}
}

BuyResult_e CCSPlayer::RebuyPrimaryWeapon()
{
	CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );
	if (primary != NULL)
	{
		return BUY_ALREADY_HAVE;	// don't drop primary weapons via rebuy - if the player picked up a different weapon, he wants to keep it.
	}

	if( strlen( m_rebuyStruct.m_szPrimaryWeapon ) > 0 )
		return HandleCommand_Buy(m_rebuyStruct.m_szPrimaryWeapon);

	return BUY_ALREADY_HAVE;
}

BuyResult_e CCSPlayer::RebuySecondaryWeapon()
{
	CBaseCombatWeapon *pistol = Weapon_GetSlot( WEAPON_SLOT_PISTOL );
	if (pistol != NULL && !m_bUsingDefaultPistol)
	{
		return BUY_ALREADY_HAVE;	// don't drop pistols via rebuy if we've bought one other than the default pistol
	}

	if( strlen( m_rebuyStruct.m_szSecondaryWeapon ) > 0 )
		return HandleCommand_Buy(m_rebuyStruct.m_szSecondaryWeapon);

	return BUY_ALREADY_HAVE;
}

BuyResult_e CCSPlayer::RebuyPrimaryAmmo()
{
	CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE );

	if (primary == NULL)
	{
		return BUY_ALREADY_HAVE;	// can't buy ammo when we don't even have a gun.
	}

	// Ensure that the weapon uses ammo
	int nAmmo = primary->GetPrimaryAmmoType();
	if ( nAmmo == -1 )
	{
		return BUY_ALREADY_HAVE;
	}

	// if we had more ammo before than we have now, buy more.
	if (m_rebuyStruct.m_primaryAmmo > GetAmmoCount( nAmmo ))
	{
		return HandleCommand_Buy("primammo");
	}

	return BUY_ALREADY_HAVE;
}


BuyResult_e CCSPlayer::RebuySecondaryAmmo()
{
	CBaseCombatWeapon *secondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL );

	if (secondary == NULL)
	{
		return BUY_ALREADY_HAVE; // can't buy ammo when we don't even have a gun.
	}

	// Ensure that the weapon uses ammo
	int nAmmo = secondary->GetPrimaryAmmoType();
	if ( nAmmo == -1 )
	{
		return BUY_ALREADY_HAVE;
	}

	if (m_rebuyStruct.m_secondaryAmmo > GetAmmoCount( nAmmo ))
	{
		return HandleCommand_Buy("secammo");
	}

	return BUY_ALREADY_HAVE;
}

BuyResult_e CCSPlayer::RebuyHEGrenade()
{
	CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_hegrenade" );

	int numGrenades = 0;

	if( pGrenade )
	{
		int nAmmo = pGrenade->GetPrimaryAmmoType();
		if ( nAmmo == -1 )
		{
			return BUY_ALREADY_HAVE;
		}

		numGrenades = GetAmmoCount( nAmmo );
	}

	BuyResult_e overallResult = BUY_ALREADY_HAVE;
	int numToBuy = MAX( 0, m_rebuyStruct.m_heGrenade - numGrenades );
	for (int i = 0; i < numToBuy; ++i)
	{
		BuyResult_e result = HandleCommand_Buy("hegrenade");
		overallResult = CombineBuyResults( overallResult, result );
	}

	return overallResult;
}

BuyResult_e CCSPlayer::RebuyFlashbang()
{
	CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_flashbang" );

	int numGrenades = 0;

	if( pGrenade )
	{
		int nAmmo = pGrenade->GetPrimaryAmmoType();
		if ( nAmmo == -1 )
		{
			return BUY_ALREADY_HAVE;
		}
		numGrenades = GetAmmoCount( nAmmo );

	}

	BuyResult_e overallResult = BUY_ALREADY_HAVE;
	int numToBuy = MAX( 0, m_rebuyStruct.m_flashbang - numGrenades );
	for (int i = 0; i < numToBuy; ++i)
	{
		BuyResult_e result = HandleCommand_Buy("flashbang");
		overallResult = CombineBuyResults( overallResult, result );
	}

	return overallResult;
}

BuyResult_e CCSPlayer::RebuySmokeGrenade()
{
	CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_smokegrenade" );

	int numGrenades = 0;

	if( pGrenade )
	{
		int nAmmo = pGrenade->GetPrimaryAmmoType();
		if ( nAmmo == -1 )
		{
			return BUY_ALREADY_HAVE;
		}

		numGrenades = GetAmmoCount( nAmmo );
	}

	BuyResult_e overallResult = BUY_ALREADY_HAVE;
	int numToBuy = MAX( 0, m_rebuyStruct.m_smokeGrenade - numGrenades );
	for (int i = 0; i < numToBuy; ++i)
	{
		BuyResult_e result = HandleCommand_Buy("smokegrenade");
		overallResult = CombineBuyResults( overallResult, result );
	}

	return overallResult;
}

BuyResult_e CCSPlayer::RebuyDefuser()
{
	//If we don't have a defuser, and we want one, buy it!
	if( !HasDefuser() && m_rebuyStruct.m_defuser )
	{
		return HandleCommand_Buy("defuser");
	}

	return BUY_ALREADY_HAVE;
}

BuyResult_e CCSPlayer::RebuyNightVision()
{
	//if we don't have night vision and we want one, buy it!
	if( !m_bHasNightVision && m_rebuyStruct.m_nightVision )
	{
		return HandleCommand_Buy("nvgs");
	}

	return BUY_ALREADY_HAVE;
}

BuyResult_e CCSPlayer::RebuyArmor()
{
	if (m_rebuyStruct.m_armor > 0 )
	{
		int armor = 0;

		if( m_bHasHelmet )
			armor = 2;
		else if( ArmorValue() > 0 )
			armor = 1;

		if( armor < m_rebuyStruct.m_armor )
		{
			if (m_rebuyStruct.m_armor == 1)
			{
				return HandleCommand_Buy("vest");
			}
			else
			{
				return HandleCommand_Buy("vesthelm");
			}
		}

	}

	return BUY_ALREADY_HAVE;
}

bool CCSPlayer::IsUseableEntity( CBaseEntity *pEntity, unsigned int requiredCaps )
{
	CWeaponCSBase *pCSWepaon = dynamic_cast<CWeaponCSBase*>(pEntity);

	if( pCSWepaon )
	{
		// we can't USE dropped weapons
		return true;
	}

	CBaseCSGrenadeProjectile *pGrenade = dynamic_cast<CBaseCSGrenadeProjectile*>(pEntity);
	if ( pGrenade )
	{
		// we can't USE thrown grenades
	}

	return BaseClass::IsUseableEntity( pEntity, requiredCaps );
}

CBaseEntity *CCSPlayer::FindUseEntity()
{
	CBaseEntity *entity = NULL;

	// Check to see if the bomb is close enough to use before attempting to use anything else.

	if ( CSGameRules()->IsBombDefuseMap() && GetTeamNumber() == TEAM_CT )
	{
		// This is done separately since there might be something blocking our LOS to it
		// but we might want to use it anyway if it's close enough.  This should eliminate
		// the vast majority of bomb placement exploits (places where the bomb can be planted
		// but can't be "used".  This also mimics goldsrc cstrike behavior.
		CBaseEntity *bomb = gEntList.FindEntityByClassname( NULL, PLANTED_C4_CLASSNAME );
		if (bomb != NULL)
		{
			Vector bombPos = bomb->GetAbsOrigin();
			Vector vecLOS = EyePosition() - bombPos;

			if (vecLOS.LengthSqr() < (96*96)) // 64 is the distance in Goldsrc.  However since Goldsrc did distance from the player's origin and we're doing distance from the player's eye, make the radius a bit bigger.
			{
				// bomb is close enough, now make sure the player is facing the bomb.
				Vector forward;
				AngleVectors(EyeAngles(), &forward, NULL, NULL);

				vecLOS.NormalizeInPlace();

				float flDot = DotProduct(forward, vecLOS);
				if (flDot < -0.7) // 0.7 taken from Goldsrc, +/- ~45 degrees
				{
					entity = bomb;
				}
			}
		}
	}

	if ( entity == NULL )
	{
		entity = BaseClass::FindUseEntity();
	}

	return entity;
}

void CCSPlayer::StockPlayerAmmo( CBaseCombatWeapon *pNewWeapon )
{
	CWeaponCSBase *pWeapon =  dynamic_cast< CWeaponCSBase * >( pNewWeapon );

	if ( pWeapon )
	{
		if ( pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE )
			return;

		int nAmmo = pWeapon->GetPrimaryAmmoType();

		if ( nAmmo != -1 )
		{
			GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName );
			pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
		}

		return;
	}

	pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ));

	if ( pWeapon )
	{
		int nAmmo = pWeapon->GetPrimaryAmmoType();

		if ( nAmmo != -1 )
		{
			GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName );
			pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
		}
	}

	pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL ));

	if ( pWeapon )
	{
		int nAmmo = pWeapon->GetPrimaryAmmoType();

		if ( nAmmo != -1 )
		{
			GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName );
			pWeapon->m_iClip1 = pWeapon->GetMaxClip1();
		}
	}
}

CBaseEntity	*CCSPlayer::GiveNamedItem( const char *pszName, int iSubType )
{
	EHANDLE pent;

	if ( !pszName || !pszName[0] )
		return  NULL;

#ifndef CS_SHIELD_ENABLED
	if ( !Q_stricmp( pszName, "weapon_shield" ) )
		return NULL;
#endif

	pent = CreateEntityByName(pszName);
	if ( pent == NULL )
	{
		Msg( "NULL Ent in GiveNamedItem!\n" );
		return NULL;
	}

	pent->SetLocalOrigin( GetLocalOrigin() );
	pent->AddSpawnFlags( SF_NORESPAWN );

	CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>( (CBaseEntity*)pent );
	if ( pWeapon )
	{
		if ( iSubType )
		{
			pWeapon->SetSubType( iSubType );
		}
	}

	DispatchSpawn( pent );

	m_bIsBeingGivenItem = true;
	if ( pent != NULL && !(pent->IsMarkedForDeletion()) )
	{
		pent->Touch( this );
	}
	m_bIsBeingGivenItem = false;

	StockPlayerAmmo( pWeapon );
	return pent;
}


void CCSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
{
	if ( event == PLAYERANIMEVENT_THROW_GRENADE )
	{
		// Grenade throwing has to synchronize exactly with the player's grenade weapon going away,
		// and events get delayed a bit, so we let CCSPlayerAnimState pickup the change to this
		// variable.
		m_iThrowGrenadeCounter = (m_iThrowGrenadeCounter+1) % (1<<THROWGRENADE_COUNTER_BITS);
	}
	else
	{
		m_PlayerAnimState->DoAnimationEvent( event, nData );
		TE_PlayerAnimEvent( this, event, nData );	// Send to any clients who can see this guy.
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CCSPlayer::FlashlightIsOn( void )
{
	return IsEffectActive( EF_DIMLIGHT );
}

extern ConVar flashlight;

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCSPlayer::FlashlightTurnOn( void )
{
	if( flashlight.GetInt() > 0 && IsAlive() )
	{
		AddEffects( EF_DIMLIGHT );
		EmitSound( "Player.FlashlightOn" );
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCSPlayer::FlashlightTurnOff( void )
{
	RemoveEffects( EF_DIMLIGHT );

	if( IsAlive() )
	{
		EmitSound( "Player.FlashlightOff" );
	}
}


//Drop the appropriate weapons:
// Defuser if we have one
// C4 if we have one
// The best weapon we have, first check primary,
// then secondary and drop the best one

//=============================================================================
// HPE_BEGIN:
// [tj] Added a parameter so we know if it was death that caused the drop
// [menglish] Clear all previously dropped equipment and add the c4 to the dropped equipment
//=============================================================================
 
void CCSPlayer::DropWeapons( bool fromDeath, bool friendlyFire )
{
	for ( int i = 0; i < DROPPED_COUNT; ++i )
	{
		m_hDroppedEquipment[i] = NULL;
	}

	CBaseCombatWeapon *pC4 = Weapon_OwnsThisType( "weapon_c4" );
	if ( pC4 )
	{
		CSWeaponDrop( pC4, false, true );
		if( fromDeath )
		{
			if( friendlyFire )
			{
				(static_cast<CC4*> (pC4))->SetDroppedFromDeath(true);
			}
			m_hDroppedEquipment[DROPPED_C4] = static_cast<CBaseEntity *>(pC4);
		}
	}

	//NOTE: Function continues beyond comment block. This is just the part I touched.
 
//=============================================================================
// HPE_END
//=============================================================================


	if( HasDefuser() )
	{
		//Drop an item_defuser
		Vector vForward, vRight;
		AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );

		CBaseAnimating *pDefuser = (CBaseAnimating *)CBaseEntity::Create( "item_defuser", WorldSpaceCenter(), GetLocalAngles(), this );
		pDefuser->ApplyAbsVelocityImpulse( vForward * 200 + vRight * random->RandomFloat( -50, 50 ) );

		RemoveDefuser();

		//=============================================================================
		// HPE_BEGIN:
		// [menglish] Add the newly created defuser to the dropped equipment list
		//=============================================================================
		 
		if(fromDeath)
		{
			m_hDroppedEquipment[DROPPED_DEFUSE] = static_cast<CBaseEntity *>(pDefuser);
		}
		 
		//=============================================================================
		// HPE_END
		//=============================================================================
	}

	if( HasShield() )
	{
		DropShield();
	}
	else
	{
		//drop the best weapon we have
		if( !DropRifle( true ) )
			DropPistol( true );
	}

	// drop any live grenades so they explode
	CBaseCSGrenade *pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_hegrenade"));
	if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) )
	{
		pGrenade->DropGrenade();
		pGrenade->DecrementAmmo( this );
	}
	else
	{
		pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_flashbang"));
		if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) )
		{
			pGrenade->DropGrenade();
			pGrenade->DecrementAmmo( this );
		}
		else
		{
			pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_smokegrenade"));
			if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) )
			{
				pGrenade->DropGrenade();
				pGrenade->DecrementAmmo( this );
			}
		}
	}

	// drop the "best" grenade remaining
	CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType("weapon_hegrenade");
	bool grenadeDrop = false;
	if ( pWeapon && pWeapon->HasAmmo() )
	{
		grenadeDrop = CSWeaponDrop(pWeapon, false);
	}
	else
	{
		pWeapon = Weapon_OwnsThisType("weapon_flashbang");
		if ( pWeapon && pWeapon->HasAmmo() )
		{
			grenadeDrop = CSWeaponDrop(pWeapon, false);
		}
		else
		{
			pWeapon = Weapon_OwnsThisType("weapon_smokegrenade");
			if ( pWeapon && pWeapon->HasAmmo() )
			{
				grenadeDrop = CSWeaponDrop(pWeapon, false);
			}
		}
	}

	//=============================================================================
	// HPE_BEGIN:
	// [menglish] Add whichever, if any, grenade was dropped
	//=============================================================================

	if( pWeapon && grenadeDrop )
	{
		m_hDroppedEquipment[DROPPED_GRENADE] = static_cast<CBaseEntity *>(pWeapon);
	}

	//=============================================================================
	// HPE_END
	//=============================================================================
}

//-----------------------------------------------------------------------------
// Purpose: Put the player in the specified team
//-----------------------------------------------------------------------------
void CCSPlayer::ChangeTeam( int iTeamNum )
{
	if ( !GetGlobalTeam( iTeamNum ) )
	{
		Warning( "CCSPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum );
		return;
	}

	int iOldTeam = GetTeamNumber();

	// if this is our current team, just abort
	if ( iTeamNum == iOldTeam )
		return;

	//=============================================================================
	// HPE_BEGIN:	
	//=============================================================================
	 
	// [tj] Added a parameter so we know if it was death that caused the drop
	// Drop Our best weapon
	DropWeapons(false, false);

	// [tj] Clear out dominations
	RemoveNemesisRelationships();
	 
	//=============================================================================
	// HPE_END
	//=============================================================================
	

	// Always allow a change to spectator, and don't count it as one of our team changes.
	// We now store the old team, so if a player changes once to one team, then to spectator,
	// they won't be able to change back to their old old team, but will still be able to join
	// the team they initially changed to.
	if( iTeamNum != TEAM_SPECTATOR )
	{
		m_bTeamChanged = true;
	}
	else
	{
		m_iOldTeam = iOldTeam;
	}

	// do the team change:
	BaseClass::ChangeTeam( iTeamNum );

	//reset class
	m_iClass = (int)CS_CLASS_NONE;

	// update client state

	if ( iTeamNum == TEAM_UNASSIGNED )
	{
		State_Transition( STATE_OBSERVER_MODE );
	}
	else if ( iTeamNum == TEAM_SPECTATOR )
	{
		//=============================================================================
		// HPE_BEGIN:
		// [tj] Removed these lines so players keep their money when switching to spectator.
		//=============================================================================		
		//Reset money
		//m_iAccount = 0;		
		//=============================================================================
		// HPE_END
		//=============================================================================		
		RemoveAllItems( true );

		State_Transition( STATE_OBSERVER_MODE );
	}
	else // active player
	{
		if ( iOldTeam == TEAM_SPECTATOR )
		{
			// If they're switching from being a spectator to ingame player
			//=============================================================================
			// HPE_BEGIN:
			// [tj] Changed this so players either retain their existing money or, 
			//		if they have less than the default, give them the default.
			//=============================================================================
			int startMoney = CSGameRules()->GetStartMoney();
			if (startMoney > m_iAccount)
			{
				m_iAccount = startMoney;
			} 			
			//=============================================================================
			// HPE_END
			//============================================================================= 
		}

		// bots get to this state on TEAM_UNASSIGNED, yet they are marked alive.  Don't kill them.
		else if ( iOldTeam != TEAM_UNASSIGNED  && !IsDead() )
		{
			// Kill player if switching teams while alive
			CommitSuicide();
		}

		// Put up the class selection menu.
		State_Transition( STATE_PICKINGCLASS );
	}

	// Initialize the player counts now that a player has switched teams
	int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
	CSGameRules()->InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
}

//-----------------------------------------------------------------------------
// Purpose: Put the player in the specified team without penalty
//-----------------------------------------------------------------------------
void CCSPlayer::SwitchTeam( int iTeamNum )
{
	if ( !GetGlobalTeam( iTeamNum ) || (iTeamNum != TEAM_CT && iTeamNum != TEAM_TERRORIST) )
	{
		Warning( "CCSPlayer::SwitchTeam( %d ) - invalid team index.\n", iTeamNum );
		return;
	}

	int iOldTeam = GetTeamNumber();

	// if this is our current team, just abort
	if ( iTeamNum == iOldTeam )
		return;

	// Always allow a change to spectator, and don't count it as one of our team changes.
	// We now store the old team, so if a player changes once to one team, then to spectator,
	// they won't be able to change back to their old old team, but will still be able to join
	// the team they initially changed to.
	m_bTeamChanged = true;

	// do the team change:
	BaseClass::ChangeTeam( iTeamNum );

	if( HasDefuser() )
	{
		RemoveDefuser();
	}

	//reset class
	switch ( m_iClass )
	{
	// Terrorist -> CT
	case CS_CLASS_PHOENIX_CONNNECTION:
		m_iClass = (int)CS_CLASS_SEAL_TEAM_6;
		break;
	case CS_CLASS_L337_KREW:
		m_iClass = (int)CS_CLASS_GSG_9;
		break;
	case CS_CLASS_ARCTIC_AVENGERS:
		m_iClass = (int)CS_CLASS_SAS;
		break;
	case CS_CLASS_GUERILLA_WARFARE:
		m_iClass = (int)CS_CLASS_GIGN;
		break;

	// CT -> Terrorist
	case CS_CLASS_SEAL_TEAM_6:
		m_iClass = (int)CS_CLASS_PHOENIX_CONNNECTION;
		break;
	case CS_CLASS_GSG_9:
		m_iClass = (int)CS_CLASS_L337_KREW;
		break;
	case CS_CLASS_SAS:
		m_iClass = (int)CS_CLASS_ARCTIC_AVENGERS;
		break;
	case CS_CLASS_GIGN:
		m_iClass = (int)CS_CLASS_GUERILLA_WARFARE;
		break;

	case CS_CLASS_NONE:
	default:
		break;
	}

	// Initialize the player counts now that a player has switched teams
	int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
	CSGameRules()->InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
}

void CCSPlayer::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set )
{
	// this is for giving player info to the hostage response system
	// and is as yet unused.
	// Eventually we could give the hostage a few tidbits about this player,
	// eg their health, what weapons they have, and the hostage could
	// comment accordingly.

	//do not append any player data to the Criteria!

	//we don't know which player we should be caring about
}

static unsigned int s_BulletGroupCounter = 0;

void CCSPlayer::StartNewBulletGroup()
{
	s_BulletGroupCounter++;
}

//=======================================================
// Remember this amount of damage that we dealt for stats
//=======================================================
void CCSPlayer::RecordDamageGiven( const char *szDamageTaker, int iDamageGiven )
{
	FOR_EACH_LL( m_DamageGivenList, i )
	{
		if( Q_strncmp( szDamageTaker, m_DamageGivenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 )
		{
			m_DamageGivenList[i]->AddDamage( iDamageGiven, s_BulletGroupCounter );
			return;
		}
	}

	CDamageRecord *record = new CDamageRecord( szDamageTaker, iDamageGiven, s_BulletGroupCounter );
	int k = m_DamageGivenList.AddToTail();
	m_DamageGivenList[k] = record;
}

//=======================================================
// Remember this amount of damage that we took for stats
//=======================================================
void CCSPlayer::RecordDamageTaken( const char *szDamageDealer, int iDamageTaken )
{
	FOR_EACH_LL( m_DamageTakenList, i )
	{
		if( Q_strncmp( szDamageDealer, m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 )
		{
			m_DamageTakenList[i]->AddDamage( iDamageTaken, s_BulletGroupCounter );
			return;
		}
	}

	CDamageRecord *record = new CDamageRecord( szDamageDealer, iDamageTaken, s_BulletGroupCounter );
	int k = m_DamageTakenList.AddToTail();
	m_DamageTakenList[k] = record;
}

//=======================================================
// Reset our damage given and taken counters
//=======================================================
void CCSPlayer::ResetDamageCounters()
{
	m_DamageGivenList.PurgeAndDeleteElements();
	m_DamageTakenList.PurgeAndDeleteElements();
}

//=======================================================
// Output the damage that we dealt to other players
//=======================================================
void CCSPlayer::OutputDamageTaken( void )
{
	bool bPrintHeader = true;
	CDamageRecord *pRecord;
	char buf[64];
	int msg_dest = HUD_PRINTCONSOLE;

	FOR_EACH_LL( m_DamageTakenList, i )
	{
		if( bPrintHeader )
		{
			ClientPrint( this, msg_dest, "Player: %s1 - Damage Taken\n", GetPlayerName() );
			ClientPrint( this, msg_dest, "-------------------------\n" );
			bPrintHeader = false;
		}
		pRecord = m_DamageTakenList[i];

		if( pRecord )
		{
			if (pRecord->GetNumHits() == 1)
			{
				Q_snprintf( buf, sizeof(buf), "%d in %d hit", pRecord->GetDamage(), pRecord->GetNumHits() );
			}
			else
			{
				Q_snprintf( buf, sizeof(buf), "%d in %d hits", pRecord->GetDamage(), pRecord->GetNumHits() );
			}
			ClientPrint( this, msg_dest, "Damage Taken from \"%s1\" - %s2\n", pRecord->GetPlayerName(), buf );
		}
	}
}

//=======================================================
// Output the damage that we took from other players
//=======================================================
void CCSPlayer::OutputDamageGiven( void )
{
	bool bPrintHeader = true;
	CDamageRecord *pRecord;
	char buf[64];
	int msg_dest = HUD_PRINTCONSOLE;

	FOR_EACH_LL( m_DamageGivenList, i )
	{
		if( bPrintHeader )
		{
			ClientPrint( this, msg_dest, "Player: %s1 - Damage Given\n", GetPlayerName() );
			ClientPrint( this, msg_dest, "-------------------------\n" );
			bPrintHeader = false;
		}

		pRecord = m_DamageGivenList[i];

		if( pRecord )
		{
			if (pRecord->GetNumHits() == 1)
			{
				Q_snprintf( buf, sizeof(buf), "%d in %d hit", pRecord->GetDamage(), pRecord->GetNumHits() );
			}
			else
			{
				Q_snprintf( buf, sizeof(buf), "%d in %d hits", pRecord->GetDamage(), pRecord->GetNumHits() );
			}
			ClientPrint( this, msg_dest, "Damage Given to \"%s1\" - %s2\n", pRecord->GetPlayerName(), buf );
		}
	}
}

void CCSPlayer::CreateViewModel( int index /*=0*/ )
{
	Assert( index >= 0 && index < MAX_VIEWMODELS );

	if ( GetViewModel( index ) )
		return;

	CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" );
	if ( vm )
	{
		vm->SetAbsOrigin( GetAbsOrigin() );
		vm->SetOwner( this );
		vm->SetIndex( index );
		DispatchSpawn( vm );
		vm->FollowEntity( this, false );
		m_hViewModel.Set( index, vm );
	}
}

bool CCSPlayer::HasC4() const
{
	return ( Weapon_OwnsThisType( "weapon_c4" ) != NULL );
}

int CCSPlayer::GetNextObserverSearchStartPoint( bool bReverse )
{
	// If we are currently watching someone who is dead, they must have died while we were watching (since
	// a dead guy is not a valid pick to start watching).  He was given his killer as an observer target
	// when he died, so let's start by trying to observe his killer.  If we fail, we'll use the normal way.
	// And this is just the start point anyway, but we want to start the search here in case it is okay.
	if( m_hObserverTarget && !m_hObserverTarget->IsAlive() )
	{
		CCSPlayer *targetPlayer = ToCSPlayer(m_hObserverTarget);
		if( targetPlayer && targetPlayer->GetObserverTarget() )
			return targetPlayer->GetObserverTarget()->entindex();
	}

	return BaseClass::GetNextObserverSearchStartPoint( bReverse );
}

void CCSPlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
{
	BaseClass::PlayStepSound( vecOrigin, psurface, fvol, force );

	if ( !sv_footsteps.GetFloat() )
		return;

	if ( !psurface )
		return;

	IGameEvent * event = gameeventmanager->CreateEvent( "player_footstep" );
	if ( event )
	{
		event->SetInt("userid", GetUserID() );
		gameeventmanager->FireEvent( event );
	}

	m_bMadeFootstepNoise = true;
}


void CCSPlayer::SelectDeathPose( const CTakeDamageInfo &info )
{
	MDLCACHE_CRITICAL_SECTION();
	if ( !GetModelPtr() )
		return;

	Activity aActivity = ACT_INVALID;
	int iDeathFrame = 0;

	SelectDeathPoseActivityAndFrame( this, info, m_LastHitGroup, aActivity, iDeathFrame );
	if ( aActivity == ACT_INVALID )
	{
		SetDeathPose( ACT_INVALID );
		SetDeathPoseFrame( 0 );
		return;
	}

	SetDeathPose( SelectWeightedSequence( aActivity ) );
	SetDeathPoseFrame( iDeathFrame );
}


void CCSPlayer::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event == 4001 || pEvent->event == 4002 )
	{
		// Ignore these for now - soon we will be playing footstep sounds based on these events
		// that mark footfalls in the anims.
	}
	else
	{
		BaseClass::HandleAnimEvent( pEvent );
	}
}


bool CCSPlayer::CanChangeName( void )
{
	if ( IsBot() )
		return true;

	// enforce the minimum interval
	if ( (m_flNameChangeHistory[0] + MIN_NAME_CHANGE_INTERVAL) >= gpGlobals->curtime )
	{
		return false;
	}

	// enforce that we dont do more than NAME_CHANGE_HISTORY_SIZE
	// changes within NAME_CHANGE_HISTORY_INTERVAL
	if ( (m_flNameChangeHistory[NAME_CHANGE_HISTORY_SIZE-1] + NAME_CHANGE_HISTORY_INTERVAL) >= gpGlobals->curtime )
	{
		return false;
	}

	return true;
}

void CCSPlayer::ChangeName( const char *pszNewName )
{
	// make sure name is not too long
	char trimmedName[MAX_PLAYER_NAME_LENGTH];
	Q_strncpy( trimmedName, pszNewName, sizeof( trimmedName ) );

	const char *pszOldName = GetPlayerName();

	// send colored message to everyone
	CReliableBroadcastRecipientFilter filter;
	UTIL_SayText2Filter( filter, this, false, "#Cstrike_Name_Change", pszOldName, trimmedName );

	// broadcast event
	IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" );
	if ( event )
	{
		event->SetInt( "userid", GetUserID() );
		event->SetString( "oldname", pszOldName );
		event->SetString( "newname", trimmedName );
		gameeventmanager->FireEvent( event );
	}

	// change shared player name
	SetPlayerName( trimmedName );

	// tell engine to use new name
	engine->ClientCommand( edict(), "name \"%s\"", trimmedName );

	// remember time of name change
	for ( int i=NAME_CHANGE_HISTORY_SIZE-1; i>0; i-- )
	{
		m_flNameChangeHistory[i] = m_flNameChangeHistory[i-1];
	}

	m_flNameChangeHistory[0] = gpGlobals->curtime; // last change
}

bool CCSPlayer::StartReplayMode( float fDelay, float fDuration, int iEntity )
{
	if ( !BaseClass::StartReplayMode( fDelay, fDuration, iEntity ) )
		return false;

	CSingleUserRecipientFilter filter( this );
	filter.MakeReliable();

	UserMessageBegin( filter, "KillCam" );
		WRITE_BYTE( OBS_MODE_IN_EYE );

		if ( m_hObserverTarget.Get() )
		{
			WRITE_BYTE( m_hObserverTarget.Get()->entindex() );	// first target
			WRITE_BYTE( entindex() );	//second target
		}
		else
		{
			WRITE_BYTE( entindex() );	// first target
			WRITE_BYTE( 0 );	//second target
		}
	MessageEnd();

	ClientPrint( this, HUD_PRINTCENTER, "Kill Cam Replay" );

	return true;
}

void CCSPlayer::StopReplayMode()
{
	BaseClass::StopReplayMode();

	CSingleUserRecipientFilter filter( this );
	filter.MakeReliable();

	UserMessageBegin( filter, "KillCam" );
		WRITE_BYTE( OBS_MODE_NONE );
		WRITE_BYTE( 0 );
		WRITE_BYTE( 0 );
	MessageEnd();
}

void CCSPlayer::PlayUseDenySound()
{
	// Don't do a sound here because it can mute your footsteps giving you an advantage.
	// The CS:S content for this sound is silent anyways.
	//EmitSound( "Player.UseDeny" );
}

//=============================================================================
// HPE_BEGIN:
//=============================================================================

// [menglish, tj] This is where we reset all the per-round information for achievements for this player
void CCSPlayer::ResetRoundBasedAchievementVariables()
{
	m_KillingSpreeStartTime = -1;

	int numCTPlayers = 0, numTPlayers = 0;
	for (int i = 0; i < g_Teams.Count(); i++ )
	{
		if(g_Teams[i])
		{
			if ( g_Teams[i]->GetTeamNumber() == TEAM_CT )
				numCTPlayers = g_Teams[i]->GetNumPlayers();
			else if(g_Teams[i]->GetTeamNumber() == TEAM_TERRORIST)
				numTPlayers = g_Teams[i]->GetNumPlayers();
		}
	}
	m_NumEnemiesKilledThisRound = 0;
	if(GetTeamNumber() == TEAM_CT)
		m_NumEnemiesAtRoundStart = numTPlayers;
	else if(GetTeamNumber() == TEAM_TERRORIST)
		m_NumEnemiesAtRoundStart = numCTPlayers;


	//Clear the previous owner field for currently held weapons
	CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE ));
	if ( pWeapon )
	{
		pWeapon->SetPreviousOwner(NULL);
	}
	pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL));
	if ( pWeapon )
	{
		pWeapon->SetPreviousOwner(NULL);
	}

	//Clear list of weapons used to get kills
	m_killWeapons.RemoveAll();

	//Clear sliding window of kill times
	m_killTimes.RemoveAll();

	//clear round kills
	m_enemyPlayersKilledThisRound.RemoveAll();

	m_killsWhileBlind = 0;

	m_bSurvivedHeadshotDueToHelmet = false;

	m_gooseChaseStep = GC_NONE;
	m_defuseDefenseStep = DD_NONE;
	m_pGooseChaseDistractingPlayer = NULL;

	m_bMadeFootstepNoise = false;

	m_bombPickupTime = -1;

	m_bMadePurchseThisRound = false;

	m_bKilledDefuser = false;
	m_bKilledRescuer = false;
	m_maxGrenadeKills = 0;
	m_grenadeDamageTakenThisRound = 0;

	//=============================================================================
	// HPE_BEGIN:
	// [dwenger] Needed for fun-fact implementation
	//=============================================================================

	WieldingKnifeAndKilledByGun(false);

	m_WeaponTypesUsed.RemoveAll();

	m_bPickedUpDefuser = false;
	m_bDefusedWithPickedUpKit = false;

	//=============================================================================
	// HPE_END
	//=============================================================================
}


/**
 *	static public CCSPlayer::GetCSWeaponIDCausingDamage()
 *
 *		Helper function to get the ID of the weapon used to kill a player.
 *		This is slightly non-trivial because the grenade because a separate
 *		entity when thrown.
 *
 *  Parameters:
 * 		info -
 *			
 *	Returns:
 *		int -
 */
CSWeaponID CCSPlayer::GetWeaponIdCausingDamange( const CTakeDamageInfo &info )
{
	CBaseEntity *pInflictor = info.GetInflictor();
	CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker());
	if (pAttacker == pInflictor)
	{
		CWeaponCSBase* pAttackerWeapon = dynamic_cast< CWeaponCSBase * >(pAttacker->GetActiveWeapon());
		if (!pAttackerWeapon)
			return WEAPON_NONE;

		return pAttackerWeapon->GetWeaponID();
	}
	else if (pInflictor && V_strcmp(pInflictor->GetClassname(), "hegrenade_projectile") == 0)
	{
		return WEAPON_HEGRENADE;
	}
	return WEAPON_NONE;
}


//=============================================================================
// HPE_BEGIN:
// [dwenger] adding tracking for weapon used fun fact
//=============================================================================
void CCSPlayer::PlayerUsedFirearm( CBaseCombatWeapon* pBaseWeapon )
{
	if ( pBaseWeapon )
	{
		CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon );

		if ( pWeapon )
		{
			CSWeaponType weaponType = pWeapon->GetCSWpnData().m_WeaponType;
			CSWeaponID weaponID = pWeapon->GetWeaponID();

			if ( weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_C4 && weaponType != WEAPONTYPE_GRENADE )
			{
				if ( m_WeaponTypesUsed.Find( weaponID ) == -1 )
				{
					// Add this weapon to the list of weapons used by the player
					m_WeaponTypesUsed.AddToTail( weaponID );
				}
			}
		}
	}
}


/**
 *	public CCSPlayer::ProcessPlayerDeathAchievements()
 *
 *		Do Achievement processing whenever a player is killed
 *
 *  Parameters:
 * 		pAttacker -
 * 		pVictim -
 * 		info -
 */
void CCSPlayer::ProcessPlayerDeathAchievements( CCSPlayer *pAttacker, CCSPlayer *pVictim, const CTakeDamageInfo &info )
{
	Assert(pVictim != NULL);
	CBaseEntity *pInflictor = info.GetInflictor();	
  
	// all these achievements require a valid attacker on a different team
	if ( pAttacker != NULL && pVictim != NULL && pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() )
	{
		// get the weapon used - some of the achievements will need this data
		CWeaponCSBase* pAttackerWeapon = dynamic_cast< CWeaponCSBase * >(pAttacker->GetActiveWeapon());

		//=============================================================================
		// HPE_BEGIN:
		// [dwenger] Fun-fact processing
		//=============================================================================

		CWeaponCSBase* pVictimWeapon = dynamic_cast< CWeaponCSBase* >(pVictim->GetActiveWeapon());

		//=============================================================================
		// HPE_END
		//=============================================================================

		CSWeaponID attackerWeaponId = GetWeaponIdCausingDamange(info);

		if (pVictim->m_bIsDefusing)
		{
			pAttacker->AwardAchievement(CSKilledDefuser);			
			pAttacker->m_bKilledDefuser = true;

			if (attackerWeaponId == WEAPON_HEGRENADE)
			{
				pAttacker->AwardAchievement(CSKilledDefuserWithGrenade);
			}
		}

		// [pfreese] Achievement check for attacker killing player while reloading
		if (pVictim->IsReloading())
		{
			pAttacker->AwardAchievement(CSKillEnemyReloading);
		}

		if (pVictim->IsRescuing())
		{
			// Ensure the killer did not injure any hostages
			if ( !pAttacker->InjuredAHostage() && pVictim->GetNumFollowers() == g_Hostages.Count() )
			{
				pAttacker->AwardAchievement(CSKilledRescuer);
				pAttacker->m_bKilledRescuer = true;
			}
		}

		// [menglish] Achievement check for doing 95% or more damage to a player and having another player kill them
		FOR_EACH_LL( pVictim->m_DamageTakenList, i )
		{
			if( pVictim->m_DamageTakenList[i]->GetDamage() >= pVictim->GetMaxHealth() - AchievementConsts::DamageNoKill_MaxHealthLeftOnKill &&
				Q_strncmp( pAttacker->GetPlayerName(), pVictim->m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) != 0 )
			{
				//Now find the player who did that amount of damage
				for ( int j = 1; j <= MAX_PLAYERS; j++ )
				{
					CBasePlayer *pPlayerIter = UTIL_PlayerByIndex( j );

					if ( pPlayerIter && Q_strncmp( pPlayerIter->GetPlayerName(), pVictim->m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0  &&
						pPlayerIter->GetTeamNumber() != pVictim->GetTeamNumber() )
					{
						ToCSPlayer(pPlayerIter)->AwardAchievement(CSDamageNoKill);
						break;
					}
				}
			}
		}

		pAttacker->m_NumEnemiesKilledThisRound++;

		//store a list of kill times for spree tracking
		pAttacker->m_killTimes.AddToTail(gpGlobals->curtime);

		//Add the victim to the list of players killed this round
		pAttacker->m_enemyPlayersKilledThisRound.AddToTail(pVictim);

		//Calculate Avenging for all players the victim has killed
		for ( int avengedIndex = 0; avengedIndex < pVictim->m_enemyPlayersKilledThisRound.Count(); avengedIndex++ )        
		{
			CCSPlayer* avengedPlayer = pVictim->m_enemyPlayersKilledThisRound[avengedIndex];

			if (avengedPlayer)
			{
				//Make sure you are avenging someone on your own team (This is the expected flow. Just here to avoid edge cases like team-switching).
				if (pAttacker->GetTeamNumber() == avengedPlayer->GetTeamNumber())
				{
					CCS_GameStats.Event_PlayerAvengedTeammate(pAttacker, pVictim->m_enemyPlayersKilledThisRound[avengedIndex]);
				}
			}
		}



		//remove elements older than a certain time
		while (pAttacker->m_killTimes.Count() > 0 && pAttacker->m_killTimes[0] + AchievementConsts::KillingSpree_WindowTime < gpGlobals->curtime)
		{
			pAttacker->m_killTimes.Remove(0);
		}

		//If we killed enough players in the time window, award the achievement
		if (pAttacker->m_killTimes.Count() >= AchievementConsts::KillingSpree_Kills)
		{
			pAttacker->m_KillingSpreeStartTime = gpGlobals->curtime;
			pAttacker->AwardAchievement(CSKillingSpree);
		}

		// Did the attacker just kill someone on a killing spree?
		if (pVictim->m_KillingSpreeStartTime >= 0 && pVictim->m_KillingSpreeStartTime - gpGlobals->curtime <= AchievementConsts::KillingSpreeEnder_TimeWindow)
		{
			pAttacker->AwardAchievement(CSKillingSpreeEnder);
		}

		//Check the "killed someone with their own weapon" achievement
		if (pAttackerWeapon && pAttackerWeapon->GetPreviousOwner() == pVictim)
		{
			pAttacker->AwardAchievement(CSKillEnemyWithFormerGun);
		}

		//If this player has killed the entire team award him the achievement
		if (pAttacker->m_NumEnemiesKilledThisRound == pAttacker->m_NumEnemiesAtRoundStart && pAttacker->m_NumEnemiesKilledThisRound >= AchievementConsts::KillEnemyTeam_MinKills)
		{
			pAttacker->AwardAchievement(CSKillEnemyTeam);
		}

		//If this is a posthumous kill award the achievement
		if (!pAttacker->IsAlive() && attackerWeaponId == WEAPON_HEGRENADE)
		{
			CCS_GameStats.IncrementStat(pAttacker, CSSTAT_GRENADE_POSTHUMOUSKILLS, 1);
			ToCSPlayer(pAttacker)->AwardAchievement(CSPosthumousGrenadeKill);
		}

		if (pAttacker->GetActiveWeapon() && pAttacker->GetActiveWeapon()->Clip1() == 0 && pAttackerWeapon && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_SNIPER_RIFLE)
		{
			if (pInflictor == pAttacker)
			{
				pAttacker->AwardAchievement(CSKillEnemyLastBullet);
				CCS_GameStats.IncrementStat(pAttacker, CSSTAT_KILLS_WITH_LAST_ROUND, 1);
			}
		}

		//=============================================================================
		// HPE_BEGIN:
		// [dwenger] Fun-fact processing
		//=============================================================================

		if (pVictimWeapon && pVictimWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_KNIFE && pAttackerWeapon &&
			pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_KNIFE && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_C4 && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_GRENADE)
		{
			// Victim was wielding knife when killed by a gun
			pVictim->WieldingKnifeAndKilledByGun(true);
		}

		//=============================================================================
		// HPE_END
		//=============================================================================

		//see if this is a unique weapon		
		if (attackerWeaponId != WEAPON_NONE)
		{
			if (pAttacker->m_killWeapons.Find(attackerWeaponId) == -1)
			{
				pAttacker->m_killWeapons.AddToTail(attackerWeaponId);
				if (pAttacker->m_killWeapons.Count() >= AchievementConsts::KillsWithMultipleGuns_MinWeapons)
				{
					pAttacker->AwardAchievement(CSKillsWithMultipleGuns);					
				}
			}
		}

		//Check for kills while blind
		if (pAttacker->IsBlindForAchievement())
		{
			//if this is from a different blinding, restart the kill counter and set the time
			if (pAttacker->m_blindStartTime != pAttacker->m_firstKillBlindStartTime)
			{
				pAttacker->m_killsWhileBlind = 0;
				pAttacker->m_firstKillBlindStartTime = pAttacker->m_blindStartTime;
			}

			++pAttacker->m_killsWhileBlind;
			if (pAttacker->m_killsWhileBlind >= AchievementConsts::KillEnemiesWhileBlind_Kills)
			{
				pAttacker->AwardAchievement(CSKillEnemiesWhileBlind);
			}

			if (pAttacker->m_killsWhileBlind >= AchievementConsts::KillEnemiesWhileBlindHard_Kills)
			{
				pAttacker->AwardAchievement(CSKillEnemiesWhileBlindHard);
			}
		}

		//Check sniper killing achievements
		bool victimZoomed = ( pVictim->GetFOV() != pVictim->GetDefaultFOV() );
		bool attackerZoomed = ( pAttacker->GetFOV() != pAttacker->GetDefaultFOV() );
		bool attackerUsedSniperRifle = pAttackerWeapon && pAttackerWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SNIPER_RIFLE && pInflictor == pAttacker;
		if (victimZoomed && attackerUsedSniperRifle)
		{
			pAttacker->AwardAchievement(CSKillSniperWithSniper);
		}

		if (attackerWeaponId == WEAPON_KNIFE && victimZoomed)
		{
			pAttacker->AwardAchievement(CSKillSniperWithKnife);
		}
		if (attackerUsedSniperRifle && !attackerZoomed)
		{
			pAttacker->AwardAchievement(CSHipShot);
		}

		//Kill a player at low health
		if (pAttacker->IsAlive() && pAttacker->GetHealth() <= AchievementConsts::KillWhenAtLowHealth_MaxHealth)
		{
			pAttacker->AwardAchievement(CSKillWhenAtLowHealth);
		}

		//Kill a player with a knife during the pistol round
		if (CSGameRules()->IsPistolRound())
		{
			if (attackerWeaponId == WEAPON_KNIFE)
			{
				pAttacker->AwardAchievement(CSPistolRoundKnifeKill);
			}
		}

		//[tj] Check for dual elites fight
		CWeaponCSBase* victimWeapon = pVictim->GetActiveCSWeapon();

		if (victimWeapon)
		{
			CSWeaponID victimWeaponID = victimWeapon->GetWeaponID();

			if (attackerWeaponId == WEAPON_ELITE && victimWeaponID == WEAPON_ELITE)
			{
				pAttacker->AwardAchievement(CSWinDualDuel);
			}
		}

		//[tj] See if the attacker or defender are in the air [sbodenbender] dont include ladders
		bool attackerInAir = pAttacker->GetMoveType() != MOVETYPE_LADDER && pAttacker->GetNearestSurfaceBelow(AchievementConsts::KillInAir_MinimumHeight) == NULL;
		bool victimInAir = pVictim->GetMoveType() != MOVETYPE_LADDER && pVictim->GetNearestSurfaceBelow(AchievementConsts::KillInAir_MinimumHeight) == NULL;

		if (attackerInAir)
		{
			pAttacker->AwardAchievement(CSKillWhileInAir);
		}
		if (victimInAir)
		{
			pAttacker->AwardAchievement(CSKillEnemyInAir);
		}
		if (attackerInAir && victimInAir)
		{
			pAttacker->AwardAchievement(CSKillerAndEnemyInAir);
		}

		//[tj] advance to the next stage of the defuse defense achievement
		if (pAttacker->m_defuseDefenseStep == DD_STARTED_DEFUSE)
		{
			pAttacker->m_defuseDefenseStep = DD_KILLED_TERRORIST;            
		}

		if (pVictim->HasC4() && pVictim->GetBombPickuptime() + AchievementConsts::KillBombPickup_MaxTime > gpGlobals->curtime)
		{
			pAttacker->AwardAchievement(CSKillBombPickup);
		}
		
	}


	//If you kill a friendly player while blind (from an enemy player), give the guy that blinded you an achievement    
	if ( pAttacker != NULL && pVictim != NULL && pVictim->GetTeamNumber() == pAttacker->GetTeamNumber() && pAttacker->IsBlind())
	{
		CCSPlayer* flashbangAttacker = pAttacker->GetLastFlashbangAttacker();
		if (flashbangAttacker && pAttacker->GetTeamNumber() != flashbangAttacker->GetTeamNumber())
		{
			flashbangAttacker->AwardAchievement(CSCauseFriendlyFireWithFlashbang);
		}
	}

	// do a scan to determine count of players still alive
	int livePlayerCount = 0;
	int teamCount[TEAM_MAXCOUNT];
	int teamIgnoreCount[TEAM_MAXCOUNT];
	memset(teamCount, 0, sizeof(teamCount));
	memset(teamIgnoreCount, 0, sizeof(teamIgnoreCount));
	CCSPlayer *pAlivePlayer = NULL;
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
		if (pPlayer)
		{
			int teamNum = pPlayer->GetTeamNumber();
			if ( teamNum >= 0 )
			{
				++teamCount[teamNum];
				if (pPlayer->WasNotKilledNaturally())
				{
					teamIgnoreCount[teamNum]++;
				}
			}
			if (pPlayer->IsAlive() && pPlayer != pVictim)
			{
				++livePlayerCount;
				pAlivePlayer = pPlayer;
			}
		}
	}

	// Achievement check for being the last player alive in a match
	if (pAlivePlayer)
	{		
		int alivePlayerTeam = pAlivePlayer->GetTeamNumber();
		int alivePlayerOpposingTeam = alivePlayerTeam == TEAM_CT ? TEAM_TERRORIST : TEAM_CT;
		if (livePlayerCount == 1 
			&& CSGameRules()->m_iRoundWinStatus == WINNER_NONE
			&& teamCount[alivePlayerTeam] - teamIgnoreCount[alivePlayerTeam] >= AchievementConsts::LastPlayerAlive_MinPlayersOnTeam
			&& teamCount[alivePlayerOpposingTeam] - teamIgnoreCount[alivePlayerOpposingTeam] >= AchievementConsts::DefaultMinOpponentsForAchievement
			&& ( !(pAlivePlayer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED) ))
		{
			pAlivePlayer->AwardAchievement(CSLastPlayerAlive);
		}
	}

	// [tj] Added hook into player killed stat that happens before weapon drop
	CCS_GameStats.Event_PlayerKilled_PreWeaponDrop(pVictim, info);
}

//[tj]  traces up to maxTrace units down and returns any standable object it hits
//      (doesn't check slope for standability)
CBaseEntity* CCSPlayer::GetNearestSurfaceBelow(float maxTrace)
{
	trace_t trace;
	Ray_t ray;

	Vector traceStart = this->GetAbsOrigin();
	Vector traceEnd = traceStart;
	traceEnd.z -= maxTrace;

	Vector minExtent = this->m_Local.m_bDucked  ? VEC_DUCK_HULL_MIN_SCALED( this ) : VEC_HULL_MIN_SCALED( this );
	Vector maxExtent = this->m_Local.m_bDucked  ? VEC_DUCK_HULL_MAX_SCALED( this ) : VEC_HULL_MAX_SCALED( this );

	ray.Init( traceStart, traceEnd, minExtent, maxExtent );
	UTIL_TraceRay( ray, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );

	return trace.m_pEnt;
}

// [tj] Added a way to react to the round ending before we reset.
//      It is important to note that this happens before the bomb explodes, so a player may die
//      after this from a bomb explosion or a late kill after a defuse/detonation/rescue.
void CCSPlayer::OnRoundEnd(int winningTeam, int reason)
{    
	if (winningTeam == WINNER_CT || winningTeam == WINNER_TER)
	{
		int losingTeamId = (winningTeam == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT;
		
		CTeam* losingTeam = GetGlobalTeam(losingTeamId);

		int losingTeamPlayers = 0;

		if (losingTeam)
		{
			losingTeamPlayers = losingTeam->GetNumPlayers();
			
			int ignoreCount = 0;
			for ( int i = 1; i <= gpGlobals->maxClients; i++ )
			{
				CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
				if (pPlayer)
				{
					int teamNum = pPlayer->GetTeamNumber();
					if ( teamNum == losingTeamId )
					{					
						if (pPlayer->WasNotKilledNaturally())
						{
							ignoreCount++;
						}
					}
					
				}
			}

			losingTeamPlayers -= ignoreCount;
		}

		//Check fast round win achievement
		if (    IsAlive() && 
				gpGlobals->curtime - CSGameRules()->GetRoundStartTime() < AchievementConsts::FastRoundWin_Time &&
				GetTeamNumber() == winningTeam &&
				losingTeamPlayers >= AchievementConsts::DefaultMinOpponentsForAchievement)
		{
			AwardAchievement(CSFastRoundWin);
		}

		//Check goosechase achievement
		if (IsAlive() && reason == Target_Bombed && m_gooseChaseStep == GC_STOPPED_AFTER_GETTING_SHOT && m_pGooseChaseDistractingPlayer)
		{
			m_pGooseChaseDistractingPlayer->AwardAchievement(CSGooseChase);
		}

		//Check Defuse Defense achievement
		if (IsAlive() && reason == Bomb_Defused && m_defuseDefenseStep == DD_KILLED_TERRORIST)
		{
			AwardAchievement(CSDefuseDefense);
		}

		//Check silent win
		if (m_NumEnemiesKilledThisRound > 0 && GetTeamNumber() == winningTeam && !m_bMadeFootstepNoise)
		{
			AwardAchievement(CSSilentWin);
		}

		//Process && Check "win rounds without buying" achievement
		if (GetTeamNumber() == winningTeam && !m_bMadePurchseThisRound)
		{
			m_roundsWonWithoutPurchase++;
			if (m_roundsWonWithoutPurchase > AchievementConsts::WinRoundsWithoutBuying_Rounds)
			{
				AwardAchievement(CSWinRoundsWithoutBuying);
			}
		}
		else
		{
			m_roundsWonWithoutPurchase = 0;
		}
	}

	m_lastRoundResult = reason;
}

void CCSPlayer::OnPreResetRound()
{
	//Check headshot survival achievement
	if (IsAlive() && m_bSurvivedHeadshotDueToHelmet)
	{
		AwardAchievement(CSSurvivedHeadshotDueToHelmet);
	}

	if (IsAlive() && m_grenadeDamageTakenThisRound > AchievementConsts::SurviveGrenade_MinDamage)
	{
		AwardAchievement(CSSurviveGrenade);
	}


	//Check achievement for surviving attacks from multiple players.
	if (IsAlive())
	{
		int numberOfEnemyDamagers = GetNumEnemyDamagers();

		if (numberOfEnemyDamagers >= AchievementConsts::SurviveManyAttacks_NumberDamagingPlayers)
		{
			AwardAchievement(CSSurviveManyAttacks);
		}
	}
}

void CCSPlayer::OnCanceledDefuse()
{
	if (m_gooseChaseStep == GC_SHOT_DURING_DEFUSE)
	{
		m_gooseChaseStep = GC_STOPPED_AFTER_GETTING_SHOT;
	}
}


void CCSPlayer::OnStartedDefuse()
{
	if (m_defuseDefenseStep == DD_NONE)
	{
		m_defuseDefenseStep = DD_STARTED_DEFUSE;
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCSPlayer::AttemptToExitFreezeCam( void )
{
	float fEndFreezeTravel = m_flDeathTime + CS_DEATH_ANIMATION_TIME + spec_freeze_traveltime.GetFloat();
	if ( gpGlobals->curtime < fEndFreezeTravel )
		return;

	m_bAbortFreezeCam = true;
}

//-----------------------------------------------------------------------------
// Purpose: Sets whether this player is dominating the specified other player
//-----------------------------------------------------------------------------
void CCSPlayer::SetPlayerDominated( CCSPlayer *pPlayer, bool bDominated )
{
	int iPlayerIndex = pPlayer->entindex();
	m_bPlayerDominated.Set( iPlayerIndex, bDominated );
	pPlayer->SetPlayerDominatingMe( this, bDominated );
}

//-----------------------------------------------------------------------------
// Purpose: Sets whether this player is being dominated by the other player
//-----------------------------------------------------------------------------
void CCSPlayer::SetPlayerDominatingMe( CCSPlayer *pPlayer, bool bDominated )
{
	int iPlayerIndex = pPlayer->entindex();
	m_bPlayerDominatingMe.Set( iPlayerIndex, bDominated );
}


//-----------------------------------------------------------------------------
// Purpose: Returns whether this player is dominating the specified other player
//-----------------------------------------------------------------------------
bool CCSPlayer::IsPlayerDominated( int iPlayerIndex )
{
	return m_bPlayerDominated.Get( iPlayerIndex );
}

bool CCSPlayer::IsPlayerDominatingMe( int iPlayerIndex )
{
	return m_bPlayerDominatingMe.Get( iPlayerIndex );
}

//=============================================================================
// HPE_BEGIN:
// [menglish] MVP functions
//=============================================================================
 
void CCSPlayer::IncrementNumMVPs( CSMvpReason_t mvpReason )
{
	//=============================================================================
	// HPE_BEGIN:
	// [Forrest] Allow MVP to be turned off for a server
	//=============================================================================
	if ( sv_nomvp.GetBool() )
	{
		Msg( "Round MVP disabled: sv_nomvp is set.\n" );
		return;
	}
	//=============================================================================
	// HPE_END
	//=============================================================================

	m_iMVPs++;
	CCS_GameStats.Event_MVPEarned( this );
	IGameEvent *mvpEvent = gameeventmanager->CreateEvent( "round_mvp" );

	if ( mvpEvent )
	{
		mvpEvent->SetInt( "userid", GetUserID() );
		mvpEvent->SetInt( "reason", mvpReason );
		gameeventmanager->FireEvent( mvpEvent );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Sets the number of rounds this player has caused to be won for their team
//-----------------------------------------------------------------------------
void CCSPlayer::SetNumMVPs( int iNumMVP )
{
	m_iMVPs = iNumMVP;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the number of rounds this player has caused to be won for their team
//-----------------------------------------------------------------------------
int CCSPlayer::GetNumMVPs()
{
	return m_iMVPs;
}
 
//=============================================================================
// HPE_END
//=============================================================================

//-----------------------------------------------------------------------------
// Purpose: Removes all nemesis relationships between this player and others
//-----------------------------------------------------------------------------
void CCSPlayer::RemoveNemesisRelationships()
{
	for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
	{
		CCSPlayer *pTemp = ToCSPlayer( UTIL_PlayerByIndex( i ) );
		if ( pTemp && pTemp != this )
		{        
			// set this player to be not dominating anyone else
			SetPlayerDominated( pTemp, false );

			// set no one else to be dominating this player		
			pTemp->SetPlayerDominated( this, false );
		}
	}	
}

void CCSPlayer::CheckMaxGrenadeKills(int grenadeKills)
{
	if (grenadeKills > m_maxGrenadeKills)
	{
		m_maxGrenadeKills = grenadeKills;
	}
}

void CCSPlayer::CommitSuicide( bool bExplode /*= false*/, bool bForce /*= false*/ )
{
	m_wasNotKilledNaturally = true;
	BaseClass::CommitSuicide(bExplode, bForce);
}

void CCSPlayer::CommitSuicide( const Vector &vecForce, bool bExplode /*= false*/, bool bForce /*= false*/ )
{
	m_wasNotKilledNaturally = true;
	BaseClass::CommitSuicide(vecForce, bExplode, bForce);
}

int CCSPlayer::GetNumEnemyDamagers()
{
	int numberOfEnemyDamagers = 0;
	FOR_EACH_LL( m_DamageTakenList, i )
	{
		for ( int j = 1; j <= MAX_PLAYERS; j++ )
		{
			CBasePlayer *pPlayer = UTIL_PlayerByIndex( j );

			if ( pPlayer && V_strncmp( pPlayer->GetPlayerName(), m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0  &&
				pPlayer->GetTeamNumber() != GetTeamNumber() )
			{
				numberOfEnemyDamagers++;
			}
		}
	}
	return numberOfEnemyDamagers;
}


int CCSPlayer::GetNumEnemiesDamaged()
{
	int numberOfEnemiesDamaged = 0;
	FOR_EACH_LL( m_DamageGivenList, i )
	{
		for ( int j = 1; j <= MAX_PLAYERS; j++ )
		{
			CBasePlayer *pPlayer = UTIL_PlayerByIndex( j );

			if ( pPlayer && V_strncmp( pPlayer->GetPlayerName(), m_DamageGivenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0  &&
				pPlayer->GetTeamNumber() != GetTeamNumber() )
			{
				numberOfEnemiesDamaged++;
			}
		}
	}
	return numberOfEnemiesDamaged;
}

//=============================================================================
// HPE_END
//=============================================================================

void UTIL_AwardMoneyToTeam( int iAmount, int iTeam, CBaseEntity *pIgnore )
{
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );

		if ( !pPlayer )
			continue;

		if ( pPlayer->GetTeamNumber() != iTeam )
			continue;

		if ( pPlayer == pIgnore )
			continue;

		pPlayer->AddAccount( iAmount );
	}
}

