//===========================================================================//
// File:	AI.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 "windows.h"

#include "gameinfo.hpp"
#include "AI.hpp"
#include "AI_UserConstants.hpp"
#include "adept\Interface.hpp"
#include "adept\Application.hpp"
#include "adept\NameTable.hpp"
#include "adept\Map.hpp"
#include "adept\EntityManager.hpp"
#include "server\comline.hpp"
#include "mwobject.hpp"
#include "obstacle.hpp"
#include "adept\nametable.hpp"
#include "moverai.hpp"
#include "HeatManager.hpp"
#include "mech.hpp"
#include "mwmission.hpp"
#include <ElementRenderer\groupelement.hpp>
//#include "windows.h"
#include "data_client.hpp"
#include "lancemate.hpp"
#include "sensor.hpp"
#include "mwplayer.hpp"
#include "MWApplication.hpp"
#include "CameraShip.hpp"
#include "MWDebugHelper.hpp"
#include <mbstring.h>
#define __mbsrchr(s,c) (char*)_mbsrchr((const unsigned char*)(s),(c))

using namespace MW4AI;
using namespace MechWarrior4;

Graveyard* AI::m_Graveyard = 0;
int AI::m_AlwaysActiveCount = 0;
bool AI::m_GlobalTriggers[MW4AI::MAX_GLOBAL_TRIGGERS];
const MAXALWAYSACTIVE = 4;

namespace ABL
{
	extern long execLineNumber;
};

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

AI::ClassData* AI::DefaultData = NULL;
int AI::m_BoardGameMechAI=0,AI::m_LastBoardGameMechAI=0;
int AI::m_BoardGameVehicleAI=0,AI::m_LastBoardGameVehicleAI=0;
int AI::m_ActiveMechAI=0,AI::m_LastActiveMechAI=0;
int AI::m_ActiveVehicleAI=0,AI::m_LastActiveVehicleAI=0;

ElementRenderer::GroupElement *AI::m_GroupElement = NULL;
MemoryBlockOf<CommandEntry> *CommandEntry::m_EmptyCommand;
int MW4AI::g_AIExecutionNumber = 0;
DWORD MechWarrior4::Executed_AI_Count = 0;
DWORD MechWarrior4::Path_Calcs_Count = 0;
DWORD MechWarrior4::GridPath_Calcs_Count = 0;
DWORD MechWarrior4::QuickPath_Calcs_Count = 0;
DWORD MechWarrior4::QuickRail_Calcs_Count = 0;
DWORD MechWarrior4::FailedPath_Calcs_Count = 0;
namespace MW4AI
{
	__int64 tABLTime;
	HGOSHEAP g_AIHeap,g_MoverAIHeap,g_CombatAIHeap,g_RailHeap,g_RootAIHeap;
};

const Stuff::Scalar default_is_shot_radius = 80;

void MW4AI::SetupAIStagger (void)
{
	int j,size,current;
	NameTable *table = NameTable::GetInstance();

	size = table->nameTableArray[NameTable::AIArray].GetLength ();
	current = 0;
	for (j=0;j<size;j++)
	{
		NameTableEntry *data;
		AI* ai;
		Entity *ent;

		data = &table->nameTableArray [NameTable::AIArray][j];
		ent = data->dataPointer->GetCurrent ();
		if (!ent)
			continue;
		if ((ent->IsDerivedFrom (AI::DefaultData)) && (!ent->IsDerivedFrom (MoverAI::DefaultData)))
		{
			ai = Cast_Object (AI *,ent);
//			if ((ai->aiBrain) || (ai->aiDLLBrain))
			{
				ai->ExecutionNumber (current);
				current++;
			}
		}
		current %= AIExecutionDelta;
	}
	for (j=0;j<size;j++)
	{
		NameTableEntry *data;
		AI* ai;
		Entity *ent;

		data = &table->nameTableArray [NameTable::AIArray][j];
		ent = data->dataPointer->GetCurrent ();
		if (!ent)
			continue;
		if (ent->IsDerivedFrom (MoverAI::DefaultData))
		{
			ai = Cast_Object (AI *,ent);
			ai->ExecutionNumber (current);
			current++;
		}
		current %= AIExecutionDelta;
	}

}

