//===========================================================================//
// File:	MechAI.cpp																	     //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 07/13/99  AHF  Created file                          //
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Microsoft										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "gameinfo.hpp"
#include "mw4.hpp"
#include "moverai.hpp"
#include "Adept\Interface.hpp"
#include "Adept\Application.hpp"
#include "Adept\player.hpp"
#include "torso.hpp"
#include <Adept\CollisionGrid.hpp>
#include "mwobject.hpp"
#include "building.hpp"
#include <adept\collisionvolume.hpp>
#include "move_formation.hpp"
#include "mwplayer.hpp"
#include "bridge.hpp"
#include "jumpjet.hpp"
#include "cheap_move.hpp"
#include "adept\map.hpp"
#include "dropship.hpp"
#include "field_base.hpp"
#include "shooterai.hpp"
#include "vehicleinterface.hpp"


#pragma warning (push)
#include <algorithm>
#pragma warning (pop)

const int OBSTACLELOOKAHEAD = 50;
const int MAX_FAILS_PATROL = 5;
const Scalar BOARD_RADIUS = 1.5f;

//using namespace stlport;

using namespace MechWarrior4;
using namespace MW4AI;

namespace ABL
{
	extern MechWarrior4::MoverAI  *CurMoverAI;
};
namespace MW4AI
{
	extern __int64  tPathTime,tGridPathTime;
};

//#############################################################################
//###############################    AI      ##############################
//#############################################################################

MoverAI::ClassData* MoverAI::DefaultData = NULL;
__int64 tMoverAITime,tDynamicObstacleTime;
bool MoverAI::g_AlwaysIgnoreObstacles = false;
bool MoverAI::m_AIJumpAllowed = true;


