//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//

#ifndef AI_MOVETYPES_H
#define AI_MOVETYPES_H

#if defined( _WIN32 )
#pragma once
#endif

#include "ai_navtype.h"

class CAI_Path;

//-----------------------------------------------------------------------------
// Debugging code
//
// Use this function to set breakpoints to find out where movement is failing
//
#ifdef DEBUG
extern void DebugNoteMovementFailure();
#define DebugNoteMovementFailureIfBlocked( moveResult ) if ( !IsMoveBlocked( moveResult ) ) ((void)0); else DebugNoteMovementFailure()
#else
#define DebugNoteMovementFailure() ((void)0)
#define DebugNoteMovementFailureIfBlocked( moveResult ) ((void)0)
#endif

enum AIMoveResult_t
{
	AIMR_BLOCKED_ENTITY			= -1,	         // Move was blocked by an entity
	AIMR_BLOCKED_WORLD			= -2,	         // Move was blocked by the world
	AIMR_BLOCKED_NPC			= -3,	         // Move was blocked by an NPC
	AIMR_ILLEGAL				= -4,	         // Move is illegal for some reason

	AIMR_OK						= 0,
	
	AIMR_CHANGE_TYPE,	                         // Locomotion method has changed
};


#ifdef DEBUG
extern AIMoveResult_t DbgResult( AIMoveResult_t result );
#else
inline AIMoveResult_t DbgResult( AIMoveResult_t result ) { return result; } // inline not macro for compiler typing
#endif


//-----------------------------------------------------------------------------
// Movement related constants and base types
//-----------------------------------------------------------------------------
#ifdef PHYSICS_NPC_SHADOW_DISCREPENCY
const float AI_EPS_CASTS = 0.3;  // The amount physics and hull cast can disagree
#endif


inline bool IsMoveBlocked( AIMoveResult_t moveResult )
{
	return (moveResult < AIMR_OK );
}

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

enum StepGroundTest_t
{
	STEP_DONT_CHECK_GROUND = 0,
	STEP_ON_VALID_GROUND,
	STEP_ON_INVALID_GROUND,
};


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

struct AIMoveTrace_t
{
	AIMoveTrace_t()
	{
		memset( this, 0, sizeof(*this) );
	}
	
	AIMoveResult_t 	fStatus;				// See AIMoveResult_t
	Vector 			vEndPosition;			// The last point that could be moved to
	Vector			vHitNormal;				// The normal of a hit, if any. vec3_origin if none. Can be none even if "hit"
	CBaseEntity* 	pObstruction;			// The obstruction I bumped into (if any)
	float			flTotalDist;
	float 			flDistObstructed; 	// FIXME: This is a strange number. In the case
											// of calling MoveLimit with navtype NAV_GROUND,
											// it represents a 2D distance to the obstruction.
											// In the case of other navtypes, it represents a
											// 3D distance to the obstruction
	Vector 			vJumpVelocity;			// FIXME: Remove this; it's bogus
											// It's only returned by JumpMoveLimit
											// which seems to be a bogus concept to begin with
	float			flStepUpDistance;
};

inline bool IsMoveBlocked( const AIMoveTrace_t &moveTrace )
{
	return (moveTrace.fStatus < AIMR_OK );
}


// Categorizes the blocker and sets the appropriate bits
AIMoveResult_t		AIComputeBlockerMoveResult( CBaseEntity *pBlocker );


//-------------------------------------
// Purpose: Specifies an immediate, localized, straight line movement goal
//-------------------------------------

enum AILocalMoveGoalFlags_t
{
	AILMG_NONE,
	AILMG_TARGET_IS_GOAL		= 0x01,
	AILMG_CONSUME_INTERVAL		= 0x02,
	AILMG_TARGET_IS_TRANSITION 	= 0x04,
	AILMG_NO_STEER 				= 0x08,
	AILMG_NO_AVOIDANCE_PATHS 	= 0x10,
};

struct AILocalMoveGoal_t
{
	AILocalMoveGoal_t()
	{
		memset( this, 0, sizeof(*this) );
	}
	
	// Object of the goal
	Vector			target;

	// The actual move. Note these need not always agree with "target"
	Vector			dir; 
	Vector			facing;
	float			speed;
	