void MW4AI::ResetGlobalTriggers()
{
	{for (int i = 0;
		  i < MAX_GLOBAL_TRIGGERS;
		  ++i)
	{
		AI::SetGlobalTrigger(i,false);
	}}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::InitializeClass()
{

	g_RootAIHeap = gos_CreateMemoryHeap ("Root AI");
	g_AIHeap = gos_CreateMemoryHeap ("Misc AI",0,g_RootAIHeap);
	g_MoverAIHeap = gos_CreateMemoryHeap ("Mover AI",0,g_RootAIHeap);
	g_CombatAIHeap = gos_CreateMemoryHeap ("Combat AI",0,g_RootAIHeap);
	g_RailHeap = gos_CreateMemoryHeap ("Movement System",0,g_RootAIHeap);
#if 0
//	char user_name[200];
//	DWORD username_size = 200;
//	GetUserName((char*)&user_name,&username_size);

//	if (!strcmp (user_name,"dabzug"))
//		g_AbzugFlag = true;
//	else
//		g_AbzugFlag = false;
//	if (s_NoAbzug)
//		g_AbzugFlag = false;

#endif

	Verify(!DefaultData);
	DefaultData =
	new ClassData(
							 AIClassID,
							 "Adept::AI",
							 Driver::DefaultData,
							 0, NULL,
							 (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);

	CommandEntry::m_EmptyCommand = new MemoryBlockOf<CommandEntry> (100,10,"Command Entry Blocks");
	InitObstacle ();

#if !defined(NO_TIMERS)
	g_TimerStack = new std::stack<__int64*>;
	StatisticFormat("");
	StatisticFormat("AI Routines");
	StatisticFormat("===========");
	StatisticFormat("");

	AddStatistic( "AI Script Time", "%", gos_timedata, (void*)&tABLTime, 0 );
#endif
	Register_Object (CommandEntry::m_EmptyCommand);
	m_GroupElement = new ElementRenderer::GroupElement ();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
	Unregister_Object (CommandEntry::m_EmptyCommand);
	delete CommandEntry::m_EmptyCommand;
	CommandEntry::m_EmptyCommand = NULL;
	CleanObstacle ();
	Check_Object(m_GroupElement);
	delete m_GroupElement;
	gos_DestroyMemoryHeap (g_CombatAIHeap);
	gos_DestroyMemoryHeap (g_MoverAIHeap);
	gos_DestroyMemoryHeap (g_AIHeap);
	gos_DestroyMemoryHeap (g_RailHeap);
#if !defined(NO_TIMERS)
	delete g_TimerStack;
#endif
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::Save (MemoryStream *stream)
{
	*stream << m_GunnerySkill;
	*stream << m_PilotSkill;
	*stream << m_EliteSkill;
	*stream << m_MinHeatSkill;
	*stream << m_MaxHeatSkill;
	*stream << m_SensorSkill;
	*stream << m_BlindFightingSkill;
	*stream << m_LongRangeGunnerySkill;
	*stream << m_ShortRangeGunnerySkill;
	*stream << m_TowardsPilotSkill;
	*stream << m_TowardsGunnerySkill;
	*stream << m_TowardsEliteSkill;
	*stream << m_StartGunnerySkill;
	*stream << m_StartPilotSkill;
	*stream << m_StartEliteSkill;
	*stream << m_EntropyMood;
	*stream << m_CurrentMood;
	*stream << m_LastMoodChange;
	*stream << m_UsingCoolant;
	*stream << m_IgnoringFriendlyFire;
	*stream << m_ShotBy;
	*stream << m_LastTimeShot;
	*stream << m_RammedBy;
	*stream << m_LastTimeRammed;
	*stream << m_FirstFrame;
	*stream << m_SecondFrame;
	*stream << m_Active;
	*stream << m_Debugging;
	*stream << m_Dead;
	*stream << m_ScriptRunning;
	*stream << m_ExecutionNumber;
	*stream << m_AlwaysActive;
	*stream << m_IsShotRadius;
	*stream << m_Deactive;

	if (m_CurrentTarget.GetCurrent())
	{
		*stream << true;
		*stream << m_CurrentTarget.GetCurrent()->objectID;
	}
	else
		*stream	<< false;

	int i;
	for (i=0;i<MW4AI::NUM_MEMORY_CELLS;i++)
	{
		*stream << memory[i];
	}
	if (aiBrain)
	{
		aiBrain->write (stream);
	}
	// pass save up to driver
}

void AI::Load (MemoryStream *stream)
{
	*stream >> m_GunnerySkill;
	*stream >> m_PilotSkill;
	*stream >> m_EliteSkill;
	*stream >> m_MinHeatSkill;
	*stream >> m_MaxHeatSkill;
	*stream >> m_SensorSkill;
	*stream >> m_BlindFightingSkill;
	*stream >> m_LongRangeGunnerySkill;
	*stream >> m_ShortRangeGunnerySkill;
	*stream >> m_TowardsPilotSkill;
	*stream >> m_TowardsGunnerySkill;
	*stream >> m_TowardsEliteSkill;
	*stream >> m_StartGunnerySkill;
	*stream >> m_StartPilotSkill;
	*stream >> m_StartEliteSkill;
	*stream >> m_EntropyMood;
	*stream >> m_CurrentMood;
	*stream >> m_LastMoodChange;
	*stream >> m_UsingCoolant;
	*stream >> m_IgnoringFriendlyFire;
	*stream >> m_ShotBy;
	*stream >> m_LastTimeShot;
	*stream >> m_RammedBy;
	*stream >> m_LastTimeRammed;
	*stream >> m_FirstFrame;
	*stream >> m_SecondFrame;
	*stream >> m_Active;
	*stream >> m_Debugging;
	*stream >> m_Dead;
	*stream >> m_ScriptRunning;
	*stream >> m_ExecutionNumber;
	*stream >> m_AlwaysActive;
	*stream >> m_IsShotRadius;
	*stream >> m_Deactive;

	bool flag;
	*stream >> flag;
	m_CurrentTarget.Remove();
	if (flag)
	{
		ObjectID object_id;
		*stream >> object_id;
		m_CurrentTarget.Add(Cast_Object (MWObject *,NameTable::GetInstance()->FindData(object_id)));
	}

	int i;
	for (i=0;i<MW4AI::NUM_MEMORY_CELLS;i++)
	{
		*stream >> memory[i];
	}

	if (aiBrain)
	{
		aiBrain->read (stream);
	}
	if (vehicle->IsDerivedFrom (MWObject::DefaultData))
	{
		MWObject *veh = Cast_Object (MWObject *,vehicle);
		Sensor *sensor = veh->GetSensor ();
		sensor->m_UpdateTime = (Scalar) SensorSkill ();
	}
	// pass load up to driver
}

Replicator::CreateMessage* AI::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
{
	Check_Object(this);
	Check_Object(stream);

	stream->AllocateBytes(sizeof(CreateMessage));
	Driver::SaveMakeMessage(stream, res_file);
	CreateMessage *message = Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);

	message->entityID = NameTable::GetInstance()->FindID(vehicle->instanceName);
	Str_Copy (message->scriptName,scriptName.c_str(),128);
	return message;
}

bool AI::ShouldRun (bool execnumonly) const
{
	if (m_Dead)
	{
		return false;
	}
	if ((!execnumonly) && (m_Deactive))
		return false;
	if (m_FirstFrame)
		return false;
	return m_ExecutionNumber == MW4AI::g_AIExecutionNumber;
}

bool AI::LoadScript (char *filename)
{
	scriptName = filename;
	return true;
}

void AI::UnloadScript (void)
{
	if (aiBrain)
	{
		Check_Pointer (aiBrain);
		delete aiBrain;
		aiBrain = NULL;
	}
}

bool AI::ReloadScript (ABL::ABLError *err)
{
	m_FirstFrame = true;
	UnloadScript ();

/*														// TODO: use this implementation or remove it
	if (m_ScriptParams.size() > 0)
	{
		aiScriptHandle = ABL::ABLi_preProcessFromParams(m_ScriptParams,(char*)instanceName);
		aiBrain = new ABL::ABLModule;
		long brainErr = aiBrain->init(aiScriptHandle);
		gosASSERT(brainErr == 0);
		if (brainErr)
			STOP(("1???"));			// todo fix this to proper exit.
//		aiBrain->setName((char*)script_name.c_str());	// is this necessary?
	}
	else
	{
*/
		if (scriptName[0] != 0)
		{
			char name[128];
			Str_Copy(name, scriptName.c_str(), sizeof(name));

			AutoHeap local_heap (g_AIHeap);
			aiScriptHandle = ABL::ABLi_preProcess(name, err,NULL,NULL);
			if (aiScriptHandle <0)
				return false;

			aiBrain = new ABL::ABLModule;
			gosASSERT(aiBrain != NULL);

			long brainErr = aiBrain->init(aiScriptHandle);
			gosASSERT(brainErr == 0);
			if (brainErr)
				STOP(("1???"));			// todo fix this to proper exit.

			aiBrain->setName(__mbsrchr (scriptName.c_str(),'\\'));
		}
		else
		{
			aiScriptHandle = 0;
			aiBrain = NULL;
			scriptName[0] = 0;
		}
//	}

	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AI::AI(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element)
: Driver(class_data, message, base_id, element)
, m_ShotBy(0xFFFFFFFF)
, m_LastTimeShot(0)
, m_CurrentTarget(NULL)
, m_EntropyMood(5)
, m_CurrentMood(5)
, m_LastMoodChange(0)
, m_FirstFrame(true)
, m_SecondFrame(false)
, m_Active(true)
, m_DebugPrintCallback(NULL)
, m_Debugging(false)
, m_Dead(false)
, aiScriptHandle(0)
, aiBrain(NULL)
, m_IgnoringFriendlyFire(false)
, m_ScriptRunning(true)
, m_IsShotRadius(-1)
, m_RammedBy(0xFFFFFFFF)
, m_LastTimeRammed(0)
, m_UsingCoolant(false)
, m_Deactive (false)
, m_Lancemate (NULL)
, m_ExecutionNumber(0)
{
	Check_Pointer(this);
	Check_Object(message);

	//
	//----------------------------------------------
	//Setup the script
	//----------------------------------------------
	//
	MW4AI::UserConstants::IncrementRefCount();

	AutoHeap local_heap (g_AIHeap);

	AddCreationData ("AI", (const char *) instanceName,objectID);
	aiDLLBrain = NULL;
	memcpy (memory,message->cellValues,sizeof (Stuff::Scalar)*DEFAULT_MEMORYCELL_VALUES);

	m_StartupRequest = false;
	m_ShutdownRequest = false;
	m_SelfDestructing = false;
	m_GunnerySkill = message->gunnerySkill;
	m_PilotSkill = message->pilotSkill;
	m_EliteSkill = message->eliteSkill;
	m_MinHeatSkill = message->minHeatSkill;
	m_MaxHeatSkill = message->maxHeatSkill;
	m_SensorSkill = message->sensorSkill;
	m_BlindFightingSkill = message->blindFightingSkill;
	m_LongRangeGunnerySkill = message->longRangeGunnerySkill;
	m_ShortRangeGunnerySkill = message->shortRangeGunnerySkill;
	m_TowardsGunnerySkill = message->towardsGunnerySkill;
	m_TowardsPilotSkill = message->towardsPilotSkill;
	m_TowardsEliteSkill = message->towardsEliteSkill;
//	m_ScriptParams = message->scriptParams;
	m_StartGunnerySkill = m_GunnerySkill;
	m_StartPilotSkill = m_PilotSkill;
	m_StartEliteSkill = m_EliteSkill;

//	m_ScriptParams = "G_Ambush.abl,5,1.0,2.0,100,50,50,50,80,500,750"; 	// TEMPORARY FOR TESTING

	if (message->scriptName[0] != 0)
	{
		scriptName = message->scriptName;
	}
	else
		scriptName[0]	= 0;

	//
	//----------------------------------------------
	//Connect the AI Driver to the specified Vehicle
	//----------------------------------------------
	//
	if (message->entityID != NameTable::NullObjectID)
	{
		Check_Object(NameTable::GetInstance());
		vehicle = NameTable::GetInstance()->FindData(message->entityID);
		Check_Object (vehicle);
		Verify (vehicle->IsDerivedFrom (MWObject::DefaultData));

		if (Network::GetInstance()->AmIServer())
		{
		ConnectEntity ((MWObject *) vehicle);
		if (vehicle->IsDerivedFrom (MWObject::DefaultData))
		{
			MWObject *veh = Cast_Object (MWObject *,vehicle);
				veh->HasAI(true);
			Sensor *sensor = veh->GetSensor ();
			if (sensor)
				sensor->m_UpdateTime = (Scalar) SensorSkill ();
		}
		}
		else
		{
			if (vehicle->IsDerivedFrom (MWObject::DefaultData))
			{
				MWObject *veh = Cast_Object (MWObject *,vehicle);
				veh->HasAI(true);
			}
		executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
		}
	}

	if (!vehicle)
		m_Deactive = false;
	

	m_AlwaysActive = false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AI::~AI()
{
	if (aiBrain)
	{
		Check_Pointer (aiBrain);
		delete aiBrain;
		aiBrain = NULL;
	}
	if (aiDLLBrain)
	{
		aiDLLExecute = NULL;
		FreeLibrary ((HINSTANCE) aiDLLBrain);
		aiDLLBrain = NULL;
	}
	MW4AI::UserConstants::DecrementRefCount();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	AI::Respawn(Entity::CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);

	Driver::Respawn(message);

	if (!Network::GetInstance()->AmIServer())
	{
		executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::AlwayActive (bool p1)
{
	if (!m_AlwaysActive && p1)
	{
		m_AlwaysActiveCount++;
#ifdef LAB_ONLY
		if (m_AlwaysActiveCount > MAXALWAYSACTIVE)
		{
			int size;
			NameTable *table = NameTable::GetInstance();
			size = table->nameTableArray[NameTable::AIArray].GetLength ();
			char test[255];
			sprintf (test,"%d always active ai's of %d total ai's",m_AlwaysActiveCount,size);
			SPEWALWAYS ((NULL,test));
			AddMessageData (test);
		}
#endif
	}
	m_AlwaysActive = p1;
}
void AI::SelfDestruct (void)
{
	m_SelfDestructing = true;
}

void AI::Shutdown (void)
{
	ablREPORT (vehicle->IsDerivedFrom (MWObject::DefaultData),"tried to shutdown a unit that is not an mwobject");

	if (m_Active == false)
	{
		return;
	}

	MWObject *obj = Cast_Object (MWObject *,vehicle);
	obj->vehicleShutDown = true;
	obj->vehicleJustPoweredUp = false;
	m_Active = false;
}

void AI::Startup (void)
{
	ablREPORT (vehicle->IsDerivedFrom (Vehicle::DefaultData),"tried to startup a unit that is not a vehicle");

	if (m_Active == true)
	{
		return;
	}

	MWObject *obj = Cast_Object (MWObject *,vehicle);
	obj->vehicleShutDown = false;
	obj->vehicleJustPoweredUp = true;
	m_Active = true;
}

inline int GetDefaultedSkill(int skill_value, MW4AI::UserConstants::ID id)
{
	Verify(MW4AI::UserConstants::Instance());

	if (skill_value == -1)
	{
		return ((int)MW4AI::UserConstants::Instance ()->Get(id));
	}

	return skill_value;
}

int AI::GunnerySkill (void) const
{
	float value;
	int align;

	value = (float) GetDefaultedSkill(m_GunnerySkill,MW4AI::UserConstants::default_gunnery);

	align = Entity::DefaultAlignment;
	if (vehicle)
		align = vehicle->GetAlignment();
	switch (align)
	{
	case Entity::Player:
		switch (MWApplication::GetInstance()->GetDifficultyLevel())
		{
		case MWApplication::EASY_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::friend_easy_gunnery_mod);
			break;
		case MWApplication::MEDIUM_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::friend_medium_gunnery_mod);
			break;
		case MWApplication::HARD_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::friend_hard_gunnery_mod);
			break;
		case MWApplication::IMPOSSIBLE_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::friend_impossible_gunnery_mod);
			break;
		}
		break;
	case Entity::DefaultAlignment:
		switch (MWApplication::GetInstance()->GetDifficultyLevel())
		{
		case MWApplication::EASY_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::neutral_easy_gunnery_mod);
			break;
		case MWApplication::MEDIUM_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::neutral_medium_gunnery_mod);
			break;
		case MWApplication::HARD_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::neutral_hard_gunnery_mod);
			break;
		case MWApplication::IMPOSSIBLE_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::neutral_impossible_gunnery_mod);
			break;
		}
		break;
	default:
		switch (MWApplication::GetInstance()->GetDifficultyLevel())
		{
		case MWApplication::EASY_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::enemy_easy_gunnery_mod);
			break;
		case MWApplication::MEDIUM_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::enemy_medium_gunnery_mod);
			break;
		case MWApplication::HARD_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::enemy_hard_gunnery_mod);
			break;
		case MWApplication::IMPOSSIBLE_DIFFICULTY:
			value *= MW4AI::UserConstants::Instance ()->Get(MW4AI::UserConstants::enemy_impossible_gunnery_mod);
			break;
		}
		break;
	}
	return (int) value;
}
int AI::PilotSkill (void) const
{
	return (GetDefaultedSkill(m_PilotSkill,MW4AI::UserConstants::default_pilot));
}
int AI::SensorSkill (void) const
{
	return (GetDefaultedSkill(m_SensorSkill,MW4AI::UserConstants::default_sensor));
}
int AI::BlindFightingSkill (void) const
{
	return (GetDefaultedSkill(m_BlindFightingSkill,MW4AI::UserConstants::default_blind));
}
int AI::LongRangeGunnerySkill (void) const
{
	return (GetDefaultedSkill(m_LongRangeGunnerySkill,MW4AI::UserConstants::default_longrange));
}
int AI::ShortRangeGunnerySkill (void) const
{
	return (GetDefaultedSkill(m_ShortRangeGunnerySkill,MW4AI::UserConstants::default_shortrange));
}
int AI::EliteSkill (void) const
{
	return (GetDefaultedSkill(m_EliteSkill,MW4AI::UserConstants::default_elite));
}
int AI::MinHeatSkill (void) const
{
	return (GetDefaultedSkill(m_MinHeatSkill,MW4AI::UserConstants::default_minheat));
}
int AI::MaxHeatSkill (void) const
{
	return (GetDefaultedSkill(m_MaxHeatSkill,MW4AI::UserConstants::default_maxheat));
}