const Receiver::MessageEntry
MoverAI::MessageEntries[]=
{
	MESSAGE_ENTRY(MoverAI, DebugText),
};


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MoverAI::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
	new ClassData(
							 MoverAIClassID,
							 "MechWarrior4::MoverAI",
							 AI::DefaultData,
							 ELEMENTS(MessageEntries), MessageEntries,
							 (Entity::Factory)Make,
							 (Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
							 ExecutionStateEngine::DefaultData,
							 (Entity::GameModel::Factory)GameModel::ConstructGameModel,
							 NULL,
							 (Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
							 (Entity::GameModel::ModelWrite)GameModel::WriteToText,
							 (Entity::GameModel::ModelSave)GameModel::SaveGameModel
							 );
	Register_Object(DefaultData);

#if !defined(NO_TIMERS)
	AddStatistic( "MoverAI ", "%", gos_timedata, (void*)&tMoverAITime, 0 );
	AddStatistic( "Dynamic Obstacle ", "%", gos_timedata, (void*)&tDynamicObstacleTime, 0 );
#endif

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MoverAI::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MoverAI* MoverAI::Make(CreateMessage *message,ReplicatorID *base_id)
{
	Check_Object(message);
	AutoHeap local_heap (g_MoverAIHeap);
	MoverAI *new_entity = new MoverAI(DefaultData, message, base_id, NULL);
	Check_Object(new_entity);
	new_entity->SyncMatrices(true);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MoverAI::MoverAI(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element)
: AI(class_data, message, base_id, element),
m_CombatLeashRadius(0)
{
	Check_Pointer(this);
	Check_Object(message);

	AutoHeap local_heap (g_MoverAIHeap);
	m_ObstacleRequest = NULL;
	m_PathBlockedRect = NULL;
	m_CurMove = NULL;
	m_CurPath = NULL;
	m_CheapMover = NULL;
	m_PathBad = false;
	m_RecalcTime = 0;
	m_MoveHead = Stuff::Point3D (-1,-1,-1);
	m_OutVec = Stuff::Point3D (-1,-1,-1);
	m_NewMoveHead = Stuff::Point3D (-1,-1,-1);
	m_NewOutVec = Stuff::Point3D (-1,-1,-1);
	m_OldMoveHead = Stuff::Point3D (-1,-1,-1);
	m_DropShipWait = Stuff::Point3D (-1,-1,-1);
	m_DropShipBoard = Stuff::Point3D (-1,-1,-1);
	m_DropShipCenter = Stuff::Point3D (-1,-1,-1);
	m_DropShipFinal = Stuff::Point3D (-1,-1,-1);
	m_BoardingDropShip = DROP_NOT_ACTIVE;
	m_DropShip = NULL;
	m_OtherDropShipBoarders.push_back(NULL);
	m_OtherDropShipBoarders.push_back(NULL);
	m_OtherDropShipBoarders.push_back(NULL);
	m_OtherDropShipBoarders.push_back(NULL);
	m_OtherDropShipBoarders.push_back(NULL);
	m_DropShipID = 0;
	m_ReadyToStart = false;
	m_CanStartBoard = false;
	m_CombatLeash.Zero();
	m_EnteringFieldBase = FIELDBASE_NOT_ACTIVE;

	m_SpeedRequest = 0;
	m_NewSpeedRequest = 0;
	m_EndSpeedRequest = 0;
	m_NewEndSpeedRequest = 0;
	SetOrderSpeed (0);
	m_NeedHeadingAdjust = false;
	m_NeedSpeedAdjust = false;
	m_PathBlocked = false;
	m_Jumping = false;
	m_JumpRequested = false;
	m_PathLock.reserve (10);
	m_LockedRects.reserve (20);
//	m_ObstacleAvoidMode = false;
	m_ObstacleAvoidPoint = Point3D (-1,-1,-1);
	m_RamTarget = NULL;
	m_FailCount = 0;
	m_TimeNotMoving = 0;

	m_LastTimeFinishedFieldBase = 0;

	m_Vehicle = NULL;
	m_MWObject = NULL;
	Entity *ent;

	ent = getEntity ();
	if (ent)
	{
		Check_Object(ent);
		if (ent->IsDerivedFrom (Vehicle::DefaultData))
			m_Vehicle = (Vehicle *)ent;
		if (ent->IsDerivedFrom (MWObject::DefaultData))
			m_MWObject = (MWObject *)ent;
	}

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MoverAI::~MoverAI()
{
	Check_Object (this);
	ClearPathLock (true,false);
	delete m_CurMove;
	m_CurMove = NULL;
	delete m_CurPath;
	m_CurPath = NULL;
	delete m_PathBlockedRect;
	if (m_CheapMover)
		delete m_CheapMover;
	ClearTempLockRects ();
	m_CheapMover = NULL;
	m_PathBlockedRect = NULL;
	if (m_ObstacleRequest)
	{
		g_PathManager->RemoveRequest (m_ObstacleRequest);
		delete m_ObstacleRequest;
		m_ObstacleRequest = NULL;
	}

	m_ObstacleList.clear ();
	
	Verify (m_PathLock.size () == 0);
	stlport::vector<MW4AI::LockData*>::iterator iter;
	for (iter = m_PathLock.begin ();iter != m_PathLock.end ();iter++)
	{
		delete (*iter);
	}

	m_PathLock.clear ();
	m_LockedRects.clear ();
}

void MoverAI::Save (MemoryStream *stream)
{
	if (m_Vehicle)
		*stream << m_Vehicle->objectID;
	else
		*stream << (ObjectID) -1;
	if (m_MWObject)
		*stream << m_MWObject->objectID;
	else
		*stream << (ObjectID) -1;

	if (m_CurMove)
	{
		*stream << true;
		*stream << ((int) m_CurMove->MoveType ());
	}
	else
	{
		*stream << false;
	}
	if (m_CurPath)
	{
		*stream << true;
		m_CurPath->Save (stream);
	}
	else
	{
		*stream << false;
	}
	*stream << m_TimeNotMoving;
	*stream << m_MoveHead;
	*stream << m_OutVec;
	*stream << m_OldMoveHead;
	*stream << m_NewMoveHead;
	*stream << m_NewOutVec;
	*stream << m_SpeedRequest;
	*stream << m_NewSpeedRequest;
	*stream << m_EndSpeedRequest;
	*stream << m_NewEndSpeedRequest;
	*stream << m_OrderSpeed;
	*stream << m_StartJump;
	*stream << m_BurnTime;
	*stream << m_NeedHeadingAdjust;
	*stream << m_NeedSpeedAdjust;
	*stream << m_Jumping;
	*stream << m_JumpRequested;

	*stream << m_PathBlocked;
	if (m_RamTarget)
		*stream << m_RamTarget->objectID;
	else
		*stream << (ObjectID) -1;

	*stream << m_PathBlockedCell;
	if (m_PathBlockedRect)
	{
		*stream << true;
		m_PathBlockedRect->Save (stream);
	}
	else
	{
		*stream << false;
	}
	*stream << m_PathBlockedRectTimeDelta;
	*stream << m_LockTime;
	*stream << m_RecalcTime;
	*stream << m_PathBad;
	*stream << m_ObstacleAvoidPoint;
	*stream << m_CombatLeash;
	*stream << m_CombatLeashRadius;

	AI::Save(stream);
}
void MoverAI::Load (MemoryStream *stream)
{
	ObjectID temp;
	bool flag;
	AutoHeap local_heap (g_MoverAIHeap);

	*stream >> temp;					// get attached vehicle id
	if (m_Vehicle)
		Verify (temp == m_Vehicle->objectID);
	else
		Verify (temp == -1);
	*stream >> temp;					// get attached mwobject id
	if (m_MWObject)
		Verify (temp == m_MWObject->objectID);
	else
		Verify (temp == -1);

	*stream >> flag;
	delete m_CurMove;
	m_CurMove = NULL;
	if (flag)
	{
    int move;

		*stream >> move;
		switch (move)
		{
			case MOVE_LOOKOUT:
				m_CurMove = new CLookoutData (g_MissionGraph,this);
				break;
			case MOVE_PATROL:
				m_CurMove = new CPatrolData (g_MissionGraph,this);
				break;
			case MOVE_SEMIPATROL:
				m_CurMove = new CSemiPatrolData (g_MissionGraph,this);
				break;
			case MOVE_RIGIDPATROL:
				m_CurMove = new CRigidPatrolData (g_MissionGraph,this);
				break;
			case MOVE_FLEE:
				m_CurMove = new CFleeData (g_MissionGraph,this);
				break;
			case MOVE_FOLLOW:
				m_CurMove = new CFollowData (g_MissionGraph,this);
				break;
			case MOVE_SIT:
				m_CurMove = new CSitData (g_MissionGraph,this);
				break;
			case MOVE_LOCPOINT:
				m_CurMove = new CMoveLocPointData (g_MissionGraph,this);
				break;
			case MOVE_OBJECT:
				m_CurMove = new CMoveObjectData (g_MissionGraph,this);
				break;
			default:
				STOP (("unknown type of move command in save game file"));
				break;
		}
		Check_Pointer (m_CurMove);
		m_CurMove->Load (stream);
	}
	*stream >> flag;
	delete m_CurPath;
	m_CurPath = NULL;
	if (flag)
	{
		AutoHeap local_heap (g_RailHeap);
		m_CurPath = new CRailPath (g_MissionGraph,this);
		Check_Pointer (m_CurPath);
		m_CurPath->Load (stream);
	}
	*stream >> m_TimeNotMoving;
	*stream >> m_MoveHead;
	*stream >> m_OutVec;
	*stream >> m_OldMoveHead;
	*stream >> m_NewMoveHead;
	*stream >> m_NewOutVec;
	*stream >> m_SpeedRequest;
	*stream >> m_NewSpeedRequest;
	*stream >> m_EndSpeedRequest;
	*stream >> m_NewEndSpeedRequest;
	*stream >> m_OrderSpeed;
	*stream >> m_StartJump;
	*stream >> m_BurnTime;
	*stream >> m_NeedHeadingAdjust;
	*stream >> m_NeedSpeedAdjust;
	*stream >> m_Jumping;
	*stream >> m_JumpRequested;

	*stream >> m_PathBlocked;

	*stream >> temp;
	if (temp != -1)
	{
		Check_Object (NameTable::GetInstance());
		m_RamTarget = Cast_Object (MWObject * ,NameTable::GetInstance()->FindData(temp));
	}
	else
	{
		m_RamTarget = NULL;
	}

	*stream >> m_PathBlockedCell;
	*stream >> flag;
	delete m_PathBlockedRect;
	m_PathBlockedRect = NULL;
	if (flag)
	{
		m_PathBlockedRect = new Rect4D ();
		Check_Pointer (m_PathBlockedRect);
		m_PathBlockedRect->Load (stream);
	}
	*stream >> m_PathBlockedRectTimeDelta;
	*stream >> m_LockTime;
	*stream >> m_RecalcTime;
	*stream >> m_PathBad;
	*stream >> m_ObstacleAvoidPoint;
	*stream >> m_CombatLeash;
	*stream >> m_CombatLeashRadius;

	AI::Load (stream);
}

bool MoverAI::AllowTenMeter (void)
{
	Check_Object (this);
	Check_Object (m_Vehicle);
	if (m_Vehicle->IsDerivedFrom (Mech::DefaultData))
		return false;
	OBB *obb;
	CollisionVolume *col;
	col = m_Vehicle->GetHierarchicalVolume ();
	if (!col)
		col = m_Vehicle->GetSolidVolume ();
	if (col)
	{
		OBB world_bounds;
		world_bounds.Multiply(col->m_localSpaceBounds, m_Vehicle->GetLocalToWorld());
		obb = &(world_bounds);
		ExtentBox box (*obb);
		if (((box.maxX - box.minX) <= 6.0f) && (((box.maxZ - box.minZ) <= 6.0f)))
			return true;
	}

	return false;
}

bool MoverAI::PointIsOK(const Stuff::Point3D& point) const
{
	AutoHeap local_heap (g_MoverAIHeap);
	Check_Object (this);
	Check_Object (MW4AI::g_MissionGraph);
	Check_Object (m_Vehicle);

//	if (LockedByMe (point))
//		return true;

	Check_Object (m_Vehicle->GetGameModel ());
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;

	int usage = MW4AI::ConvertTypetoUsage (model->moveTypeFlag);
	int movepass = ConvertUsagetoMovePass (usage);
	Check_Object (this);
	int sqrindex[9][2] = {
		{-1,-1},
		{0,-1},
		{1,-1},
		{-1,0},
		{1,0},
		{-1,1},
		{0,1},
		{1,1},
		{0,0}
	};
	int cx,cz,sx,sz;

	sx = (int) point.x/10;
	sz = (int) point.z/10;
	int i;
	for (i=0;i<9;i++)
	{
		cx = sx+sqrindex[i][0];
		cz = sz+sqrindex[i][1];

		if (BlockerLocal (cx,cz) == (MWObject *)this)
			continue;

		if (!g_MissionGraph->MoveTo (cx,cz,0,movepass))
		{
			return false;
		}
	}
	return true;

#pragma warning (disable : 4800)
//	return(!(CheckCanPass(MW4AI::g_MissionGraph->Passable (point.x,point.z) , movepass)));
#pragma warning (default : 4800)
}

void MoverAI::NotifyNoPath(void)
{
	Check_Object (this);
	m_RecalcTime = gos_GetElapsedTime () + ((Random::GetFraction ()*2.0f)+1.0f);
	m_PathBad = true;
	SetMoveHead (Stuff::Point3D (-1,-1,-1),0);
	SetOrderSpeed (0);
	CommandEntry *com = CreateKillTurnToCommand ();
	Check_Pointer (com);
	ExecuteCommand (com);
}

void MoverAI::NotifyReachedDestination(void)
{
	Check_Object (this);
}

void MoverAI::NotifyCollided(Entity *who)
{
	Check_Object (this);
}

bool MoverAI::EqualRamTarget (Entity *target)
{
	Check_Object (this);
	if (!m_RamTarget)
		return false;

	Entity *comp1=NULL,*comp2=NULL;
	if (target->IsDerivedFrom (AI::DefaultData))
	{
		comp1 = ((AI*)target)->getEntity();
	}
	else if (target->IsDerivedFrom (MWPlayer::DefaultData))
	{
		comp2 = ((MWPlayer *)target)->vehicle;
	}
	else if (target->IsDerivedFrom (MWObject::DefaultData))
	{
		comp1 = (MWObject *) target;
	}

	if (m_RamTarget->IsDerivedFrom (AI::DefaultData))
	{
		comp2 = ((AI*)m_RamTarget)->getEntity ();
	}
	else if (m_RamTarget->IsDerivedFrom (MWPlayer::DefaultData))
	{
		comp2 = ((MWPlayer *)m_RamTarget)->vehicle;
	}
	else if (m_RamTarget->IsDerivedFrom (MWObject::DefaultData))
	{
		comp2 = (MWObject *) m_RamTarget;
	}
	if (comp1 == comp2)
		return true;
	return false;
}

void MoverAI::SlopeCollide (void)
{
	Check_Object (this);
	HandleObstacle ();
}

void MoverAI::HandleObstacle (MW4AI::Rect4D *p1,Adept::Entity *p2)
{
	Check_Object (this);
	Check_Object (m_Vehicle);
	Check_Object (m_Vehicle->GetGameModel ());
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	AutoHeap local_heap (g_MoverAIHeap);
	if (!m_CurMove)
		return;
	if (p1)
	{
		if ((!p1->who) || (!p1->who->IsDerivedFrom (MoverAI::DefaultData)))
		{
			if ((m_CurMove) && (m_CurMove->MoveType () == MOVE_RIGIDPATROL))
				m_CurMove->Done (true);
			else
			{
				AddTempLockRect (*p1);
				RecalcPath(gos_GetElapsedTime ());
//				UnTempLock ();
			}
			return;
		}
		else
		{
			Rect4D *newrect;

			MoverAI *mover;
			mover = Cast_Object (MoverAI *, p1->who);
			if (!mover->m_PathBlocked)
			{
				newrect = new Rect4D;
				NotifyNoPath ();
				PathDone (gos_GetElapsedTime (),true);
				m_PathBad = false;
				m_LockTime = gos_GetElapsedTime ()+((Random::GetFraction ()*2.0f)+1.0f);
				m_PathBlocked = true;
				*newrect = *p1;
				newrect->who = this;
				m_PathBlockedRect = newrect;
				m_PathBlockedRectTimeDelta = newrect->time - ((int) (gos_GetElapsedTime () * Rect4DTimeDelta));
			}
			else
			{
				AddTempLockRect (*p1);
				RecalcPath(gos_GetElapsedTime ());
//				UnTempLock ();
			}
		}
	}
	else if (p2)
	{
		if (p2->IsDerivedFrom (Bridge::DefaultData))
			return;
//		if ((!p2->IsDerivedFrom (Vehicle::DefaultData)) && (!p2->IsDerivedFrom (AI::DefaultData)))
		{
				int mask = 0;
				switch (model->moveTypeFlag)
				{
					case MWObject__GameModel::LEG_MOVETYPE:
						mask = LEGPASS_FLAG;
						break;
					case MWObject__GameModel::LEGJUMP_MOVETYPE:
						mask = LEGPASS_FLAG;
						break;
					case MWObject__GameModel::TRACK_MOVETYPE:
						mask = TRACKPASS_FLAG;
						break;
					case MWObject__GameModel::WHEEL_MOVETYPE:
						mask = WHEELPASS_FLAG;
						break;
					case MWObject__GameModel::HOVER_MOVETYPE:
						mask = HOVERPASS_FLAG;
						break;
					case MWObject__GameModel::WATER_MOVETYPE:
						mask = WATERPASS_FLAG;
						break;
					default:
						mask = BLOCKED_FLAGS;
						break;
				}
				Point3D loc,buildloc;

				Verify (m_Vehicle);
				loc = (Point3D) m_Vehicle->GetLocalToWorld ();
				buildloc = (Point3D) p2->GetLocalToWorld ();

				Rect4D lockrect;
				OBB *obb;
				CollisionVolume *col;
				col = p2->GetHierarchicalVolume ();
				if (!col)
					col = p2->GetSolidVolume ();
				if (col)
				{
					OBB world_bounds;
					world_bounds.Multiply(col->m_localSpaceBounds, p2->GetLocalToWorld());
					obb = &(world_bounds);
					ExtentBox box (*obb);
					int x,z;
					int minx,maxx,minz,maxz;

					minx = (int) box.minX;
					maxx = (int) box.maxX;
					minz = (int) box.minZ;
					maxz = (int) box.maxZ;
					minx /= BLOCK_GRID_RES;
					maxx /= BLOCK_GRID_RES;
					minz /= BLOCK_GRID_RES;
					maxz /= BLOCK_GRID_RES;
					if (minx < (MinX/10))
						minx = (int) (MinX/10);
					if (minz < (MinZ/10))
						minz = (int) (MinZ/10);
					if (maxx >= (MaxX/10.0f))
						maxx = (int) (MaxX/10)-1;
					if (maxz >= (MaxZ/10.0f))
						maxz = (int) (MaxZ/10)-1;

					lockrect.left = box.minX;
					lockrect.right = box.maxX;
					lockrect.top = box.minZ;
					lockrect.bottom = box.maxZ;

					bool mark = true;
					if (p2->IsDerivedFrom (Vehicle::DefaultData))
					{
						Vehicle *veh;
						veh = Cast_Object (Vehicle *,p2);
						if (veh->GetAI ())
						{
							if (veh->GetAI ()->IsDerivedFrom (ShooterAI::DefaultData))
								mark = true;
							else
								mark = false;
						}
						else
							mark = true;
					}
					if (mark)
					{
						for (x=minx;x<=maxx;x++)
							for (z=minz;z<=maxz;z++)
								MW4AI::g_MissionGraph->PassableLocal (x,z,(unsigned short) mask);
					}
				}
				else
				{
					bool mark = true;
					if (p2->IsDerivedFrom (Vehicle::DefaultData))
					{
						Vehicle *veh;
						veh = Cast_Object (Vehicle *,p2);
						if (veh->GetAI ())
						{
							if (veh->GetAI ()->IsDerivedFrom (ShooterAI::DefaultData))
								mark = true;
							else
								mark = false;
						}
						else
							mark = true;
					}
					if (mark)
					{
						MW4AI::g_MissionGraph->Passable (buildloc.x,buildloc.z,(unsigned short) mask);
						lockrect.left = buildloc.x-2.5f;
						lockrect.right = buildloc.x+2.5f;
						lockrect.top = buildloc.z-2.5f;
						lockrect.bottom = buildloc.z+2.5f;
					}
				}

			if ((m_CurMove) && (m_CurMove->MoveType () == MOVE_RIGIDPATROL))
				m_CurMove->Done (true);

			Check_Object (p2);
			Check_Object (m_Vehicle);
			Vector3D vector_to_target;
			Point3D local_to_world(m_Vehicle->GetLocalToWorld());
			Point3D target_local_to_world(p2->GetLocalToWorld());

			vector_to_target.Subtract(target_local_to_world, local_to_world);
			if (vector_to_target.x < 0)
				lockrect.left -= BLOCK_GRID_RES;
			else if (vector_to_target.x > 0)
				lockrect.right += BLOCK_GRID_RES;

			if (vector_to_target.z < 0)
				lockrect.top -= BLOCK_GRID_RES;
			else if (vector_to_target.z > 0)
				lockrect.bottom += BLOCK_GRID_RES;

			if (Abs (vector_to_target.x) > Abs (vector_to_target.z))
			{
				lockrect.top -= BLOCK_GRID_RES;
				lockrect.bottom += BLOCK_GRID_RES;
			}
			else if (Abs (vector_to_target.x) < Abs (vector_to_target.z))
			{
				lockrect.left -= BLOCK_GRID_RES;
				lockrect.right += BLOCK_GRID_RES;
			}

			AddTempLockRect (lockrect);
			RecalcPath(gos_GetElapsedTime ());
//			UnTempLock ();
			CommandEntry *com;
			com = CreateSetSpeedCommand (0);
			Check_Pointer (com);
			ExecuteCommand (com);
		}
#if 0
		else
		{
			ClearPathLock (false,false);
			PathLock (false,false);				// should cause the pathlock to fail then wait for a time to recalc
			if (!m_PathBlockedRect)
			{
				NotifyNoPath ();
				PathDone (gos_GetElapsedTime (),true);
				m_PathBad = false;
				RecalcPath(gos_GetElapsedTime ());
				CommandEntry *com;
				com = CreateSetSpeedCommand (0);
				Check_Pointer (com);
				ExecuteCommand (com);
			}
		}
#endif
	}
	else
	{
		Point3D loc,buildloc;

		Verify (m_Vehicle);
		loc = (Point3D) m_Vehicle->GetLocalToWorld ();

		int mask = 0;
		switch (model->moveTypeFlag)
		{
			case MWObject__GameModel::LEG_MOVETYPE:
				mask = LEGPASS_FLAG;
				break;
			case MWObject__GameModel::LEGJUMP_MOVETYPE:
				mask = LEGPASS_FLAG;
				break;
			case MWObject__GameModel::TRACK_MOVETYPE:
				mask = TRACKPASS_FLAG;
				break;
			case MWObject__GameModel::WHEEL_MOVETYPE:
				mask = WHEELPASS_FLAG;
				break;
			case MWObject__GameModel::HOVER_MOVETYPE:
				mask = HOVERPASS_FLAG;
				break;
			case MWObject__GameModel::WATER_MOVETYPE:
				mask = WATERPASS_FLAG;
				break;
		}
		OBB *obb;
		CollisionVolume *col;
		col = m_Vehicle->GetHierarchicalVolume ();
		if (!col)
			col = m_Vehicle->GetSolidVolume ();
		if (col)
		{
			OBB world_bounds;
			world_bounds.Multiply(col->m_localSpaceBounds, m_Vehicle->GetLocalToWorld());
			obb = &(world_bounds);
			ExtentBox box (*obb);
			int x,z;
			int minx,maxx,minz,maxz;

			UnitVector3D forward;
			Point3D fordir;
			m_Vehicle->GetLocalToWorld().GetLocalForwardInWorld(&forward);
			fordir = forward;
			fordir *= obb->sphereRadius;
			minx = (int) (box.minX+fordir.x);
			maxx = (int) (box.maxX+fordir.x);
			minz = (int) (box.minZ+fordir.z);
			maxz = (int) (box.maxZ+fordir.z);
			minx /= BLOCK_GRID_RES;
			maxx /= BLOCK_GRID_RES;
			minz /= BLOCK_GRID_RES;
			maxz /= BLOCK_GRID_RES;
			if (minx < (MinX/10))
				minx = (int) (MinX/10);
			if (minz < (MinZ/10))
				minz = (int) (MinZ/10);
			if (maxx >= (MaxX/10.0f))
				maxx = (int) (MaxX/10)-1;
			if (maxz >= (MaxZ/10.0f))
				maxz = (int) (MaxZ/10)-1;

			for (x=minx;x<=maxx;x++)
			{
				for (z=minz;z<=maxz;z++)
				{
					MW4AI::g_MissionGraph->PassableLocal (x,z,(unsigned short) mask);
				}
			}
		}
		else
			MW4AI::g_MissionGraph->Passable	(loc.x,loc.z,(unsigned short) mask);

		if ((m_CurMove) && (m_CurMove->MoveType () == MOVE_RIGIDPATROL))
			m_CurMove->Done (true);
		if ((model->moveTypeFlag != MWObject__GameModel::WATER_MOVETYPE) || (!m_Deactive))
			RecalcPath(gos_GetElapsedTime ());
	}
}

bool MoverAI::ReactToCollision(Stuff::DynamicArrayOf<CollisionData> *collisions,bool& shoulddamage)
{
	Check_Object (this);

	shoulddamage = true;
	inherited::ReactToCollision(collisions,shoulddamage);

	if ((getEntity() != 0) &&
		(getEntity()->IsPlayerVehicle() == false) &&
		(g_AlwaysIgnoreObstacles == true))
	{
		bool player_vehicle = false;

		{for (int i = 0; i < collisions->GetLength(); ++i)			// check for player vehicle
		{
			CollisionData *data = &(*collisions)[i];
			
			if (data->m_otherEntity->IsPlayerVehicle() == true)
			{
				player_vehicle = true;
			}
		}}

		if (player_vehicle == false)
		{
			shoulddamage = false;
			return false;
		}
	}

	if (m_EnteringFieldBase != FIELDBASE_NOT_ACTIVE)
	{
		shoulddamage = false;
		return false;
	}

	Point3D loc (m_Vehicle->GetLocalToWorld ());
	if (loc.y <= -1500.0f)
	{
		shoulddamage = false;
		return false;
	}
	if ((m_BoardingDropShip != DROP_NOT_ACTIVE) && (m_BoardingDropShip != DROP_FIRST_POINT))
	{
		shoulddamage = false;
		return false;					// ignore all collisions when trying to board dropship
	}

	Check_Object(this);

	int i;
	if (IsDerivedFrom (PlayerAI::DefaultData))
	{
		for ( i=0; i<collisions->GetLength(); ++i)			// check for player vehicle
		{
			CollisionData *data = &(*collisions)[i];

			if (m_Vehicle->GetAlignment () == data->m_otherEntity->GetAlignment ())
				shoulddamage = false;
		}
	}

	if (executionState->GetState() == ExecutionStateEngine::NeverExecuteState)
		return true;

	if (Adept::Player::GetInstance())
	{
		MWPlayer *player;
		Check_Object (Adept::Player::GetInstance());				
		player = Cast_Object (MWPlayer *,Adept::Player::GetInstance());

		for ( i=0; i<collisions->GetLength(); ++i)			// check for player vehicle
		{
			CollisionData *data = &(*collisions)[i];
/*
			if (data->m_otherEntity->IsDerivedFrom (Bridge::DefaultData))
			{
				shoulddamage = false;
				return false;
			}
*/
			if (player->vehicle == data->m_otherEntity)
			{
				if (m_Vehicle->GetAlignment () == player->vehicle->GetAlignment ())
					shoulddamage = false;
				HandleObstacle (data->m_otherEntity);
				return true;
			}
		}
	}
	
	
	if (m_MoveHead == Stuff::Point3D (-1,-1,-1))			// ai should not be moving to handle collision
		return true;

	if (m_CheapMover)				// outside player area so ignore collisions
	{
		shoulddamage = false;
		return false;
	}

	Scalar heading_adjustment,absadjust;
	Point3D distance_to_target;
	LinearMatrix4D tempmat (m_Vehicle->GetLocalToWorld ());
	tempmat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);
	distance_to_target.MultiplyByInverse(m_MoveHead,tempmat);
	if (m_OrderSpeed < 0)			// reverse direction
	{
		distance_to_target.x = -distance_to_target.x;
		distance_to_target.z = -distance_to_target.z;
	}
	if (Small_Enough(distance_to_target.z) && Small_Enough(distance_to_target.x))
		distance_to_target.z = 1.0f;
	heading_adjustment = Arctan(distance_to_target.x, distance_to_target.z);
	absadjust = heading_adjustment<0?-1.0f*heading_adjustment:heading_adjustment;

	if ((absadjust > Pi_Over_4) && (!m_Jumping))
	{
		return false;				// ai is trying to turn away from a collision, so ignore them for now.
	}

	for (i=0; i<collisions->GetLength(); ++i)
	{
		CollisionData *data = &(*collisions)[i];

		Check_Object(data);
		Check_Object(data->m_otherEntity);
		Verify(this != data->m_otherEntity);
		Verify(GetInterestLevel() != DormantInterestLevel);
		Check_Object(m_Vehicle);
//		if ((data->m_otherEntity->IsACollider()) || (data->m_otherEntity->IsACollide ()))
		{
			Vector3D vector_to_target;
			Point3D local_to_world(m_Vehicle->GetLocalToWorld());
			Point3D target_local_to_world(data->m_otherEntity->GetLocalToWorld());
			if ((data->m_otherEntity->GetWalkThruType () == AnythingCanWalkThruType) && (m_Vehicle->GetClassID () == MechClassID))
				return true;
			vector_to_target.Subtract(target_local_to_world, local_to_world);
			UnitVector3D current_motion;
			if (m_OrderSpeed < 0)
				m_Vehicle->GetLocalToWorld().GetLocalBackwardInWorld(&current_motion);
			else
				m_Vehicle->GetLocalToWorld().GetLocalForwardInWorld(&current_motion);
		
			Scalar dot_product;
			dot_product = current_motion * vector_to_target;
		
			if (dot_product <= 0.0f)				// collision behind us to ignore, trying to move away
				continue;
		
			if (EqualRamTarget (data->m_otherEntity))
			{
				shoulddamage = true;
				return true;
			}
		
			HandleObstacle (data->m_otherEntity);
			shoulddamage = false;
		}
		return true;
	}
	return false;			// unknown type so ignore collision
}

void MoverAI::PathDone (Stuff::Time till,bool failed)
{
	Check_Object (this);
	if (m_CurMove)
	{
		m_CurMove->PathDone (this,till,failed);
	}
	if (!failed)
	{
		if ((m_CurMove) && (m_CurMove->Done ()))
			NotifyReachedDestination ();
		else if (!m_CurMove)
			NotifyReachedDestination ();
		m_FailCount = 0;
	}
}

void MoverAI::SetMoveHead (const Stuff::Point3D& head,Stuff::Scalar speedreq,const Stuff::Point3D& outvec,Stuff::Scalar endspeed)
{
	Check_Object (this);
	m_NewMoveHead = head;
	m_NewOutVec = outvec;
	if (m_NewMoveHead == Point3D (-1,-1,-1))
	{
		speedreq = 0;
	}
	m_NewSpeedRequest = speedreq;
	m_NewEndSpeedRequest = endspeed < speedreq ? endspeed : speedreq;
}

void MoverAI::ClearPathLock (bool quitting,bool fromram)
{
	Check_Object (this);
	MOVER_LOGIC("ClearPathLock");
#if !defined(NO_TIMERS)
	my_AutoTimer fred ((void *) &tDynamicObstacleTime);
#endif

	AutoHeap local_heap (g_MoverAIHeap);
	Verify (m_LockedRects.size () == 0);
	stlport::vector<Rect4D *>::iterator iter2;
	for (iter2 = m_LockedRects.begin ();iter2 != m_LockedRects.end ();iter2++)
	{
		g_Rect4DHash->RemoveRect (*iter2);
		g_Rect4DHash->VerifyRectGone (*iter2);
		delete *iter2;
	}
	m_LockedRects.clear ();

	if (m_PathBlocked)
		return;

	if ((!quitting) && (!fromram))
		ClearRamLock ();
	stlport::vector<LockData *>::iterator iter;

	for (iter = m_PathLock.begin ();iter != m_PathLock.end ();iter++)
	{
		if (!quitting)
		{
			MW4AI::g_MissionGraph->PassableLocalOverride ((*iter)->locx,(*iter)->locz,(*iter)->olddata);
			MW4AI::RemoveBlockLocal ((*iter)->locx,(*iter)->locz,(MWObject *) this);
		}
		delete (*iter);
	}
	m_PathLock.clear ();

}

bool MoverAI::AddLockCell (const Stuff::Point3D& loc)
{
	Check_Object (this);
	unsigned short value;
	int locx,locz;
	Point3D pt;

	if ((m_BoardingDropShip != DROP_NOT_ACTIVE) && (m_BoardingDropShip != DROP_FIRST_POINT))
		return false;					// ignore all collisions when trying to board dropship

	if (loc.x < MW4AI::MinX)
		return true;
	if (loc.z < MW4AI::MinX)
		return true;
	if (loc.x >= MW4AI::MaxX)
		return true;
	if (loc.z >= MW4AI::MaxZ)
		return true;
	AutoHeap local_heap (g_MoverAIHeap);
	locx = (int) (loc.x);
	locz = (int) (loc.z);
//	locx = Truncate_Float_To_Word(loc.x);
//	locz = Truncate_Float_To_Word(loc.z);
	locx /= BLOCK_GRID_RES;
	locz /= BLOCK_GRID_RES;

	MWObject *who;

	who = MW4AI::BlockerLocal (locx,locz);
	if (who == (MWObject *) this)
		return true;
	if (who)
		return false;

	value = MW4AI::g_MissionGraph->PassableLocal (locx,locz);
	if (value & (MOVEPASSNOW_FLAG))
		return false;

	MW4AI::g_MissionGraph->PassableLocalOverride (locx,locz,(unsigned short)(value|MOVEPASSNOW_FLAG));
	if (MW4AI::AddBlockLocal (locx,locz,(MWObject *) this))
		m_PathLock.push_back (new LockData (locx,locz,value));

	return true;
}

bool MoverAI::CheckSquare (const Point3D& loc,Stuff::Scalar rad,int dir,int mask,bool tenmeter)
{
	Check_Object (this);
	int sqrindex[9][2] = {
		{-1,-1},
		{0,-1},
		{1,-1},
		{-1,0},
		{1,0},
		{-1,1},
		{0,1},
		{1,1},
		{0,0}
	};
	int cx,cz,sx,sz;

	sx = (int) loc.x/10;
	sz = (int) loc.z/10;
	AutoHeap local_heap (g_MoverAIHeap);
	tenmeter = true;
	if (tenmeter)
	{
			cx = sx+sqrindex[8][0];
			cz = sz+sqrindex[8][1];

			if (!(BlockerLocal (cx,cz) == (MWObject *)this))
				if (!g_MissionGraph->MoveTo (cx,cz,dir,mask))
					return false;
	}
#if 0
	else
	{
		int i;
		for (i=0;i<9;i++)
		{
			cx = sx+sqrindex[i][0];
			cz = sz+sqrindex[i][1];

			if (BlockerLocal (cx,cz) == (MWObject *)this)
				continue;

			if (!g_MissionGraph->MoveTo (cx,cz,dir,mask))
			{
				return false;
			}
		}
	}
#endif
	return true;
}

bool MoverAI::LockSquare (const Point3D& loc,Stuff::Scalar rad,Point3D& culprit)
{
	Check_Object (this);
	int sqrindex[8][2] = {
		{-1,-1},
		{0,-1},
		{1,-1},
		{-1,0},
		{1,0},
		{-1,1},
		{0,1},
		{1,1}
	};
	int i;
	Point3D pt (0,0,0);

	AutoHeap local_heap (g_MoverAIHeap);
	for (i=0;i<8;i++)
	{
		pt.x = loc.x+(sqrindex[i][0]*rad);
		pt.z = loc.z+(sqrindex[i][1]*rad);

		if (LockedByMe (pt))
			continue;

		if (!AddLockCell (pt))
		{
			culprit = pt;
			return true;
		}
	}
	return false;
}

bool MoverAI::AddLockRect (Rect4D *newrect,bool carecollide,Stuff::Time till)
{
	Verify (false);
	delete newrect;
#if 0
	Check_Object (this);
	Rect4D *crect;
	Point3D loc;
	AutoHeap local_heap (g_MoverAIHeap);

	if ((m_BoardingDropShip != DROP_NOT_ACTIVE) && (m_BoardingDropShip != DROP_FIRST_POINT))
		return false;					// ignore all collisions when trying to board dropship

	if ((m_PathBlocked))
	{
		g_Rect4DHash->AddRect (newrect);
		m_LockedRects.push_back (newrect);
		return true;
	}
	if (!carecollide)
	{
		g_Rect4DHash->AddRect (newrect);
		m_LockedRects.push_back (newrect);
		return false;
	}
	crect = g_Rect4DHash->Collide (*newrect);
	if (crect)
	{
		Verify (m_Vehicle);
		loc = (Point3D) m_Vehicle->GetLocalToWorld ();
		UnitVector3D p1,p2;
		Point3D extra,otherloc;
		if (m_OrderSpeed < 0)
			m_Vehicle->GetLocalToWorld ().GetLocalBackwardInWorld (&p1);
		else
			m_Vehicle->GetLocalToWorld ().GetLocalForwardInWorld (&p1);
		Scalar dot = 5.0f;
		if (EqualRamTarget (crect->who))
		{
			dot = -5.0f;
			// ignore ram target
		}
		else if ((crect->who) && (crect->who->IsDerivedFrom (AI::DefaultData)))
		{
			dot = 1.0f;
#if 0
			AI* tempai;
			tempai = (AI*) crect->who;
			if (tempai->getEntity ())
			{
				otherloc = (Point3D) tempai->getEntity ()->GetLocalToWorld ();
				extra.Subtract (otherloc,loc);
				if (Small_Enough (extra.GetLength ()))
					dot = 0.1f;
				else
				{
					p2 = extra;
					dot = (p1.x*p2.x) + (p1.z*p2.z);
				}
			}
#endif
		}
		else if ((crect->who) && (crect->who->IsDerivedFrom (MWPlayer::DefaultData)))
		{
			dot = 1.0f;
#if 0
			MWPlayer *player;
			player = (MWPlayer *) crect->who;
			if (player->vehicle)
			{
				otherloc = (Point3D) player->vehicle->GetLocalToWorld ();
				extra.Subtract (otherloc,loc);
				if (Small_Enough (extra.GetLength ()))
					dot = 0.0f;
				else
				{
					p2 = extra;			// normalizes
					dot = (p1.x*p2.x) + (p1.z*p2.z);
				}
			}
#endif
		}
		else if ((crect->who) && (crect->who->IsDerivedFrom (Building::DefaultData)))
		{
			dot = -1.0f;
		}
		if (dot > 0)
		{
			HandleObstacle (crect);
			delete newrect;
			return true;
		}
		else
		{
			g_Rect4DHash->AddRect (newrect);
			m_LockedRects.push_back (newrect);
		}
	}
	else
	{
		g_Rect4DHash->AddRect (newrect);
		m_LockedRects.push_back (newrect);
	}
#endif
	return false;
}

void MoverAI::ClearTempLockRects (void)
{
	stlport::vector<Rect4D *>::iterator iter;
	
	for (iter = m_TempLockRects.begin ();iter != m_TempLockRects.end ();iter++)
	{
		delete (*iter);
	}
	m_TempLockRects.clear ();
}

void MoverAI::CalcTempLock (void)
{
	Check_Object (this);

	stlport::vector<Rect4D *>::iterator iter;
	
	for (iter = m_TempLockRects.begin ();iter != m_TempLockRects.end ();iter++)
	{
		Verify ((*iter));
		int sx,sy;
		int ex,ey;
		int cx,cy;
	
		sx = (int) (*iter)->left;
		sy = (int) (*iter)->top;
		ex = (int) (*iter)->right;
		ey = (int) (*iter)->bottom;
		sx /= BLOCK_GRID_RES;
		sy /= BLOCK_GRID_RES;
		ex /= BLOCK_GRID_RES;
		ey /= BLOCK_GRID_RES;
		AutoHeap local_heap (g_MoverAIHeap);
	
//		m_TempLock.clear ();
	
		for (cx = sx;cx <= ex;cx++)
		{
			for (cy = sy;cy<= ey;cy++)
			{
				unsigned short value;
				Point3D pt;
	
				if (cx < MW4AI::MinX)
					continue;
				if (cy < MW4AI::MinZ)
					continue;
				if (cx >= MW4AI::MaxX)
					continue;
				if (cy >= MW4AI::MaxZ)
					continue;
	
				value = MW4AI::g_MissionGraph->PassableLocal (cx,cy);
	
				MW4AI::g_MissionGraph->PassableLocalOverride (cx,cy,(unsigned short)(value|MOVEPASSNOW_FLAG));
				m_TempLock.push_back (new LockData (cx,cy,value));
			}
		}
	}
}

void MoverAI::CalcUnTempLock (void)
{
	Check_Object (this);
	stlport::vector<LockData *>::iterator iter;

	ClearTempLockRects ();

	if (m_TempLock.size () == 0)
		return;
	for (iter = m_TempLock.end ()-1;iter != m_TempLock.begin ();iter--)
	{
		MW4AI::g_MissionGraph->PassableLocalOverride ((*iter)->locx,(*iter)->locz,(*iter)->olddata);
		delete (*iter);
	}
	m_TempLock.clear ();
}

void MoverAI::CalcPathLock (CPathRequest *req)
{
	if (req)
	{
		Point3D loc (m_Vehicle->GetLocalToWorld ());
		Point3D end;
		CRailPath *node;
		CGridPath *grid;
		node = req->NodePath ();
		grid = req->GridPath ();
		if (node)
		{
			end = node->End();
		}
		else if (grid)
		{
			end = grid->End();
		}
		req->UpdateSrcDest (loc,end);
	}
	CalcTempLock ();
	if ((m_PathBlockedRect) && (m_PathBlockedRect->who))
	{

		Stuff::Point3D extra;
		float sx,sy,dx,dy;
		int ix,iy,ex,ey;
		int count;
		int startx,starty,endx,endy;

		extra = (Point3D) m_PathBlockedRect->who->GetLocalToWorld ();
		startx = (int) extra.x;
		starty = (int) extra.z;
		endx = (int) (((m_PathBlockedRect->right-m_PathBlockedRect->left)/2.0f) + m_PathBlockedRect->left);
		endy = (int) (((m_PathBlockedRect->bottom-m_PathBlockedRect->top)/2.0f) + m_PathBlockedRect->top);
		startx /= BLOCK_GRID_RES;
		starty /= BLOCK_GRID_RES;
		endx /= BLOCK_GRID_RES;
		endy /= BLOCK_GRID_RES;
		if (startx == endx)
			endx = startx+1;
		if (starty == endy)
			endy = starty+1;
		dx = (float) endx - startx;
		dy = (float) endy - starty;
		if (dx == 0)
			dx = 1;
		if (dy == 0)
			dy = 1;
		sx = dx < 0 ? dx*-1.0f : dx;
		sy = dy < 0 ? dy*-1.0f : dy;

		if (sx > sy)
		{
			dy = dy / sx;
			count = (int) (dx<0.0 ? dx*-1.0f : dx);
			dx = dx<0 ? -1.0f : 1.0f;
		}
		else
		{
			dx = dx / sy;
			count = (int) (dy<0.0 ? dy*-1.0f : dy);
			dy = dy<0 ? -1.0f : 1.0f;
		}

		sx = (float) startx;
		sy = (float) starty;
		ex = endx;
		ey = endy;
		ix = (int) (sx);
		iy = (int) (sy);
		while (count)
		{
			count--;
			sx += dx;
			sy += dy;
			ix = (int) (sx);
			iy = (int) (sy);
			unsigned short value;

			value = MW4AI::g_MissionGraph->PassableLocal (ix,iy);
			if (value & MOVEPASSNOW_FLAG)
				continue;
			MW4AI::g_MissionGraph->PassableLocalOverride (ix,iy,(unsigned short)(value|MOVEPASSNOW_FLAG));
			m_TempLock.push_back (new LockData (ix,iy,value));
		}

	}
}

void MoverAI::CalcPathUnlock (CPathRequest *reg)
{
	Check_Object (this);
	stlport::vector<LockData*>::iterator iter;
	for (iter = m_TempLock.begin ();iter != m_TempLock.end ();iter++)
	{
		MW4AI::g_MissionGraph->PassableLocalOverride ((*iter)->locx,(*iter)->locz,(*iter)->olddata);
	}
	CalcUnTempLock ();
}

bool MoverAI::PathLock (bool secondpass,bool fromram)
{
	if (g_AlwaysIgnoreObstacles == true)
		return (false);
	if (m_EnteringFieldBase != FIELDBASE_NOT_ACTIVE)
		return false;

	Check_Object (this);
	if (!m_Vehicle)
		return false;

	MOVER_LOGIC("PathLock");
#if !defined(NO_TIMERS)
	my_AutoTimer fred ((void *) &tDynamicObstacleTime);
#endif

	AutoHeap local_heap (g_MoverAIHeap);
	Check_Object (m_Vehicle);
	Check_Object (m_Vehicle->GetGameModel ());


	OBB *obb;
	CollisionVolume *col;
	col = m_Vehicle->GetHierarchicalVolume ();
	if (!col)
		col = m_Vehicle->GetSolidVolume ();
	Stuff::Scalar rad = 10.0f;
	if (!col)
		return false;

	OBB world_bounds;
	world_bounds.Multiply(col->m_localSpaceBounds, m_Vehicle->GetLocalToWorld());
	obb = &(world_bounds);
	if (!obb)
		return false;
	ExtentBox box (*obb);

	rad = obb->sphereRadius;
	rad *= 2.0f;

	Stuff::Scalar speed,movemps,slowtime;
	UnitVector3D forward;
	Point3D step,loc;
	int i,timelen;

	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;

	loc = (Point3D) m_Vehicle->GetLocalToWorld ();
	if (m_OrderSpeed < 0)
		m_Vehicle->GetLocalToWorld ().GetLocalBackwardInWorld (&forward);
	else
		m_Vehicle->GetLocalToWorld ().GetLocalForwardInWorld (&forward);
	step = forward;

	LockSquare (loc,rad,m_PathBlockedCell);
	AddLockCell (loc);

	if (m_CheapMover)
		return false;

	speed = m_Vehicle->currentSpeedMPS;
	if ((m_Vehicle->speedDemand == 0) || (m_ObstacleRequest))
	{
		MOVER_LOGIC("PathLock 2");
		if ((m_OrderSpeed == 0) || (m_ObstacleRequest))
		{
			m_PathBlocked = false;
			delete m_PathBlockedRect;
			m_PathBlockedRect = NULL;
		}
		if (!fromram)
			RamLock ();
		return false;
	}

	if (speed < 0)
	{
		movemps = -speed;
		Max_Clamp(movemps, -model->maxReverseSpeed);
		Min_Clamp(movemps, -model->minReverseSpeed);
		slowtime = movemps / (model->decceleration*model->reverseDeccelerationMultiplier);
	}
	else
	{
		movemps = speed;
		Max_Clamp(movemps, model->maxSpeed);
		Min_Clamp(movemps, model->minSpeed);
		slowtime = movemps / model->decceleration;
	}
	Min_Clamp (movemps,1);
	step *= movemps;

	slowtime += 1.0f;		//extra second of buffer
	timelen = (int) (slowtime);

	int maxi;
	int mask;
	bool tenmeter = AllowTenMeter ();
	Scalar delta;
	delta = 10.0f/movemps;
	step *= delta;

	maxi = (int) (timelen / delta);

	mask = MW4AI::ConvertTypetoUsage (model->moveTypeFlag);
	mask = MW4AI::ConvertUsagetoMovePass (mask);
	Min_Clamp (maxi,1);
	loc += step;
	{
		MOVER_LOGIC("PathLock 3");
		for (i=0;i<maxi;i++)
		{
			if ((!secondpass) && (!fromram))
			{
				if (!CheckSquare (loc,rad,0,mask,tenmeter))
				{
					ClearPathLock (false,false);
					PathLock (true,false);
					RecalcPath (gos_GetElapsedTime ());
					if (!fromram)
						RamLock ();
					return false;
				}
				loc += step;
			}
		}
	}

	m_PathBlocked = false;

	if (!fromram)
		RamLock ();
	return false;
}
void MoverAI::SetRamTarget (MWObject *obj)
{
	Check_Object (this);
	m_RamTarget = obj;
}
void MoverAI::ClearRamLock (void)
{
	Check_Object (this);
	if (m_RamTarget)
	{
		MoverAI *ai = NULL;
		MWObject *playervehicle;
		if (MWPlayer::GetInstance())
		{
			Entity *tent;
			tent = MWPlayer::GetInstance()->vehicle;
			playervehicle = Cast_Object (MWObject *,tent);
		}
		else
			playervehicle = NULL;

		if (m_RamTarget->IsDerivedFrom (MoverAI::DefaultData))
		{
			ai = (MoverAI *) m_RamTarget;
		}
		else if (m_RamTarget->GetAI ())
		{
			AI *tempai;
			tempai = m_RamTarget->GetAI ();
			if (tempai->IsDerivedFrom (MoverAI::DefaultData))
				ai = (MoverAI *) tempai;
		}
		if ((m_RamTarget == playervehicle) && (MWPlayer::GetInstance()))
			((MWPlayer *) MWPlayer::GetInstance())->ClearPathLock (false);
		else if (ai)
			ai->ClearPathLock (false,true);
	}
}

void MoverAI::RamLock (void)
{
	Check_Object (this);
	if (m_RamTarget)
	{
		MoverAI *ai = NULL;
		MWObject *playervehicle;
		if (MWPlayer::GetInstance())
		{
			Entity *tent;
			tent = MWPlayer::GetInstance()->vehicle;
			playervehicle = Cast_Object (MWObject *,tent);
		}
		else
			playervehicle = NULL;

		if (m_RamTarget->IsDerivedFrom (MoverAI::DefaultData))
		{
			ai = (MoverAI *) m_RamTarget;
		}
		else if (m_RamTarget->GetAI ())
		{
			AI *tempai;
			tempai = m_RamTarget->GetAI ();
			if (tempai->IsDerivedFrom (MoverAI::DefaultData))
				ai = (MoverAI *) tempai;
		}
		if ((m_RamTarget == playervehicle) && (MWPlayer::GetInstance()))
			((MWPlayer *) MWPlayer::GetInstance())->PathLock ();
		else if (ai)
			ai->PathLock (false,true);
	}
}

void MoverAI::RecalcPath (Stuff::Time till)
{
	Check_Object (this);
	Verify (m_Vehicle);
	AutoHeap local_heap (g_MoverAIHeap);
	if (m_CurPath)
	{
		if (m_FailCount > MAX_FAILS_PATROL)
		{
			m_FailCount = 0;
			if ((m_CurMove) && ((m_CurMove->MoveType () == MOVE_PATROL) || (m_CurMove->MoveType () == MOVE_SEMIPATROL)))
			{
				if (m_CurMove->MoveType () == MOVE_SEMIPATROL)
				{
					CSemiPatrolData *pat;
					pat = (CSemiPatrolData *) m_CurMove;
					if (pat->CurRequest () < (pat->RequestSize ()-1))
					{
						delete m_CurPath;
						m_CurPath = NULL;
						m_CurMove->PathDone (this,till,false);
						return;
					}
				}
				else
				{
					CPatrolData *pat;
					pat = (CPatrolData *) m_CurMove;
					if (pat->CurRequest () < (pat->RequestSize ()-1))
					{
						delete m_CurPath;
						m_CurPath = NULL;
						m_CurMove->PathDone (this,till,false);
						return;
					}
				}
			}
		}
		m_FailCount++;
		Point3D loc,end,outvec;
		loc = (Point3D) m_Vehicle->GetLocalToWorld ();
		end = m_CurPath->End ();

		if (m_ObstacleRequest)
		{
			if (!m_ObstacleRequest->Done ())
			{
				if (!m_ObstacleRequest->UpdateSrcDest (loc,end))
				{
					g_PathManager->RemoveRequest (m_ObstacleRequest);
					delete m_ObstacleRequest;
					m_ObstacleRequest = NULL;
				}
				else
				{
					delete m_CurPath;
					m_CurPath = NULL;
					return;
				}
			}
			else
			{
				Point3D pathend;
				Scalar dist,dx,dz;
				pathend = m_ObstacleRequest->GetEndPoint ();
				dx = pathend.x - end.x;
				dz = pathend.z - end.z;
				dist = (dx*dx) + (dz*dz);
				if (dist > 400.0f)			// new end is > 20 meters to this point so recalc
				{
					g_PathManager->RemoveRequest (m_ObstacleRequest);
					delete m_ObstacleRequest;
					m_ObstacleRequest = NULL;
				}
				else
				{
					delete m_CurPath;
					m_CurPath = NULL;
					return;
				}
			}
		}

		const Vehicle__GameModel *model = static_cast<const MechWarrior4::Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;

		gos_PushCurrentHeap (g_RailHeap);

		Verify (m_CurMove);

		m_ObstacleRequest = g_PathManager->AddRequest (gos_GetElapsedTime (),MW4AI::g_MissionGraph,loc,end,MW4AI::ConvertTypetoUsage (model->moveTypeFlag),this,AllowTenMeter ());
		delete m_CurPath;
		m_CurPath = NULL;
		gos_PopCurrentHeap ();
	}
	else
	{
		if (m_CurMove && (m_CurMove->MoveType () == MOVE_SEMIPATROL))
		{
			CSemiPatrolData *pat;
			pat = (CSemiPatrolData *) m_CurMove;
			Point3D loc;
			loc = (Point3D) m_Vehicle->GetLocalToWorld ();
			if (m_ObstacleRequest)
			{
				pat->UpdatePathRequest (loc,m_ObstacleRequest);
			}
			else
			{
				const Vehicle__GameModel *model = static_cast<const MechWarrior4::Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
				m_ObstacleRequest = pat->NeedPathRequest (loc,MW4AI::ConvertTypetoUsage (model->moveTypeFlag));
			}
		}
	}
}

void MoverAI::ExecuteMoveHeadCollision (Stuff::Time till)
{
	Check_Object (this);
	Verify (m_PathBlocked);
	Verify (m_Vehicle);
	CommandEntry *com;
	AutoHeap local_heap (g_MoverAIHeap);

	if (m_MoveHead != Stuff::Point3D (-1,-1,-1))
	{
		com = CreateTurnToCommand (m_MoveHead,m_OrderSpeed < 0);
		Check_Pointer (com);
		ExecuteCommand (com);
	}
	else
	{
		com = CreateKillTurnToCommand ();
		Check_Pointer (com);
		ExecuteCommand (com);
	}
	if (m_Vehicle->speedDemand != 0)
	{
		com = CreateSetSpeedCommand (0);
		Check_Pointer (com);
		ExecuteCommand (com);
	}
	Verify (m_PathBlockedRect);
	if (m_PathBlockedRect)
	{
		m_PathBlockedRect->time = (int) ((float) (till * Rect4DTimeDelta));
		m_PathBlockedRect->time += m_PathBlockedRectTimeDelta;
		Rect4D *crect;
		crect = g_Rect4DHash->Collide (*m_PathBlockedRect);
		Scalar dot = -5.0f;
		if (crect)
		{
			Point3D loc;
			UnitVector3D p1,p2;
			Point3D extra,otherloc;
			if (m_OrderSpeed < 0)
				m_Vehicle->GetLocalToWorld ().GetLocalBackwardInWorld (&p1);
			else
				m_Vehicle->GetLocalToWorld ().GetLocalForwardInWorld (&p1);
			loc = (Point3D) m_Vehicle->GetLocalToWorld ();
			if ((crect->who) && (crect->who->IsDerivedFrom (AI::DefaultData)))
			{
				dot = 1.0f;
#if 0
				AI* tempai;
				tempai = (AI*) crect->who;
				if (tempai->getEntity ())
				{
					otherloc = (Point3D) tempai->getEntity ()->GetLocalToWorld ();
					extra.Subtract (otherloc,loc);
					if (Small_Enough (extra.GetLength ()))
						dot = 0.0f;
					else
					{
						p2 = extra;			// normalizes
						dot = (p1.x*p2.x) + (p1.z*p2.z);
					}
				}
#endif
			}
			else if ((crect->who) && (crect->who->IsDerivedFrom (MWPlayer::DefaultData)))
			{
				dot = 1.0f;
#if 0
				MWPlayer *player;
				player = (MWPlayer *) crect->who;
				if (player->vehicle)
				{
					otherloc = (Point3D) player->vehicle->GetLocalToWorld ();
					extra.Subtract (otherloc,loc);
					if (Small_Enough (extra.GetLength ()))
						dot = 0.0f;
					else
					{
						p2 = extra;			// normalizes
						dot = (p1.x*p2.x) + (p1.z*p2.z);
					}
				}
#endif
			}
		}
		if (dot >= 0)
		{
			if (gos_GetElapsedTime () > m_LockTime)
			{
				if (!((m_CurMove) && (m_CurMove->MoveType () == MOVE_RIGIDPATROL)))
				{
					m_PathBlocked = false;
//					m_ObstacleAvoidMode = false;
					ClearPathLock (false,false);
					RecalcPath (till);
					delete m_PathBlockedRect;
					m_PathBlockedRect = NULL;
				}
			}
		}
		else
		{
			m_PathBlocked = false;
//			m_ObstacleAvoidMode = false;
			delete m_PathBlockedRect;
			m_PathBlockedRect = NULL;
		}
	}
}

bool MoverAI::ObstacleNearBy (const Point3D& loc)
{
	return false;
/*
	if (g_AlwaysIgnoreObstacles == true)
	{
		return (false);
	}

	int x,y;
	int count;

	x = (int) (loc.x / MACROBLOCK_GRID_RES);
	y = (int) (loc.z / MACROBLOCK_GRID_RES);
	count = 0;
	count = g_MissionGraph->MacroPassable (loc.x,loc.z);
	return (count != 0);
*/	
}

void MoverAI::ExecuteMoveHead (Stuff::Time till)
{
	Check_Object (this);
	if (!m_Vehicle)
		return;

	bool m_SpeedHitSlow = false;
	AutoHeap local_heap (g_MoverAIHeap);
	if (m_Jumping)
		Jump (till,m_MoveHead);
	if (m_NewMoveHead != m_MoveHead)
	{
		if (m_Jumping)
		{
			m_BurnTime = -1.0f;
			Jump (till,m_MoveHead);
		}
		if (m_MoveHead != Stuff::Point3D (-1,-1,-1))
		{
			m_OldMoveHead = m_MoveHead;
			ConstructRect (m_OldMoveHead,m_NewMoveHead,5.0f);
		}
		else
		{
			m_OldMoveHead = (Point3D) m_Vehicle->GetLocalToWorld ();
			ConstructRect (m_OldMoveHead,m_NewMoveHead,5.0f);
		}
		m_MoveHead = m_NewMoveHead;
		m_NeedHeadingAdjust = true;
	}
	if (m_NewOutVec != m_OutVec)
	{
		m_OutVec = m_NewOutVec;
	}
	if (m_ObstacleRequest)
	{
		m_NewSpeedRequest = m_NewEndSpeedRequest = 0;
		m_MoveHead = Point3D (-1,-1,-1);
	}
	m_EndSpeedRequest = m_NewEndSpeedRequest;
	if (m_NewSpeedRequest != m_Vehicle->speedDemand)
	{
		m_SpeedRequest = m_NewSpeedRequest;
		m_NeedSpeedAdjust = true;
	}
	if (m_JumpRequested)
	{
		Verify (!m_Jumping);
		Jump (till,m_MoveHead);
	}


	Point3D loc(0,0,0);
	CommandEntry *com;
	Stuff::Scalar speed;

//	CheckObstacle (till);

	if (m_PathBlocked)
	{
		ExecuteMoveHeadCollision (till);
		return;
	}

	if (m_MoveHead == Stuff::Point3D (-1,-1,-1))
	{
		m_NeedHeadingAdjust = false;
		m_SpeedRequest = 0;
		com = CreateKillTurnToCommand ();
		Check_Pointer (com);
		ExecuteCommand (com);
		com = CreateSetSpeedCommand (m_SpeedRequest);
		Check_Pointer (com);
		ExecuteCommand (com);
		return;
	}

	loc = (Point3D) m_Vehicle->GetLocalToWorld ();

	Point3D realheadpoint;
	if (((m_BoardingDropShip == DROP_NOT_ACTIVE) || ((m_BoardingDropShip == DROP_FIRST_POINT))) && (m_EnteringFieldBase == FIELDBASE_NOT_ACTIVE))
	{
		switch (HeadPointInRect (m_Vehicle->GetLocalToWorld (),m_Vehicle->currentSpeedMPS,GetTimeSlice (till)))
		{
			case LEFT_INSIDE_HIT:
				realheadpoint = m_MoveHead;
//				realheadpoint = RightHeadPoint (loc,m_MoveHead);
//				m_SpeedHitSlow = true;
				break;
			case RIGHT_INSIDE_HIT:
				realheadpoint = m_MoveHead;
//				realheadpoint = LeftHeadPoint (loc,m_MoveHead);
//				m_SpeedHitSlow = true;
				break;
			case TOP_INSIDE_HIT:
				realheadpoint = m_MoveHead;
				break;
			case BOTTOM_INSIDE_HIT:
				realheadpoint = m_MoveHead;
				break;
			case LEFT_OUTSIDE_HIT:
				realheadpoint = m_MoveHead;
//				realheadpoint = RightHeadPoint (loc,m_MoveHead);
//				m_SpeedHitSlow = true;
				break;
			case RIGHT_OUTSIDE_HIT:
				realheadpoint = m_MoveHead;
//				realheadpoint = LeftHeadPoint (loc,m_MoveHead);
//				m_SpeedHitSlow = true;
				break;
			case TOP_OUTSIDE_HIT:
				realheadpoint = m_MoveHead;
				break;
			case BOTTOM_OUTSIDE_HIT:
				realheadpoint = m_MoveHead;
				break;
			case NO_HIT:
				realheadpoint = m_MoveHead;
				break;
		}
	}
	else
	{
			realheadpoint = m_MoveHead;
	}

	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;

	speed = m_Vehicle->speedDemand;
	loc.y = 0;

	Verify (realheadpoint != Stuff::Point3D (-1,-1,-1));
	Scalar timeturn;
	Point3D a,b;
	Scalar dist,movemps,heading_adjustment,absadjust;
	Point3D distance_to_target;

	LinearMatrix4D tempmat (m_Vehicle->GetLocalToWorld ());
	tempmat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);
	distance_to_target.MultiplyByInverse(realheadpoint,tempmat);
	if ((m_OrderSpeed < 0) || (m_SpeedRequest < 0))			// reverse direction
	{
		distance_to_target.x = -distance_to_target.x;
		distance_to_target.z = -distance_to_target.z;
	}
	if (Small_Enough(distance_to_target.z) && Small_Enough(distance_to_target.x))
		distance_to_target.z = 1.0f;
	heading_adjustment = Arctan(distance_to_target.x, distance_to_target.z);
	absadjust = heading_adjustment<0?-1.0f*heading_adjustment:heading_adjustment;

	a.Subtract (loc,realheadpoint);
	dist = a.GetLength   ();

	if ((absadjust > 0.05f))
	{
		m_NeedHeadingAdjust = true;
		if (ObstacleNearBy (loc))
		{
			if (absadjust > 0.05f)
			{
				m_SpeedRequest = 0;
				m_NeedSpeedAdjust = true;
			}
		}

		if (absadjust > Pi_Over_4)
		{
			m_SpeedRequest = 0;
			m_NeedSpeedAdjust = true;
		}
	}
	if ((m_OrderSpeed >= 0) && (m_SpeedRequest >= 0))
	{
		Stuff::Scalar slowtime,slowdist,slowspeed;
		if (m_OutVec == Stuff::Point3D (-1,-1,-1))
		{
			slowspeed = m_EndSpeedRequest;
		}
		else
		{
			LinearMatrix4D tempmat (m_Vehicle->GetLocalToWorld ());
			tempmat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);
			distance_to_target.MultiplyByInverse(m_OutVec,tempmat);
			if (m_OrderSpeed < 0)			// reverse direction
			{
				distance_to_target.x = -distance_to_target.x;
				distance_to_target.z = -distance_to_target.z;
			}
			if (Small_Enough(distance_to_target.z) && Small_Enough(distance_to_target.x))
				distance_to_target.z = 1.0f;
			heading_adjustment = Arctan(distance_to_target.x, distance_to_target.z);
			timeturn = heading_adjustment / model->fullStopTurnRate;
			timeturn = timeturn<0 ? timeturn*-1.0f : timeturn;
			if (Small_Enough (timeturn))
				slowspeed = 0;
			else
				slowspeed = 10.0f/timeturn;
		}

	//	if (m_OutVec == Stuff::Point3D (-1,-1,-1))
		{
			movemps = m_Vehicle->currentSpeedMPS;
			movemps = movemps<0 ? movemps*-1.0f : movemps;
			if (movemps > slowspeed)
			{
				movemps -= slowspeed;
				if (speed < 0)
				{
					slowtime = movemps / (model->decceleration*model->reverseDeccelerationMultiplier);
					slowdist = (0.5f * (model->decceleration*model->reverseDeccelerationMultiplier) * (slowtime*slowtime)) + (movemps*slowtime);
				}
				else
				{
					slowtime = movemps / model->decceleration;
					slowdist = (0.5f * model->decceleration * (slowtime*slowtime)) + (movemps*slowtime);
				}
				a.Subtract (loc,realheadpoint);
				dist = a.GetLength  ();
				if (m_OldMoveHead != Stuff::Point3D (-1,-1,-1))
				{
					Stuff::Scalar olddist;
					b.Subtract (loc,m_OldMoveHead);
					olddist = b.GetLength ();
					if (olddist < dist)
						dist = olddist;
				}

				if (dist <= slowdist)
				{
					Scalar temp;
					temp = dist - (0.5f * model->decceleration * (slowtime*slowtime));
					movemps = temp / slowtime;
					if (movemps < 0)
						movemps = 0;

					speed = ((movemps) - (float) model->minSpeed) / ((float) model->maxSpeed - (float) model->minSpeed);
					if (speed < m_SpeedRequest)
					{
						m_SpeedRequest = speed;
						m_NeedSpeedAdjust = true;
					}
				}
			}
		}
	}
	if (m_NeedSpeedAdjust)
	{
		if (m_SpeedHitSlow)
		{
			Scalar fred;
			fred = m_SpeedRequest/2.0f;
			const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ());
			if (fred > 0)
			{
				if (fred < model->minSpeed / model->maxSpeed)
				{
					if (m_Vehicle->IsDerivedFrom (Mech::DefaultData))
					{
						Mech *mech;
						mech = Cast_Object (Mech *,m_Vehicle);
						fred = (model->minSpeed+1.0f) / mech->GetMaxSpeed ();
					}
					else
					{
						fred = (model->minSpeed+1.0f) / model->maxSpeed;
					}
				}
			}
			else
			{
				if ((fred != 0) &&
					(fred > -model->minReverseSpeed / model->maxReverseSpeed))
				{
					fred = -model->minReverseSpeed / model->maxReverseSpeed;
				}
			}

			com = CreateSetSpeedCommand (fred);
		}
		else
			com = CreateSetSpeedCommand (m_SpeedRequest);
		Check_Pointer (com);
		ExecuteCommand (com);
		m_NeedSpeedAdjust = false;
	}

	m_NeedHeadingAdjust = false;
	com = CreateTurnToCommand (realheadpoint,(m_OrderSpeed < 0) || (m_SpeedRequest<0));
	Check_Pointer (com);
	ExecuteCommand (com);
}
void MoverAI::SetOrderSpeed (Stuff::Scalar newvalue)
{
	m_OrderSpeed = newvalue;
}

