// NextBotPlayerLocomotion.h
// Locomotor for CBasePlayer derived bots
// Author: Michael Booth, November 2005
//========= Copyright Valve Corporation, All rights reserved. ============//

#ifndef _NEXT_BOT_PLAYER_LOCOMOTION_H_
#define _NEXT_BOT_PLAYER_LOCOMOTION_H_

#include "NextBot.h"
#include "NextBotLocomotionInterface.h"
#include "Path/NextBotPathFollow.h"

class CBasePlayer;

//--------------------------------------------------------------------------------------------------
/**
 * Basic player locomotion implementation
 */
class PlayerLocomotion : public ILocomotion
{
public:
	DECLARE_CLASS( PlayerLocomotion, ILocomotion );

	PlayerLocomotion( INextBot *bot );
	virtual ~PlayerLocomotion() { }

	virtual void Reset( void );							// reset to initial state
	virtual void Update( void );						// update internal state

	virtual void Approach( const Vector &pos, float goalWeight = 1.0f );	// move directly towards the given position
	virtual void DriveTo( const Vector &pos );			// Move the bot to the precise given position immediately, 

	//
	// ILocomotion modifiers
	//
	virtual bool ClimbUpToLedge( const Vector &landingGoal, const Vector &landingForward, const CBaseEntity *obstacle );	// initiate a jump to an adjacent high ledge, return false if climb can't start
	virtual void JumpAcrossGap( const Vector &landingGoal, const Vector &landingForward );	// initiate a jump across an empty volume of space to far side
	virtual void Jump( void );								// initiate a simple undirected jump in the air
	virtual bool IsClimbingOrJumping( void ) const;					// is jumping in any form
	virtual bool IsClimbingUpToLedge( void ) const;			// is climbing up to a high ledge
	virtual bool IsJumpingAcrossGap( void ) const;			// is jumping across a gap to the far side

	virtual void Run( void );								// set desired movement speed to running
	virtual void Walk( void );								// set desired movement speed to walking
	virtual void Stop( void );								// set desired movement speed to stopped
	virtual bool IsRunning( void ) const;
	virtual void SetDesiredSpeed( float speed );			// set desired speed for locomotor movement
	virtual float GetDesiredSpeed( void ) const;			// returns the current desired speed
	virtual void SetMinimumSpeedLimit( float limit );		// speed cannot drop below this 
	virtual void SetMaximumSpeedLimit( float limit );		// speed cannot rise above this 

	virtual bool IsOnGround( void ) const;					// return true if standing on something
	virtual CBaseEntity *GetGround( void ) const;			// return the current ground entity or NULL if not on the ground
	virtual const Vector &GetGroundNormal( void ) const;	// surface normal of the ground we are in contact with

	virtual void ClimbLadder( const CNavLadder *ladder, const CNavArea *dismountGoal );		// climb the given ladder to the top and dismount
	virtual void DescendLadder( const CNavLadder *ladder, const CNavArea *dismountGoal );		// descend the given ladder to the bottom and dismount
	virtual bool IsUsingLadder( void ) const;
	virtual bool IsAscendingOrDescendingLadder( void ) const;	// we are actually on the ladder right now, either climbing up or down
	virtual bool IsAbleToAutoCenterOnLadder( void ) const;

	virtual void FaceTowards( const Vector &target );		// rotate body to face towards "target"

	virtual void SetDesiredLean( const QAngle &lean )	{ }
	virtual const QAngle &GetDesiredLean( void ) const	{ static QAngle junk; return junk; }

	//
	// ILocomotion information
	//
	virtual const Vector &GetFeet( void ) const;			// return position of "feet" - point below centroid of bot at feet level

	virtual float GetStepHeight( void ) const;				// if delta Z is greater than this, we have to jump to get up
	virtual float GetMaxJumpHeight( void ) const;			// return maximum height of a jump
	virtual float GetDeathDropHeight( void ) const;			// distance at which we will die if we fall

	virtual float GetRunSpeed( void ) const;				// get maximum running speed
	virtual float GetWalkSpeed( void ) const;				// get maximum walking speed

