/*
**	Command & Conquer Generals(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

////////////////////////////////////////////////////////////////////////////////
//																																						//
//  (c) 2001-2003 Electronic Arts Inc.																				//
//																																						//
////////////////////////////////////////////////////////////////////////////////

// AIStateMachine.h
// Finite state machine encapsulation
// Author: Michael S. Booth, January 2002

#pragma once

#ifndef _AI_STATE_MACHINE_H_
#define _AI_STATE_MACHINE_H_

#include "Lib/Basetype.h"

#include "Common/AudioEventRTS.h"
#include "Common/GameMemory.h"
#include "Common/StateMachine.h"

#include "GameLogic/TerrainLogic.h"

// FORWARD DECLARATIONS ///////////////////////////////////////////////////////////////////////////
class AIGuardMachine;
class AITNGuardMachine;
class Weapon;
class Team;
class AIAttackState;
class AttackStateMachine;
class AIGroup;
class Squad;

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


/** 
 * The AI state IDs.
 * Each of these constants will be associated with an instance of a State class
 * in a given StateMachine.
 */
enum AIStateType
{
	AI_IDLE,
	AI_MOVE_TO,																///< move to the GoalObject or GoalPosition
	AI_FOLLOW_WAYPOINT_PATH_AS_TEAM,					///< follow a waypoint path as a team
	AI_FOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS,		///< follow a waypoint path
	AI_FOLLOW_WAYPOINT_PATH_AS_TEAM_EXACT,		///< follow a waypoint path as a team
	AI_FOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS_EXACT,///< follow a waypoint path
	AI_FOLLOW_PATH,														///< follow a simple list of points
	AI_FOLLOW_EXITPRODUCTION_PATH,						///< the same, but only when exiting production facility
	AI_WAIT,
	AI_ATTACK_POSITION,												///< attempt to kill GoalPosition
	AI_ATTACK_OBJECT,													///< attempt to kill GoalObject
	AI_FORCE_ATTACK_OBJECT,										///< attempt to kill GoalObject, force fire on it.
	AI_ATTACK_AND_FOLLOW_OBJECT,							///< attempt to kill GoalObject, following it if necessary (and possible)
	AI_DEAD,
	AI_DOCK,																	///< dock with GoalObject, if GoalObject has a DockUpdate module
	AI_ENTER,																	///< move to GoalObject and "enter" it when close
	AI_GUARD,																	///< guard your current location
	AI_HUNT,																	///< seek and destroy behavior
	AI_WANDER,																///< Wander around following a waypoint path.
	AI_PANIC,																	///< Run around screaming following a waypoint path.
	AI_ATTACK_SQUAD,													///< Set the unit to attempt to kill all objects in goalSquad.
	AI_GUARD_TUNNEL_NETWORK,									///< Guard from inside a tunnel network.
	AI_GET_REPAIRED,													///< Get repaired at a repair depot
	AI_MOVE_OUT_OF_THE_WAY,										///< Move out of the way of another unit.
	AI_MOVE_AND_TIGHTEN,											///< Move in order to tighten up a formation.
	AI_MOVE_AND_EVACUATE,											///< Move to, then empty transport.
	AI_MOVE_AND_EVACUATE_AND_EXIT,						///< Move to, then empty transport.
	AI_MOVE_AND_DELETE,												///< Move to, then delete self.
	AI_ATTACK_AREA,														///< Attack units in an area.
	AI_HACK_INTERNET,													///< Hack internet for free money (no target required).
	AI_ATTACK_MOVE_TO,												///< Attack-move to a location
	AI_ATTACKFOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS,	///< Attack-Follow down a waypoint path as individuals
	AI_ATTACKFOLLOW_WAYPOINT_PATH_AS_TEAM,				///< Attack-Follow down a waypoint path as a team
	AI_FACE_OBJECT,
	AI_FACE_POSITION,
	AI_RAPPEL_INTO,														/**< rappel from current pos down to target object.
																								if target is building, will enter and kill lots of folks.
																								if target is null, will rappel to ground. */
	AI_COMBATDROP,														/**< attempt to send AI_RAPPEL_INTO to contents. */
	AI_EXIT,																	///< exit the obj, waiting if necessary
	AI_PICK_UP_CRATE,													///< Pick up a crate created by a kill.  jba.
	AI_MOVE_AWAY_FROM_REPULSORS,							///< Civilians are running away from repulsors. (enemies or dead civs, usually) jba
	AI_WANDER_IN_PLACE,												///< Civilians just wander around a spot, rather than along a path.
	AI_BUSY,																	///< This is a state that things will be in when they are busy doing random stuff that doesn't require AI interaction.
	NUM_AI_STATES
};