bool MoverAI::RequestJump (Stuff::Time till)
{
	if (!m_AIJumpAllowed)
		return false;
	if (m_OrderSpeed < 0)
		return false;
	if (!m_CurPath)
		return false;
	if (!m_CurPath->Calced ())
		return false;
	if (m_CurPath->JumpSoon ())
		return false;

	return Jump (till,m_MoveHead,true);
}

bool MoverAI::Jump (Stuff::Time till,const Stuff::Point3D& dest,bool request)
{
	Verify (m_Vehicle);
	Verify (m_Vehicle->IsDerivedFrom (Mech::DefaultData));
	Verify (m_Vehicle->GetGameModel ()->moveTypeFlag == MechWarrior4::MWObject__GameModel::LEGJUMP_MOVETYPE);
	if (m_Jumping)
	{
		if ((till - m_StartJump) >= m_BurnTime)
		{
			Mech *mech;
			mech = Cast_Object (Mech *,m_Vehicle);
			mech->StopJumpRequest();
		}
	}
	else
	{
		Mech *mech;
		int forcespeed=0;
		JumpJet *jumpjet;
		Stuff::Scalar dist;
		Point3D extra,loc(m_Vehicle->GetLocalToWorld ());
		extra.Subtract (dest,loc);
		dist = extra.GetLength ();
		mech = Cast_Object (Mech *,m_Vehicle);
		jumpjet = mech->GetJumpJet ();
		if (!jumpjet)
		{
#if defined(LAB_ONLY)
			PAUSE (("tried to jump with no jump jet"));
#endif
			if (request)
				return false;
			if (m_CurPath)
			{
				CRailLink *link;
				int linkid;

				linkid = m_CurPath->CurLink ();
				if (linkid != -1)
				{
					link = g_MissionGraph->Link (linkid);
					if (link)
					{
						link->Weight (-1.0f,JUMPINDEX,link->Location (0));
						link->Weight (-1.0f,JUMPINDEX,link->Location (1));
						link->Jump (false,link->Location (0));
						link->Jump (false,link->Location (1));
					}
				}
			}
			if ((m_CurMove) && (m_CurMove->MoveType () == MOVE_RIGIDPATROL))
				m_CurMove->Done (true);
			RecalcPath(gos_GetElapsedTime ());
			return false;
		}
		else
		{
			Check_Object (jumpjet);
			m_BurnTime = jumpjet->CalcBurnTime (dist,dest.y - loc.y,mech->currentSpeedMPS);
			if (m_BurnTime > jumpjet->CurrentCharge ())
			{
				m_BurnTime = jumpjet->CalcBurnTime (dist,dest.y - loc.y,mech->currentSpeedMPS+5.0f);
				if (m_BurnTime > jumpjet->CurrentCharge ())
				{
					if (request)
						return false;
					m_BurnTime = jumpjet->CalcBurnTime (dist,dest.y - loc.y,m_Vehicle->GetGameModel ()->maxSpeed);
					if (m_BurnTime > jumpjet->CurrentCharge ())
					{
						if (m_CurPath)
						{
							CRailLink *link;
							int linkid;
	
							linkid = m_CurPath->CurLink ();
							if (linkid != -1)
							{
								link = g_MissionGraph->Link (linkid);
								if (link)
								{
									link->Weight (-1.0f,JUMPINDEX,link->Location (0));
									link->Weight (-1.0f,JUMPINDEX,link->Location (1));
									link->Jump (false,link->Location (0));
									link->Jump (false,link->Location (1));
								}
							}
						}
						if ((m_CurMove) && (m_CurMove->MoveType () == MOVE_RIGIDPATROL))
							m_CurMove->Done (true);
						RecalcPath(gos_GetElapsedTime ());
						return false;
					}
					else
						forcespeed = 2;
				}
				else
					forcespeed = 1;
			}
		}
		Point3D distance_to_target;
		YawPitchRoll rot(m_Vehicle->GetLocalToWorld ());

		LinearMatrix4D tempmat (m_Vehicle->GetLocalToWorld ());
		tempmat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);

		distance_to_target.MultiplyByInverse(dest, tempmat);

		Scalar heading_adjustment = Arctan(distance_to_target.x, distance_to_target.z);
		if ((heading_adjustment > Pi_Over_8) || (heading_adjustment < -Pi_Over_8))
		{
			if (request)
				return false;
		}
		if (mech->JumpRequest())
		{
			m_Jumping = true;
			m_JumpRequested = false;
			m_StartJump = till;

			if (forcespeed)
			{
				mech->m_ForceSpeed = true;
				if (forcespeed == 1)
					mech->m_ForceSpeedValue = mech->currentSpeedMPS + 5.0f;
				else
					mech->m_ForceSpeedValue	= m_Vehicle->GetGameModel ()->maxSpeed;
			}
			mech->m_ForceYaw = true;
			mech->m_ForceYawValue = rot.yaw + heading_adjustment;
		}
	}
	return true;
}