	// The distance maximum distance intended to travel in path length
	float			maxDist;

	// The distance expected to move this think
	float			curExpectedDist;

	Navigation_t	navType;
	CBaseEntity *	pMoveTarget;

	unsigned		flags;

	// The path from which this goal was derived
	CAI_Path *		pPath;

	// The result if a forward probing trace has been done
	bool			bHasTraced;
	AIMoveTrace_t	directTrace;
	AIMoveTrace_t	thinkTrace;

#ifdef DEBUG
	int				solveCookie;
#endif
};

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

enum AIMotorMoveResult_t
{
	AIM_FAILED,
	AIM_SUCCESS,
	
	// Partial successes
	AIM_PARTIAL_HIT_NPC,
	AIM_PARTIAL_HIT_WORLD,
	AIM_PARTIAL_HIT_TARGET,
	
	AIM_NUM_RESULTS
};

//-----------------------------------------------------------------------------
// Purpose: The set of callbacks used by lower-level movement classes to
//			notify and receive guidance from higher level-classes
//-----------------------------------------------------------------------------

abstract_class IAI_MovementSink
{
public:
	//---------------------------------
	//
	// Queries
	//
	virtual float CalcYawSpeed( void ) = 0;

	//---------------------------------
	//
	// Local navigation notifications, each allows services provider to overridde default result
	//
	virtual bool OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, 
								float distClear, 
								AIMoveResult_t *pResult ) = 0;

	virtual bool OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, 
											float distClear, 
											AIMoveResult_t *pResult ) = 0;

	virtual bool OnFailedSteer( AILocalMoveGoal_t *pMoveGoal, 
									float distClear, 
									AIMoveResult_t *pResult ) = 0;

	virtual bool OnFailedLocalNavigation( AILocalMoveGoal_t *pMoveGoal,
									float distClear, 
									AIMoveResult_t *pResult ) = 0;
	
	virtual bool OnInsufficientStopDist( AILocalMoveGoal_t *pMoveGoal, 
											 float distClear, 
											 AIMoveResult_t *pResult ) = 0;


	virtual bool OnMoveBlocked( AIMoveResult_t *pResult ) = 0;
	
	//---------------------------------
	//
	// Motor notifications, each allows services provider to overridde default result
	//
	virtual bool OnMoveStalled( const AILocalMoveGoal_t &move ) = 0;
	virtual bool OnMoveExecuteFailed( const AILocalMoveGoal_t &move, 
									const AIMoveTrace_t &trace, 
									AIMotorMoveResult_t fMotorResult, 
									AIMoveResult_t *pResult ) = 0;
};

//-----------------------------------------------------------------------------
// Purpose: Default implementations of IAI_MovementSink
//-----------------------------------------------------------------------------

class CAI_DefMovementSink : public IAI_MovementSink
{
public:
	//---------------------------------
	//
	// Queries
	//
	virtual float CalcYawSpeed( void ) { return -1.0; } 


	//---------------------------------
	//
	// Local navigation notifications, each allows services provider to overridde default result
	//
	virtual bool OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) { return false; }
	virtual bool OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) { return false;	}
	virtual bool OnFailedSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) { return false;	}
	virtual bool OnFailedLocalNavigation( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) { return false;	}
	virtual bool OnInsufficientStopDist( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) { return false;	}
	virtual bool OnMoveBlocked( AIMoveResult_t *pResult ) { return false;	}
	
	//---------------------------------
	//
	// Motor notifications, each allows services provider to overridde default result
	//
	virtual bool OnMoveStalled( const AILocalMoveGoal_t &move ) { return false;	}
	virtual bool OnMoveExecuteFailed( const AILocalMoveGoal_t &move, const AIMoveTrace_t &trace, AIMotorMoveResult_t fMotorResult, AIMoveResult_t *pResult ) { return false;	}
	
};

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

class CAI_ProxyMovementSink : public CAI_DefMovementSink
{
public:
	CAI_ProxyMovementSink()
	 :	m_pProxied( NULL )
	{
	}
	
	//---------------------------------
	
	void Init( IAI_MovementSink *pMovementServices ) { m_pProxied = pMovementServices; }
	
	//---------------------------------
	//
	// Queries
	//
	virtual float CalcYawSpeed( void );