//-----------------------------------------------------------------------------------------------------------
// generically state transition conditions
extern Bool outOfWeaponRangeObject( State *thisState, void* userData );
extern Bool outOfWeaponRangePosition( State *thisState, void* userData );
extern Bool wantToSquishTarget( State *thisState, void* userData );
	
//-----------------------------------------------------------------------------------------------------------
/**
  The AI state machine.  This is used by AIUpdate to implement all of the 
  commands in the AICommandInterface.
 
	NOTE NOTE NOTE NOTE NOTE

	Do NOT subclass this unless you want ALL of the states this machine possesses.
	If you only want SOME of the states, please make a new StateMachine, descended
	from StateMachine, NOT AIStateMachine. Thank you. (srj)

	NOTE NOTE NOTE NOTE NOTE

 */
class AIStateMachine : public StateMachine
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( AIStateMachine, "AIStateMachine" );

public:
	/** 
	 * The implementation of this constructor defines the states
	 * used by this machine.
	 */
	AIStateMachine( Object *owner, AsciiString name );

	virtual void clear();
	virtual StateReturnType resetToDefaultState();
	virtual StateReturnType setState( StateID newStateID );
	
	/// @todo Rethink state parameter passing. Continuing in this fashion will have a pile of params in the machine (MSB)
	void setGoalPath( const std::vector<Coord3D>* path );
	void addToGoalPath( const Coord3D *pathPoint );
	const Coord3D *getGoalPathPosition( Int i ) const;		///< return path position at index "i"
	Int getGoalPathSize() const { return m_goalPath.size(); }


	void setGoalWaypoint( const Waypoint *way );		///< move toward this waypoint, continue if connected
	const Waypoint *getGoalWaypoint();

	// All of these wind up storing a Squad, as it is the only one that can always be safely created.
	void setGoalTeam( const Team *team );
	void setGoalSquad( const Squad *squad );
	void setGoalAIGroup( const AIGroup *group );

	Squad *getGoalSquad(void);
	
	StateReturnType setTemporaryState( StateID newStateID, Int frameLimitCoount );			///< change the temporary state of the machine, and number of frames limit.
	StateID getTemporaryState(void) const {return m_temporaryState?m_temporaryState->getID():INVALID_STATE_ID;}

public:	// overrides.
	virtual StateReturnType updateStateMachine();				///< run one step of the machine
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getCurrentStateName() const ;
#endif
	
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	std::vector<Coord3D>	m_goalPath;					///< defines a simple path to follow
	const Waypoint *			m_goalWaypoint;
	Squad *								m_goalSquad;

	/** A temporary state to run for a while (usually AI_MOVE_OUT_OF_THE_WAY).  
	Doesn't clear or reset the state machine, so it goes back to doing what it was doing.  jba. */
	State									*m_temporaryState;			 
	UnsignedInt						m_temporaryStateFramEnd; ///< Last frame to run m_temporaryState.
};


//-----------------------------------------------------------------------------------------------------------
class AttackStateMachine : public StateMachine
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( AttackStateMachine, "AttackStateMachine" );

public:
// Attack states.
enum StateType
	{
		CHASE_TARGET,													///< Chase a moving target (optionally following it)
		APPROACH_TARGET,											///< Approach a non-moving target.
		AIM_AT_TARGET,												///< rotate to face GoalObject or GoalPosition
		FIRE_WEAPON,													///< fire the machine owner's current weapon
		NUM_ATTACK_STATES
	};
	AttackStateMachine( Object *owner, AIAttackState* att, AsciiString name, Bool follow, Bool attackingObject, Bool forceAttacking );

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();
};

//-----------------------------------------------------------------------------------------------------------
class AIIdleState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIIdleState, "AIIdleState")		
private:

	UnsignedShort m_initialSleepOffset;
	Bool m_shouldLookForTargets;
	Bool m_inited;

	void doInitIdleState();