void MoverAI::TurnOn (void)
{
	delete m_CheapMover;
	m_CheapMover = NULL;
	inherited::TurnOn ();
}

void MoverAI::TurnOff (void)
{
	Verify (!m_CheapMover);
	inherited::TurnOff ();
	if (m_Vehicle)
	{
		m_CheapMover = new CCheapMover ((Point3D) m_Vehicle->GetLocalToWorld ());
	}
}

void MoverAI::ExecutePath (Stuff::Time till)
{
	Check_Object (this);
	if (!m_CurPath)
		return;
	gosREPORT (m_Vehicle,"somehow a mover ai got a path when no vehicle is attached to it");
	bool jump=false,prejump=false;

	Point3D loc(0,0,0),head(0,0,0);
	Stuff::Point3D outvec (-1,-1,-1);

	Verify (m_CurMove);
	Verify (m_CurMove->Executing ());

	if (m_PathBad)
	{
		if (gos_GetElapsedTime () >= m_RecalcTime)
		{
			RecalcPath (till);
			m_PathBlocked = false;
			m_PathBad = false;
		}
		else
		{
			return;
		}
	}
	if (!m_CurPath)			// could be killed from recalcpath
		return;
	if (!m_CurPath->Calced ())
	{
		NotifyNoPath ();
		PathDone (till,true);
		return;
	}
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;

	CRailPath::PATHSTATES state;
	state = m_CurPath->State ();

	__int64 subtime = GetCycles ();
	{
		Scalar cursp;
		if (m_CheapMover)
			cursp = m_SpeedRequest*model->maxSpeed*0.9f;
		else
			cursp = m_Vehicle->currentSpeedMPS;
	
		loc = (Point3D) m_Vehicle->GetLocalToWorld ();
	
		loc.y = 0;
		m_CurPath->Tick (loc,GetTimeSlice (till),cursp,outvec,jump,prejump);
		subtime = GetCycles () - subtime;
	}
	if (m_CurPath->State () == CRailPath::RAIL_MOVE_WAITING)
	{
		SetMoveHead (Stuff::Point3D (-1,-1,-1),0);
		return;
	}

	if (!m_CurPath->Calced ())
	{
		NotifyNoPath ();
		PathDone (till,true);
		return;
	}

	m_FailCount = 0;
	head = m_CurPath->Heading ();
	if (jump)
		Jump (till,head);
	else
		m_Jumping	= false;

	if (m_CurMove)
	{
		if (m_CurMove->MoveType () == MOVE_PATROL)
		{
			if (outvec == Point3D (-1,-1,-1))
			{
				CPatrolData *fred = (CPatrolData *) m_CurMove;
				if (fred->CurRequest () < (fred->RequestSize ()-1))
				{
					outvec = fred->Location (fred->CurRequest ()+1);
				}
			}
		}
	}

	if (m_CurPath->State () != CRailPath::RAIL_FINISH)
	{
//		if ((prejump) || (jump))
//			SetMoveHead (head,1.0f,outvec);
//		else
		SetMoveHead (head,m_OrderSpeed,outvec);
	}
	else
	{
		Verify (m_CurMove);
		Verify (m_CurMove->Executing ());
		NewPath (NULL);
		PathDone (till,false);
	}
}