bool AI::PilotSkillCheck (Stuff::Scalar damage_amount,bool jump)
{
	Scalar value,comp,mod;

	Verify (MW4AI::UserConstants::Instance ());
	Verify (getEntity ()->IsDerivedFrom (Mech::DefaultData));
	Mech *mech = Cast_Object (Mech *, getEntity ());
	MW4AI::UserConstants *data = MW4AI::UserConstants::Instance ();
	mod = 1.0f;
	if (damage_amount >= 0)				// less than zero means ignore damage
	{
		if (damage_amount < data->Get (MW4AI::UserConstants::damage_amount))
			return true;			// did not take enough damage to cause check
		else if (damage_amount > (2*data->Get (MW4AI::UserConstants::damage_amount)))
			mod *= data->Get (MW4AI::UserConstants::double_damage_mod);
		else
			mod	*= data->Get (MW4AI::UserConstants::damage_mod);
	}
	if (jump)
		mod *= data->Get (MW4AI::UserConstants::jump_recover_mod);

	Point3D loc(mech->GetLocalToWorld ());
	unsigned short mapdata = g_MissionGraph->Passable (loc.x,loc.z);
	mapdata &= WATER_MASK;
	mapdata >>= WATER_SHIFT;
	if (mapdata == SHALLOW_WATER)
		mod *= data->Get (MW4AI::UserConstants::low_water_mod);
	else if (mapdata == MEDIUM_WATER)
		mod *= data->Get (MW4AI::UserConstants::mid_water_mod);

	if (mech->gimpState != Mech::NotGimpedMode)
	{
		mod *= data->Get (MW4AI::UserConstants::gimped_mod);
	}
	Check_Object (mech->GetGameModel ());
	const Mech__GameModel *model = static_cast<const Mech__GameModel *> (mech->GetGameModel ()) ;
	if (mech->currentSpeedMPS > (model->maxSpeed / 2.0f))
	{
		mod *= data->Get (MW4AI::UserConstants::moving_fast_mod);
	}
	BYTE material;

	GetMapY (loc.x,loc.z,mech,material,loc.y);
	if ((material == ConcreteMaterial) || (material == DarkConcreteMaterial))
	{
		mod *= data->Get (MW4AI::UserConstants::concrete_mod);
	}

	value = Stuff::Random::GetFraction ();
	comp = mod * PilotSkill ();
	value *= SKILL_MULT;
	if (comp < 5.0f)
		comp = 5.0f;
	else if (comp > 95.0f)
		comp = 95.0f;

	if (value >= comp)
	{
		NotifyFailedPilotingRoll();
	}

	return(value < comp);
}

