//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=============================================================================
#ifndef TF_PLAYER_H
#define TF_PLAYER_H
#pragma once

#include "basemultiplayerplayer.h"
#include "server_class.h"
#include "tf_achievementdata.h"
#include "tf_playeranimstate.h"
#include "tf_shareddefs.h"
#include "tf_obj.h"
#include "tf_player_shared.h"
#include "tf_playerclass.h"
#include "entity_tfstart.h"
#include "steam/steam_gameserver.h"
#include "ihasattributes.h"
#include "tf_item_inventory.h"

class CTFPlayer;
class CTFTeam;
class CTFGoal;
class CTFGoalItem;
class CTFItem;
class CTFWeaponBuilder;
//class CBaseObject;
class CTFWeaponBase;
class CIntroViewpoint;
class CTriggerAreaCapture;
class CTFWeaponBaseGun;
class CCaptureZone;
class CTFReviveMarker;
class CWaveSpawnPopulator;
class CTFTauntProp;
class CTFDroppedWeapon;

//=============================================================================
//
// Player State Information
//
class CPlayerStateInfo
{
public:

	int				m_nPlayerState;
	const char		*m_pStateName;

	// Enter/Leave state.
	void ( CTFPlayer::*pfnEnterState )();	
	void ( CTFPlayer::*pfnLeaveState )();

	// Think (called every frame).
	void ( CTFPlayer::*pfnThink )();
};

enum EAmmoSource
{
	kAmmoSource_Pickup,					// this came from either a box of ammo or a player's dropped weapon
	kAmmoSource_Resupply,				// resupply cabinet and/or full respawn
	kAmmoSource_DispenserOrCart,		// the player is standing next to an engineer's dispenser or pushing the cart in a payload game
};

//=============================================================================
//
// TF Player
//
class CTFPlayer : public CBaseMultiplayerPlayer, public IHasAttributes, public IInventoryUpdateListener
{
public:
	DECLARE_CLASS( CTFPlayer, CBaseMultiplayerPlayer );
	DECLARE_SERVERCLASS();
	DECLARE_DATADESC();

	CTFPlayer();
	~CTFPlayer();

	//=============================================================================
	// HPE_BEGIN:
	// [msmith]	Added a player type so we can distinguish between bots and humans.
	//=============================================================================
	enum TFPlayerType{
		HUMAN_PLAYER,
		TEMP_BOT,
		TRAINING_BOT
	};
	//=============================================================================
	// HPE_END
	//=============================================================================

	// Creation/Destruction.
	static CTFPlayer	*CreatePlayer( const char *className, edict_t *ed );
	static CTFPlayer	*Instance( int iEnt );

	virtual int			ShouldTransmit( const CCheckTransmitInfo *pInfo );

	virtual void		SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize );
	virtual void		Spawn();
	virtual void		ForceRespawn();
	void				ForceRegenerateAndRespawn( void );
	virtual CBaseEntity	*EntSelectSpawnPoint( void );
	virtual void		InitialSpawn();
	static void			PrecacheMvM();
	static void			PrecacheKart();
private:
	static void			PrecachePlayerModels();
	static void			PrecacheTFPlayer();