public:
	enum AIIdleTargetingType
	{
		LOOK_FOR_TARGETS,
		DO_NOT_LOOK_FOR_TARGETS
	};
	virtual Bool isIdle(void) const { return true; }
	AIIdleState( StateMachine *machine, AIIdleTargetingType shouldLookForTargets);
	virtual StateReturnType onEnter();
	virtual StateReturnType update();

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

};
EMPTY_DTOR(AIIdleState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Basic pathfinding and moving to a goal position.
 * Not for direct use (hence no associated state ID), but for deriving from.
 */
class AIInternalMoveToState : public State
{
	MEMORY_POOL_GLUE_ABC(AIInternalMoveToState)		
public:
	AIInternalMoveToState( StateMachine *machine, AsciiString name ) : State( machine, name ) 
	{ 
		m_goalPosition.zero();
		m_pathGoalPosition.zero();
		m_pathTimestamp = 0;
		m_blockedRepathTimestamp = 0;
		m_adjustDestinations = true;
		m_goalLayer = LAYER_INVALID;
	}

	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();

protected:

	void setAdjustsDestination(Bool b) { m_adjustDestinations = b; }
	Bool getAdjustsDestination() const;

	void setGoalPos(const Coord3D* pos) { m_goalPosition = *pos; }
	const Coord3D* getGoalPos() const { return &m_goalPosition; }

	virtual Bool computePath();												///< compute the path

	void forceRepath()
	{
		m_pathGoalPosition.x = m_pathGoalPosition.y = m_pathGoalPosition.z = -100.0f;
		m_pathTimestamp = -MIN_REPATH_TIME;
	}

private:
	void startMoveSound();

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	enum { MIN_REPATH_TIME = 10 };										///< minimum # of frames must elapse before re-pathing
protected:
	Coord3D							m_goalPosition;											///< the goal position to move to
	PathfindLayerEnum		m_goalLayer;										///< The layer we are moving towards.
	Coord3D				m_pathGoalPosition;									///< the position our current path leads to
private:
	AudioHandle		m_ambientPlayingHandle;							///< Audio handle for the looping sound that we may play.
	UnsignedInt		m_pathTimestamp;										///< time of last pathfind
	UnsignedInt		m_blockedRepathTimestamp;						///< time of last blocked pathfind
	Bool					m_adjustDestinations;								///< Adjust destinations to avoid stacking units on top of each other.  Normally true, but 
																										//   occasionally false for things like car bombs.
protected:
	Bool					m_waitingForPath;										///< If we are waiting for a path.
	Bool					m_tryOneMoreRepath;									///< If true, after we complete movement do another compute path.
};
EMPTY_DTOR(AIInternalMoveToState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Move to the GoalPosition, or GoalObject.
 */
class AIRappelState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIRappelState, "AIRappelState")		
protected:
	Real m_rappelRate;
	Real m_destZ;
	Bool m_targetIsBldg;
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

public:
	AIRappelState( StateMachine *machine );
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
};
EMPTY_DTOR(AIRappelState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Spin. Busy-like
 */
class AIBusyState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIBusyState, "AIBusyState")		
public:
	AIBusyState( StateMachine *machine ) : State( machine, "AIBusyState" ) { }
	virtual StateReturnType onEnter() { return STATE_CONTINUE; }
	virtual void onExit( StateExitType status ) { }
	virtual StateReturnType update() { return STATE_CONTINUE; }
	virtual Bool isBusy(void) const { return true; }
protected:
	// snapshot interface STUBBED.
	virtual void crc( Xfer *xfer ){};
	virtual void xfer( Xfer *xfer ){XferVersion cv = 1;	XferVersion v = cv; xfer->xferVersion( &v, cv );}
	virtual void loadPostProcess(){};

};
EMPTY_DTOR(AIBusyState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Move to the GoalPosition, or GoalObject.
 */
class AIMoveToState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIMoveToState, "AIMoveToState")		
protected:
	Bool m_isMoveTo;
public:
	AIMoveToState( StateMachine *machine );
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
};
EMPTY_DTOR(AIMoveToState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Moves out of the way of another object.
 */
class AIMoveOutOfTheWayState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIMoveOutOfTheWayState, "AIMoveOutOfTheWayState")		
public:
	AIMoveOutOfTheWayState( StateMachine *machine ) : AIInternalMoveToState( machine, "AIMoveOutOfTheWayState" ) { }
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
protected:
	virtual Bool computePath();												///< compute the path

};
EMPTY_DTOR(AIMoveOutOfTheWayState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Moves toward goal pos to tighen up a formation.
 */
class AIMoveAndTightenState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIMoveAndTightenState, "AIMoveAndTightenState")		
public:
	AIMoveAndTightenState( StateMachine *machine ) : AIInternalMoveToState( machine, "AIMoveAndTightenState" ) 
	{
		m_checkForPath = false;
		m_okToRepathTimes = 0;
	}
	AIMoveAndTightenState( StateMachine *machine, const char *name ) : AIInternalMoveToState( machine, name ) 
	{
		m_checkForPath = false;
		m_okToRepathTimes = 0;
	}
	virtual StateReturnType onEnter();
	virtual StateReturnType update();