void MoverAI::ExecuteMoveCommand (Stuff::Time till)
{
	Check_Object (this);
	if (!m_CurMove)
		return;
	gosREPORT (((m_CurMove->MoveType () == MOVE_LOOKOUT) || (m_Vehicle)  ),"somehow a moverai got a move command when no vehicle is attached to it");

	if (m_CurMove->Done ())
	{
		return;
	}

	if (!m_CurMove->Executing ())
	{
		m_CurMove->StartExecuting (this,till);
		SetOrderSpeed (m_CurMove->Speed ());
	}
	else
	{
		m_CurMove->Execute (this,till);
		SetOrderSpeed (m_CurMove->Speed ());
	}
}

void MoverAI::NewPath (CRailPath *path,bool overridemove)
{
	Check_Object (this);
	if (overridemove)
	{
		ClearMoveOrder ();
	}
	delete m_CurPath;
	m_CurPath = path;
	if (m_ObstacleRequest)
	{
		g_PathManager->RemoveRequest (m_ObstacleRequest);
		delete m_ObstacleRequest;
		m_ObstacleRequest = NULL;
	}

	if ((m_CurPath == NULL) && (m_CurMove==NULL))
	{
		CommandEntry *com;
		com = CreateTurnCommand (0);
		Check_Pointer (com);
		ExecuteCommand (com);
		com = CreateSetSpeedCommand (0.0f);
		Check_Pointer (com);
		ExecuteCommand (com);
		com = CreateKillTurnToCommand ();
		Check_Pointer (com);
		ExecuteCommand (com);
		SetMoveHead (Stuff::Point3D (-1,-1,-1),0);
		SetOrderSpeed (0);
		return;
	}
	if (m_CurPath == NULL)
		return;

	Verify (m_CurMove);
	Verify (m_CurMove->Executing ());

	Stuff::Point3D outvec,loc;

	Verify (m_Vehicle);
	loc = (Point3D) m_Vehicle->GetLocalToWorld ();
	loc.y = 0;
	if (m_CurMove)
	{
		SetOrderSpeed (m_CurMove->Speed ());
	}

	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Scalar cursp;
	if (m_CheapMover)
		cursp = m_SpeedRequest*model->maxSpeed*0.9f;
	else
		cursp = m_Vehicle->currentSpeedMPS;

	bool jump,prejump;
	if (m_CurPath->Calced ())
		m_CurPath->Tick (m_CurPath->Start (),GetTimeSlice (gos_GetElapsedTime ()),cursp,outvec,jump,prejump);
	if (m_CurPath->Calced ())
		m_CurPath->Tick (loc,GetTimeSlice (gos_GetElapsedTime ()),cursp,outvec,jump,prejump);

	if (!m_CurPath->Calced ())
	{
		SetMoveHead (Stuff::Point3D (-1,-1,-1),0);
		CommandEntry *com;
		com = CreateSetSpeedCommand (0.0f);
		Check_Pointer (com);
		ExecuteCommand (com);
	}
	else
	{
		if (m_CurPath->State () != CRailPath::RAIL_MOVE_WAITING)
			m_FailCount = 0;
		m_PathBlocked = false;
		m_PathBad = false;
	}

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MoverAI::Die (void)
{
	Check_Object (this);
	inherited::Die ();
	ClearMoveOrder ();
	ClearPathLock (false,false);
#if 0
	if (m_Vehicle)
	{
		OBB *obb;
		CollisionVolume *col;
		col = m_Vehicle->GetHierarchicalVolume ();
		if (!col)
			col = m_Vehicle->GetSolidVolume ();

		if (col)
		{
			OBB world_bounds;
			world_bounds.Multiply(col->m_localSpaceBounds, who->GetLocalToWorld());
			obb = &(world_bounds);
			ExtentBox box (*obb);
			int x,z;
			int minx,maxx,minz,maxz;

			minx = (int) box.minX;
			maxx = (int) box.maxX;
			minz = (int) box.minZ;
			maxz = (int) box.maxZ;
			minx /= BLOCK_GRID_RES;
			maxx /= BLOCK_GRID_RES;
			minz /= BLOCK_GRID_RES;
			maxz /= BLOCK_GRID_RES;
			if (minx < (MinX/10.0f))
				minx = (MinX/10.0f);
			if (minz < (MinZ/10.0f))
				minz = (MinZ/10.0f);
			if (maxx >= (MaxX/10.0f))
				maxx = (int) (MaxX/10)-1;
			if (maxz >= (MaxZ/10.0f))
				maxz = (int) (MaxZ/10)-1;

			for (x=minx;x<=maxx;x++)
			{
				for (z=minz;z<=maxz;z++)
				{
					MW4AI::g_MissionGraph->PassableLocal (x,z,BLOCKED_FLAGS);
				}
			}
			Rect4D temprect;
			temprect.left = box.minX;
			temprect.right = box.maxX;
			temprect.top = box.minY;
			temprect.bottom = box.maxY;
			temprect.who = NULL;
			g_Rect4DHash->AddPermRect (temprect);
		}
	}
#endif
}

void MoverAI::PreCollisionExecute(Time till)
{
  extern int g_nMR;
  if (g_nMR == 2)
       return;

	Check_Object(this);
	AI_LOGIC("Pre-Collision::MoverAI");
#if defined(LAB_ONLY)
		if (!instanceName)
			MWGameInfo::g_LastMWObject[0] = '\0';
		else
		{
			strncpy(MWGameInfo::g_LastMWObject, instanceName, sizeof(MWGameInfo::g_LastMWObject)-1);
			MWGameInfo::g_LastMWObject[sizeof(MWGameInfo::g_LastMWObject)-1] = '\0';
		}
		MWGameInfo::g_LastMWObjectPos = GetLocalToWorld();
#endif

#if !defined(NO_TIMERS)
	my_AutoTimer fred ((void *) &tMoverAITime);
#endif

	//
	//---------------------------------
	// Verify that this always executes
	//---------------------------------
	//


	AutoHeap local_heap (g_MoverAIHeap);
	Check_Object(executionState);
	Verify(executionState->GetState() != ExecutionStateEngine::NeverExecuteState);

	if (!ShouldRun (true))
	{
		ExecutePostPath (till);
		if ((m_PathBad) || (m_CurPath && !m_CurPath->Calced ()) || (m_CurMove && m_CurMove->Speed () == 0))
		{
			CommandEntry *com;
			com = CreateKillTurnToCommand ();
			Check_Pointer (com);
			ExecuteCommand (com);
			com = CreateTurnCommand (0);
			Check_Pointer (com);
			ExecuteCommand (com);
			com = CreateSetSpeedCommand (0);
			Check_Pointer (com);
			ExecuteCommand (com);
		}
		ABL::CurMoverAI = this;
		AI::PreCollisionExecute (till);
		ABL::CurMoverAI = NULL;
		return;
	}

	ClearPathLock (false,false);

	if (m_ObstacleRequest)
	{
		if (m_ObstacleRequest->Done ())
		{
			CRailPath *path;
			path = m_ObstacleRequest->DetachNodePath ();
			Check_Pointer (path);
			delete m_ObstacleRequest;
			m_ObstacleRequest = NULL;
			NewPath (path);
		}
	}

	if (m_OldMoveHead == Stuff::Point3D (-1,-1,-1))
	{
		if (m_Vehicle)
			m_OldMoveHead = (Point3D) m_Vehicle->GetLocalToWorld ();
	}

	ExecuteMoveCommand (till);
	ExecutePath (till);
	ExecutePostPath (till);


	PathLock (false,false);
	if ((m_PathBad) || (m_CurPath && !m_CurPath->Calced ()) || (m_CurMove && m_CurMove->Speed () == 0))
	{
		CommandEntry *com;
		com = CreateKillTurnToCommand ();
		Check_Pointer (com);
		ExecuteCommand (com);
		com = CreateTurnCommand (0);
		Check_Pointer (com);
		ExecuteCommand (com);
		com = CreateSetSpeedCommand (0);
		Check_Pointer (com);
		ExecuteCommand (com);
	}

	ABL::CurMoverAI = this;
	AI::PreCollisionExecute(till);
	ABL::CurMoverAI = NULL;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MoverAI::DebugTextMessageHandler(Adept::ReceiverDataMessageOf<int> *message)
{
	Check_Object(this);
	Check_Object(message);
	Verify(message->messageID == DebugTextMessageID);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MoverAI::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

void MoverAI::ExecutePostPath (Stuff::Time till)
{
	if (m_BoardingDropShip != DROP_NOT_ACTIVE)
	{
		CheckDropBoard (GetTimeSlice (till));
		ExecuteMoveHead (till);
	}
	else if (m_EnteringFieldBase != FIELDBASE_NOT_ACTIVE)
	{
		CheckFieldBase (GetTimeSlice (till));
		ExecuteMoveHead (till);
	}
	else if (m_Deactive)
	{
		if (m_CheapMover)
		{
			Check_Object (m_Vehicle);

			const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
			Check_Object (model);

			if (m_MoveHead != Stuff::Point3D (-1,-1,-1))
			{
				m_OldMoveHead = m_MoveHead;
				ConstructRect (m_OldMoveHead,m_NewMoveHead,5.0f);
			}
			else
			{
				m_OldMoveHead = (Point3D) m_Vehicle->GetLocalToWorld ();
				ConstructRect (m_OldMoveHead,m_NewMoveHead,5.0f);
			}
			m_MoveHead = m_NewMoveHead;

			m_SpeedRequest = m_NewSpeedRequest;
			if (m_MoveHead != Point3D (-1,-1,-1))
			{
				m_CheapMover->UpdatePos (m_MoveHead,m_SpeedRequest*model->maxSpeed*0.9f,GetTimeSlice (till));			// fudge to slow down for turns and slopes
				m_CheapMover->UpdatePos (m_Vehicle,m_MoveHead);
			}
		}
		else
		{
			if (m_Vehicle)
				m_CheapMover = new CCheapMover ((Point3D) m_Vehicle->GetLocalToWorld ());
		}

	}
	else
	{
		ExecuteMoveHead (till);
	}
}

void MoverAI::ExecuteMoveCommand (MW4AI::CommandEntry *command)
{
	Check_Object (this);
	Check_Pointer (command);

	ClearMoveOrder ();
	Verify (!m_ObstacleRequest);

	m_CurMove = (CMoveData *) command->DataPtr ();
	gosREPORT (((m_CurMove->MoveType () == MOVE_LOOKOUT) || (m_Vehicle)),"cannot give a move command to a moverai with no vehicle");
	const MWObject__GameModel *model = static_cast<const MechWarrior4::MWObject__GameModel *> (m_MWObject->GetGameModel ()) ;

	Stuff::Point3D temp;
	temp = (Stuff::Point3D) m_MWObject->GetLocalToWorld ();
	temp.y = 0;
	if (m_Vehicle)
		m_CurMove->CreateRequests (temp,MW4AI::ConvertTypetoUsage (model->moveTypeFlag));
	SetOrderSpeed (m_CurMove->Speed ());

	Check_Pointer (m_CurMove);
}

void MoverAI::ClearMoveOrder (void)
{
	Check_Object (this);
	CommandEntry *com;

	m_PathBad = false;
	delete m_CurMove;
	m_CurMove = NULL;
	delete m_CurPath;
	m_CurPath = NULL;
	delete m_ObstacleRequest;
	m_ObstacleRequest = NULL;

	m_MoveHead = Stuff::Point3D (-1,-1,-1);
	m_OutVec = Stuff::Point3D (-1,-1,-1);
	m_NewMoveHead = Stuff::Point3D (-1,-1,-1);
	m_NewOutVec = Stuff::Point3D (-1,-1,-1);
	m_OldMoveHead = Stuff::Point3D (-1,-1,-1);
	SetOrderSpeed (0);
	SetMoveHead (m_MoveHead,0);

	if (m_Vehicle)
	{
		com = CreateSetSpeedCommand (0.0f);
		Check_Pointer (com);
		ExecuteCommand (com);

		com = CreateTurnCommand (0.0f);
		Check_Pointer (com);
		ExecuteCommand (com);

		com = CreateKillTurnToCommand ();
		Check_Pointer (com);
		ExecuteCommand (com);
	}

	if (m_MWObject)
	{
		com = CreateKillTrackCommand ();
		Check_Pointer (com);
		ExecuteCommand (com);
	}
}

void MoverAI::ExecuteHighCommand (MW4AI::CommandEntry *command)
{
	Check_Object (this);
	Check_Pointer (command);
	switch (command->Type ())
	{
		case MOVE_HIGHID:
			ExecuteMoveCommand (command);
			break;
		case TELEPORT_HIGHID:
			break;
		case FLEE_HIGHID:
			break;
		case FORMONSPOT_HIGHID:
			break;
		case FOLLOW_HIGHID:
			break;
		default:
			inherited::ExecuteHighCommand (command);
			break;
	}
}

void MoverAI::ExecuteLowCommand (MW4AI::CommandEntry *command)
{
	Check_Object (this);
	Check_Pointer (command);
	switch (command->Type ())
	{
		case MOVETO_LOWID:
			break;
		case TURNTO_LOWID:
//			gosREPORT (m_Vehicle,"cannot give a turnto command to a moverai with no vehicle");
			if (m_Vehicle)
			{
				m_Vehicle->dirRequest = command->DataPoint3D ();
				m_Vehicle->followDirRequest = true;
				m_Vehicle->followDirRequestReverse = command->DataBoolean ();
			}
			break;
		case TRACK_LOWID:
//			gosREPORT (m_MWObject,"cannot give a track command to a moverai with no mwobject");
			if ((m_MWObject) &&
				(m_MWObject->GetTorso()))
			{
				gosREPORT (m_MWObject->GetTorso (),"cannot give a track command to a moverai that is attached to an object with no torso");
				m_MWObject->GetTorso()->torsoRequest = command->DataPoint3D ();
				m_MWObject->GetTorso()->followTorsoRequest = true;
			}
			break;
		case TRACKTO_LOWID:
//			gosREPORT (m_MWObject,"cannot give a trackto command to a moverai with no mwobject");
			if (m_MWObject)
			{
				Torso *torso;

				torso = m_MWObject->GetTorso ();
				gosREPORT (torso,"cannot give a trackto command to a moverai with no mwobject with no torso");
				torso->yawDemand = command->DataScalar();
			}
			break;
		case KILLTURNTO_LOWID:
//			gosREPORT (m_Vehicle,"cannot give a killturnto command to a moverai with no vehicle");
			if (m_Vehicle)
				m_Vehicle->followDirRequest = false;
			break;
		case KILLTRACK_LOWID:
//			gosREPORT (m_MWObject,"cannot give a killtrack command to a moverai with no mwobject");
			if ((m_MWObject) &&
				(m_MWObject->GetTorso()))
				m_MWObject->GetTorso()->followTorsoRequest = false;
			break;
		case TURN_LOWID:
//			gosREPORT (m_Vehicle,"cannot give a turn command to a moverai with no vehicle");
			if (m_Vehicle)
				m_Vehicle->yawDemand = command->DataScalar ();
			break;
		case SETSPEED_LOWID:
		{
//			gosREPORT (m_Vehicle,"cannot give a setspeed command to a moverai with no vehicle");
			Verify (command->DataScalar () >=-1.0f);
			Verify (command->DataScalar () <= 1.0f);

			Stuff::Scalar speed = command->DataScalar();
			if ((speed > 0) &&
				(speed < GetMinSpeed()))
			{
				speed = GetMinSpeed();
			}

			if (m_Vehicle)
			{
				if (m_PathBlocked)
					m_Vehicle->speedDemand = 0;				// to protect against a flood of collision requests, or just walking off the map
				else if ((m_CurPath) && ((!m_CurPath->Calced ()) || (m_CurPath->State () == CRailPath::RAIL_MOVE_WAITING)))
					m_Vehicle->speedDemand = 0;				// to protect against a flood of collision requests, or just walking off the map
				else
					m_Vehicle->speedDemand = speed;
			}
			break;
		}
		default:
			inherited::ExecuteHighCommand (command);
			break;
	}
}


void  MoverAI::orderMoveLookOut (void)
{
	Check_Object (this);
	if (!m_Active)
		return;
	CLookoutData *newmove;
	MW4AI::CommandEntry *com;
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_LOOKOUT)
			return;

	if (m_MWObject->GetTorso() == 0)
	{
		ClearMoveOrder();
		return;
	}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CLookoutData (MW4AI::g_MissionGraph,this);

	newmove->ClearEnd (false);

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);
}

bool  MoverAI::orderMoveTo (Path *path,int speed,int type,bool forwards,bool clear)
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a moveto order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return false;
	CPatrolData *newmove;
	MW4AI::CommandEntry *com;

	float normalspeed;

	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove = (CPatrolData *) m_CurMove;
	Check_Object (path);
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_PATROL)
				if (newmove->PathID () == path->objectID)
				{
					m_CurMove->Speed (normalspeed);
					if (m_CurMove->Done ())
					{
						if (m_CurMove->ClearEnd ())
							ClearMoveOrder ();
						return true;
					}
					else
						return false;
				}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CPatrolData (MW4AI::g_MissionGraph,this);
	newmove->SetupPath (path);

	newmove->Speed (normalspeed);
	newmove->PatrolType ((Patrol_Type) type);
	newmove->ClearEnd (clear);

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return false;
}