void AI::AddTowardGunnery (int value)
{
	Verify (UserConstants::Instance());
	if (value == -1)
		value = (int) UserConstants::Instance()->Get(UserConstants::default_addgunnery);
	m_TowardsGunnerySkill += value;
	if (m_TowardsGunnerySkill >= m_GunnerySkill)
	{
		if ((m_GunnerySkill - m_StartGunnerySkill) < UserConstants::Instance()->Get(UserConstants::max_skill_add))
		{
			m_GunnerySkill += 1;
			DataClient::Add_AddSkill(1,objectID);
		}
		m_TowardsGunnerySkill -= (m_GunnerySkill-1);
	}
}

void AI::AddTowardPilot (int value)
{
	Verify (UserConstants::Instance());
	if (value == -1)
		value = (int) UserConstants::Instance()->Get(UserConstants::default_addpilot);
	m_TowardsPilotSkill += value;
	if (m_TowardsPilotSkill >= m_PilotSkill)
	{
		if ((m_PilotSkill - m_StartPilotSkill) < UserConstants::Instance()->Get(UserConstants::max_skill_add))
		{
			m_PilotSkill += 1;
			DataClient::Add_AddSkill(0,objectID);
		}
		m_TowardsPilotSkill -= (m_PilotSkill-1);
	}
}