public:
	virtual void		Precache();
	virtual bool		IsReadyToPlay( void );
	virtual bool		IsReadyToSpawn( void );
	virtual bool		ShouldGainInstantSpawn( void );
	virtual void		ResetScores( void );
	virtual void		UpdateOnRemove( void );
	void				CheckInstantLoadoutRespawn( void );

	virtual void		ResetPerRoundStats( void );

	void				HandleCommand_JoinTeam( const char *pTeamName );
	void				HandleCommand_JoinClass( const char *pClassName, bool bAllowSpawn = true );
	void				HandleCommand_JoinTeam_NoMenus( const char *pTeamName );

	void				CreateViewModel( int iViewModel = 0 );
	CBaseViewModel		*GetOffHandViewModel();
	void				SendOffHandViewModelActivity( Activity activity );

	virtual void		CheatImpulseCommands( int iImpulse );
	virtual void		PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper );

	virtual void		CommitSuicide( bool bExplode = false, bool bForce = false );

	// Combats
	virtual void		TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
	virtual int			TakeHealth( float flHealth, int bitsDamageType );
	virtual	void		Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
	virtual void		Event_Killed( const CTakeDamageInfo &info );
	virtual void		PlayerDeathThink( void );
	virtual void		DetermineAssistForKill( const CTakeDamageInfo &info );
	virtual void		SetNumberofDominations( int iDominations )
	{
		// Check for some bogus values, which are sneaking in somehow
		if ( iDominations < 0 )
		{
			Assert( iDominations >= 0 );
			iDominations = 0;
		}
		else if ( iDominations >= MAX_PLAYERS )
		{
			Assert( iDominations < MAX_PLAYERS );
			iDominations = MAX_PLAYERS-1;
		}
		m_iNumberofDominations = iDominations;
	}
	virtual int			GetNumberofDominations( void ) { return m_iNumberofDominations; }
	void				OnKilledOther_Effects( CBaseEntity *pVictim, const CTakeDamageInfo &info );

	virtual int			OnTakeDamage( const CTakeDamageInfo &inputInfo );
	void				AddConnectedPlayers( CUtlVector<CTFPlayer*> &vecPlayers, CTFPlayer *pPlayerToConsider );
	virtual int			OnTakeDamage_Alive( const CTakeDamageInfo &info );
	virtual void		DamageEffect(float flDamage, int fDamageType);

	void				OnDealtDamage( CBaseCombatCharacter *pVictim, const CTakeDamageInfo &info );		// invoked when we deal damage to another victim
	int					GetDamagePerSecond( void ) const;
	void				ResetDamagePerSecond( void );

	virtual	bool		ShouldCollide( int collisionGroup, int contentsMask ) const;
	void				ApplyPushFromDamage( const CTakeDamageInfo &info, Vector vecDir );
	void				PlayDamageResistSound( float flStartDamage, float flModifiedDamage );
	bool				CheckBlockBackstab( CTFPlayer *pTFAttacker );

	virtual bool		Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon );

	void				SetHealthBuffTime( float flTime )		{ m_flHealthBuffTime = flTime; }

	CTFWeaponBase		*GetActiveTFWeapon( void ) const;
	bool				IsActiveTFWeapon( const CSchemaItemDefHandle &weaponHandle ) const;
	bool				IsActiveTFWeapon( CEconItemDefinition *weaponHandle ) const;
	virtual void		RemoveAllWeapons();
	virtual void		Weapon_Equip( CBaseCombatWeapon *pWeapon ) OVERRIDE;			// Adds weapon to player

	void				SaveMe( void );


	void				FireBullet( CTFWeaponBase *pWpn, const FireBulletsInfo_t &info, bool bDoEffects, int nDamageType, int nCustomDamageType = TF_DMG_CUSTOM_NONE );
	void				ImpactWaterTrace( trace_t &trace, const Vector &vecStart );
	void				NoteWeaponFired();

	bool				HasItem( void ) const;					// Currently can have only one item at a time.
	void				SetItem( CTFItem *pItem );
	CTFItem				*GetItem( void ) const;
	
	void				SaveLastWeaponSlot( void );
	void				SetRememberLastWeapon( bool bRememberLastWeapon ) { m_bRememberLastWeapon = bRememberLastWeapon; }
	void				SetRememberActiveWeapon( bool bRememberActiveWeapon ) { m_bRememberActiveWeapon = bRememberActiveWeapon; }

	void				Regenerate( bool bRefillHealthAndAmmo = true );
	float				GetNextRegenTime( void ){ return m_flNextRegenerateTime; }
	void				SetNextRegenTime( float flTime ){ m_flNextRegenerateTime = flTime; }

	float				GetNextChangeClassTime( void ){ return m_flNextChangeClassTime; }
	void				SetNextChangeClassTime( float flTime ){ m_flNextChangeClassTime = flTime; }

	float				GetNextChangeTeamTime( void ){ return m_flNextChangeTeamTime; }
	void				SetNextChangeTeamTime( float flTime ){ m_flNextChangeTeamTime = flTime; }

	virtual	void		RemoveAllItems( bool removeSuit );

	virtual bool		BumpWeapon( CBaseCombatWeapon *pWeapon );
	bool				DropCurrentWeapon( void );
	void				DropFlag( bool bSilent = false );
	void				DropRune( bool bApplyForce = true, int nTeam = TEAM_ANY );
	void				TFWeaponRemove( int iWeaponID );
	bool				TFWeaponDrop( CTFWeaponBase *pWeapon, bool bThrowForward );

	// Class.
	CTFPlayerClass		 *GetPlayerClass( void ) 					{ return &m_PlayerClass; }
	const CTFPlayerClass *GetPlayerClass( void ) const				{ return &m_PlayerClass; }
	int					GetDesiredPlayerClassIndex( void )			{ return m_Shared.m_iDesiredPlayerClass; }
	void				SetDesiredPlayerClassIndex( int iClass )	{ m_Shared.m_iDesiredPlayerClass = iClass; }

	// Team.
	void				ForceChangeTeam( int iTeamNum, bool bFullTeamSwitch = false );
	virtual void		ChangeTeam( int iTeamNum, bool bAutoTeam, bool bSilent, bool bAutoBalance = false ) OVERRIDE;
	virtual void		ChangeTeam( int iTeamNum ) OVERRIDE { BaseClass::ChangeTeam( iTeamNum ); }

	// mp_fadetoblack
	void				HandleFadeToBlack( void );

	// Flashlight controls for SFM - JasonM
	virtual int FlashlightIsOn( void );
	virtual void FlashlightTurnOn( void );
	virtual void FlashlightTurnOff( void );

	// Think.
	virtual void		PreThink();
	virtual void		PostThink();

	virtual void		ItemPostFrame();
	virtual void		Weapon_FrameUpdate( void );
	virtual void		Weapon_HandleAnimEvent( animevent_t *pEvent );
	virtual bool		Weapon_ShouldSetLast( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon );

	virtual void		GetStepSoundVelocities( float *velwalk, float *velrun );
	virtual void		SetStepSoundTime( stepsoundtimes_t iStepSoundTime, bool bWalking );
	virtual const char *GetOverrideStepSound( const char *pszBaseStepSoundName );

	virtual void		OnEmitFootstepSound( const CSoundParameters& params, const Vector& vecOrigin, float fVolume );

	virtual void		ModifyEmitSoundParams( EmitSound_t &params );

	// Utility.
	void				UpdateModel( void );
	void				UpdateSkin( int iTeam );

	int					GiveAmmo( int iCount, int iAmmoIndex, bool bSuppressSound, EAmmoSource eAmmoSource );
	virtual int			GiveAmmo( int iCount, int iAmmoIndex, bool bSuppressSound = false );
	virtual void		RemoveAmmo( int iCount, int iAmmoIndex );
	virtual void		RemoveAmmo( int iCount, const char *szName );
	virtual int			GetAmmoCount( int iAmmoIndex ) const;
	int					GetMaxAmmo( int iAmmoIndex, int iClassIndex = -1 );
	virtual int			GetMaxHealth()  const OVERRIDE;
	int					GetMaxHealthForBuffing()  const;
	int					GetRuneHealthBonus() const;

	//-----------------------------------------------------------------------------------------------------
	// Return true if we are a "mini boss" in Mann Vs Machine mode
	bool				IsMiniBoss( void ) const;
	void				SetIsMiniBoss( bool isMiniBoss ) { m_bIsMiniBoss = isMiniBoss; }

	bool				CanAttack( int iCanAttackFlags = 0 );

	void				RemoveMeleeCrit( void );

	// This passes the event to the client's and server's CPlayerAnimState.
	void				DoAnimationEvent( PlayerAnimEvent_t event, int mData = 0 );

	virtual void		HandleAnimEvent( animevent_t *pEvent ) OVERRIDE;

	virtual bool		ClientCommand( const CCommand &args );
	void				ClientHearVox( const char *pSentence );
	void				DisplayLocalItemStatus( CTFGoal *pGoal );

	int					BuildObservableEntityList( void );
	virtual int			GetNextObserverSearchStartPoint( bool bReverse ); // Where we should start looping the player list in a FindNextObserverTarget call
	virtual CBaseEntity *FindNextObserverTarget(bool bReverse);
	virtual bool		IsValidObserverTarget(CBaseEntity * target); // true, if player is allowed to see this target
	virtual bool		SetObserverTarget(CBaseEntity * target);
	virtual bool		ModeWantsSpectatorGUI( int iMode ) { return (iMode != OBS_MODE_FREEZECAM && iMode != OBS_MODE_DEATHCAM); }
	void				FindInitialObserverTarget( void );
	CBaseEntity		    *FindNearestObservableTarget( Vector vecOrigin, float flMaxDist );
	virtual void		ValidateCurrentObserverTarget( void );

	void CheckUncoveringSpies( CTFPlayer *pTouchedPlayer );
	void Touch( CBaseEntity *pOther );

	virtual	void RefreshCollisionBounds( void );

	float GetMovementForwardPull( void ) const;
	bool CanPlayerMove() const;
	float TeamFortress_CalculateMaxSpeed( bool bIgnoreSpecialAbility = false ) const;
	void TeamFortress_SetSpeed();
	EHANDLE TeamFortress_GetDisguiseTarget( int nTeam, int nClass );

	void TeamFortress_ClientDisconnected();
	void RemoveAllOwnedEntitiesFromWorld( bool bExplodeBuildings = false );
	void RemoveOwnedProjectiles();
	int GetNumActivePipebombs( void );

	Vector EstimateProjectileImpactPosition( CTFWeaponBaseGun *weapon );				// estimate where a projectile fired from the given weapon will initially hit (it may bounce on from there)
	Vector EstimateProjectileImpactPosition( float pitch, float yaw, float initVel );	// estimate where a projectile fired will initially hit (it may bounce on from there)
	Vector EstimateStickybombProjectileImpactPosition( float pitch, float yaw, float charge );	// Estimate where a stickybomb projectile will hit, using given pitch, yaw, and weapon charge (0-1)

	CTFTeamSpawn *GetSpawnPoint( void ){ return m_pSpawnPoint; }
		
	void SetAnimation( PLAYER_ANIM playerAnim );

	bool IsPlayerClass( int iClass ) const;

	void PlayFlinch( const CTakeDamageInfo &info );

	float PlayCritReceivedSound( void );
	void PainSound( const CTakeDamageInfo &info );
	void DeathSound( const CTakeDamageInfo &info );
	virtual const char* GetSceneSoundToken( void );
	void StunSound( CTFPlayer* pAttacker, int iStunFlags, int iOldStunFlags=0 );

	void SetSeeCrit( bool bAllSeeCrit, bool bMiniCrit, bool bShowDisguisedCrit ) { m_bAllSeeCrit = bAllSeeCrit; m_bMiniCrit = bMiniCrit; m_bShowDisguisedCrit = bShowDisguisedCrit;  }
	void SetAttackBonusEffect( EAttackBonusEffects_t effect ) { m_eBonusAttackEffect = effect; }
	EAttackBonusEffects_t GetAttackBonusEffect( void ) { return m_eBonusAttackEffect; }

	// TF doesn't want the explosion ringing sound
	virtual void OnDamagedByExplosion( const CTakeDamageInfo &info ) { return; }

	void OnBurnOther( CTFPlayer *pTFPlayerVictim, CTFWeaponBase *pWeapon );

	// Buildables
	void SetWeaponBuilder( CTFWeaponBuilder *pBuilder );
	CTFWeaponBuilder *GetWeaponBuilder( void );

	int GetBuildResources( void );
	void RemoveBuildResources( int iAmount );
	void AddBuildResources( int iAmount );

	bool IsBuilding( void );
	int CanBuild( int iObjectType, int iObjectMode = 0 );

	CBaseObject	*GetObject( int index ) const;
	CBaseObject	*GetObjectOfType( int iObjectType, int iObjectMode = 0 ) const;
	int	GetObjectCount( void ) const;
	int GetNumObjects( int iObjectType, int iObjectMode = 0 );
	void RemoveAllObjects( bool bExplodeBuildings = false );
	void StopPlacement( void );
	int	StartedBuildingObject( int iObjectType );
	void StoppedBuilding( int iObjectType );
	void FinishedObject( CBaseObject *pObject );
	void AddObject( CBaseObject *pObject );
	void OwnedObjectDestroyed( CBaseObject *pObject );
	void RemoveObject( CBaseObject *pObject );
	bool PlayerOwnsObject( CBaseObject *pObject );
	void DetonateObjectOfType( int iObjectType, int iObjectMode = 0, bool bIgnoreSapperState = false );
	void StartBuildingObjectOfType( int iType, int iObjectMode = 0 );
	float GetObjectBuildSpeedMultiplier( int iObjectType, bool bIsRedeploy ) const;

	void OnSapperPlaced( CBaseEntity *sappedObject );			// invoked when we place a sapper on an enemy building
	bool IsPlacingSapper( void ) const;							// return true if we are a spy who placed a sapper on a building in the last few moments
	void OnSapperStarted( float flStartTime );
	void OnSapperFinished( float flStartTime );
	bool IsSapping( void ) const;
	int GetSappingEvent( void) const;
	void ClearSappingEvent( void );
	void ClearSappingTracking( void );

	CTFTeam *GetTFTeam( void );
	CTFTeam *GetOpposingTFTeam( void );

	void TeleportEffect( void );
	void RemoveTeleportEffect( void );
	bool HasTheFlag( ETFFlagType exceptionTypes[] = NULL, int nNumExceptions = 0 ) const;
	virtual bool IsAllowedToPickUpFlag( void ) const;

	// Death & Ragdolls.
	virtual void CreateRagdollEntity( void );
	void CreateRagdollEntity( bool bGib, bool bBurning, bool bElectrocuted, bool bOnGround, bool bCloakedCorpse, bool bGoldRagdoll, bool bIceRagdoll, bool bBecomeAsh, int iDamageCustom = 0, bool bCritOnHardHit = false );
	void DestroyRagdoll( void );
	CNetworkHandle( CBaseEntity, m_hRagdoll );	// networked entity handle 
	virtual bool ShouldGib( const CTakeDamageInfo &info ) OVERRIDE;
	bool HasBombinomiconEffectOnDeath( void );
	void StopRagdollDeathAnim( void );
	void SetGibbedOnLastDeath( bool bGibbed ) { m_bGibbedOnLastDeath = bGibbed; }
	bool WasGibbedOnLastDeath( void ) { return m_bGibbedOnLastDeath; }

	// Feign Death
	void SpyDeadRingerDeath( const CTakeDamageInfo& info );
	void FeignDeath( const CTakeDamageInfo& info );
	void CreateFeignDeathRagdoll( const CTakeDamageInfo& info, bool bGib, bool bBurning, bool bDisguised );

	// Dropping Ammo
	bool ShouldDropAmmoPack( void );
	void DropAmmoPack( const CTakeDamageInfo &info, bool bEmpty, bool bDisguisedWeapon );
	void DropExtraAmmo( const CTakeDamageInfo& info, bool bFromDeath = false );
	void DropHealthPack( const CTakeDamageInfo &info, bool bEmpty );
	void DropCurrencyPack( CurrencyRewards_t nSize = TF_CURRENCY_PACK_SMALL, int nAmount = 0, bool bForceDistribute = false, CBasePlayer* pMoneyMaker = NULL );	// Only pass in an amount when nSize = TF_CURRENCY_PACK_CUSTOM
	
	bool CanDisguise( void );
	bool CanDisguise_OnKill( void );
	bool CanGoInvisible( bool bAllowWhileCarryingFlag = false );
	void RemoveInvisibility( void );

	bool CanStartPhase( void );

	void RemoveDisguise( void );

	bool DoClassSpecialSkill( void );
	bool EndClassSpecialSkill( void );

	bool	CanPickupBuilding( CBaseObject *pPickupObject );
	bool TryToPickupBuilding( void );

	float GetLastDamageReceivedTime( void ) { return m_flLastDamageTime; }
	float GetLastEntityDamagedTime( void ) { return m_flLastDamageDoneTime; }
	void SetLastEntityDamagedTime( float flTime ) { m_flLastDamageDoneTime = flTime; }
	CBaseEntity *GetLastEntityDamaged( void ) { return m_hLastDamageDoneEntity; }
	void SetLastEntityDamaged( CBaseEntity *pEnt ) { m_hLastDamageDoneEntity = pEnt; }

	void SetClassMenuOpen( bool bIsOpen );
	bool IsClassMenuOpen( void );

	float GetCritMult( void ) { return m_Shared.GetCritMult(); }
	void  RecordDamageEvent( const CTakeDamageInfo &info, bool bKill, int nVictimPrevHealth ) { m_Shared.RecordDamageEvent(info,bKill,nVictimPrevHealth); }

	bool GetHudClassAutoKill( void ){ return m_bHudClassAutoKill; }
	void SetHudClassAutoKill( bool bAutoKill ){ m_bHudClassAutoKill = bAutoKill; }

	bool GetMedigunAutoHeal( void ){ return m_bMedigunAutoHeal; }
	void SetMedigunAutoHeal( bool bMedigunAutoHeal ){ m_bMedigunAutoHeal = bMedigunAutoHeal; }
	CBaseEntity		*MedicGetHealTarget( void );
	float			MedicGetChargeLevel( CTFWeaponBase **pRetMedigun = NULL );
	bool IsCallingForMedic( void ) const;			// return true if this player has called for a Medic in the last few seconds
	float GetTimeSinceCalledForMedic( void ) const;
	void NoteMedicCall( void );

	bool ShouldAutoRezoom( void ) { return m_bAutoRezoom; }
	void SetAutoRezoom( bool bAutoRezoom ) { m_bAutoRezoom = bAutoRezoom; }
	bool ShouldAutoReload( void ){ return m_bAutoReload; }
	void SetAutoReload( bool bAutoReload ) { m_bAutoReload = bAutoReload; }

	virtual void	ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet );

	virtual bool CanHearAndReadChatFrom( CBasePlayer *pPlayer );
	virtual bool CanBeAutobalanced();

	Vector 	GetClassEyeHeight( void );

	void	UpdateExpression( void );
	void	ClearExpression( void );

	virtual IResponseSystem *GetResponseSystem();
	virtual bool			SpeakConceptIfAllowed( int iConcept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );

	virtual bool CanSpeakVoiceCommand( void );
	virtual bool ShouldShowVoiceSubtitleToEnemy( void );
	virtual void NoteSpokeVoiceCommand( const char *pszScenePlayed );
	void	SpeakWeaponFire( int iCustomConcept = MP_CONCEPT_NONE );
	void	ClearWeaponFireScene( void );

	virtual int DrawDebugTextOverlays( void );

	float m_flNextVoiceCommandTime;
	int m_iVoiceSpamCounter;

	float m_flNextSpeakWeaponFire;

	virtual int	CalculateTeamBalanceScore( void );

	bool ShouldAnnounceAchievement( void ) OVERRIDE;
	virtual void OnAchievementEarned( int iAchievement );

	void CheckObserverSettings(); // checks, if target still valid (didn't die etc)

	CTriggerAreaCapture *GetControlPointStandingOn( void );
	CCaptureZone *GetCaptureZoneStandingOn( void );
	CCaptureZone *GetClosestCaptureZone( void );

	// given a vector of points, return the point we can actually travel to the quickest (requires a nav mesh)
	CTeamControlPoint *SelectClosestControlPointByTravelDistance( CUtlVector< CTeamControlPoint * > *pointVector ) const;

	bool CanAirDash( void ) const;

	virtual bool CanBreatheUnderwater() const OVERRIDE;
	bool CanGetWet() const;

	virtual bool IsDeflectable() { return true; }
	//=============================================================================
	// HPE_BEGIN:
	// [msmith]	Added a player type so we can distinguish between bots and humans.
	//=============================================================================
	inline TFPlayerType GetPlayerType(){ return m_playerType; }
	inline void SetPlayerType( TFPlayerType playerType ){ m_playerType = playerType; }
	//=============================================================================
	// HPE_END
	//=============================================================================

	virtual void OnNavAreaChanged( CNavArea *enteredArea, CNavArea *leftArea );	// invoked (by UpdateLastKnownArea) when we enter a new nav area (or it is reset to NULL)

	bool IsThreatAimingTowardMe( CBaseEntity *threat, float cosTolerance = 0.8f ) const;	// return true if the given threat is aiming in our direction
	bool IsThreatFiringAtMe( CBaseEntity *threat ) const;		// return true if the given threat is aiming in our direction and firing its weapon
	bool IsInCombat( void ) const;								// return true if we are engaged in active combat

	void PlayerUse( void );

	void InputIgnitePlayer( inputdata_t &inputdata );
	void InputSetCustomModel( inputdata_t &inputdata );
	void InputSetCustomModelOffset( inputdata_t &inputdata );
	void InputSetCustomModelRotation( inputdata_t &inputdata );
	void InputClearCustomModelRotation( inputdata_t &inputdata );
	void InputSetCustomModelRotates( inputdata_t &inputdata );
	void InputSetCustomModelVisibleToSelf( inputdata_t &inputdata );
	void InputSetForcedTauntCam( inputdata_t &inputdata );
	void InputExtinguishPlayer( inputdata_t &inputdata );
	void InputBleedPlayer( inputdata_t &inputdata );
	void InputTriggerLootIslandAchievement( inputdata_t &inputdata );
	void InputTriggerLootIslandAchievement2( inputdata_t &inputdata );
	void InputRollRareSpell( inputdata_t &inputdata );
	void InputRoundSpawn( inputdata_t &inputdata );

	bool InAirDueToExplosion( void ) { return (!(GetFlags() & FL_ONGROUND) && (GetWaterLevel() == WL_NotInWater) && (m_iBlastJumpState != 0) ); }
	bool InAirDueToKnockback( void ) { return (!(GetFlags() & FL_ONGROUND) && (GetWaterLevel() == WL_NotInWater) && ( (m_iBlastJumpState != 0) || m_Shared.InCond( TF_COND_KNOCKED_INTO_AIR ) || m_Shared.InCond( TF_COND_GRAPPLINGHOOK ) || m_Shared.InCond( TF_COND_GRAPPLINGHOOK_SAFEFALL ) ) ); }

	bool IsCoaching() const { return m_bIsCoaching; }
	void SetIsCoaching( bool bIsCoaching );

	void SetCoach( CTFPlayer *pCoach ) { m_hCoach = pCoach; }
	CTFPlayer* GetCoach() const { return m_hCoach; }

	void SetStudent( CTFPlayer *pStudent ) { m_hStudent = pStudent; }
	CTFPlayer* GetStudent() const { return m_hStudent; }

	void DoNoiseMaker(); // Halloween event item support.

	bool IsWormsGearEquipped( void ) const;
	bool IsRobotCostumeEquipped( void ) const;
	bool IsDemowolf( void ) const;
	bool IsFrankenHeavy( void ) const;
	bool IsFairyHeavy( void ) const;
	bool IsZombieCostumeEquipped( void ) const;
	bool HasWearablesEquipped( const CSchemaItemDefHandle *ppItemDefs, int nWearables ) const;

	CEconItemView *GetEquippedItemForLoadoutSlot( int iLoadoutSlot ){ return m_Inventory.GetInventoryItemByItemID( m_EquippedLoadoutItemIndices[iLoadoutSlot] ); }
	CBaseEntity *GetEntityForLoadoutSlot( int iLoadoutSlot );			//Gets whatever entity is associated with the loadout slot (wearable or weapon)
	CTFWearable *GetEquippedWearableForLoadoutSlot( int iLoadoutSlot );

	//Base entity overrides
	// Functions that intercept Base Calls for Attribute Checking
	void ApplyAbsVelocityImpulse ( const Vector &vecImpulse );
	bool ApplyPunchImpulseX ( float flImpulse );
	void ApplyAirBlastImpulse( const Vector &vecImpulse );

	void SetUseBossHealthBar( bool bUseBossHealthBar ) { m_bUseBossHealthBar = bUseBossHealthBar; }

	void SetUsingVRHeadset( bool bState ){ m_bUsingVRHeadset = bState; }

	static bool m_bTFPlayerNeedsPrecache;

	// IHasAttributes
	CAttributeManager		*GetAttributeManager( void ) { return &m_AttributeManager; }
	CAttributeContainer		*GetAttributeContainer( void ) { return NULL; }
	CBaseEntity				*GetAttributeOwner( void ) { return NULL; }
	CAttributeList			*GetAttributeList( void ) { return &m_AttributeList; }
	virtual void			ReapplyProvision( void ) { return; }