	//---------------------------------
	//
	// Local navigation notifications, each allows services provider to overridde default result
	//
	virtual bool OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, 
								float distClear, 
								AIMoveResult_t *pResult );

	virtual bool OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, 
											float distClear, 
											AIMoveResult_t *pResult );
	virtual bool OnFailedSteer( AILocalMoveGoal_t *pMoveGoal, 
									float distClear, 
									AIMoveResult_t *pResult );
	virtual bool OnFailedLocalNavigation( AILocalMoveGoal_t *pMoveGoal, 
									float distClear, 
									AIMoveResult_t *pResult );
	virtual bool OnInsufficientStopDist( AILocalMoveGoal_t *pMoveGoal, 
											 float distClear, 
											 AIMoveResult_t *pResult );
	virtual bool OnMoveBlocked( AIMoveResult_t *pResult );
	
	//---------------------------------
	//
	// Motor notifications, each allows services provider to overridde default result
	//
	virtual bool OnMoveStalled( const AILocalMoveGoal_t &move );
	virtual bool OnMoveExecuteFailed( const AILocalMoveGoal_t &move, const AIMoveTrace_t &trace, AIMotorMoveResult_t fMotorResult, AIMoveResult_t *pResult );
	
	IAI_MovementSink *m_pProxied;
};

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

inline float CAI_ProxyMovementSink::CalcYawSpeed( void )
{
	float result;
	if ( m_pProxied && ( result = m_pProxied->CalcYawSpeed() ) != -1.0 )
		return result;
	return CAI_DefMovementSink::CalcYawSpeed();
}

inline bool CAI_ProxyMovementSink::OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
{
	if ( m_pProxied && m_pProxied->OnCalcBaseMove( pMoveGoal, distClear, pResult ) )
		return true;
	return CAI_DefMovementSink::OnCalcBaseMove( pMoveGoal, distClear, pResult );
}

inline bool CAI_ProxyMovementSink::OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
{
	if ( m_pProxied && m_pProxied->OnObstructionPreSteer( pMoveGoal, distClear, pResult ) )
		return true;
	return CAI_DefMovementSink::OnObstructionPreSteer( pMoveGoal, distClear, pResult );
}

inline bool CAI_ProxyMovementSink::OnFailedSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
{
	if ( m_pProxied && m_pProxied->OnFailedSteer( pMoveGoal, distClear, pResult ) )
		return true;
	return CAI_DefMovementSink::OnFailedSteer( pMoveGoal, distClear, pResult );
}

inline bool CAI_ProxyMovementSink::OnFailedLocalNavigation( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
{
	if ( m_pProxied && m_pProxied->OnFailedLocalNavigation( pMoveGoal, distClear, pResult ) )
		return true;
	return CAI_DefMovementSink::OnFailedLocalNavigation( pMoveGoal, distClear, pResult );
}

inline bool CAI_ProxyMovementSink::OnInsufficientStopDist( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
{
	if ( m_pProxied && m_pProxied->OnInsufficientStopDist( pMoveGoal, distClear, pResult ) )
		return true;
	return CAI_DefMovementSink::OnInsufficientStopDist( pMoveGoal, distClear, pResult );
}

inline bool CAI_ProxyMovementSink::OnMoveStalled( const AILocalMoveGoal_t &move )
{
	if ( m_pProxied && m_pProxied->OnMoveStalled( move ) )
		return true;
	return CAI_DefMovementSink::OnMoveStalled( move );
}

inline bool CAI_ProxyMovementSink::OnMoveExecuteFailed( const AILocalMoveGoal_t &move, const AIMoveTrace_t &trace, AIMotorMoveResult_t fMotorResult, AIMoveResult_t *pResult )
{
	if ( m_pProxied && m_pProxied->OnMoveExecuteFailed( move, trace, fMotorResult, pResult ) )
		return true;
	return CAI_DefMovementSink::OnMoveExecuteFailed( move, trace, fMotorResult, pResult );
}

inline bool CAI_ProxyMovementSink::OnMoveBlocked( AIMoveResult_t *pResult )
{
	if ( m_pProxied && m_pProxied->OnMoveBlocked( pResult ) )
		return true;
	return CAI_DefMovementSink::OnMoveBlocked( pResult );
}

//=============================================================================

#endif // AI_MOVETYPES_H
