// NextBotBodyInterface.h
// Control and information about the bot's body state (posture, animation state, etc)
// Author: Michael Booth, April 2006
//========= Copyright Valve Corporation, All rights reserved. ============//

#ifndef _NEXT_BOT_BODY_INTERFACE_H_
#define _NEXT_BOT_BODY_INTERFACE_H_

#include "animation.h"
#include "NextBotComponentInterface.h"

class INextBot;
struct animevent_t;


//----------------------------------------------------------------------------------------------------------------
/**
 * The interface for control and information about the bot's body state (posture, animation state, etc)
 */
class IBody : public INextBotComponent
{
public:
	IBody( INextBot *bot ) : INextBotComponent( bot ) { }
	virtual ~IBody() { }

	virtual void Reset( void ) { INextBotComponent::Reset(); }			// reset to initial state
	virtual void Update( void ) { }										// update internal state

	/**
	 * Move the bot to a new position.
	 * If the body is not currently movable or if it
	 * is in a motion-controlled animation activity 
	 * the position will not be changed and false will be returned.
	 */
	virtual bool SetPosition( const Vector &pos );

	virtual const Vector &GetEyePosition( void ) const;					// return the eye position of the bot in world coordinates
	virtual const Vector &GetViewVector( void ) const;					// return the view unit direction vector in world coordinates

	enum LookAtPriorityType
	{
		BORING,
		INTERESTING,				// last known enemy location, dangerous sound location
		IMPORTANT,					// a danger
		CRITICAL,					// an active threat to our safety
		MANDATORY					// nothing can interrupt this look at - two simultaneous look ats with this priority is an error
	};
	virtual void AimHeadTowards( const Vector &lookAtPos, 
								 LookAtPriorityType priority = BORING, 
								 float duration = 0.0f,
								 INextBotReply *replyWhenAimed = NULL,
								 const char *reason = NULL );		// aim the bot's head towards the given goal
	virtual void AimHeadTowards( CBaseEntity *subject,
								 LookAtPriorityType priority = BORING, 
								 float duration = 0.0f,
								 INextBotReply *replyWhenAimed = NULL,
								 const char *reason = NULL );		// continually aim the bot's head towards the given subject

	virtual bool IsHeadAimingOnTarget( void ) const;				// return true if the bot's head has achieved its most recent lookat target
	virtual bool IsHeadSteady( void ) const;						// return true if head is not rapidly turning to look somewhere else
	virtual float GetHeadSteadyDuration( void ) const;				// return the duration that the bot's head has not been rotating
	virtual float GetHeadAimSubjectLeadTime( void ) const;			// return how far into the future we should predict our moving subject's position to aim at when tracking subject look-ats
	virtual float GetHeadAimTrackingInterval( void ) const;			// return how often we should sample our target's position and velocity to update our aim tracking, to allow realistic slop in tracking
	virtual void ClearPendingAimReply( void ) { }					// clear out currently pending replyWhenAimed callback

	virtual float GetMaxHeadAngularVelocity( void ) const;			// return max turn rate of head in degrees/second

	enum ActivityType 
	{ 
		MOTION_CONTROLLED_XY	= 0x0001,	// XY position and orientation of the bot is driven by the animation.
		MOTION_CONTROLLED_Z		= 0x0002,	// Z position of the bot is driven by the animation.
		ACTIVITY_UNINTERRUPTIBLE= 0x0004,	// activity can't be changed until animation finishes
		ACTIVITY_TRANSITORY		= 0x0008,	// a short animation that takes over from the underlying animation momentarily, resuming it upon completion
		ENTINDEX_PLAYBACK_RATE	= 0x0010,	// played back at different rates based on entindex
	};

	/**
	 * Begin an animation activity, return false if we cant do that right now.
	 */
	virtual bool StartActivity( Activity act, unsigned int flags = 0 );
	virtual int SelectAnimationSequence( Activity act ) const;			// given an Activity, select and return a specific animation sequence within it

	virtual Activity GetActivity( void ) const;							// return currently animating activity
	virtual bool IsActivity( Activity act ) const;						// return true if currently animating activity matches the given one
	virtual bool HasActivityType( unsigned int flags ) const;			// return true if currently animating activity has any of the given flags

	enum PostureType
	{
		STAND,
		CROUCH,
		SIT,
		CRAWL,
		LIE
	};
	virtual void SetDesiredPosture( PostureType posture ) { }			// request a posture change
	virtual PostureType GetDesiredPosture( void ) const;				// get posture body is trying to assume
	virtual bool IsDesiredPosture( PostureType posture ) const;			// return true if body is trying to assume this posture
	virtual bool IsInDesiredPosture( void ) const;						// return true if body's actual posture matches its desired posture

	virtual PostureType GetActualPosture( void ) const;					// return body's current actual posture
	virtual bool IsActualPosture( PostureType posture ) const;			// return true if body is actually in the given posture