protected:
	CNetworkVarEmbedded( CAttributeContainerPlayer, m_AttributeManager );

	//----------------------------
	// INVENTORY MANAGEMENT
public:
	// IInventoryUpdateListener
	virtual void InventoryUpdated( CPlayerInventory *pInventory );

	virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner ) { m_Shared.SetLoadoutUnavailable( true ); }

	// Inventory access
	CTFPlayerInventory	*Inventory( void ) { return &m_Inventory; }

private:
	CTFPlayerInventory			m_Inventory;
	// Items that have been equipped on this player instance (the inventory loadout may have changed)
	itemid_t				m_EquippedLoadoutItemIndices[CLASS_LOADOUT_POSITION_COUNT];

public:
	void		UpdateInventory( bool bInit );
	void		VerifySOCache();

	CNetworkVarEmbedded( CTFPlayerShared, m_Shared );
	friend class CTFPlayerShared;

	int m_flNextTimeCheck;		// Next time the player can execute a "timeleft" command

	CNetworkVar( bool, m_bSaveMeParity );
	
	CNetworkVar( bool, m_bIsCoaching);
	CNetworkHandle( CTFPlayer, m_hCoach );
	CNetworkHandle( CTFPlayer, m_hStudent );
	float	m_flLastCoachCommand;

	CNetworkVar( bool, m_bIsABot );
	CNetworkVar( int, m_nBotSkill );

	int					StateGet( void ) const;

	void				SetOffHandWeapon( CTFWeaponBase *pWeapon );
	void				HolsterOffHandWeapon( void );
	CTFWeaponBase*		GetOffHandWeapon( void ) { return m_hOffHandWeapon; }

	float				GetSpawnTime() { return m_flSpawnTime; }

	
	virtual void 		SelectItem( const char *pstr, int iSubType = 0 ) OVERRIDE;
	virtual bool		Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex = 0 ) OVERRIDE;
	virtual void		Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget , const Vector *pVelocity ) OVERRIDE;

	virtual void		OnMyWeaponFired( CBaseCombatWeapon *weapon );	// call this when this player fires a weapon to allow other systems to react

	bool				ItemsMatch( TFPlayerClassData_t *pData, CEconItemView *pCurWeaponItem, CEconItemView *pNewWeaponItem, CTFWeaponBase *pWpnEntity = NULL );
	void				ManageRegularWeapons( TFPlayerClassData_t *pData );
	void				ManageRegularWeaponsLegacy( TFPlayerClassData_t *pData );	// Older, pre-inventory method of managing regular weapons
	void				ManageBuilderWeapons( TFPlayerClassData_t *pData );
	virtual CBaseEntity	*GiveNamedItem( const char *szName, int iSubType = 0, const CEconItemView *pScriptItem = NULL, bool bForce = false );
	void				PostInventoryApplication( void );
	bool				ItemIsAllowed( CEconItemView *pItem );
	void				RemovePlayerAttributes( bool bSetBonuses );
	void				ApplySetBonuses( void );
	void				GetActiveSets( CUtlVector<const CEconItemSetDefinition *> *pItemSets );
	void				ValidateWeapons(  TFPlayerClassData_t *pData, bool bResetWeapons );
	void				ValidateWearables( TFPlayerClassData_t *pData );
	CEconItemView* GetLoadoutItem( int iClass, int iSlot, bool bReportWhitelistFails = false );
	void				UseActionSlotItemPressed( void );
	void				UseActionSlotItemReleased( void );

	bool				IsFireproof( void ) const;
	bool				AddToSpyKnife( float value, bool force );