bool  MoverAI::orderMoveToSemi (Path *path,int speed,int type,bool forwards,bool clear)
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a moveto order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return false;
	CSemiPatrolData *newmove;
	MW4AI::CommandEntry *com;

	float normalspeed;

	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove = (CSemiPatrolData *) m_CurMove;
	Check_Object (path);
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_SEMIPATROL)
				if (newmove->PathID () == path->objectID)
				{
					m_CurMove->Speed (normalspeed);
					if (m_CurMove->Done ())
					{
						if (m_CurMove->ClearEnd ())
							ClearMoveOrder ();
						return true;
					}
					else
						return false;
				}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CSemiPatrolData (MW4AI::g_MissionGraph,this);
	newmove->SetupPath (path);

	newmove->Speed (normalspeed);
	newmove->PatrolType ((Patrol_Type) type);
	newmove->ClearEnd (clear);

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return false;
}

bool  MoverAI::orderMoveToRigid (Path *path,int speed,int type,bool forwards,bool clear)
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a moveto order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return false;
	CRigidPatrolData *newmove;
	MW4AI::CommandEntry *com;

	newmove = (CRigidPatrolData *) m_CurMove;
	Check_Object (path);
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_RIGIDPATROL)
			if (newmove->PathID () == path->objectID)
			{
				if (m_CurMove->Done ())
				{
					if (m_CurMove->ClearEnd ())
						ClearMoveOrder ();
					return true;
				}
				else
					return false;
			}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CRigidPatrolData (MW4AI::g_MissionGraph,this);
	newmove->SetupPath (path);

	float normalspeed;

	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove->Speed (normalspeed);
	newmove->PatrolType ((Patrol_Type) type);
	newmove->ClearEnd (clear);

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return false;
}
int   MoverAI::orderMoveFlee (Entity *away,int speed)
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a flee order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return INT_MAX;
	CFleeData *newmove;
	MW4AI::CommandEntry *com;
	float normalspeed;

	Check_Object (m_Vehicle);
	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove = (CFleeData *) m_CurMove;
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_FLEE)
			if (!newmove->Done ())
				if (newmove->FleeFrom ()->objectID == away->objectID)
					if (m_CurMove->Speed () == normalspeed)
						return newmove->FleeDistance (m_Vehicle);

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CFleeData (MW4AI::g_MissionGraph,this);


	newmove->ClearEnd (false);
	newmove->InsertObject (away);
	newmove->Speed (normalspeed);

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return newmove->FleeDistance (m_Vehicle);
}
bool  MoverAI::orderMoveResumePatrol (Path *path,int speed,int type,bool forwards,bool clear)				// todo fix this to start at the closest point
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a resume patrol order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return false;
	CPatrolData *newmove;
	MW4AI::CommandEntry *com;

	newmove = (CPatrolData *) m_CurMove;
	Check_Object (path);
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_PATROL)
			if (newmove->PathID () == path->objectID)
			{
				if (m_CurMove->Done ())
				{
					if (m_CurMove->ClearEnd ())
						ClearMoveOrder ();

					return true;
				}
				return false;
			}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CPatrolData (MW4AI::g_MissionGraph,this);
	newmove->SetupPath (path);

	float normalspeed;

	Check_Object (m_Vehicle);
	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove->Speed (normalspeed);
	newmove->PatrolType ((Patrol_Type) type);
	newmove->ClearEnd (clear);

	newmove->StartPathAt (path->GetNearestIndex ((Stuff::Point3D) m_Vehicle->GetLocalToWorld ()));

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return false;
}