void AI::AddTowardElite (int value)
{
	Verify (UserConstants::Instance());
	if (value == -1)
		value = (int) UserConstants::Instance()->Get(UserConstants::default_addelite);
	m_TowardsEliteSkill += value;
	if (m_TowardsEliteSkill >= m_EliteSkill)
	{
		if ((m_EliteSkill - m_StartEliteSkill) < UserConstants::Instance()->Get(UserConstants::max_skill_add))
		{
			m_EliteSkill += 1;
			DataClient::Add_AddSkill(2,objectID);
		}
		m_TowardsEliteSkill -= (m_EliteSkill-1);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::TurnOn (void)
{
	MWObject *obj;
	Verify (!m_FirstFrame);
	if (vehicle && !m_FirstFrame)
	{
		m_TurningOn = true;
		obj = Cast_Object (MWObject *,vehicle);
		obj->TurnOn ();
		m_TurningOn = false;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::TurnOff (void)
{
	MWObject *obj;
	if (vehicle && !m_FirstFrame)
	{
		obj = Cast_Object (MWObject *,vehicle);
		obj->TurnOff ();
	}
	else if (vehicle && vehicle->IsDerivedFrom (Vehicle::DefaultData))
	{
		vehicle->executionState->RequestState(Vehicle::ExecutionStateEngine::AIMotionState);
	}
	else
	{
		obj = Cast_Object (MWObject *,vehicle);
		obj->TurnOff ();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void AI::DetermineExecution(Stuff::Time till)
{
	if (!m_FirstFrame && m_SecondFrame)
	{
		if ((m_AlwaysActive) || (MWApplication::GetInstance()->networkingFlag))
		{
			if (m_Deactive)
			{
				m_Deactive = false;
				TurnOn ();
			}
		}
		else
		{
			Scalar dist;
			MWMission *inst;
			Check_Object (MWMission::GetInstance());
			inst = Cast_Object (MWMission *,MWMission::GetInstance());
			dist = inst->m_DistanceCheck;
			Point3D loc (vehicle->GetLocalToWorld ());
			ChainIteratorOf<Entity *> iterator(&inst->m_CheckChain);
			Entity *ent;
			bool flag = false;
			if (loc != Point3D::HellPoint)
			{
				while ((ent = iterator.ReadAndNext()) != NULL)
				{
					if (ent->IsWithin (vehicle,dist,true))
					{
						flag = true;
						if (m_Deactive)
						{
							m_Deactive = false;
							TurnOn ();
						}
						break;
					}
				}
			}
			if (!flag)
			{
//				if (!Target ())
				{
					if (!m_Deactive)
					{
						m_Deactive = true;
						TurnOff ();
					}
				}
			}
		}
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#pragma warning (disable : 4706)
void AI::PreCollisionExecute(Time till)
{
  extern int g_nMR;
  if (g_nMR == 2)
       return;

	AI_LOGIC("Pre-Collision::AI");
	if (Application::GetInstance()->GetApplicationState() != ApplicationStateEngine::PreRenderState)
	{
	#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
		Check_Object(this);
	
		//
		//---------------------------------
		// Verify that this always executes
		//---------------------------------
		//
#ifdef LAB_ONLY
	if (IsDerivedFrom(MechAI::DefaultData))
	{

		if ((!m_Dead) && (ShouldRun (true)))
		{
			if (m_Deactive)
				m_BoardGameMechAI++;
			else
				m_ActiveMechAI++;
		}
	}
	else
	{
		if ((!m_Dead) && (ShouldRun (true)))
		{
			if (m_Deactive)
				m_BoardGameVehicleAI++;
			else
				m_ActiveVehicleAI++;
		}
	}
#endif

		Check_Object(executionState);
		if (executionState->GetState() ==  ExecutionStateEngine::NeverExecuteState)
			return;
	
		UpdateMood();
	
		Check_Object (vehicle);
		if (!m_FirstFrame && m_SecondFrame)
		{
			if (m_ExecutionNumber != MW4AI::g_AIExecutionNumber)
			{
				Driver::PreCollisionExecute (till);
				return;
			}
		}
		if (m_ScriptRunning && ((aiBrain || aiDLLExecute) && (!m_FirstFrame)))
		{
			if ((ShouldRunScript() == true) ||
				(m_SecondFrame == false))
			{
				AutoHeap local_heap (g_AIHeap);
				ABL::CurWarrior = this;
		//		aiBrain->setTrace (true);
				PRECOLLISION_LOGIC("AI::Execute");
		#if !defined(NO_TIMERS)
				my_AutoTimer fred ((void *) &tABLTime);
		#endif
		#ifdef LAB_ONLY
				if (aiDLLExecute)
				{
					try
					{
						aiDLLExecute (this,till,NameTable::GetInstance());
					}
					catch (...)
					{
						ablREPORT (false," Critical failure in ai dll, turning off dll.  Continue at your own risk");
						aiDLLExecute = NULL;
						FreeLibrary ((HINSTANCE) aiDLLBrain);
					}
				}
				else
				{
					try
					{
						aiBrain->execute ();
						m_SecondFrame = true;
					}
					catch (...)
					{
						ablREPORT (false,ABL::ablERROR (" Critical failure in abl script, turning off script.  Continue at your own risk"));
						delete aiBrain;
						aiBrain = NULL;
					}
				}
		#else
				if (aiDLLExecute)
				{
					aiDLLExecute (this,till,NameTable::GetInstance());
				}
				else
				{
					aiBrain->execute();
					m_SecondFrame = true;
				}
		
		#endif
				ABL::CurWarrior = NULL;
			}
		}
		else if (m_FirstFrame)
		{
			AutoHeap local_heap (g_AIHeap);
			aiDLLExecute = NULL;
			aiBrain = NULL;
			aiDLLBrain = NULL;
			if (scriptName[0] != 0)
			{
	
				char *fred;
	
				fred = strrchr (scriptName.c_str(),'.');
				if (!stricmp (fred,".ai"))
				{
					aiDLLBrain = LoadLibrary (scriptName.c_str());
					if (aiDLLBrain)
						aiDLLExecute = (AIExecuteFunction) GetProcAddress ((HINSTANCE) aiDLLBrain,"aiexecute");
				}
				else
				{
	/*															// TODO: use this implementation or remove it
					if (m_ScriptParams.size() > 0)
					{
						aiScriptHandle = ABL::ABLi_preProcessFromParams(m_ScriptParams,(char*)instanceName);
						aiBrain = new ABL::ABLModule;
						long brainErr = aiBrain->init(aiScriptHandle);
						gosASSERT(brainErr == 0);
						if (brainErr)
							STOP(("1???"));			// todo fix this to proper exit.
	
	//					aiBrain->setName((char*)script_name.c_str());	// is this necessary?
					}
					else
					{
	*/
						char name[128];
	
						ABL::ABLError err;
	
						Str_Copy(name, scriptName.c_str(), sizeof(name));
						aiScriptHandle = ABL::ABLi_preProcess(name, &err,NULL,NULL);
						if (aiScriptHandle < 0)
						{
							ablREPORT (false,err.Details ());
							aiScriptHandle = 0;
							aiBrain = NULL;
						}
						else
						{
							aiBrain = new ABL::ABLModule;
							gosASSERT(aiBrain != NULL);
	
							long brainErr = aiBrain->init(aiScriptHandle);
							gosASSERT(brainErr == 0);
							if (brainErr)
								STOP(("1???"));			// todo fix this to proper exit.
	
							aiBrain->setName(__mbsrchr (scriptName.c_str(),'\\'));
						}
	//				}
				}
			}
			else
			{
				aiScriptHandle = 0;
				aiBrain = NULL;
				m_SecondFrame = true;
			}
			m_Deactive = true;
			TurnOff ();
		}
		m_FirstFrame = false;
	
		if (m_UsingCoolant == true)
		{
			if ((getEntity() != 0) &&
					(getEntity()->IsDerivedFrom(MechWarrior4::MWObject::DefaultData) == true))
			{
				MechWarrior4::MWObject* mwobject = Cast_Object(MechWarrior4::MWObject*,getEntity());
	
				if (mwobject->m_heatManager != 0)
				{
					if (mwobject->m_heatManager->GetHeatPercentage() < ((Stuff::Scalar)MinHeatSkill() / 100.0f) - 0.01f)
					{
						mwobject->SetCooling(0);
						m_UsingCoolant = false;
					}
				}
				else
				{
					mwobject->SetCooling(0);
					m_UsingCoolant = false;
				}
			}
			else
			{
				m_UsingCoolant = false;
			}
		}
	}

	Set_Statistic(Executed_AI_Count, Executed_AI_Count+1);
	Driver::PreCollisionExecute(till);
	if (!IsUsingPostCollision())
	{
		lastExecuted = till;
	}

}
#pragma warning (default : 4706)

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::ConnectLancemate(LancematePlug *lancemate)
{
	Check_Object(this);
	Check_Object(lancemate);
	m_Lancemate = lancemate;

	m_GunnerySkill = m_Lancemate->m_GunnerySkill;
	m_PilotSkill = m_Lancemate->m_PilotSkill;
	m_EliteSkill = m_Lancemate->m_EliteSkill;
	m_MinHeatSkill = m_Lancemate->m_MinHeatSkill;
	m_MaxHeatSkill = m_Lancemate->m_MaxHeatSkill;
	m_SensorSkill =  m_Lancemate->m_SensorSkill;
	m_BlindFightingSkill = m_Lancemate->m_BlindFightingSkill;
	m_LongRangeGunnerySkill = m_Lancemate->m_LongRangeGunnerySkill;
	m_ShortRangeGunnerySkill = m_Lancemate->m_ShortRangeGunnerySkill;
	m_TowardsPilotSkill = m_Lancemate->m_TowardsPilotSkill;
	m_TowardsGunnerySkill = m_Lancemate->m_TowardsGunnerySkill;
	m_TowardsEliteSkill = m_Lancemate->m_TowardsEliteSkill;
	m_StartGunnerySkill = m_GunnerySkill;
	m_StartPilotSkill = m_PilotSkill;
	m_StartEliteSkill = m_EliteSkill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::DisconnectLancemate(void)
{
	Check_Object(this);
	if (!m_Lancemate)
	{
		return;
	}

	m_PilotSkill += (int) ((Stuff::Random::GetFraction () * 3) + 3);
	if (m_PilotSkill > 100)
	{
		m_PilotSkill = 100;
	}
	m_Lancemate->m_GunnerySkill = m_GunnerySkill;
	m_Lancemate->m_PilotSkill = m_PilotSkill;
	m_Lancemate->m_EliteSkill = m_EliteSkill;
	m_Lancemate->m_MinHeatSkill = m_MinHeatSkill;
	m_Lancemate->m_MaxHeatSkill = m_MaxHeatSkill;
	m_Lancemate->m_SensorSkill = m_SensorSkill;
	m_Lancemate->m_BlindFightingSkill = m_BlindFightingSkill;
	m_Lancemate->m_LongRangeGunnerySkill = m_LongRangeGunnerySkill;
	m_Lancemate->m_ShortRangeGunnerySkill = m_ShortRangeGunnerySkill;
	m_Lancemate->m_TowardsPilotSkill = m_TowardsPilotSkill;
	m_Lancemate->m_TowardsGunnerySkill = m_TowardsGunnerySkill;
	m_Lancemate->m_TowardsEliteSkill = m_TowardsEliteSkill;
	m_Lancemate->m_StartGunnerySkill = m_StartGunnerySkill;
	m_Lancemate->m_StartPilotSkill = m_StartPilotSkill;
	m_Lancemate->m_StartEliteSkill = m_StartEliteSkill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::ConnectEntity(MWObject *p1)
{
	Check_Object(this);
	Check_Object(p1);

	Verify ((!vehicle->IsDerivedFrom (Vehicle::DefaultData )) || (!vehicle->IsTileBound ()));
	vehicle = p1;
	p1->AttachAI (this);

	if (GetGraveyard() != 0)
	{
		GetGraveyard()->NotifyCreated(vehicle->objectID);
	}
}

void AI::PostCollisionExecute(Time till)
{
  extern int g_nMR;
  if (g_nMR == 2)
       return;

	AI_LOGIC("Post-Collision::AI");

	Check_Object(this);
	lastExecuted = till;
	if (m_SelfDestructing)
	{
		Entity *ent;

		m_SelfDestructing = false;
		ent = getEntity ();
		if (ent)
		{
			Vehicle *veh;

			if (ent->IsDerivedFrom (Vehicle::DefaultData))
			{
				veh = Cast_Object (Vehicle *,ent);
				veh->ProcessSelfDestruct ();
			}
		}
	}
}

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

void AI::Die (void)
{
	if (m_Dead)
	{
		return;
	}
	if (m_AlwaysActive)
	{
		m_AlwaysActiveCount--;
	}
	if (aiBrain)
		aiBrain->SwitchStates ("deadstate");

	m_Dead = true;

	if (m_Lancemate != 0)
	{
		if (m_Lancemate->ShouldEjectSafely() == true)
		{
			Eject ();
		}
	}
	else
	{
		Stuff::Scalar normalized_chance_to_eject = UserConstants::Instance()->Get(UserConstants::eject_chance_for_non_lancemate) * 0.01f;
		if (Stuff::Random::GetFraction() < normalized_chance_to_eject)
		{
			Eject ();
		}
	}

	if ((getEntity() != 0) &&
		(GetGraveyard() != 0))
	{
		GetGraveyard()->NotifyDeceased(getEntity()->objectID,m_ShotBy,m_LastTimeShot);
	}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Command Support
//

void AI::ExecuteHighCommand (CommandEntry *command)
{
	Verify (command);
	switch (command->Type ())
	{
		case ENDMISSION_HIGHID:
			break;
		case EJECT_HIGHID:
			break;
		case SETPROP_HIGHID:
			break;
		default:
			break;
	}
}
void AI::ExecuteLowCommand (CommandEntry *command)
{
	Verify (command);
}

void AI::ExecuteCommand (CommandEntry *command)
{

	if (!command)
		return;
	if (command->HighLevel ())
		ExecuteHighCommand (command);
	else
		ExecuteLowCommand	(command);
	delete command;
}

void AI::Info (void (*printCallback)(char* s))
{
	char text[100];

	sprintf (text,"Script name %s",scriptName);
	printCallback (text);
	if (m_Dead)
		printCallback ("Dead");
	else
		printCallback ("Alive");
}

void AI::UpdateMood()
{
	if (CurrentMood() == EntropyMood())
	{
		return;
	}

	if ((gos_GetElapsedTime() - m_LastMoodChange) < 2.0f)
	{
		return;
	}

	Verify(UserConstants::Instance() != 0);

	Stuff::Scalar mood = CurrentMood();
	const Stuff::Scalar min_mood_change_per_2_seconds = UserConstants::Instance()->Get(UserConstants::entropy_regeneration_at_0);
	const Stuff::Scalar max_mood_change_per_2_seconds = UserConstants::Instance()->Get(UserConstants::entropy_regeneration_at_100);
	const Stuff::Scalar mood_change_delta(max_mood_change_per_2_seconds - min_mood_change_per_2_seconds);

	const Stuff::Scalar normalized_elite_skill(((Stuff::Scalar)EliteSkill()) * 0.01f);

	const Stuff::Scalar mood_change(min_mood_change_per_2_seconds + (mood_change_delta * normalized_elite_skill));

	if (EntropyMood() > CurrentMood())
	{
		mood += mood_change;
		if (mood > EntropyMood())
		{
			mood = EntropyMood();
		}
	}
	else
	{
		mood -= mood_change;
		if (mood < EntropyMood())
		{
			mood = EntropyMood();
		}
	}

	CurrentMood(mood);
}

bool AI::ReactToCollision(Stuff::DynamicArrayOf<CollisionData> *collisions,bool& shoulddamage)
{
	for (int i=0; i<collisions->GetLength(); ++i)
	{
		CollisionData *data = &(*collisions)[i];
		if ((getEntity() != 0) &&
			(data->m_otherEntity != 0) &&
			(getEntity() != data->m_otherEntity))
		{
			if ((getEntity()->GetAlignment() != data->m_otherEntity->GetAlignment()) &&
				(getEntity()->IsDerivedFrom(Vehicle::DefaultData) == true) &&
				(data->m_otherEntity->IsDerivedFrom(Vehicle::DefaultData) == true))
			{
				Vehicle* us = Cast_Object(Vehicle*,getEntity());
				Vehicle* them = Cast_Object(Vehicle*,data->m_otherEntity);

				if (them->currentSpeedMPS > us->currentSpeedMPS)
				{
					m_RammedBy = them->objectID;
					m_LastTimeRammed = (Stuff::Scalar)gos_GetElapsedTime();
				}
			}

			if ((data->m_otherEntity->IsDerivedFrom(MWObject::DefaultData) == true) &&
				(getEntity()->IsDerivedFrom(MWObject::DefaultData) == true))
			{
				MWObject* mwobject = Cast_Object(MWObject*,data->m_otherEntity);
				MWObject* me = Cast_Object(MWObject*,getEntity());

				if (mwobject->IsPlayerVehicle() == false)
				{
					if (me->GetTargetDesirability() == MWObject::desirability_never)
					{
						shoulddamage = false;					// "ignore" units can't be damaged by collisions with anyone but the player
					}
					else
					{
						if ((mwobject->GetAI() != 0) ||
							(mwobject->GetAlignment() == me->GetAlignment()))
						{
							shoulddamage = false;				// friendly AI units don't damage each other
						}
					}
				}
			}
		}
	}

	return true;
}

Entity* AI::RammedBy(Stuff::Time lastvalid) const
{
	if ((lastvalid >= m_LastTimeRammed) ||
			(NameTable::GetInstance() == 0) ||
			(m_RammedBy == 0xFFFFFFFF))
	{
		return(0);
	}

	Entity* rv = NameTable::GetInstance()->FindData(m_RammedBy);

	if ((rv == 0) ||
			(rv->IsDestroyed() == true))
	{
		return(0);
	}

	return(rv);
}

void AI::NotifyShot(ObjectID who, bool fCanBeRecursive)
{
	Verify(NameTable::GetInstance() != 0);

	if ((getEntity() != 0) &&
		(getEntity()->IsDestroyed() == true))
	{
		return;
	}

	if (who == -1)
	{
		return;
	}

	Adept::Entity* entity = NameTable::GetInstance()->FindData(who);

	if ((entity == 0) ||
		(entity->IsDestroyed() == true))
	{
		return;
	}

	if (entity == getEntity())
	{
		return;
	}

	if ((getEntity() != 0) &&
		(entity->GetAlignment() == getEntity()->GetAlignment()) &&
		(GetIgnoringFriendlyFire() == true))
	{
		NotifyFriendlyFire(*entity);
		return;
	}

	m_LastTimeShot = (Stuff::Scalar)gos_GetElapsedTime();
	m_ShotBy = who;
}

void AI::NotifyShotFired(const Adept::Entity::CollisionQuery& query, MWObject& shooter)
{
	if ((vehicle != 0) &&
		(vehicle != &shooter) &&
		(shooter.GetAlignment() != vehicle->GetAlignment()) &&
		(LinePenetrates(*(query.m_line),(Stuff::Point3D)vehicle->GetLocalToWorld(),GetIsShotRadius()) == true))
	{
		NotifyShot(shooter.objectID);

/*	TODO -- comment this back in  ...  currently commented out awaiting Jerry's re-implementation of flinching that looks good from the outside

			if (GetSelf().IsDerivedFrom(Mech::DefaultData) == true)
			{
				Mech* mech = Cast_Object(Mech*,&GetSelf());

				Stuff::Sphere sphere(at_who,GetIsShotRadius());
				Stuff::Scalar penetration;
				query.line->GetDistanceTo(sphere,&penetration);

				if ((GetIsShotRadius() - penetration) < 20.0f)
				{
					Stuff::Point3D closest_point;
					query.line->GetClosestPointTo((Stuff::Point3D)GetSelf().GetLocalToWorld(),&closest_point);

					mech->FlinchFromPoint(closest_point,25,25,25);
				}
			}
*/
	}
}

void AI::SetIgnoringFriendlyFire(bool fIgnoreFriendlyFire)
{
	m_IgnoringFriendlyFire = fIgnoreFriendlyFire;
}

Stuff::Scalar AI::GetIsShotRadius() const
{
	if (m_IsShotRadius < 0)
	{
		return(default_is_shot_radius);
	}

	return(m_IsShotRadius);
}

void AI::SetIsShotRadius(Stuff::Scalar radius)
{
	Verify(radius >= 0);

	m_IsShotRadius = radius;
}

void AI::NotifyHeatShutdownImminent()
{
	if (m_Active == false)
	{
		return;
	}

	if ((getEntity() != 0) && (getEntity()->IsDerivedFrom(MechWarrior4::Mech::DefaultData) == true))
	{
		MechWarrior4::MWObject* mwobject = Cast_Object(MechWarrior4::MWObject*,getEntity());

		if ((m_UsingCoolant == false) &&
				(mwobject->m_heatManager != 0))
		{
			m_UsingCoolant = true;
			mwobject->SetCooling(1);
		}
	}
}

Graveyard* AI::GetGraveyard()
{
	return(m_Graveyard);
}

void AI::CreateGraveyard()
{
	DestroyGraveyard();

	m_Graveyard = new Graveyard;
}

void AI::DestroyGraveyard()
{
	if (m_Graveyard != 0)
	{
		delete m_Graveyard;
		m_Graveyard = 0;
	}
}

bool AI::GetIgnoringFriendlyFire() const
{
	return(m_IgnoringFriendlyFire);
}

void AI::StartExecute (void)
{
	AlwayActive (true);
}

void AI::StopExecute (void)
{
	AlwayActive (false);
}

void AI::Eject (void)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AI::AddStatsToString(std::string& s)
{
	if (m_Active == false)
	{
		s += "INACTIVE";
		return;
	}

	if (m_Deactive == true)
	{
		s += "DEACTIVE";
	}

	s += "ABL State: ";
	if ((aiBrain != 0) &&
		(aiBrain->GetCurState() != 0) &&
		(aiBrain->GetCurState()->name != 0))
	{
		s += aiBrain->GetCurState()->name;
	}
	else
	{
		s += "n/a";
	}

	s += "\nMood: ";
	int mood = (int)CurrentMood();
	Auto_Ptr<Moods::Mood> m = Moods::CreateMood((Moods::ID)mood);
	s += m->GetName();
	s += " (";
	s += ScalarToString(CurrentMood());
	s += "/";
	s += ScalarToString(EntropyMood());
	s += ")\n";

	if (Target() != 0)
	{
		s += "TARGET: ";
		s += Target()->instanceName;
		s += "\n";
	}
	else
	{
		s += "NO TARGET";
	}
}

void AI::NotifyRespawned()
{
	if (aiBrain != 0)
	{
		delete aiBrain;
		aiBrain = NULL;

		#ifdef LAB_ONLY
		if (MechWarrior4::DebugHelper::Checked_DisableAI() == false)
		{
		#endif

//		aiBrain = new ABL::ABLModule;
//		aiBrain->init(aiScriptHandle);

		#ifdef LAB_ONLY
		}
		#endif
	}

	m_ShotBy = 0xFFFFFFFF;
	m_LastTimeShot = 0;
	m_CurrentTarget.Remove();
	m_EntropyMood = 5;
	m_CurrentMood = 5;
	m_LastMoodChange = 0;
	m_FirstFrame = true;
	m_SecondFrame = false;
	m_Active = true;
	m_DebugPrintCallback = NULL;
	m_Debugging = false;
	m_Dead = false;
	aiScriptHandle = 0;
	aiBrain = NULL;
	m_IgnoringFriendlyFire = false;
	m_ScriptRunning = true;
	m_IsShotRadius = -1;
	m_RammedBy = 0xFFFFFFFF;
	m_LastTimeRammed = 0;
	m_UsingCoolant = false;
	m_Deactive = false;
	m_Lancemate = NULL;
	m_ExecutionNumber = 0;
	m_StartupRequest = false;
	m_ShutdownRequest = false;
	m_SelfDestructing = false;
	if (!vehicle)
		m_Deactive = false;
	m_AlwaysActive = false;

	#ifdef LAB_ONLY
	if (MechWarrior4::DebugHelper::Checked_DisableAI() == true)
	{
		UnloadScript ();
		m_Active = false;
	}
	#endif
}

Stuff::Scalar AI::GetLeastSquaredSensorDistance(const Stuff::Point3D& point) const
{
	return (GetLengthSquared((Stuff::Point3D)vehicle->GetLocalToWorld(),point));
}

bool AI::GetLeaderAlignment(int& alignment)
{
	return (false);
}

void AI::Target(Adept::Entity* newtarget)
{
	#ifdef _ARMOR
	if (newtarget != 0)
	{
		Verify(newtarget != getEntity());
	}
	#endif

	m_CurrentTarget.Remove();

	if (newtarget)									// null is a valid value for this pointer
	{
		Check_Pointer (newtarget);
		if ((newtarget->IsDestroyed() == true) ||
			(newtarget == getEntity()) ||
			(newtarget->IsDerivedFrom(CameraShip::DefaultData) == true))
		{
			return;
		}
		m_CurrentTarget.Add(newtarget);
	}
}

Stuff::MString AI::GetTalkerSuffix() const
{
	if (GetLancemate() != 0)
	{
		return (GetLancemate()->m_TalkerSuffix);
	}

	if (getEntity() == 0)
	{
		return ("");
	}

	switch (getEntity()->objectID % 5)
	{
		case 0:		return ("_DAM");
		case 1:		return ("_CAS");
		case 2:		return ("_JEN");
		case 3:		return ("_GON");
	}

	return ("_TER");
}