private:
	void				GetHorriblyHackedRailgunPosition( const Vector& vStart, Vector *out_pvStartPos );
	void				MaybeDrawRailgunBeam( IRecipientFilter *pFilter, CTFWeaponBase *pWeapon, const Vector& vStartPos, const Vector& vEndPos );

// Taunts
public:
	bool				IsReadyToTauntWithPartner( void ) const { return m_bIsReadyToHighFive; }
	CTFPlayer *			GetTauntPartner( void )		{ return m_hHighFivePartner; }
	float				GetTauntYaw( void )				{ return m_flTauntYaw; }
	float				GetPrevTauntYaw( void )		{ return m_flPrevTauntYaw; }
	void				SetTauntYaw( float flTauntYaw );
	CTFPlayer *			FindPartnerTauntInitiator( void );
	void				AcceptTauntWithPartner( CTFPlayer *initiator );
	void				MimicTauntFromPartner( CTFPlayer *initiator );
	bool				CanMoveDuringTaunt();
	bool				ShouldStopTaunting();
	bool				IsTauntInitiator() const { return m_bIsTauntInitiator; }
	bool				IsTauntForceMovingForward() const { return m_bTauntForceMoveForward; }
	float				GetTauntMoveAcceleration() const { return m_flTauntMoveAccelerationTime; }
	float				GetTauntMoveSpeed() const { return m_flTauntForceMoveForwardSpeed; }
	float				GetTauntTurnAccelerationTime() const { return m_flTauntTurnAccelerationTime; }
	virtual int			GetAllowedTauntPartnerTeam() const;
	CEconItemView		*GetTauntEconItemView() { return m_TauntEconItemView.IsValid() ? &m_TauntEconItemView : NULL; }

	int					GetTauntConcept( CEconItemDefinition *pItemDef );
	bool				PlayTauntSceneFromItem( CEconItemView *pEconItemView );
	
	void				OnTauntSucceeded( const char* pszSceneName, int iTauntIndex = 0, int iTauntConcept = 0 );
	void				Taunt( taunts_t iTauntIndex = TAUNT_BASE_WEAPON, int iTauntConcept = 0 );
	bool				IsTaunting( void ) const { return m_Shared.InCond( TF_COND_TAUNTING ); }
	void				DoTauntAttack( void );
	bool				IsAllowedToTaunt( void );
	bool				FindOpenTauntPartnerPosition( CEconItemView *pEconItemView, Vector &position, float *flTolerance );
	bool				IsAllowedToInitiateTauntWithPartner( CEconItemView *pEconItemView, char *pszErrorMessage = NULL, int cubErrorMessage = 0 );
	void				CancelTaunt( void );
	void				StopTaunt( void );
	void				EndLongTaunt();
	float				GetTauntRemoveTime( void ) const { return m_flTauntRemoveTime; }
	bool				IsAllowedToRemoveTaunt() const { return m_bAllowedToRemoveTaunt; }
	void				HandleTauntCommand( int iTauntSlot = 0 );
	QAngle				m_angTauntCamera;

	CHandle< CBaseEntity > m_hTauntItem;

	void				ClearTauntAttack();
	float				GetTauntAttackTime() const { return m_flTauntAttackTime; }

	void				SetRPSResult( int iRPSResult ) { m_iTauntRPSResult = iRPSResult; }

	void				HandleWeaponSlotAfterTaunt();

	float				GetCurrentTauntMoveSpeed() const { return m_flCurrentTauntMoveSpeed; }
	void				SetCurrentTauntMoveSpeed( float flSpeed ) { m_flCurrentTauntMoveSpeed = flSpeed; }

	float				GetVehicleReverseTime() const { return m_flVehicleReverseTime; }
	void				SetVehicleReverseTime( float flTime ) { m_flVehicleReverseTime = flTime; }