bool  MoverAI::orderMoveResumePatrolSemi (Path *path,int speed,int type,bool forwards,bool clear)				// todo fix this to start at the closest point
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a resume patrol order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return false;
	CSemiPatrolData *newmove;
	MW4AI::CommandEntry *com;

	newmove = (CSemiPatrolData *) m_CurMove;
	Check_Object (path);
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_SEMIPATROL)
			if (newmove->PathID () == path->objectID)
			{
				if (m_CurMove->Done ())
				{
					if (m_CurMove->ClearEnd ())
						ClearMoveOrder ();

					return true;
				}
				return false;
			}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CSemiPatrolData (MW4AI::g_MissionGraph,this);
	newmove->SetupPath (path);

	float normalspeed;

	Check_Object (m_Vehicle);
	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove->Speed (normalspeed);
	newmove->PatrolType ((Patrol_Type) type);
	newmove->ClearEnd (clear);

	newmove->StartPathAt (path->GetNearestIndex ((Stuff::Point3D) m_Vehicle->GetLocalToWorld ()));

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return false;
}

bool MoverAI::orderMoveResumePatrolRigid (Path *path,int speed,int type,bool forwards,bool clear)				// todo fix this to start at the closest point
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a resume patrol order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return false;
	CRigidPatrolData *newmove;
	MW4AI::CommandEntry *com;

	newmove = (CRigidPatrolData *) m_CurMove;
	Check_Object (path);
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_RIGIDPATROL)
			if (newmove->PathID () == path->objectID)
			{
				if (m_CurMove->Done ())
				{
					if (m_CurMove->ClearEnd ())
						ClearMoveOrder ();

					return true;
				}
				return false;
			}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CRigidPatrolData (MW4AI::g_MissionGraph,this);
	newmove->SetupPath (path);

	float normalspeed;

	Check_Object (m_Vehicle);
	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove->Speed (normalspeed);
	newmove->PatrolType ((Patrol_Type) type);
	newmove->ClearEnd (clear);

	newmove->StartPathAt (path->GetNearestIndex ((Stuff::Point3D) m_Vehicle->GetLocalToWorld ()));

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return false;
}

void  MoverAI::orderMoveFollow (Entity *who,int offsetx,int offsetz,int maxspeed)
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a follow order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return;
	int id;

	if (who->IsDerivedFrom (AI::DefaultData))
	{
		AI *ai;

		ai = Cast_Object (AI *,who);
		Verify (ai->getEntity ());
		id = ai->getEntity ()->objectID;
	}
	else
	{
		id = who->objectID;
	}
	CFollowData *newmove;
	newmove = (CFollowData *) m_CurMove;
	MW4AI::CommandEntry *com;
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_FOLLOW)
			if (newmove->Follow ()->objectID == id)
				if (newmove->FollowXOffset () == offsetx)
					if (newmove->FollowZOffset () == offsetz)
						return;

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CFollowData (MW4AI::g_MissionGraph,this);

	newmove->ClearEnd (false);
	newmove->InsertObject (who);
	newmove->FollowXOffset ((Stuff::Scalar) offsetx);
	newmove->FollowZOffset ((Stuff::Scalar) offsetz);

	Scalar normalspeed;

	Check_Object (m_Vehicle);
	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (maxspeed > 0)
		normalspeed = ((float) maxspeed) / ((float) model->maxSpeed);
	else
		normalspeed	= 0.25f;

	if (normalspeed < 0.25f)
		normalspeed = 0.25f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove->MaxSpeed (normalspeed);

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);
}

void  MoverAI::orderMoveSit (bool shutdown)
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a sit order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return;
	CSitData *newmove;
	MW4AI::CommandEntry *com;
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_SIT)
		{
			return;
		}
	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CSitData (MW4AI::g_MissionGraph,this);

	newmove->ClearEnd (false);
	if (shutdown)
		Shutdown ();
	else
		Startup	();

	ClearMoveOrder ();
	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);
}

bool  MoverAI::orderMoveToLocPoint (const Stuff::Point3D& where,int speed,bool forwards,bool clear)
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a movetoloc order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return false;
	CMoveLocPointData *newmove;
	MW4AI::CommandEntry *com;
	Point3D comppt;

	comppt = where;

	ConvertPointToGrid (comppt);
	newmove = (CMoveLocPointData *) m_CurMove;
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_LOCPOINT)
			if (newmove->LocPoint () == comppt)
			{
				if (m_CurMove->Done ())
				{
					if (m_CurMove->ClearEnd ())
						ClearMoveOrder ();
					return true;
				}
				else
					return false;
			}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CMoveLocPointData (MW4AI::g_MissionGraph,this);

	float normalspeed;

	Check_Object (m_Vehicle);
	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove->InsertPoint (where);
	newmove->Speed (normalspeed);
	newmove->ClearEnd (clear);

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return false;
}
bool  MoverAI::orderMoveToObject (Entity *what,int speed)
{
	Check_Object (this);
	gosREPORT (m_Vehicle,"cannot give a movetoobject order to a moverai with no vehicle attached to it");
	if (!m_Active)
		return false;
	CMoveObjectData *newmove;
	MW4AI::CommandEntry *com;

	newmove = (CMoveObjectData *) m_CurMove;
	Check_Object (what);
	if (m_CurMove)
		if (m_CurMove->MoveType () == MOVE_OBJECT)
			if (newmove->TargetObject ()->objectID == what->objectID)
			{
				return m_CurMove->Done ();
			}

	AutoHeap local_heap (g_MoverAIHeap);
	newmove = new CMoveObjectData (MW4AI::g_MissionGraph,this);

	float normalspeed;

	Check_Object (m_Vehicle);
	Verify (m_Vehicle->IsDerivedFrom (Vehicle::DefaultData));
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	if (speed > 0)
		normalspeed = ((float) speed) / ((float) model->maxSpeed);
	else
		normalspeed	= -((float) speed) / ((float) model->maxReverseSpeed);

	if (normalspeed < -1.0f)
		normalspeed = -1.0f;
	if (normalspeed > 1.0f)
		normalspeed = 1.0f;

	newmove->InsertObject (what);
	newmove->Speed (normalspeed);
	newmove->ClearEnd (false);

	m_TimeNotMoving = 0;

	com = CreateMoveCommand (newmove);
	Check_Pointer (com);
	ExecuteCommand (com);

	return false;
}

void MoverAI::Info (void (*printCallback)(char* s))
{
	Check_Object (this);
	char text[100];

	inherited::Info (printCallback);
	if (m_CurMove)
		sprintf (text,"There is a current move order");
	else
		sprintf	(text,"There is no current move order");
	printCallback (text);
}

void MoverAI::NotifyRespawned()
{
	ClearMoveOrder();

	ClearPathLock (false,false);
	delete m_PathBlockedRect;
	if (m_CheapMover)
		delete m_CheapMover;
	ClearTempLockRects ();
	m_CheapMover = NULL;
	m_PathBlockedRect = NULL;

	m_TimeNotMoving = 0;
	m_CombatLeashRadius = 0;
	m_CombatLeash.Zero();
	AI::NotifyRespawned();
}


void MoverAI::ConstructRect (const Point3D& start,const Point3D& end,Stuff::Scalar width)
{
	m_StartPoint = start;
	m_EndPoint = end;
	m_StartPoint.y = m_EndPoint.y = 0;

	if (start == end)
	{
		m_TopLeft.x = start.x + 10.0f;
		m_TopLeft.z = start.z + 10.0f;
		m_TopRight.x = start.x + 10.0f;
		m_TopRight.z = start.z + 10.0f;
		m_BottomLeft.x = end.x + 10.0f;
		m_BottomLeft.z = end.z + 10.0f;
		m_BottomRight.x = end.x + 10.0f;
		m_BottomRight.z = end.z + 10.0f;
		return;
	}
	Point3D dir,left,right;
	dir.Subtract (end,start);
	dir.Normalize (dir);
	left.x = -dir.z;
	left.z = dir.x;
	right.x = dir.z;
	right.z = dir.x;
	left *= (width/2.0f);
	right *= (width/2.0f);
	m_TopLeft.y = m_TopRight.y = m_BottomLeft.y = m_BottomRight.y = 0;

	m_TopLeft.x = start.x + left.x;
	m_TopLeft.z = start.z + left.z;
	m_TopRight.x = start.x + right.x;
	m_TopRight.z = start.z + right.z;
	m_BottomLeft.x = end.x + left.x;
	m_BottomLeft.z = end.z + left.z;
	m_BottomRight.x = end.x + right.x;
	m_BottomRight.z = end.z + right.z;
}

bool MoverAI::Intersect (const Point3D& start,const Point3D& end,const Point3D& p1,const Point3D& p2)
{
	Scalar b1,b2,xi,m1,m2,t;
	Scalar dx1,dx2;

	dx1 = p2.x - p1.x;
	dx2 = end.x - start.x;
	if (dx1 == 0)
		dx1 = 100000000.0f;
	if (dx2 == 0)
		dx2 = 100000000.0f;
	m1 = ((p2.z-p1.z)/(dx1));
	m2 = ((end.z-start.z)/(dx2));
	b1 = p1.z - m1*p1.x;
	b2 = start.z - m2*start.x;

	if (m1 == m2)
		xi = (b2-b1)/100000000.0f;
	else
		xi = (b2-b1)/(m1-m2);
	t = (xi-start.x)/(dx2);
	return ((t>=-0.02f) && (t<=1.02f));
}

Point3D MoverAI::LeftHeadPoint (const Point3D& loc,const Stuff::Point3D& head)
{

	Point3D toret,dir;
	UnitVector3D extra;

	if (m_StartPoint == m_EndPoint)
		return m_EndPoint;
	dir.Subtract (m_EndPoint,m_StartPoint);
	extra = dir;
	Line3D line (m_StartPoint,extra,10000.0f);
	line.GetClosestPointTo (loc,&toret);
	dir = extra;
	dir *= 10.0f;
	toret += dir;
	return toret;
}

Point3D MoverAI::RightHeadPoint (const Point3D& loc,const Stuff::Point3D& head)
{

	Point3D toret,dir;
	UnitVector3D extra;

	if (m_StartPoint == m_EndPoint)
		return m_EndPoint;
	dir.Subtract (m_EndPoint,m_StartPoint);
	extra = dir;
	Line3D line (m_StartPoint,extra,10000.0f);
	line.GetClosestPointTo (loc,&toret);
	dir = extra;
	dir *= 10.0f;
	toret += dir;
	return toret;
}

MoverAI::HeadHit MoverAI::HeadPointInRect (const LinearMatrix4D& loc,Scalar speed,Stuff::Scalar delta)
{
	Point3D start(loc), end;
	Point3D dir,inter;
	UnitVector3D extra;

	speed *= delta;
	if (speed < 0)
	{
		loc.GetLocalBackwardInWorld (&extra);
		speed = -speed;
	}
	else
	{
		loc.GetLocalForwardInWorld (&extra);
	}
	dir = extra;
	dir *= speed;
	end.Add (start,dir);

	if (Intersect (start,end,m_TopLeft,m_BottomLeft))
		return LEFT_INSIDE_HIT;
	if (Intersect (start,end,m_TopRight,m_BottomRight))
		return RIGHT_INSIDE_HIT;
	if (Intersect (start,end,m_TopLeft,m_TopRight))
		return TOP_INSIDE_HIT;
	if (Intersect (start,end,m_BottomLeft,m_BottomRight))
		return BOTTOM_INSIDE_HIT;
	
	end = m_EndPoint;
	if (Intersect (start,end,m_TopLeft,m_BottomLeft))
		return LEFT_OUTSIDE_HIT;
	if (Intersect (start,end,m_TopRight,m_BottomRight))
		return RIGHT_OUTSIDE_HIT;
	if (Intersect (start,end,m_TopLeft,m_TopRight))
		return TOP_OUTSIDE_HIT;
	if (Intersect (start,end,m_BottomLeft,m_BottomRight))
		return BOTTOM_OUTSIDE_HIT;
	return NO_HIT;

}

#if 0
		Stuff::Point3D m_DropShipWait;				// point to wait to board drop ship
		Stuff::Point3D m_DropShipBoard;				// point outside doors
		Stuff::Point3D m_DropShipCenter;			// point at center of dropship
		Stuff::Point3D m_DropShipFinal;				// final point to stand in drop ship
		bool m_BoardingDropShip;
		std::vector<MoverAI *> m_OtherDropShipBoarders;
		int m_DropshipID;											// which of the five we are
		Dropship *m_DropShip;
#endif

void MoverAI::AddDropshipBoarder (int id,MoverAI *who)
{
	Verify (id>=0);
	Verify (id<5);
	m_OtherDropShipBoarders[id] = who;
	if (who == this)
		m_DropShipID = id;
}