protected:
	virtual Bool computePath();												///< compute the path
	Int m_okToRepathTimes; 
	Bool m_checkForPath;
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();
};
EMPTY_DTOR(AIMoveAndTightenState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Moves toward goal pos to get away from an enemy.
 */
class AIMoveAwayFromRepulsorsState : public AIMoveAndTightenState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIMoveAwayFromRepulsorsState, "AIMoveAwayFromRepulsorsState")		
public:
	AIMoveAwayFromRepulsorsState( StateMachine *machine ) : AIMoveAndTightenState( machine, "AIMoveAwayFromRepulsors" ) { }
	virtual StateReturnType onEnter();
	virtual StateReturnType update();
	virtual void onExit( StateExitType status );
protected:
	virtual Bool computePath();												///< compute the path

};
EMPTY_DTOR(AIMoveAwayFromRepulsorsState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Compute a valid spot to fire on GoalObject, and move there.
 */
class AIAttackApproachTargetState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackApproachTargetState, "AIAttackApproachTargetState")		
public:
	AIAttackApproachTargetState( StateMachine *machine, Bool follow, Bool attackingObject, Bool forceAttacking ) : 
		AIInternalMoveToState( machine, "AIAttackApproachTargetState" ), 
		m_follow(follow),
		m_isAttackingObject(attackingObject),
		m_isInitialApproach(true),
		m_isForceAttacking(forceAttacking)
	{ 
		// we're setting m_isInitialApproach to true in the constructor because we want the first pass 
		// through this state to allow a unit to attack incidental targets (if it is turreted)
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
protected:
	virtual Bool computePath();												///< compute the path

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:

	enum { MIN_RECOMPUTE_TIME = 10 };

	StateReturnType updateInternal( void );

	Coord3D				m_prevVictimPos;									///< Where we think our victim is
	UnsignedInt		m_approachTimestamp;							///< When we last computed an approach goal
	Bool					m_follow;													///< If true, follow object until it dies
	Bool					m_isAttackingObject;							///< If false, attacking position
	Bool					m_stopIfInRange;									///< If true, we check and stop as soon we can fire.  Used when we have to path to the object instead of to a firing position.
	Bool					m_isInitialApproach;							///< If true, we can attack other units along the way. We will check for them every N frames (N specified in AI.ini)
	Bool					m_isForceAttacking;
};
EMPTY_DTOR(AIAttackApproachTargetState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Chases a moving GoalObject.  If goal is not object, or not moving, or attacker lacks turret, exits with success.
 */
class AIAttackPursueTargetState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackPursueTargetState, "AIAttackPursueTargetState")		
public:
	AIAttackPursueTargetState( StateMachine *machine, Bool follow, Bool attackingObject, Bool forceAttacking ) : 
		AIInternalMoveToState( machine, "AIAttackPursueTargetState" ), 
		m_follow(follow),
		m_isAttackingObject(attackingObject),
		m_isInitialApproach(true),
		m_isForceAttacking(forceAttacking),
		m_approachTimestamp(0),
		m_stopIfInRange(false)
	{ 
		// we're setting m_isInitialApproach to true in the constructor because we want the first pass 
		// through this state to allow a unit to attack incidental targets (if it is turreted)
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
protected:
	virtual Bool computePath();												///< compute the path

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:

	enum { MIN_RECOMPUTE_TIME = 10 };

	StateReturnType updateInternal( void );

	Coord3D				m_prevVictimPos;									///< Where we think our victim is
	UnsignedInt		m_approachTimestamp;							///< When we last computed an approach goal
	Bool					m_follow;													///< If true, follow object until it dies
	Bool					m_isAttackingObject;							///< If false, attacking position
	Bool					m_stopIfInRange;									///< If true, we check and stop as soon we can fire.  Used when we have to path to the object instead of to a firing position.
	Bool					m_isInitialApproach;							///< If true, we can attack other units along the way. We will check for them every N frames (N specified in AI.ini)
	Bool					m_isForceAttacking;
};
EMPTY_DTOR(AIAttackPursueTargetState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Pick up an upgrade crate after a kill.
 */
class AIPickUpCrateState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIPickUpCrateState, "AIPickUpCrateState")		
public:
	AIPickUpCrateState( StateMachine *machine ) : 
		AIInternalMoveToState( machine, "AIAttackPickUpCrateState" )
	{ 
		// we're setting m_isInitialApproach to true in the constructor because we want the first pass 
		// through this state to allow a unit to attack incidental targets (if it is turreted)
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
protected:
	virtual Bool computePath();												///< compute the path

private:
	Int			m_delayCounter;
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();
};
EMPTY_DTOR(AIPickUpCrateState)