private:
	void				GetReadyToTauntWithPartner( void );
	void				CancelTauntWithPartner( void );
	void				StopTauntSoundLoop();
	float				PlayTauntOutroScene();
	float				PlayTauntRemapInputScene();
	void				ParseSharedTauntDataFromEconItemView( CEconItemView *pEconItemView );

	CNetworkVar( bool, m_bAllowMoveDuringTaunt );
	CNetworkVar( bool, m_bIsReadyToHighFive );
	CNetworkHandle( CTFPlayer, m_hHighFivePartner );
	CNetworkVar( int, m_nForceTauntCam );
	CNetworkVar( float, m_flTauntYaw );
	CNetworkVar( int, m_nActiveTauntSlot );
	CNetworkVar( item_definition_index_t, m_iTauntItemDefIndex );
	CNetworkVar( float, m_flCurrentTauntMoveSpeed );
	CNetworkVar( float, m_flVehicleReverseTime );

	bool				m_bTauntForceMoveForward;
	float				m_flTauntForceMoveForwardSpeed;
	float				m_flTauntMoveAccelerationTime;
	float				m_flTauntTurnSpeed;
	float				m_flTauntTurnAccelerationTime;

	float				m_flPrevTauntYaw;
	EHANDLE				m_hTauntScene;
	CHandle< CTFTauntProp >	m_hTauntProp;
	bool				m_bInitTaunt;
	bool				m_bTauntMimic;
	bool				m_bIsTauntInitiator;
	float				m_flTauntSoundTime;
	CUtlString			m_strTauntSoundName;
	float				m_flTauntSoundLoopTime;
	CUtlString			m_strTauntSoundLoopName;
	CEconItemView		m_TauntEconItemView;

	enum TauntStage_t
	{
		TAUNT_NONE = 0,
		TAUNT_INTRO,
		TAUNT_OUTRO
	} m_TauntStage;

	bool				m_bAllowedToRemoveTaunt;
	float				m_flTauntStartTime;
	float				m_flTauntRemoveTime;
	float				m_flTauntOutroTime;
	Vector				m_vecTauntStartPosition;

	float				m_flNextAllowTauntRemapInputTime;
	float				m_flTauntAttackTime;
	float				m_flTauntInhaleTime;
	taunt_attack_t		m_iTauntAttack;
	int					m_iTauntAttackCount;
	int					m_iTauntRPSResult;
	int					m_iPreTauntWeaponSlot;
	int					m_iPreTauntFOV;

	float				m_flNextReflectZap;