void MoverAI::CreateDropShipPoints (int id,Dropship *dropship)
{
	Verify (m_OtherDropShipBoarders[id] == this);
	Verify (m_DropShipID == id);
	Check_Object (dropship);
	m_DropShip = dropship;
	m_DropShipCenter = (Point3D) dropship->GetLocalToWorld ();
	m_DropShipID = id;
	OBB *obb;
	CollisionVolume *col;
	col = m_DropShip->GetHierarchicalVolume ();
	if (!col)
		col = m_DropShip->GetSolidVolume ();
	Verify (col);
	OBB world_bounds;
	world_bounds.Multiply(col->m_localSpaceBounds, m_Vehicle->GetLocalToWorld());
	obb = &(world_bounds);
	ExtentBox box (*obb);

	Scalar dist = obb->sphereRadius;
	m_DropShipWait = m_DropShipCenter;
	m_DropShipBoard = m_DropShipCenter;
	m_DropShipFinal = m_DropShipCenter;
	switch (m_DropShipID)
	{
		case 0:
			m_DropShipWait.z -= dist*BOARD_RADIUS;
			m_DropShipWait.x -= dist*(BOARD_RADIUS);
			m_DropShipBoard.z -= dist*BOARD_RADIUS;
			m_DropShipFinal.z -= dist * 0.5f;
			m_DropShipFinal.x -= dist * 0.25f;
			break;
		case 1:
			m_DropShipWait.z += dist*BOARD_RADIUS;
			m_DropShipWait.x -= dist*BOARD_RADIUS;
			m_DropShipBoard.z += dist*BOARD_RADIUS;
			m_DropShipFinal.z += dist * 0.5f;
			m_DropShipFinal.x -= dist * 0.25f;
			break;
		case 2:
			m_DropShipWait.z -= dist*BOARD_RADIUS;
			m_DropShipWait.x += dist*BOARD_RADIUS;
			m_DropShipBoard.z -= dist*BOARD_RADIUS;
			m_DropShipFinal.z -= dist * 0.5f;
			m_DropShipFinal.x += dist * 0.25f;
			break;
		case 3:
			m_DropShipWait.z += dist*BOARD_RADIUS;
			m_DropShipWait.x += dist*BOARD_RADIUS;
			m_DropShipBoard.z += dist*BOARD_RADIUS;
			m_DropShipFinal.z += dist * 0.5f;
			m_DropShipFinal.x += dist * 0.25f;
			break;
		case 4:
			m_DropShipWait.z += dist*BOARD_RADIUS*2.0f;
			m_DropShipBoard.z += dist*BOARD_RADIUS;
			break;
	}
}

void MoverAI::PrepareBoardDropShip (void)
{
	int speed;

	m_BoardingDropShip = DROP_FIRST_POINT;
	speed = ConvertKPHtoMPS (1000);

	orderMoveToLocPoint (m_DropShipWait,speed,true,true);
	if (m_DropShipID == 0)
	{
		m_CanStartBoard = true;
	}
}

void MoverAI::CheckDropBoard (Time delta)
{

	if (m_ReadyToStart && m_CanStartBoard)
	{
		NextBoardDropShip ();
		return;
	}
	Point3D loc (m_Vehicle->GetLocalToWorld ());
	Point3D dest (0,0,0);

	switch (m_BoardingDropShip)
	{
		case DROP_NOT_ACTIVE:
#if defined(LAB_ONLY)
			PAUSE (("check board drop ship called when not boarding a dropship"));
#endif
			return;
			break;
		case DROP_FIRST_POINT:		// check for at wait point
			dest = m_DropShipWait;
			break;
		case DROP_SECOND_POINT:		// check for at point in front of doors
			dest = m_DropShipBoard;
			break;
		case DROP_THIRD_POINT:		// check for at center point
			dest = m_DropShipCenter;
			break;
		case DROP_FOURTH_POINT:		// check for at final point
			dest = m_DropShipFinal;
			break;
		case DROP_DONE:
			return;
			break;
	}
	Scalar dist,speed;
	Point3D extra;

	speed = m_Vehicle->currentSpeedMPS;
	speed *= speed;
	extra.Subtract (dest,loc);
	dist = extra.GetLengthSquared ();
	if ((dist<49.0f) || (dist < speed))
	{
		ClearMoveOrder ();
		if (m_BoardingDropShip == DROP_FIRST_POINT)
			m_ReadyToStart = true;
		else
			NextBoardDropShip ();
	}
	if ((m_BoardingDropShip == DROP_THIRD_POINT) && (m_DropShipID != 4))
	{
		if (m_OtherDropShipBoarders[m_DropShipID+1])
		{
			m_OtherDropShipBoarders[m_DropShipID+1]->StartDropShipBoard ();
		}
	}
}

void MoverAI::NextBoardDropShip (void)
{
	Scalar normalspeed,endspeed;
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	switch (m_BoardingDropShip)
	{
#if defined(LAB_ONLY)
		case DROP_NOT_ACTIVE:
			PAUSE (("next board drop ship called when not boarding a dropship"));
			break;
#endif
		case DROP_FIRST_POINT:				// at wait point ready to point in front of doors
			m_BoardingDropShip = DROP_SECOND_POINT;
			m_ReadyToStart = false;
			m_CanStartBoard = false;
			normalspeed = ((float) ConvertKPHtoMPS(35)) / ((float) model->maxSpeed);
			endspeed = ((float) ConvertKPHtoMPS(25)) / ((float) model->maxSpeed);
			SetMoveHead (m_DropShipBoard,normalspeed,Point3D (-1,-1,-1),endspeed);
			break;
		case DROP_SECOND_POINT:				// at point in front of doors, ready to go to center
			m_BoardingDropShip = DROP_THIRD_POINT;
			normalspeed = ((float) ConvertKPHtoMPS(25)) / ((float) model->maxSpeed);
			endspeed = ((float) ConvertKPHtoMPS(25)) / ((float) model->maxSpeed);
			SetMoveHead (m_DropShipCenter,normalspeed,Point3D (-1,-1,-1),endspeed);
			break;
		case DROP_THIRD_POINT:				// at center ready to go to final
			if (m_DropShipID == 4)
			{
				m_BoardingDropShip = DROP_DONE;
				ClearMoveOrder ();
			}
			else
			{
				m_BoardingDropShip = DROP_FOURTH_POINT;
				normalspeed = ((float) ConvertKPHtoMPS(25)) / ((float) model->maxSpeed);
				endspeed = ((float) ConvertKPHtoMPS(0)) / ((float) model->maxSpeed);
				SetMoveHead (m_DropShipFinal,normalspeed,Point3D (-1,-1,-1),endspeed);
			}
			break;
		case DROP_FOURTH_POINT:				// at final ready to stop
			m_BoardingDropShip = DROP_DONE;
			ClearMoveOrder ();
			break;
#if defined(LAB_ONLY)
		case DROP_DONE:
			PAUSE (("next board drop ship called when done boarding a dropship"));
			break;
#endif
	}
}

void MoverAI::FieldBaseDead (void)
{
	Scalar normalspeed,endspeed;
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);
	m_Vehicle->ShutDownRequest(false);
	m_EnteringFieldBase = FIELDBASE_DEAD;
	if (m_FieldBaseReverse)
	{
		normalspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxReverseSpeed);
		endspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxReverseSpeed);
	}
	else
	{
		normalspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxSpeed);
		endspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxSpeed);
	}
	SetMoveHead (m_FieldBaseExit,normalspeed,Point3D (-1,-1,-1),endspeed);
}

void MoverAI::NextFieldBase (bool frommover)
{
	Scalar normalspeed,endspeed;
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	switch (m_EnteringFieldBase)
	{
		case FIELDBASE_NOT_ACTIVE:
#ifdef LAB_ONLY
			PAUSE (("check field base called when not entering a field base"));
#endif
			return;
			break;
		case FIELDBASE_ENTER:
			m_EnteringFieldBase = FIELDBASE_HEAL;
			Shutdown ();
			break;
		case FIELDBASE_EXIT:				// at point in front of doors, ready to go to center
			m_EnteringFieldBase = FIELDBASE_NOT_ACTIVE;
			FinishFieldBase ();
			m_ScriptRunning = true;
			break;
		case FIELDBASE_HEAL:				// at center ready to go to final
			m_EnteringFieldBase = FIELDBASE_EXIT;
			if (m_FieldBaseReverse)
			{
				normalspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxReverseSpeed);
				endspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxReverseSpeed);
			}
			else
			{
				normalspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxSpeed);
				endspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxSpeed);
			}
			SetMoveHead (m_FieldBaseExit,normalspeed,Point3D (-1,-1,-1),endspeed);
			break;
		case FIELDBASE_DEAD:
			if (frommover)
			{
				m_EnteringFieldBase = FIELDBASE_NOT_ACTIVE;
				FinishFieldBase ();
				m_ScriptRunning = true;
			}
			break;
	}
}

void MoverAI::LancemateFieldBasePoint (Stuff::Point3D &pt,FieldBase *base)
{
	Check_Object (base);
	pt = (Point3D) m_FieldBase->GetLocalToWorld ();
	OBB *obb;
	CollisionVolume *col;
	col = base->GetHierarchicalVolume ();
	if (!col)
		col = base->GetSolidVolume ();
	Verify (col);
	OBB world_bounds;
	world_bounds.Multiply(col->m_localSpaceBounds, base->GetLocalToWorld());
	obb = &(world_bounds);

	Scalar dist = obb->sphereRadius;

	UnitVector3D p1,p2;
	Point3D extra,otherloc;
	base->GetLocalToWorld ().GetLocalForwardInWorld (&p1);
	Scalar dot1 = 5.0f;
	otherloc = (Point3D) m_Vehicle->GetLocalToWorld ();
	extra.Subtract (otherloc,pt);
	if (Small_Enough (extra.GetLengthSquared ()))			// get point in reference to us
		dot1 = 0.0f;
	else
	{
		p2 = extra;
		dot1 = (p1.x*p2.x) + (p1.z*p2.z);
	}

	Vector3D dir (p1);
	dir *= (dist*3.0f);
	if (dot1 > 0)	
	{
		dir.x *= -1.0f;
		dir.z *= -1.0f;
		pt.Add (pt,dir);
	}
}

void MoverAI::CheckFieldBase (Stuff::Time delta)
{
	Point3D loc (m_Vehicle->GetLocalToWorld ());
	Point3D dest (0,0,0);

	if (m_FieldBase->IsDestroyed () && m_EnteringFieldBase != FIELDBASE_DEAD)
		FieldBaseDead	();

	switch (m_EnteringFieldBase)
	{
		case FIELDBASE_NOT_ACTIVE:
#ifdef LAB_ONLY
			PAUSE (("check field base called when not entering a field base"));
#endif
			return;
			break;
		case FIELDBASE_ENTER:		
			dest = m_FieldBaseEnter;
			break;
		case FIELDBASE_HEAL:
			return;
			break;
		case FIELDBASE_DEAD:
			dest = m_FieldBaseExit;
			FieldBaseDead ();
			break;
		case FIELDBASE_EXIT:		
			dest = m_FieldBaseExit;
			break;
	}
	Scalar dist,speed;
	Point3D extra;

	speed = m_Vehicle->currentSpeedMPS;
	speed *= speed;
	extra.Subtract (dest,loc);
	dist = extra.GetLengthSquared ();
	if (m_ShouldEndFieldBase)
	{
		ClearMoveOrder ();
		NextFieldBase (true);
		return;
	}
	if ((dist<9.0f) || (dist < speed))
	{
		if ((m_EnteringFieldBase == FIELDBASE_EXIT) || (m_EnteringFieldBase == FIELDBASE_DEAD))
		{
			m_ShouldEndFieldBase = true;				// exit should work on the next frame.
		}
		else
		{
			ClearMoveOrder ();
			NextFieldBase (true);
		}
	}
}

bool MoverAI::CreateFieldBasePoints (FieldBase *base)
{
	if (m_EnteringFieldBase != FIELDBASE_NOT_ACTIVE)
		return true;

	if ((base->m_CurrentMechUsing) && (base->m_CurrentMechUsing != this))
		return false;

	m_FieldBaseReverse = false;
	if (m_Vehicle->currentSpeedMPS < 0)
		m_FieldBaseReverse = true;

	m_ShouldEndFieldBase = false;
	Check_Object (base);
	m_FieldBase = base;
	m_FieldBaseEnter = (Point3D) m_FieldBase->GetLocalToWorld ();
	OBB *obb;
	CollisionVolume *col;
	col = m_FieldBase->GetHierarchicalVolume ();
	if (!col)
		col = m_FieldBase->GetSolidVolume ();
	Verify (col);
	OBB world_bounds;
	world_bounds.Multiply(col->m_localSpaceBounds, base->GetLocalToWorld());
	obb = &(world_bounds);

	Scalar dist = obb->sphereRadius;
	m_FieldBaseExit = m_FieldBaseEnter;

	UnitVector3D p1,p2;
	Point3D extra,otherloc;
	m_FieldBase->GetLocalToWorld ().GetLocalForwardInWorld (&p1);
	Scalar dot1 = 5.0f;
	otherloc = (Point3D) m_Vehicle->GetLocalToWorld ();
	extra.Subtract (otherloc,m_FieldBaseEnter);
	if (Small_Enough (extra.GetLengthSquared ()))			// get point in reference to us
		dot1 = 0.0f;
	else
	{
		p2 = extra;
		dot1 = (p1.x*p2.x) + (p1.z*p2.z);
	}

	Vector3D dir (p1);
	Scalar fred;
	fred = m_Vehicle->currentSpeedMPS * 3.6f;
	if (fred < 0)
		fred = -fred;

	if (fred > 35.0f)
	{
		dir *= (dist*2.5f);
	}
	else
		dir *= (dist*1.5f);
	if (dot1 <= -0.965f)		// point is behind us, and cos(15) degrees
	{
		m_FieldBaseExit.Add (m_FieldBaseEnter,dir);
	}
	else if (dot1 > 0.965f)			// cos (15) degrees
	{
		dir.x *= -1.0f;
		dir.z *= -1.0f;
		m_FieldBaseExit.Add (m_FieldBaseEnter,dir);
	}
	else
		return false;

	base->m_CurrentMechUsing = this;
	return true;
}
void MoverAI::PrepareEnterFieldBase (void)
{
	m_ScriptRunning = false;
	Scalar normalspeed,endspeed;
	const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (m_Vehicle->GetGameModel ()) ;
	Check_Object (model);

	m_FieldBaseReverse = false;
	if (m_Vehicle->currentSpeedMPS < 0)
		m_FieldBaseReverse = true;

	Scalar fred;
	fred = m_Vehicle->currentSpeedMPS * 3.6f;
	if (fred < 0)
		fred = -fred;
	if (fred > 35.0f)
	{
		m_EnteringFieldBase = FIELDBASE_EXIT;
		if (m_FieldBaseReverse)
		{
			normalspeed = ((float) m_Vehicle->currentSpeedMPS) / ((float) model->maxReverseSpeed);
			endspeed = ((float) m_Vehicle->currentSpeedMPS) / ((float) model->maxReverseSpeed);
			normalspeed = -normalspeed;
			endspeed = -endspeed;
		}
		else
		{
			normalspeed = ((float) m_Vehicle->currentSpeedMPS) / ((float) model->maxSpeed);
			endspeed = ((float) m_Vehicle->currentSpeedMPS) / ((float) model->maxSpeed);
		}
		SetMoveHead (m_FieldBaseExit,normalspeed,Point3D (-1,-1,-1),endspeed);
	}
	else
	{
		m_EnteringFieldBase = FIELDBASE_ENTER;
		if (m_FieldBaseReverse)
		{
			normalspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxReverseSpeed);
			endspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) model->maxReverseSpeed);
		}
		else
		{
			normalspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) m_Vehicle->GetMaxSpeed ());
			endspeed = ((float) ConvertKPHtoMPSFloat(25)) / ((float) m_Vehicle->GetMaxSpeed ());
//			normalspeed = ((float) ConvertKPHtoMPS(25)) / ((float) model->maxSpeed);
//			endspeed = ((float) ConvertKPHtoMPS(25)) / ((float) model->maxSpeed);
		}

		SetMoveHead (m_FieldBaseEnter,normalspeed,Point3D (-1,-1,-1),endspeed);
	}
}


void MoverAI::FinishFieldBase (void)
{
	if (m_FieldBase->m_CurrentMechUsing == this)
	{
		m_FieldBase->m_CurrentMechUsing = NULL;
	}
	Startup();
	m_LastTimeFinishedFieldBase = (Stuff::Scalar)gos_GetElapsedTime();
}

bool MoverAI::ExitedFieldBaseRecently() const
{
	if (m_LastTimeFinishedFieldBase == 0)
	{
		return (false);
	}

	Stuff::Scalar t = (Stuff::Scalar)gos_GetElapsedTime();

	if ((t > m_LastTimeFinishedFieldBase) &&
		(t < m_LastTimeFinishedFieldBase + 5.0f))
	{
		return (true);
	}

	return (false);
}