//-------------------------------------------------------------------------------------------------
/**
 * Execute an Attack-Move
 */
 class AIAttackMoveToState : public AIMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackMoveToState, "AIAttackMoveToState")		
public:
	AIAttackMoveToState( StateMachine *machine );
	//virtual ~AIAttackMoveToState();

	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif

protected:
	enum {ATTACK_RETRY_COUNT=5};
	enum {ATTACK_CLOSE_ENOUGH_CELLS=8};
	CommandSourceType	m_commandSrc;		// Original command source.  We switch to CMD_FROM_AI when auto-acquiring.
	StateMachine *m_attackMoveMachine;
	UnsignedInt		m_frameToSleepUntil;
	Int						m_retryCount;
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();
};

//-----------------------------------------------------------------------------------------------------------
/**
 * Follow a waypoint path
 */
class AIFollowWaypointPathState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIFollowWaypointPathState, "AIFollowWaypointPathState")		
public:
	AIFollowWaypointPathState( StateMachine *machine, Bool asGroup ) : m_isFollowWaypointPathState(true), 
		m_moveAsGroup(asGroup), AIInternalMoveToState( machine, "AIFollowWaypointPathState" ) 
	{
		m_angle = 0.0f;
	}
	AIFollowWaypointPathState( StateMachine *machine, Bool asGroup, Bool isFollow) : 
		m_isFollowWaypointPathState(isFollow), m_moveAsGroup(asGroup), AIInternalMoveToState( machine, 
																"AIFollowWaypointPathState" ) 
	{
		m_angle = 0.0f;
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

protected:
	Coord2D m_groupOffset;
	Real		m_angle;
	Int  m_framesSleeping;
	const Waypoint *m_currentWaypoint;
	const Waypoint *m_priorWaypoint;
	Bool m_appendGoalPosition;

	const Bool m_moveAsGroup;
	// this is necessary because we derive from FollowWaypointPathState, but we do not want to incur the 
	// expense of checking RTTI to determine whether we are actually a FollowWaypointPathState or not
	const Bool m_isFollowWaypointPathState;	// derived classes should set this false.

protected:
	void computeGoal(Bool useGroupOffsets);
	Real calcExtraPathDistance(void);
	const Waypoint *getNextWaypoint(void);
	Bool hasNextWaypoint(void);
};
EMPTY_DTOR(AIFollowWaypointPathState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Follow a waypoint path exactly
 */
class AIFollowWaypointPathExactState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIFollowWaypointPathExactState, "AIFollowWaypointPathExactState")		
public:
	AIFollowWaypointPathExactState( StateMachine *machine, Bool asGroup ) : m_moveAsGroup(asGroup), 
		m_lastWaypoint(NULL),
		AIInternalMoveToState( machine, "AIFollowWaypointPathExactState" ) { }
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

protected:
	const Waypoint *m_lastWaypoint;
	const Bool m_moveAsGroup;
};
EMPTY_DTOR(AIFollowWaypointPathExactState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Follow a waypoint path, attacking targets encountered along the way
 */
class AIAttackFollowWaypointPathState : public AIFollowWaypointPathState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackFollowWaypointPathState, "AIAttackFollowWaypointPathState")		
public:
	AIAttackFollowWaypointPathState( StateMachine *machine, Bool asGroup );
	//virtual ~AIAttackFollowWaypointPathState();
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif

protected:
	// We can (and want to) use the attack-move-to logic for when to attack and when to do a normal attack.
	StateMachine *m_attackFollowMachine;
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();
};

//-----------------------------------------------------------------------------------------------------------
/**
 * Wanders around following a waypoint path.
 */