public:
	virtual int			GetSpecialDSP( void );

	virtual float		PlayScene( const char *pszScene, float flDelay = 0.0f, AI_Response *response = NULL, IRecipientFilter *filter = NULL );
	void				SetDeathFlags( int iDeathFlags ) { m_iDeathFlags = iDeathFlags; }
	int					GetDeathFlags() { return m_iDeathFlags; }
	void				SetMaxSentryKills( int iMaxSentryKills ) { m_iMaxSentryKills = iMaxSentryKills; }
	int					GetMaxSentryKills() { return m_iMaxSentryKills; }

	CNetworkVar( bool, m_iSpawnCounter );
	
	void				CheckForIdle( void );
	inline bool			IsAwayFromKeyboard( void ) { return m_bIsAFK; }
	
	void				PickWelcomeObserverPoint();

	virtual	bool		ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );

	void				StopRandomExpressions( void ) { m_flNextRandomExpressionTime = -1; }
	void				StartRandomExpressions( void ) { m_flNextRandomExpressionTime = gpGlobals->curtime; }

	virtual bool			WantsLagCompensationOnEntity( const CBasePlayer	*pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const;

	CTFWeaponBase		*Weapon_OwnsThisID( int iWeaponID ) const;
	CTFWeaponBase		*Weapon_GetWeaponByType( int iType );

	medigun_charge_types	GetChargeEffectBeingProvided( void );

	// Achievements
	void				AwardAchievement( int iAchievement, int iCount = 1 );
	void				HandleAchievement_Medic_AssistHeavy( CTFPlayer *pPunchVictim );
	void				HandleAchievement_Pyro_BurnFromBehind( CTFPlayer *pBurner );

	void				ClearPunchVictims( void ) { m_aPunchVictims.RemoveAll(); }
	void				ClearBurnFromBehindAttackers( void ) { m_aBurnFromBackAttackers.RemoveAll(); }

	int					RocketJumped( void ) { return m_iBlastJumpState & TF_PLAYER_ROCKET_JUMPED; }
	int					StickyJumped( void ) { return m_iBlastJumpState & TF_PLAYER_STICKY_JUMPED; }
	void				SetBlastJumpState( int iState, bool bPlaySound = false );
	void				ClearBlastJumpState( void );

	int					GetPreviousTeam( void ) { return m_iPreviousteam; }
	bool				IsArenaSpectator( void ) { return m_bArenaSpectator; }

	float				GetTeamJoinTime( void ) { return m_flTeamJoinTime; }
	void				MarkTeamJoinTime( void ) { m_flTeamJoinTime = gpGlobals->curtime; }
	void				PlayerJustPlayed( bool bPlayed ) { m_bJustPlayed = bPlayed; }
	bool				DidPlayerJustPlay( void ) { return m_bJustPlayed; }

	bool				IsCapturingPoint( void );

	bool				m_bSuicideExplode;

	bool				m_bScattergunJump;
	int					m_iOldStunFlags;

	bool				m_bFlipViewModels;
	int					m_iBlastJumpState;
	float				m_flBlastJumpLandTime;
	bool				m_bTakenBlastDamageSinceLastMovement;

	void				SetTargetDummy( void ){ m_bIsTargetDummy = true; }

	bool				ShouldCollideWithSentry( void ){ return m_bCollideWithSentry; }
	bool				IsAnyEnemySentryAbleToAttackMe( void ) const;		// return true if any enemy sentry has LOS and is facing me and is in range to attack

	int					GetHealthBefore( void ) { return m_iHealthBefore; }

	int					GetAutoTeam( int nPreferedTeam = TF_TEAM_AUTOASSIGN );
	bool				ShouldForceAutoTeam( void );

	float				m_flCommentOnCarrying;

	int					GetTeamChangeCount( void ) { return m_iTeamChanges; }
	int					GetClassChangeCount( void ) { return m_iClassChanges; }

	void				IncrementKillCountSinceLastDeploy( const CTakeDamageInfo &info );

	void				ForceItemRemovalOnRespawn( void ) { m_bForceItemRemovalOnRespawn = true; }

	// Item Testing
public:
	void				ItemTesting_Start( KeyValues *pKV );
	void				ItemTesting_UpdateBots( KeyValues *pKV );
	CEconItemView	*ItemTesting_GetTestItem( int iClass, int iSlot );
	void				ItemTesting_DeleteItems();

public:
	struct itemtest_t
	{
		KeyValues			*pKV;
		CEconItemView	scriptItem;
	};
	CUtlVector<itemtest_t>	m_ItemsToTest;
	bool					m_bItemTestingRespawn;

	bool				IsMissionEnemy( void ){ return m_bIsMissionEnemy; }
	void 				MarkAsMissionEnemy( void ){ m_bIsMissionEnemy = true; }
	bool				IsSupportEnemy( void ){ return m_bIsSupportEnemy; }
	void 				MarkAsSupportEnemy( void ){ m_bIsSupportEnemy = true; }
	void 				MarkAsLimitedSupportEnemy( void ){ m_bIsLimitedSupportEnemy = true; }

	// In-game currency
	int					GetCurrency( void ) const { return m_nCurrency; }
	void				SetCurrency( int nAmount ){ m_nCurrency = nAmount; }
	void				AddCurrency( int nAmount );
	void				RemoveCurrency( int nAmount );

	// Set the amount of money this bot is worth when killed. We re-use m_nCurrency, because bots don't collect currency.
	void				SetCustomCurrencyWorth( int nAmount )	{ m_nCurrency = nAmount; }

	// Bounty Mode
	int					GetExperienceLevel( void ) { return m_nExperienceLevel; }
	void				SetExperienceLevel( int nValue ) { m_nExperienceLevel.Set( MAX( nValue, 1 ) ); }
	int					GetExperiencePoints( void ) { return m_nExperiencePoints; }
	void				SetExperiencePoints( int nValue ) { m_nExperiencePoints = MAX( nValue, 0 ); }
	void				AddExperiencePoints( int nValue, bool bGiveCurrency = false, CTFPlayer *pSource = NULL );
	void				CalculateExperienceLevel( bool bAnnounce = true );
	void				RefundExperiencePoints( void );

	void				RememberUpgrade( int iPlayerClass, CEconItemView *pItem, int iUpgrade, int nCost, bool bDowngrade = false );	// store this upgrade for restoring at a checkpoint
	void				ForgetFirstUpgradeForItem( CEconItemView *pItem );						// erase the first upgrade stored for this item (for powerup bottles)
	void				ClearUpgradeHistory( void );
	void				ReapplyItemUpgrades ( CEconItemView *pItem );
	void				ReapplyPlayerUpgrades ( void );
	void				SetWaveSpawnPopulator( CWaveSpawnPopulator *pWave ){ m_pWaveSpawnPopulator = pWave; }
	CUtlVector< CUpgradeInfo >* GetRefundableUpgrades( void ) { return &m_RefundableUpgrades; }
	void				ResetRefundableUpgrades( void ) { m_RefundableUpgrades.RemoveAll(); }
	void				BeginPurchasableUpgrades( void );
	void				EndPurchasableUpgrades( void );
	bool				CanPurchaseUpgrades( void ) const { Assert( m_nCanPurchaseUpgradesCount >= 0 ); return m_nCanPurchaseUpgradesCount > 0; }

	void				PlayReadySound( void );

	void				AccumulateSentryGunDamageDealt( float damage );
	void				ResetAccumulatedSentryGunDamageDealt();
	float				GetAccumulatedSentryGunDamageDealt();

	void				IncrementSentryGunKillCount( void );
	void				ResetAccumulatedSentryGunKillCount();
	int					GetAccumulatedSentryGunKillCount();

	bool				PlaySpecificSequence( const char *pSequenceName );

	void				SetWaterExitTime( float flTime ){ m_flWaterExitTime = flTime; }
	float				GetWaterExitTime( void ){ return m_flWaterExitTime; }

	void				MerasmusPlayerBombExplode( bool bExcludeMe = true );

	void				DropDeathCallingCard( CTFPlayer* pTFAttacker, CTFPlayer* pTFVictim );


	//---------------------------------
	// support entity IO for forcing speech concepts
	void InputSpeakResponseConcept( inputdata_t &inputdata );

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

	float				GetTimeSinceLastThink( void ) const { return ( m_flLastThinkTime >= 0.f ) ? gpGlobals->curtime - m_flLastThinkTime : -1.f; }
	float				GetRespawnTimeOverride( void ) const { return m_flRespawnTimeOverride; }
	const char			*GetRespawnLocationOverride( void ) const { return ( m_strRespawnLocationOverride == NULL_STRING ) ? NULL : m_strRespawnLocationOverride.ToCStr(); }
	void				SetRespawnOverride( float flRespawnTime, string_t respawnLocation ) { m_flRespawnTimeOverride = flRespawnTime; m_strRespawnLocationOverride = respawnLocation; }
	void				ResetIdleCheck( void ) { m_flLastAction = gpGlobals->curtime; }

	// Matchmaking
	void				SetMatchSafeToLeave( bool bMatchSafeToLeave ) { m_bMatchSafeToLeave = bMatchSafeToLeave; }

	void				SetPrevRoundTeamNum( int nTeamNum ){ m_nPrevRoundTeamNum = nTeamNum; }
	int					GetPrevRoundTeamNum( void ){ return m_nPrevRoundTeamNum; }

protected:

	// Creation/Destruction.
	virtual void		InitClass( void );
	void				GiveDefaultItems();
	bool				SelectSpawnSpotByType( const char *pEntClassName, CBaseEntity* &pSpot );	// "info_player_teamspawn"
	bool				SelectSpawnSpotByName( const char *pEntName, CBaseEntity* &pSpot );			// named info_player_teamspawn, i.e. "my_blue_offense_respawns"
	void				RemoveNemesisRelationships();
	void				RemoveAllItems();

	// Think.
	void				TFPlayerThink();
	void				UpdateTimers( void );

	// Regeneration due to being a Medic, or derived from items
	void				RegenThink();
	void				RuneRegenThink();
	void				RegenAmmoInternal( int iAmmo, float flRegen );
	void				ResetPlayerClass( void );

	virtual void		Internal_HandleMapEvent( inputdata_t &inputdata ) OVERRIDE;

private:
	float				m_flAccumulatedHealthRegen;	// Regeneration can be in small amounts, so we accumulate it and apply when it's > 1
	float				m_flNextAmmoRegenAt;
	float				m_flLastHealthRegenAt;
	float				m_flAccumulatedRuneHealthRegen;
	float				m_flNextRuneAmmoRegenAt;
	float				m_flLastRuneHealthRegenAt;
	float				m_flAccumulatedAmmoRegens[TF_AMMO_SECONDARY+1];	// Only support regenerating primary & secondary right now

	// Bots.
	friend void			Bot_Think( CTFPlayer *pBot );

	// Physics.
	void				PhysObjectSleep();
	void				PhysObjectWake();

	// Ammo pack.
	bool CalculateAmmoPackPositionAndAngles( CTFWeaponBase *pWeapon, Vector &vecOrigin, QAngle &vecAngles );
	void AmmoPackCleanUp( void );

	// State.
	CPlayerStateInfo	*StateLookupInfo( int nState );
	void				StateEnter( int nState );
	void				StateLeave( void );
	void				StateTransition( int nState );
	void				StateEnterWELCOME( void );
	void				StateThinkWELCOME( void );
	void				StateEnterPICKINGTEAM( void );
	void				StateEnterACTIVE( void );
	void				StateEnterOBSERVER( void );
	void				StateThinkOBSERVER( void );
	void				StateEnterDYING( void );
	void				StateThinkDYING( void );

	virtual bool		SetObserverMode(int mode);
	virtual void		AttemptToExitFreezeCam( void );

	bool				PlayGesture( const char *pGestureName );

	bool				GetResponseSceneFromConcept( int iConcept, char *chSceneBuffer, int numSceneBufferBytes );

public:
	// Achievement data storage
	CAchievementData	m_AchievementData;
	CTFPlayerAnimState	*m_PlayerAnimState;

private:
	// Map introductions
	int					m_iIntroStep;
	CHandle<CIntroViewpoint> m_hIntroView;
	float				m_flIntroShowHintAt;
	float				m_flIntroShowEventAt;
	bool				m_bHintShown;
	bool				m_bAbortFreezeCam;
	bool				m_bSeenRoundInfo;
	bool				m_bRegenerating;

	// Items.
	CNetworkHandle( CTFItem, m_hItem );

	// Combat.
	CNetworkHandle( CTFWeaponBase, m_hOffHandWeapon );

	float					m_flHealthBuffTime;
	int						m_iHealthBefore;

	float					m_flNextRegenerateTime;
	float					m_flNextChangeClassTime;
	float					m_flNextChangeTeamTime;
	bool					m_bAllSeeCrit;
	bool					m_bMiniCrit;
	bool					m_bShowDisguisedCrit;
	EAttackBonusEffects_t	m_eBonusAttackEffect;

	int						m_iTeamChanges;
	int						m_iClassChanges;

	// Ragdolls.
	Vector					m_vecTotalBulletForce;

	// State.
	CPlayerStateInfo		*m_pStateInfo;

	// Spawn Point
	CTFTeamSpawn			*m_pSpawnPoint;

	// Networked.
	CNetworkQAngle( m_angEyeAngles );					// Copied from EyeAngles() so we can send it to the client.

	CTFPlayerClass		m_PlayerClass;
	int					m_iLastWeaponFireUsercmd;				// Firing a weapon.  Last usercmd we shot a bullet on.
	int					m_iLastWeaponSlot;				            // To save last switch between lives
	int					m_iLastSkin;
	CNetworkVar( float, m_flLastDamageTime );
	float				m_flLastDamageDoneTime;
	CHandle< CBaseEntity > m_hLastDamageDoneEntity;
	float				m_flLastHealedTime;
	float				m_flNextPainSoundTime;
	int					m_LastDamageType;
	int					m_iDeathFlags;				// TF_DEATH_* flags with additional death info
	int					m_iMaxSentryKills;			// most kills by a single sentry
	int					m_iNumberofDominations;		// number of active dominations for this player

	bool				m_bPlayedFreezeCamSound;
	bool				m_bSwitchedClass;
	bool				m_bRememberLastWeapon;
	bool				m_bRememberActiveWeapon;
	int					m_iActiveWeaponTypePriorToDeath;

	CHandle< CTFWeaponBuilder > m_hWeaponBuilder;

	CUtlVector<EHANDLE>	m_aObjects;			// List of player objects

	bool m_bIsClassMenuOpen;

	Vector m_vecLastDeathPosition;

	float				m_flSpawnTime;

	float				m_flLastAction;
	float				m_flTimeInSpawn;

	CUtlVector<EHANDLE>	m_hObservableEntities;
	CUtlVector<float>	m_aBurnOtherTimes;					// vector of times this player has burned others

	bool m_bHudClassAutoKill;

	// Background expressions
	string_t			m_iszExpressionScene;
	EHANDLE				m_hExpressionSceneEnt;
	float				m_flNextRandomExpressionTime;

	bool				m_bSpeakingConceptAsDisguisedSpy;

	bool 				m_bMedigunAutoHeal;
	bool				m_bAutoRezoom;	// does the player want to re-zoom after each shot for sniper rifles
	bool				m_bAutoReload;

	bool				m_bForceItemRemovalOnRespawn;

	int					m_nPrevRoundTeamNum;

public:
	// Powerplay cheats
	bool				SetPowerplayEnabled( bool bOn );
	bool				PlayerHasPowerplay( void );
	void				PowerplayThink( void );
	CNetworkVar( bool, m_bInPowerPlay );

	bool				IsGoingFeignDeath( void ) { return m_bGoingFeignDeath; }

	void					SetDeployingBombState( BombDeployingState_t nDeployingBombState ) { m_nDeployingBombState = nDeployingBombState; }
	BombDeployingState_t	GetDeployingBombState( void ) const { return m_nDeployingBombState; }

	void				SetPendingMerasmusPlayerBombExplode( void ){ m_bPendingMerasmusPlayerBombExplode = true; }

private:
	// Achievement data
	CUtlVector<EHANDLE> m_aPunchVictims;
	CUtlVector<EHANDLE> m_aBurnFromBackAttackers;
	int					m_iLeftGroundHealth;	// health we were at the last time we left the ground

	float				m_flTeamJoinTime;
	bool				m_bCreatedRocketJumpParticles;
	bool				m_bJustPlayed;
	int					m_iPreviousteam;
	bool				m_bGibbedOnLastDeath;
	CUtlMap<int, float> m_Cappers;		
	float				m_fMaxHealthTime;

	// Feign death.
	bool				m_bGoingFeignDeath;
	CHandle<CBaseEntity> m_hFeignRagdoll;	// Don't use the normal ragdoll.
	Vector				m_vecFeignDeathVelocity;

	CNetworkVar( bool, m_bArenaSpectator );

	bool				m_bArenaIsAFK; // used to detect when players are AFK during an Arena-mode round
	bool				m_bIsAFK;

	BombDeployingState_t	m_nDeployingBombState;

	bool				m_bIsMissionEnemy;
	bool				m_bIsSupportEnemy;
	bool				m_bIsLimitedSupportEnemy;

	// In-game currency
	CNetworkVar( int, m_nCurrency );
	CNetworkVar( bool, m_bIsMiniBoss );

	// Bounty Mode
	CNetworkVar( uint32, m_nExperienceLevel );
	CNetworkVar( uint32, m_nExperienceLevelProgress );	// Networked progress bar
	uint32				m_nExperiencePoints;			// Raw player-only value

	// Matchmaking
	// is this player bound to the match on penalty of abandon. Sync'd via local-player-only DT
	CNetworkVar( bool, m_bMatchSafeToLeave );

	CWaveSpawnPopulator *m_pWaveSpawnPopulator;
	float				m_flLastReadySoundTime;

	int						m_nCanPurchaseUpgradesCount;
	CUtlVector< CUpgradeInfo >	m_RefundableUpgrades;

public:
	// Marking for death.
	CHandle<CTFPlayer>	m_pMarkedForDeathTarget;

	CountdownTimer m_playerMovementStuckTimer;			// for destroying stuck bots in MvM

	QAngle				m_qPreviousChargeEyeAngle;		// Previous EyeAngles to compute deltas for legal mouse movement
private:

	//=============================================================================
	// HPE_BEGIN:
	// [msmith]	Added a player type so we can distinguish between bots and humans.
	//=============================================================================
	TFPlayerType m_playerType;
	//=============================================================================
	// HPE_END
	//=============================================================================
	
	bool				m_bIsTargetDummy;

	bool				m_bCollideWithSentry;
	IntervalTimer		m_calledForMedicTimer;
	CountdownTimer		m_placedSapperTimer;

	CountdownTimer		m_inCombatThrottleTimer;

	mutable char		m_bIsCalculatingMaximumSpeed;

public:

	float				GetDesiredHeadScale() const;
	float				GetHeadScaleSpeed() const;
	float				GetDesiredTorsoScale() const;
	float				GetTorsoScaleSpeed() const;
	float				GetDesiredHandScale() const;
	float				GetHandScaleSpeed() const;

	bool				IsInPurgatory( void ) const;
	bool				HasPurgatoryBuff( void ) const;

	void				SetBombHeadTimestamp();
	float				GetTimeSinceWasBombHead() const;
	
	float				GetKartSpeedBoost( void );
	float				GetKartHealth( void )				{ return m_iKartHealth; }
	void				AddKartDamage( int iDamage )		{ m_iKartHealth = Max(0, m_iKartHealth + iDamage); }
	float				GetKartKnockbackMultiplier( float flExtraMultiplier ) const;
	
	void				ResetKartDamage();
	CBaseEntity			*GetKartBombHeadTarget() const { return m_hKartBombHeadTarget; }
	void				SetKartBombHeadTarget( CBaseEntity* pEnt ) { m_hKartBombHeadTarget = pEnt; }

	void				AddHalloweenKartPushEvent( CTFPlayer *pOther, CBaseEntity *pInflictor, CBaseEntity *pWeapon, Vector vForce, int iDamage, int iDamageType = 0 );
	QAngle				GetAnimRenderAngles( void ) { return m_PlayerAnimState->GetRenderAngles(); }

	void				CancelEurekaTeleport();

	
	CNetworkVar( int,	m_iKartState );
	CNetworkVar( float, m_flKartNextAvailableBoost );
	float				m_flHHHKartAttackTime;

	// Wrenchmotron teleport
	bool				m_bIsTeleportingUsingEurekaEffect;

private:
	void				UpdateHalloween( void );

	Vector				m_vHalloweenKartPush;
	float				m_flHalloweenKartPushEventTime;
	bool				m_bCheckKartCollision;
	EHANDLE				m_hKartBombHeadTarget;
	float				m_flNextBonusDucksVOAllowedTime;

	CNetworkVar( int,	m_iKartHealth );

	float				m_flGhostLastHitByKartTime;

	bool				m_bIsInPurgatory;		// for 2011 Halloween event
	CountdownTimer		m_purgatoryBuffTimer;
	CountdownTimer		m_purgatoryPainMultiplierTimer;
	int					m_purgatoryPainMultiplier;
	CNetworkVar( float, m_flHeadScale );
	CNetworkVar( float, m_flTorsoScale );
	CNetworkVar( float, m_flHandScale );

	//CountdownTimer		m_fireproofTimer;		// if active, we're fireproof

	// Wrenchmotron teleport
	CountdownTimer		m_teleportHomeFlashTimer;
	eEurekaTeleportTargets	m_eEurekaTeleportTarget;

	float				m_accumulatedSentryGunDamageDealt;	// for Sentry Buster missions in MvM
	int					m_accumulatedSentryGunKillCount;	// for Sentry Buster missions in MvM

	static const int	DPS_Period = 90;					// The duration of the sliding window for calculating DPS, in seconds
	int					*m_damageRateArray;					// One array element per second, for accumulating damage done during that time
	int					m_lastDamageRateIndex;
	int					m_peakDamagePerSecond;

	CNetworkVar( uint16, m_nActiveWpnClip );
	uint16				m_nActiveWpnClipPrev;
	float				m_flNextClipSendTime;

	float				m_flWaterExitTime;
	bool				m_bPendingMerasmusPlayerBombExplode;
	float				m_fLastBombHeadTimestamp;

	bool				m_bIsSapping;
	int					m_iSappingEvent;
	float				m_flSapStartTime;
	float				m_flLastThinkTime;
	float				m_flRespawnTimeOverride;
	string_t			m_strRespawnLocationOverride;

	CountdownTimer		m_booTimer;

	CNetworkVar( bool, m_bUseBossHealthBar );

	CNetworkVar( bool, m_bUsingVRHeadset );

	CNetworkVar( bool, m_bForcedSkin );
	CNetworkVar( int, m_nForcedSkin );

private:
	CHandle< CTFReviveMarker >		m_hReviveMarker;
public:
	CTFReviveMarker	*GetReviveMarker( void ) { return m_hReviveMarker; }

	// Send ForcePlayerViewAngles user message. Handled in __MsgFunc_ForcePlayerViewAngles in
	// clientmode_tf.cpp. Sets Local and Abs angles, along with TauntYaw and VehicleMovingAngles.
	void ForcePlayerViewAngles( const QAngle& qTeleportAngles );

	CBaseEntity *GetGrapplingHookTarget() const { return m_hGrapplingHookTarget; }
	void SetGrapplingHookTarget( CBaseEntity *pTarget, bool bShouldBleed = false );

	bool IsUsingActionSlot() const { return m_bUsingActionSlot; }

	void SetSecondaryLastWeapon( CBaseCombatWeapon *pSecondaryLastWeapon ) { m_hSecondaryLastWeapon = pSecondaryLastWeapon; }
	CBaseCombatWeapon* GetSecondaryLastWeapon() const { return m_hSecondaryLastWeapon; }

	bool CanBeForcedToLaugh( void );

	void CreateDisguiseWeaponList( CTFPlayer *pDisguiseTarget );
	void ClearDisguiseWeaponList();

	bool CanPickupDroppedWeapon( const CTFDroppedWeapon *pWeapon );
	CTFDroppedWeapon* GetDroppedWeaponInRange();

	bool HasCampaignMedal( int iMedal );
	void SetCampaignMedalActive( int iMedal ){ m_iCampaignMedals |= iMedal; }

	void InspectButtonPressed();
	void InspectButtonReleased();
	bool IsInspecting() const;

	void SetNextScorePointForPD( float flTime ){ m_flNextScorePointForPD = flTime; }
	bool CanScorePointForPD( void ) const;

	void AddCustomAttribute( const char *pszAttributeName, float flVal, float flDuration = -1.f );
	void RemoveCustomAttribute( const char *pszAttributeName );

	int GetSkinOverride() const { return m_iPlayerSkinOverride; }

	bool ShouldGetBonusPointsForExtinguishEvent( int userID );

	void SetLastAutobalanceTime( float flTime ) { m_flLastAutobalanceTime = flTime; }
	float GetLastAutobalanceTime() { return m_flLastAutobalanceTime; }

private:
	bool PickupWeaponFromOther( CTFDroppedWeapon *pDroppedWeapon );
	bool TryToPickupDroppedWeapon();
	float m_flSendPickupWeaponMessageTime;

	void ModifyDamageInfo( CTakeDamageInfo *pInfo, const CBaseEntity *pTarget );

	CNetworkHandle( CBaseEntity, m_hGrapplingHookTarget );
	float m_flLastSeenHookTarget;
	int m_nHookAttachedPlayers;

	CNetworkHandle( CBaseCombatWeapon, m_hSecondaryLastWeapon );
	CNetworkVar( bool, m_bUsingActionSlot );

	CNetworkVar( float, m_flInspectTime );

	CUtlVector< CHandle< CTFWeaponBase > > m_hDisguiseWeaponList; // copy disguise target weapons to this list

	CNetworkVar( int, m_iCampaignMedals );

	float m_flNextScorePointForPD;

	float m_flLastRuneChargeUpdate;
	float m_flLastDamageResistSoundTime;

	void UpdateCustomAttributes();
	void RemoveAllCustomAttributes();
	CUtlMap< CUtlString, float > m_mapCustomAttributes;

	CNetworkVar( int, m_iPlayerSkinOverride );

	CUtlMap<int, float> m_PlayersExtinguished;	// userID and most recent time they were extinguished for bonus points

	float m_flLastAutobalanceTime;
	
	// begin passtime
public:
	bool SayAskForBall();
	bool m_bPasstimeBallSlippery;
	// end passtime

	virtual bool ShouldForceTransmitsForTeam( int iTeam ) OVERRIDE;

	virtual bool IsTruceValidForEnt( void ) const OVERRIDE;
};