	virtual bool IsPostureMobile( void ) const;							// return true if body's current posture allows it to move around the world
	virtual bool IsPostureChanging( void ) const;						// return true if body's posture is in the process of changing to new posture
	
	
	/**
	 * "Arousal" is the level of excitedness/arousal/anxiety of the body.
	 * Is changes instantaneously to avoid complex interactions with posture transitions.
	 */
	enum ArousalType
	{
		NEUTRAL,
		ALERT,
		INTENSE
	};
	virtual void SetArousal( ArousalType arousal ) { }					// arousal level change
	virtual ArousalType GetArousal( void ) const;						// get arousal level
	virtual bool IsArousal( ArousalType arousal ) const;				// return true if body is at this arousal level


	virtual float GetHullWidth( void ) const;							// width of bot's collision hull in XY plane
	virtual float GetHullHeight( void ) const;							// height of bot's current collision hull based on posture
	virtual float GetStandHullHeight( void ) const;						// height of bot's collision hull when standing
	virtual float GetCrouchHullHeight( void ) const;					// height of bot's collision hull when crouched
	virtual const Vector &GetHullMins( void ) const;					// return current collision hull minimums based on actual body posture
	virtual const Vector &GetHullMaxs( void ) const;					// return current collision hull maximums based on actual body posture

	virtual unsigned int GetSolidMask( void ) const;					// return the bot's collision mask (hack until we get a general hull trace abstraction here or in the locomotion interface)
	virtual unsigned int GetCollisionGroup( void ) const;
};


inline bool IBody::IsHeadSteady( void ) const
{
	return true;
}

inline float IBody::GetHeadSteadyDuration( void ) const
{
	return 0.0f;
}

inline float IBody::GetHeadAimSubjectLeadTime( void ) const
{
	return 0.0f;
}

inline float IBody::GetHeadAimTrackingInterval( void ) const
{
	return 0.0f;
}

inline float IBody::GetMaxHeadAngularVelocity( void ) const
{
	return 1000.0f;
}

inline bool IBody::StartActivity( Activity act, unsigned int flags )
{
	return false;
}

inline int IBody::SelectAnimationSequence( Activity act ) const
{
	return 0;
}

inline Activity IBody::GetActivity( void ) const
{
	return ACT_INVALID;
}

inline bool IBody::IsActivity( Activity act ) const
{
	return false;
}

inline bool IBody::HasActivityType( unsigned int flags ) const
{
	return false;
}

inline IBody::PostureType IBody::GetDesiredPosture( void ) const
{
	return IBody::STAND;
}

inline bool IBody::IsDesiredPosture( PostureType posture ) const
{
	return true;
}

inline bool IBody::IsInDesiredPosture( void ) const
{
	return true;
}

inline IBody::PostureType IBody::GetActualPosture( void ) const
{
	return IBody::STAND;
}

inline bool IBody::IsActualPosture( PostureType posture ) const
{
	return true;
}

inline bool IBody::IsPostureMobile( void ) const
{
	return true;
}

inline bool IBody::IsPostureChanging( void ) const
{
	return false;
}

inline IBody::ArousalType IBody::GetArousal( void ) const
{
	return IBody::NEUTRAL;
}

inline bool IBody::IsArousal( ArousalType arousal ) const
{
	return true;
}

//---------------------------------------------------------------------------------------------------------------------------
/**
 * Width of bot's collision hull in XY plane
 */
inline float IBody::GetHullWidth( void ) const
{
	return 26.0f;
}


//---------------------------------------------------------------------------------------------------------------------------
/**
 * Height of bot's current collision hull based on posture
 */
inline float IBody::GetHullHeight( void ) const
{
	switch( GetActualPosture() )
	{
	case LIE:
		return 16.0f;

	case SIT:
	case CROUCH:
		return GetCrouchHullHeight();

	case STAND:
	default:
		return GetStandHullHeight();
	}
}


//---------------------------------------------------------------------------------------------------------------------------
/**
 * Height of bot's collision hull when standing
 */
inline float IBody::GetStandHullHeight( void ) const
{
	return 68.0f;
}


//---------------------------------------------------------------------------------------------------------------------------
/**
 * Height of bot's collision hull when crouched
 */
inline float IBody::GetCrouchHullHeight( void ) const
{
	return 32.0f;
}


//---------------------------------------------------------------------------------------------------------------------------
/**
 * Return current collision hull minimums based on actual body posture
 */
inline const Vector &IBody::GetHullMins( void ) const
{
	static Vector hullMins;

	hullMins.x = -GetHullWidth()/2.0f;
	hullMins.y = hullMins.x;
	hullMins.z = 0.0f;

	return hullMins;
}


//---------------------------------------------------------------------------------------------------------------------------
/**
 * Return current collision hull maximums based on actual body posture
 */
inline const Vector &IBody::GetHullMaxs( void ) const
{
	static Vector hullMaxs;

	hullMaxs.x = GetHullWidth()/2.0f;
	hullMaxs.y = hullMaxs.x;
	hullMaxs.z = GetHullHeight();

	return hullMaxs;
}


inline unsigned int IBody::GetSolidMask( void ) const
{
	return MASK_NPCSOLID;
}

inline unsigned int IBody::GetCollisionGroup( void ) const
{
	return COLLISION_GROUP_NONE;
}



#endif // _NEXT_BOT_BODY_INTERFACE_H_