Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
1840 lines
45 KiB
C++
1840 lines
45 KiB
C++
//===========================================================================//
|
|
// 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");
|
|
}
|