//-----------------------------------------------------------------------------
// Purpose: Utility function to convert an entity into a tf player.
//   Input: pEntity - the entity to convert into a player
//-----------------------------------------------------------------------------
inline CTFPlayer *ToTFPlayer( CBaseEntity *pEntity )
{
	if ( !pEntity || !pEntity->IsPlayer() )
		return NULL;

	Assert( dynamic_cast<CTFPlayer*>( pEntity ) != 0 );
	return static_cast< CTFPlayer* >( pEntity );
}

inline bool CTFPlayer::IsFireproof( void ) const
{
	return m_Shared.InCond( TF_COND_FIRE_IMMUNE );
}

inline bool CTFPlayer::HasPurgatoryBuff( void ) const
{
	return m_purgatoryBuffTimer.HasStarted() && !m_purgatoryBuffTimer.IsElapsed();
}

inline void CTFPlayer::OnSapperPlaced( CBaseEntity *sappedObject )
{
	m_placedSapperTimer.Start( 3.0f );
}
inline void CTFPlayer::OnSapperStarted( float flStartTime )
{
	if (m_iSappingEvent == TF_SAPEVENT_NONE && m_flSapStartTime == 0.00 )
	{
		m_flSapStartTime = flStartTime;
		m_bIsSapping = true;
		m_iSappingEvent = TF_SAPEVENT_PLACED;
	}
}
inline void CTFPlayer::OnSapperFinished( float flStartTime )
{
	if (m_iSappingEvent == TF_SAPEVENT_NONE && flStartTime == m_flSapStartTime )
	{
		m_bIsSapping = false;
		m_flSapStartTime = 0.00;
		m_iSappingEvent = TF_SAPEVENT_DONE;
	}
}
inline bool CTFPlayer::IsSapping( void ) const
{
	return m_bIsSapping;
}