class AIWanderState : public AIFollowWaypointPathState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIWanderState, "AIWanderState")		
public:
	AIWanderState( StateMachine *machine ) : AIFollowWaypointPathState( machine, false ) 
	{ 
#ifdef STATE_MACHINE_DEBUG
		setName("AIWanderState");
#endif 
		m_timer = 0;
		m_waitFrames = 0;
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

protected:
	Int		m_waitFrames;
	Int		m_timer;
};
EMPTY_DTOR(AIWanderState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Wanders around a point.
 */
class AIWanderInPlaceState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIWanderInPlaceState, "AIWanderInPlaceState")		
public:
	AIWanderInPlaceState( StateMachine *machine ) : AIInternalMoveToState( machine, "AIWanderInPlaceState" ) 
	{
		m_origin.zero();
		m_waitFrames = 0;
		m_timer = 0;
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

protected:
	void computeWanderGoal();
	Coord3D m_origin;									///< The point we're wandering around.	
	Int		m_waitFrames;
	Int		m_timer;
};
EMPTY_DTOR(AIWanderInPlaceState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Runs around screaming following a waypoint path.
 */
class AIPanicState : public AIFollowWaypointPathState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIPanicState, "AIPanicState")		
public:
	AIPanicState( StateMachine *machine ) : AIFollowWaypointPathState( machine, false)
	{ 
#ifdef STATE_MACHINE_DEBUG
		setName("AIPanicState");
#endif	
	}
	virtual StateReturnType onEnter();
	virtual StateReturnType update();
	virtual void onExit( StateExitType status );
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

protected:
	Int		m_waitFrames;
	Int		m_timer;
};
EMPTY_DTOR(AIPanicState)


//-----------------------------------------------------------------------------------------------------------
/**
 * Follow simple list of points.
 */
class AIFollowPathState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIFollowPathState, "AIFollowPathState")		
public:
	AIFollowPathState( StateMachine *machine, AsciiString name = "AIFollowPathState" ) : 
		m_adjustFinal(true), 
		m_adjustFinalOverride(false),
		m_index(0),
		m_retryCount(10),
		AIInternalMoveToState( machine, name ) { }
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();

protected:

	Int getCurPathIndex() const { return m_index; }
	void setAdjustFinalDestination(Bool b) { m_adjustFinal = b; }
	void setAdjustFinalDestinationEvenIfNotDoingGroundMovement(Bool b) { m_adjustFinalOverride = b; }

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	Int m_index;																		///< current path index	
	Bool m_adjustFinal;
	Bool m_adjustFinalOverride;
	Int m_retryCount;
};
EMPTY_DTOR(AIFollowPathState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Move to and unload transport.
 */
class AIMoveAndEvacuateState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIMoveAndEvacuateState, "AIMoveAndEvacuateState")		
public:
	AIMoveAndEvacuateState( StateMachine *machine ) : AIInternalMoveToState( machine, "AIMoveAndEvacuateState" ) 
	{
		m_origin.zero();
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	Coord3D m_origin;													///< current position - set as goal on exit in case we follow with MoveToAndDelete.	
};
EMPTY_DTOR(AIMoveAndEvacuateState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Move to and delete self.  Used for removing reinforcement transports.
 */
class AIMoveAndDeleteState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIMoveAndDeleteState, "AIMoveAndDeleteState")		
public:
	AIMoveAndDeleteState( StateMachine *machine ) : AIInternalMoveToState( machine, "AIMoveAndDeleteState" ) 
	{
		m_appendGoalPosition = FALSE;
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

protected:
	Bool m_appendGoalPosition;
};
EMPTY_DTOR(AIMoveAndDeleteState)

//-----------------------------------------------------------------------------------------------------------
class AIAttackAimAtTargetState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackAimAtTargetState, "AIAttackAimAtTargetState")		
public:
	AIAttackAimAtTargetState( StateMachine *machine, Bool attackingObject, Bool forceAttacking ) : 
		State( machine, "AIAttackAimAtTargetState" ),
		m_isAttackingObject(attackingObject),
		m_canTurnInPlace(false),
		m_isForceAttacking(forceAttacking),
		m_setLocomotor(false)
	{ 
	}
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();
private:
	const Bool m_isAttackingObject;
	Bool m_canTurnInPlace;
	Bool m_setLocomotor;
	Bool m_isForceAttacking;
};
EMPTY_DTOR(AIAttackAimAtTargetState)

//-----------------------------------------------------------------------------------------------------------
class AIWaitState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIWaitState, "AIWaitState")		
public:
	AIWaitState( StateMachine *machine ) : State( machine,"AIWaitState" ) { }
	virtual StateReturnType update();
protected:
	// snapshot interface STUBBED.
	virtual void crc( Xfer *xfer ){};
	virtual void xfer( Xfer *xfer ){XferVersion cv = 1;	XferVersion v = cv; xfer->xferVersion( &v, cv );}
	virtual void loadPostProcess(){};
};
EMPTY_DTOR(AIWaitState)