	virtual float GetMaxAcceleration( void ) const;			// return maximum acceleration of locomotor
	virtual float GetMaxDeceleration( void ) const;			// return maximum deceleration of locomotor

	virtual const Vector &GetVelocity( void ) const;		// return current world space velocity

protected:
	virtual void AdjustPosture( const Vector &moveGoal );

private:
	CBasePlayer *m_player;									// the player we are locomoting	

	mutable bool m_isJumping;
	CountdownTimer m_jumpTimer;

	bool m_isClimbingUpToLedge;
	bool m_isJumpingAcrossGap;
	Vector m_landingGoal;
	bool m_hasLeftTheGround;

	float m_desiredSpeed;
	float m_minSpeedLimit;
	float m_maxSpeedLimit;

	bool TraverseLadder( void );			// when climbing/descending a ladder

	enum LadderState
	{
		NO_LADDER,							// not using a ladder
		APPROACHING_ASCENDING_LADDER,
		APPROACHING_DESCENDING_LADDER,
		ASCENDING_LADDER,
		DESCENDING_LADDER,
		DISMOUNTING_LADDER_TOP,
		DISMOUNTING_LADDER_BOTTOM,
	};

	LadderState m_ladderState;
	LadderState ApproachAscendingLadder( void );
	LadderState ApproachDescendingLadder( void );
	LadderState AscendLadder( void );
	LadderState DescendLadder( void );
	LadderState DismountLadderTop( void );
	LadderState DismountLadderBottom( void );
	
	const CNavLadder *m_ladderInfo;
	const CNavArea *m_ladderDismountGoal;
	CountdownTimer m_ladderTimer;			// a "give up" timer if things go awry

	bool IsClimbPossible( INextBot *me, const CBaseEntity *obstacle ) const;
};


inline float PlayerLocomotion::GetStepHeight( void ) const
{
	return 18.0f;
}


inline float PlayerLocomotion::GetMaxJumpHeight( void ) const
{
	return 57.0f;
}


inline float PlayerLocomotion::GetDeathDropHeight( void ) const
{
	return 200.0f;
}


inline float PlayerLocomotion::GetMaxAcceleration( void ) const
{
	return 100.0f;
}

inline float PlayerLocomotion::GetMaxDeceleration( void ) const
{
	return 200.0f;
}

inline void PlayerLocomotion::Run( void )
{
	m_desiredSpeed = GetRunSpeed();
}

inline void PlayerLocomotion::Walk( void )
{
	m_desiredSpeed = GetWalkSpeed();
}

inline void PlayerLocomotion::Stop( void )
{
	m_desiredSpeed = 0.0f;
}

inline bool PlayerLocomotion::IsRunning( void ) const
{
	return true;
}

inline void PlayerLocomotion::SetDesiredSpeed( float speed )
{
	m_desiredSpeed = speed;
}

inline float PlayerLocomotion::GetDesiredSpeed( void ) const
{
	return clamp( m_desiredSpeed, m_minSpeedLimit, m_maxSpeedLimit );
}

inline void PlayerLocomotion::SetMinimumSpeedLimit( float limit )
{
	m_minSpeedLimit = limit;
}

inline void PlayerLocomotion::SetMaximumSpeedLimit( float limit )
{
	m_maxSpeedLimit = limit;
}

inline bool PlayerLocomotion::IsAbleToAutoCenterOnLadder( void ) const
{
	return IsUsingLadder() && (m_ladderState == ASCENDING_LADDER || m_ladderState == DESCENDING_LADDER);
}

inline bool PlayerLocomotion::IsAscendingOrDescendingLadder( void ) const
{
	switch( m_ladderState )
	{
	case ASCENDING_LADDER:
	case DESCENDING_LADDER:
	case DISMOUNTING_LADDER_TOP:
	case DISMOUNTING_LADDER_BOTTOM:
		return true;
	default:
		// Explicitly handle the default so that clang knows not to warn us.
		// warning: enumeration values 'NO_LADDER', 'APPROACHING_ASCENDING_LADDER', and 'APPROACHING_DESCENDING_LADDER' not handled in switch [-Wswitch-enum]
		break;
	}

	return false;
}


#endif // _NEXT_BOT_PLAYER_LOCOMOTION_H_