inline int CTFPlayer::GetSappingEvent( void ) const
{
	return m_iSappingEvent;
}

inline void CTFPlayer::ClearSappingEvent( void )
{
	m_iSappingEvent = TF_SAPEVENT_NONE;
}

inline void CTFPlayer::ClearSappingTracking( void )
{
	ClearSappingEvent();
	m_bIsSapping = false;
	m_flSapStartTime = 0.00;
}

inline bool CTFPlayer::IsPlacingSapper( void ) const
{
	return !m_placedSapperTimer.IsElapsed();
}

inline int CTFPlayer::StateGet( void ) const
{
	return m_Shared.m_nPlayerState;
}

inline bool CTFPlayer::IsInCombat( void ) const
{
	// the simplest condition is whether we've been firing our weapon very recently
	return GetTimeSinceWeaponFired() < 2.0f;
}

inline bool CTFPlayer::IsCallingForMedic( void ) const
{
	return m_calledForMedicTimer.HasStarted() && m_calledForMedicTimer.IsLessThen( 5.0f );
}

inline float CTFPlayer::GetTimeSinceCalledForMedic() const
{
	return m_calledForMedicTimer.GetElapsedTime();
}

inline void CTFPlayer::NoteMedicCall( void )
{
	m_calledForMedicTimer.Start();
}

inline bool CTFPlayer::IsInPurgatory( void ) const
{
	return m_Shared.InCond( TF_COND_PURGATORY );
}

inline void CTFPlayer::AccumulateSentryGunDamageDealt( float damage )
{
	m_accumulatedSentryGunDamageDealt += damage;
}

inline void	CTFPlayer::ResetAccumulatedSentryGunDamageDealt()
{
	m_accumulatedSentryGunDamageDealt = 0.0f;
}

inline float CTFPlayer::GetAccumulatedSentryGunDamageDealt()
{
	return m_accumulatedSentryGunDamageDealt;
}

inline void CTFPlayer::IncrementSentryGunKillCount( void )
{
	++m_accumulatedSentryGunKillCount;
}

inline void	CTFPlayer::ResetAccumulatedSentryGunKillCount()
{
	m_accumulatedSentryGunKillCount = 0;
}

inline int CTFPlayer::GetAccumulatedSentryGunKillCount()
{
	return m_accumulatedSentryGunKillCount;
}

inline int CTFPlayer::GetDamagePerSecond( void ) const
{
	return m_peakDamagePerSecond;
}

inline void CTFPlayer::ResetDamagePerSecond( void )
{
	for( int i=0; i<DPS_Period; ++i )
	{
		m_damageRateArray[i] = 0;
	}

	m_lastDamageRateIndex = -1;
	m_peakDamagePerSecond = 0;
}


//=============================================================================
//
// CObserverPoint
//

class CObserverPoint : public CPointEntity
{
	DECLARE_CLASS( CObserverPoint, CPointEntity );
public:
	DECLARE_DATADESC();

	CObserverPoint();

	virtual void Activate( void );
	bool CanUseObserverPoint( CTFPlayer *pPlayer );
	virtual int UpdateTransmitState();
	void InputEnable( inputdata_t &inputdata );
	void InputDisable( inputdata_t &inputdata );

	bool IsDefaultWelcome( void ) { return m_bDefaultWelcome; }
	bool IsMatchSummary( void ) { return m_bMatchSummary; }

	void SetDisabled( bool bDisabled ){ m_bDisabled = bDisabled; }

public:
	bool		m_bDisabled;
	bool		m_bDefaultWelcome;
	EHANDLE		m_hAssociatedTeamEntity;
	string_t	m_iszAssociateTeamEntityName;
	float		m_flFOV;
	bool		m_bMatchSummary;
};

#endif	// TF_PLAYER_H