//-----------------------------------------------------------------------------------------------------------
class NotifyWeaponFiredInterface	// an ABC
{
public:
	virtual void notifyFired() = 0;
	virtual void notifyNewVictimChosen(Object* victim) = 0;
	virtual Bool isWeaponSlotOkToFire(WeaponSlotType wslot) const = 0;
	virtual Bool isAttackingObject() const = 0;
	virtual const Coord3D* getOriginalVictimPos() const = 0;
};

//-----------------------------------------------------------------------------------------------------------
class AIAttackFireWeaponState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackFireWeaponState, "AIAttackFireWeaponState")		
public:
	AIAttackFireWeaponState( StateMachine *machine, NotifyWeaponFiredInterface* att ) : 
		State( machine, "AIAttackFireWeaponState" ), 
		m_att(att) 
	{ 
	}
	virtual StateReturnType update();
	virtual void onExit( StateExitType status );
	virtual StateReturnType onEnter();
protected:
	// snapshot interface STUBBED.
	virtual void crc( Xfer *xfer ){};
	virtual void xfer( Xfer *xfer ){XferVersion cv = 1;	XferVersion v = cv; xfer->xferVersion( &v, cv );}
	virtual void loadPostProcess(){};
private:
	NotifyWeaponFiredInterface *const m_att;		// this is NOT owned by us and should not be freed
};
EMPTY_DTOR(AIAttackFireWeaponState)

//-----------------------------------------------------------------------------------------------------------
class AttackExitConditionsInterface
{
public:
	virtual Bool shouldExit(const StateMachine* machine) const = 0;
};

//-----------------------------------------------------------------------------------------------------------
class AIAttackState : public State, public NotifyWeaponFiredInterface
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackState, "AIAttackState")		
public:

	AIAttackState( StateMachine *machine, Bool follow, Bool attackingObject, Bool forceAttacking, AttackExitConditionsInterface* attackParameters);
	//~AIAttackState();

	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();

	virtual void notifyFired() { /* nothing */ }
	virtual void notifyNewVictimChosen(Object* victim);
	virtual const Coord3D* getOriginalVictimPos() const { return &m_originalVictimPos; }
	virtual Bool isWeaponSlotOkToFire(WeaponSlotType wslot) const { return true; }
	virtual Bool isAttackingObject() const { return m_isAttackingObject; } 
	virtual Bool isForceAttacking() const { return m_isForceAttacking; }
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif

	virtual Bool isAttack() const { return TRUE; }

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:

	void chooseWeapon();

	AttackStateMachine*							m_attackMachine;						///< state sub-machine for attack behavior
	AttackExitConditionsInterface*	m_attackParameters;					///< these are not owned by this, and will not be deleted on destruction
	Team*														m_victimTeam;								///< recorded onEnter because if it changes during attack , it may no longer be a valid target.
	Coord3D													m_originalVictimPos;				///< position of first obj/pos attacked... used for ContinueAttackRange.
	const Weapon*						m_lockedWeaponOnEnter;
	const Bool							m_follow;
	const Bool							m_isAttackingObject;								// if false, attacking position
	const Bool							m_isForceAttacking;
};

//-----------------------------------------------------------------------------------------------------------
class AIAttackSquadState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackSquadState, "AIAttackSquadState")		
public:
	AIAttackSquadState( StateMachine *machine, AttackExitConditionsInterface *attackParameters = NULL) : 
			State( machine , "AIAttackSquadState") {	}
	//~AIAttackSquadState();

	virtual StateReturnType onEnter( void );
	virtual void onExit( StateExitType status );
	virtual StateReturnType update( void );
	Object *chooseVictim(void);
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif


protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	StateMachine *m_attackSquadMachine;						///< state sub-machine for attack behavior
};

//-----------------------------------------------------------------------------------------------------------
class AIDeadState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIDeadState, "AIDeadState")		
public:
	AIDeadState( StateMachine *machine ) : State( machine, "AIDeadState" ) { }
	virtual StateReturnType onEnter();
	virtual StateReturnType update();
	virtual void onExit( StateExitType status );
protected:
	// snapshot interface STUBBED.
	virtual void crc( Xfer *xfer ){};
	virtual void xfer( Xfer *xfer ){XferVersion cv = 1;	XferVersion v = cv; xfer->xferVersion( &v, cv );}
	virtual void loadPostProcess(){};
};
EMPTY_DTOR(AIDeadState)

//-----------------------------------------------------------------------------------------------------------
class AIDockState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIDockState, "AIDockState")		
public:
	AIDockState( StateMachine *machine ) : State( machine, "AIDockState" ), m_dockMachine(NULL), m_usingPrecisionMovement(FALSE) { }
	//~AIDockState();
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	StateMachine *m_dockMachine;				///< state sub-machine for docking behavior
	Bool m_usingPrecisionMovement;			///< keep a record of precision movement so we can restore the right state on exit
};
//-----------------------------------------------------------------------------------------------------------
/**
 * Move close to GoalObject and enter it.
 */
class AIEnterState : public AIInternalMoveToState
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIEnterState, "AIEnterState")		
protected:
	ObjectID m_entryToClear;
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();
public:
	AIEnterState( StateMachine *machine ) : AIInternalMoveToState( machine, "AIEnterState" ) { }
	virtual StateReturnType onEnter();
	virtual StateReturnType update();
	virtual void onExit( StateExitType status );
};
EMPTY_DTOR(AIEnterState)

//-----------------------------------------------------------------------------------------------------------
class AIExitState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIExitState, "AIExitState")		
protected:
	ObjectID m_entryToClear;
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();
public:
	AIExitState( StateMachine *machine ) : State( machine, "AIExitState" ) { }
	virtual StateReturnType onEnter();
	virtual StateReturnType update();
	virtual void onExit( StateExitType status );
};
EMPTY_DTOR(AIExitState)

//-----------------------------------------------------------------------------------------------------------
/**
 * Guard location
 */
class AIGuardState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIGuardState, "AIGuardState")		
public:
	AIGuardState( StateMachine *machine ) : State( machine, "AIGuardState" ), m_guardMachine(NULL) 
	{
		m_guardMachine = NULL;
	}
	//~AIGuardState();
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	AIGuardMachine *m_guardMachine;					///< state sub-machine for guard behavior
};

//-----------------------------------------------------------------------------------------------------------
/**
 * Guard from inside a tunnel network.
 */
class AITunnelNetworkGuardState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AITunnelNetworkGuardState, "AITunnelNetworkGuardState")		
public:
	AITunnelNetworkGuardState( StateMachine *machine ) : State( machine, "AITunnelNetworkGuardState" ), m_guardMachine(NULL) 
	{
		m_guardMachine = NULL;
	}
	//~AIGuardState();
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif
protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	AITNGuardMachine *m_guardMachine;					///< state sub-machine for guard behavior
};

//-----------------------------------------------------------------------------------------------------------
/**
 * Seek and destroy behavior
 */
class AIHuntState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIHuntState, "AIHuntState")		
public:
	AIHuntState( StateMachine *machine ) : State( machine, "AIHuntState" ), m_huntMachine(NULL) 
	{ 
		m_nextEnemyScanTime = 0;
	}
	//~AIHuntState();
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	StateMachine* m_huntMachine;					///< state sub-machine for hunt behavior
	UnsignedInt m_nextEnemyScanTime;			///< time of last enemy scan
	enum { ENEMY_SCAN_RATE = LOGICFRAMES_PER_SECOND };
};

//-----------------------------------------------------------------------------------------------------------
/**
 * Seek and destroy in area
 */
class AIAttackAreaState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIAttackAreaState, "AIAttackAreaState")		
public:
	AIAttackAreaState( StateMachine *machine ) : State( machine, "AIAttackAreaState" ), m_attackMachine(NULL), 
		m_nextEnemyScanTime(0) { }
	//~AIAttackAreaState();
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
#ifdef STATE_MACHINE_DEBUG
	virtual AsciiString getName() const ;
#endif

protected:
	// snapshot interface
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

private:
	StateMachine *m_attackMachine;					///< state sub-machine for attack behavior
	UnsignedInt m_nextEnemyScanTime;			///< time of last enemy scan
	enum { ENEMY_SCAN_RATE = LOGICFRAMES_PER_SECOND };
};

//-----------------------------------------------------------------------------------------------------------
// Faces a position or an object (depending on goal)
//-----------------------------------------------------------------------------------------------------------
class AIFaceState : public State
{
	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AIFaceState, "AIFaceState")		
public:
	AIFaceState( StateMachine *machine, Bool obj ) : State( machine, "AIFaceState" ), m_canTurnInPlace(false), m_obj(obj) { }
	virtual StateReturnType onEnter();
	virtual void onExit( StateExitType status );
	virtual StateReturnType update();
protected:
	// snapshot interface .
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess();

protected:
	const Bool m_obj;
	Bool m_canTurnInPlace;
};
EMPTY_DTOR(AIFaceState)

#endif
