Files
firestorm/Gameleap/code/mw4/Code/MW4/ai.hpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
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.
2026-06-24 21:28:16 -05:00

833 lines
22 KiB
C++

//===========================================================================//
// File: ai.hpp //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 07/13/99 AHF Created File //
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Microsoft //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#pragma once
#include "adept\Adept.hpp"
#include "adept\Driver.hpp"
#include "AI_Graveyard.hpp"
namespace ElementRenderer
{
class GroupElement;
};
namespace MechWarrior4
{
class AI;
class LancematePlug;
};
#include "mwobject.hpp"
#include "abl.h"
class CCommandLineServer;
namespace MW4AI
{
extern HGOSHEAP g_AIHeap,g_MoverAIHeap,g_CombatAIHeap,g_RailHeap;
typedef void (* AIExecuteFunction) (AI *curunit,Stuff::Time till,NameTable *table);
const int AIExecutionDelta = 4; // means once every fourth frame
const int NUM_MEMORY_CELLS=60;
const Stuff::Scalar MOODMIN = 1; // change the enums in MW4::AI_MOODS.HPP if you change this
const Stuff::Scalar MOODMAX = 10; // change the enums in MW4::AI_MOODS.HPP if you change this
const Stuff::Scalar SKILL_MULT = 100.0f; // mult for a normalized number to skill range
enum { MAX_GLOBAL_TRIGGERS = 100 }; // maximum number of global triggers
void ResetGlobalTriggers();
extern int g_AIExecutionNumber;
void SetupAIStagger (void);
const int SORT_DEPTH=4;
#ifdef _ARMOR
class CommandEntry : public Signature
#else
class CommandEntry
#endif
{
friend class MechWarrior4::AI;
protected:
static Stuff::MemoryBlockOf<CommandEntry> *m_EmptyCommand;
bool highlevel;
int commandType;
Stuff::Time startTime;
int from;
union
{
Stuff::Scalar dataScalar;
int dataInt;
void *dataPtr;
};
Stuff::Point3D dataPoint;
bool dataBoolean;
public:
CommandEntry (int type,bool high)
{
commandType = type;
highlevel = high;
}
~CommandEntry (void)
{}
bool HighLevel (void) const
{ return highlevel; }
Stuff::Time StartTime (void) const
{ return startTime; }
void StartTime (Stuff::Time time)
{ startTime = time; }
int Type (void) const
{ return commandType; }
void DataScalar (Stuff::Scalar value)
{ dataScalar = value; }
Stuff::Scalar DataScalar (void)
{ return dataScalar; }
void DataInt (int value)
{ dataInt = value; }
int DataInt (void) const
{ return dataInt; }
void DataBoolean (bool value)
{ dataBoolean = value; }
bool DataBoolean (void) const
{ return dataBoolean; }
void DataPtr (void *value)
{ dataPtr = value; }
void *DataPtr (void)
{ return dataPtr; }
void DataPoint3D (Stuff::Point3D value)
{ dataPoint = value; }
Stuff::Point3D DataPoint3D (void)
{ return dataPoint; }
void TestInstance()
{}
static CommandEntry *GetBlankCommand (void) { return (CommandEntry *) m_EmptyCommand->New (); }
static void DoneCommand (CommandEntry *oldknow) { m_EmptyCommand->Delete (oldknow); }
void *operator new (size_t size) {return GetBlankCommand (); }
void operator delete (void *value,size_t size) { DoneCommand ((CommandEntry *) value); }
};
#if 0
enum KNOWLEDGE_LEVEL {KNOW_LOCATION,KNOW_AFFILIATION,KNOW_CHASSIS,KNOW_TRUE};
enum THREAT_LEVEL {IMMEDIATE_THREAT,POTENTIAL_THREAT,WARNING_THREAT,OPPORTUNITY_THREAT,NON_THREAT};
#ifdef _ARMOR
class AIKnowledge : public Signature
#else
class AIKnowledge
#endif
{
friend class MechWarrior4::AI;
private:
static Stuff::MemoryBlockOf<AIKnowledge> *m_EmptyKnowledge;
protected:
AI *unit;
KNOWLEDGE_LEVEL knowledge;
THREAT_LEVEL threat;
unsigned int subthreat;
float validCount; // decays over time
AIKnowledge *next,*prev;
AIKnowledge *sort_next,*sort_prev;
public:
AIKnowledge (void);
~AIKnowledge (void);
static AIKnowledge *GetBlankKnowledge (void) { return (AIKnowledge *) m_EmptyKnowledge->New (); }
static void DoneKnowledge (AIKnowledge *oldknow) { m_EmptyKnowledge->Delete (oldknow); }
void *operator new (size_t size) {return GetBlankKnowledge (); }
void operator delete (void *value,size_t size) { DoneKnowledge ((AIKnowledge *) value); }
void TestInstance()
{}
};
#endif
};
namespace MechWarrior4
{
const int DEFAULT_MEMORYCELL_VALUES = 10;
extern DWORD Executed_AI_Count;
extern DWORD Path_Calcs_Count;
extern DWORD GridPath_Calcs_Count;
extern DWORD QuickPath_Calcs_Count;
extern DWORD QuickRail_Calcs_Count;
extern DWORD FailedPath_Calcs_Count;
//##########################################################################
//################## AIDriver::CreateMessage #########################
//##########################################################################
class AI__CreateMessage:public Driver__CreateMessage
{
public:
int entityID;
char scriptName[128];
Stuff::Scalar cellValues[DEFAULT_MEMORYCELL_VALUES];
int gunnerySkill;
int pilotSkill;
int eliteSkill;
int minHeatSkill;
int maxHeatSkill;
int sensorSkill;
int blindFightingSkill;
int longRangeGunnerySkill;
int shortRangeGunnerySkill;
int towardsGunnerySkill;
int towardsPilotSkill;
int towardsEliteSkill;
// char scriptParams[2000];
AI__CreateMessage(
size_t length,
int priority,
int message_flags,
Stuff::RegisteredClass::ClassID class_id,
int replicator_flags,
const ResourceID& data_list_id,
const Stuff::LinearMatrix4D &creation_matrix,
Stuff::Scalar age,
int execution_state,
int name_id,
int entity_alignment,
int drop_zone,
int entity_name_id,
const char *script_name,
Stuff::Scalar cell_values[DEFAULT_MEMORYCELL_VALUES],
int gun,
int pilot,
int elite,
int minheat,
int maxheat,
int sensor,
int blind,
int longrange,
int shortrange,
int tgun,
int tpilot,
int telite
// / char* script_params
):
Driver__CreateMessage(
length,
priority,
message_flags,
class_id,
replicator_flags,
data_list_id,
creation_matrix,
age,
execution_state,
name_id,
entity_alignment,
drop_zone
),
entityID(entity_name_id)
{
Check_Pointer(script_name);
Str_Copy(scriptName, script_name, sizeof(scriptName));
memcpy (cellValues,cell_values,sizeof (Stuff::Scalar)*DEFAULT_MEMORYCELL_VALUES);
gunnerySkill = gun;
pilotSkill = pilot;
eliteSkill = elite;
minHeatSkill = minheat;
maxHeatSkill = maxheat;
sensorSkill = sensor;
blindFightingSkill = blind;
longRangeGunnerySkill = longrange;
shortRangeGunnerySkill = shortrange;
towardsGunnerySkill = tgun;
towardsPilotSkill = tpilot;
towardsEliteSkill = telite;
// strncpy(scriptParams,script_params,2000);
}
static void ConstructCreateMessage(Script *script);
};
typedef Driver__ReceiveDropZoneMessage AI__ReceiveDropZoneMessage;
//##########################################################################
//########################### AI ######################################
//##########################################################################
class AI: public Driver
{
friend class LancematePlug;
friend void MW4AI::SetupAIStagger (void);
public:
static void InitializeClass();
static void TerminateClass();
static ClassData *DefaultData;
static ElementRenderer::GroupElement *m_GroupElement;
static int m_BoardGameMechAI,m_LastBoardGameMechAI;
static int m_BoardGameVehicleAI,m_LastBoardGameVehicleAI;
static int m_ActiveMechAI,m_LastActiveMechAI;
static int m_ActiveVehicleAI,m_LastActiveVehicleAI;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Inheritance Support
//
public:
typedef AI__CreateMessage CreateMessage;
typedef AI__ReceiveDropZoneMessage DropZoneMessage;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Run-time Construction and Destruction Support
//
public:
static AI* Make(CreateMessage *message,ReplicatorID *base_id);
Replicator::CreateMessage* SaveMakeMessage(Stuff::MemoryStream *stream, Adept::ResourceFile *res_file);
virtual void Save (MemoryStream *stream);
virtual void Load (MemoryStream *stream);
void Respawn(Entity::CreateMessage *message);
protected:
AI(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element);
~AI();
public:
bool LoadScript (char *filename);
void UnloadScript (void);
bool ReloadScript (ABL::ABLError *err);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Command Support
//
protected:
virtual void ExecuteHighCommand (MW4AI::CommandEntry *command);
virtual void ExecuteLowCommand (MW4AI::CommandEntry *command);
MW4AI::CommandEntry *CreateAdjustRatioCommand (Stuff::Time start=gos_GetElapsedTime ())
{ return CreateBaseCommand (ADJUSTRATIO_LOWID,false,start); }
public:
enum
{
ENDMISSION_HIGHID,
EJECT_HIGHID,
SETPROP_HIGHID,
NextHighCommandID
};
enum
{
ADJUSTRATIO_LOWID,
NextLowCommandID
};
virtual void PostCollisionExecute(Time till);
MW4AI::CommandEntry *CreateBaseCommand (int type,bool high,Stuff::Time start=gos_GetElapsedTime ())
{
MW4AI::CommandEntry *toret;
toret = new MW4AI::CommandEntry (type,high);
Check_Object (toret);
toret->StartTime (start);
return toret;
}
MW4AI::CommandEntry *CreateEndMissionCommand (Stuff::Time start=gos_GetElapsedTime ())
{ return CreateBaseCommand (ENDMISSION_HIGHID,true,start); }
MW4AI::CommandEntry *CreateEjectCommand (Stuff::Time start=gos_GetElapsedTime ())
{ return CreateBaseCommand (EJECT_HIGHID,true,start); }
MW4AI::CommandEntry *CreateSetPropCommand (Stuff::Time start=gos_GetElapsedTime ())
{ return CreateBaseCommand (SETPROP_HIGHID,true,start); }
void ExecuteCommand (MW4AI::CommandEntry *command);
Entity *getEntity (void) const
{ return vehicle; }
virtual void EnsureNotTargeting(Adept::ObjectID object) {};
void SelfDestruct (void);
bool m_SelfDestructing;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Skill Support
//
protected:
int m_GunnerySkill;
int m_PilotSkill;
int m_EliteSkill;
int m_MinHeatSkill;
int m_MaxHeatSkill;
int m_SensorSkill;
int m_BlindFightingSkill;
int m_LongRangeGunnerySkill;
int m_ShortRangeGunnerySkill;
int m_TowardsPilotSkill;
int m_TowardsGunnerySkill;
int m_TowardsEliteSkill;
int m_StartGunnerySkill;
int m_StartPilotSkill;
int m_StartEliteSkill;
public:
int RootGunnerySkill (void) const
{ return m_GunnerySkill; }
int RootPilotSkill (void) const
{ return m_PilotSkill; }
int RootSensorSkill (void) const
{ return m_SensorSkill; }
int RootBlindFightingSkill (void) const
{ return m_BlindFightingSkill; }
int RootLongRangeGunnerySkill (void) const
{ return m_LongRangeGunnerySkill; }
int RootShortRangeGunnerySkill (void) const
{ return m_ShortRangeGunnerySkill; }
int RootEliteSkill (void) const
{ return m_EliteSkill; }
int RootMinHeatSkill (void) const
{ return m_MinHeatSkill; }
int RootMaxHeatSkill (void) const
{ return m_MaxHeatSkill; }
int GunnerySkill (void) const;
int PilotSkill (void) const;
int SensorSkill (void) const;
int BlindFightingSkill (void) const;
int LongRangeGunnerySkill (void) const;
int ShortRangeGunnerySkill (void) const;
int EliteSkill (void) const;
int MinHeatSkill (void) const;
int MaxHeatSkill (void) const;
void GunnerySkill (int newvalue)
{
m_GunnerySkill = newvalue;
m_StartGunnerySkill = newvalue;
}
virtual void AddTowardGunnery (int value=-1); // -1 means default add value
void PilotSkill (int newvalue)
{
m_PilotSkill = newvalue;
m_StartPilotSkill = newvalue;
}
virtual bool PilotSkillCheck (Stuff::Scalar damage_amount,bool jump); // negative damage_amount means ignore damage for possible check
virtual void AddTowardPilot (int value = -1); // -1 means default add value
void SensorSkill (int newvalue)
{ m_SensorSkill = newvalue; }
void BlindFightingSkill (int newvalue)
{ m_BlindFightingSkill = newvalue; }
void LongRangeGunnerySkill (int newvalue)
{ m_LongRangeGunnerySkill = newvalue; }
void ShortRangeGunnerySkill (int newvalue)
{ m_ShortRangeGunnerySkill = newvalue; }
void EliteSkill (int newvalue)
{
m_EliteSkill = newvalue;
m_StartEliteSkill = newvalue;
}
virtual void AddTowardElite (int value=-1); // -1 means default add value
void MinHeatSkill (int newvalue)
{ m_MinHeatSkill = newvalue; }
void MaxHeatSkill (int newvalue)
{ m_MaxHeatSkill = newvalue; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Mood Support
//
protected:
Stuff::Scalar m_EntropyMood;
Stuff::Scalar m_CurrentMood;
Stuff::Scalar m_LastMoodChange;
bool m_UsingCoolant;
public:
Stuff::Scalar EntropyMood () const
{ return m_EntropyMood; }
Stuff::Scalar CurrentMood () const
{ return m_CurrentMood; }
void EntropyMood (Stuff::Scalar newvalue)
{
Verify (newvalue>=MW4AI::MOODMIN);
Verify (newvalue<=MW4AI::MOODMAX);
m_EntropyMood = newvalue;
}
void CurrentMood (Stuff::Scalar newvalue)
{
Verify (newvalue>=MW4AI::MOODMIN);
Verify (newvalue<=MW4AI::MOODMAX);
m_CurrentMood = newvalue;
m_LastMoodChange = (Stuff::Scalar)gos_GetElapsedTime ();
}
void UpdateMood();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Combat Support
//
protected:
Stuff::SlotOf<Entity *> m_CurrentTarget;
bool m_IgnoringFriendlyFire;
ObjectID m_ShotBy;
Stuff::Scalar m_LastTimeShot;
ObjectID m_RammedBy;
Stuff::Scalar m_LastTimeRammed;
public:
Entity *Target (void)
{ return m_CurrentTarget.GetCurrent(); }
void Target (Entity *newtarget);
void SetIgnoringFriendlyFire(bool fIgnoreFriendlyFire);
protected:
virtual bool GetIgnoringFriendlyFire() const;
public:
Entity* ShotBy(Stuff::Time lastvalid) const
{
if (lastvalid < m_LastTimeShot)
{
if ((NameTable::GetInstance() == 0) ||
(m_ShotBy == 0xFFFFFFFF))
{
return (0);
}
Entity* rv = NameTable::GetInstance()->FindData(m_ShotBy);
if ((rv == 0) ||
(rv->IsDestroyed() == true) ||
(rv == getEntity()))
{
return (0);
}
if ((getEntity() != 0) &&
(rv->GetAlignment() == getEntity()->GetAlignment()) &&
(GetIgnoringFriendlyFire() == true))
{
return (0);
}
return (rv);
}
return (0);
}
virtual bool GetLeaderAlignment(int& alignment);
Entity* RammedBy(Stuff::Time lastvalid) const;
virtual void NotifyShot(ObjectID who, bool fCanBeRecursive = true);
virtual void NotifyShotFired(const Adept::Entity::CollisionQuery& query, MWObject& shooter);
virtual void NotifyProjectileApproaching(const Stuff::Point3D& origin) {};
virtual void NotifyLastShotWasFriendlyFire(Adept::Entity& who_i_hit) {};
virtual void NotifyInternalHit() {};
virtual void NotifyComponentDestroyed(int zone) {};
virtual void NotifyShutdown() {};
virtual void NotifyAlignmentChanged() {};
virtual void NotifyInTargetingReticule(MWObject& who) {};
virtual void NotifyHeatShutdownImminent();
virtual void NotifyFailedPilotingRoll() {};
virtual void NotifyFriendlyFire(Adept::Entity& who_shot_me) {};
virtual void NotifyRespawned();
virtual bool Attacking() const { return (false); }
virtual bool ReactToCollision(Stuff::DynamicArrayOf<CollisionData> *collisions,bool& shoulddamage);
Stuff::Scalar GetIsShotRadius() const;
void SetIsShotRadius(Stuff::Scalar radius);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execution Support
//
protected:
int
GetTableArray()
{Check_Object(this); return NameTable::AIArray;}
enum
{
Sensor_Area,
Knowledge_Area,
Communication_Area,
NextAIAreaID
};
Stuff::Scalar memory[MW4AI::NUM_MEMORY_CELLS];
std::string scriptName;
long aiScriptHandle;
ABL::ABLModulePtr aiBrain;
void *aiDLLBrain;
MW4AI::AIExecuteFunction aiDLLExecute;
bool m_FirstFrame;
bool m_SecondFrame;
bool m_Active; // from shutdown and startup commands
void (*m_DebugPrintCallback)(char* s);
bool m_Debugging;
bool m_Dead;
bool m_ScriptRunning;
int m_ExecutionNumber;
bool m_AlwaysActive; // true if the this ai should always be active
static int m_AlwaysActiveCount;
Stuff::Scalar m_IsShotRadius;
static MW4AI::Graveyard* m_Graveyard;
bool m_StartupRequest;
bool m_ShutdownRequest;
static bool m_GlobalTriggers[MW4AI::MAX_GLOBAL_TRIGGERS];
public:
bool m_Deactive; // from distance to player
bool m_TurningOn; // true when the vehicle is being told to activate
virtual void TurnOn (void);
virtual void TurnOff (void);
bool TurningOn (void)
{ return m_TurningOn; }
static MW4AI::Graveyard* GetGraveyard();
static void CreateGraveyard();
static void DestroyGraveyard();
virtual bool ShouldRunScript()
{ return (true); }
virtual void Shutdown (void);
virtual void Startup (void);
bool ShouldRun (bool execnumonly=false) const;
void ExecutionNumber (int num)
{ m_ExecutionNumber = num; }
void AlwayActive (bool p1);
const char* GetScriptName()
{ return (scriptName.c_str()); }
ABL::ABLModulePtr Brain (void)
{ return aiBrain; }
void StopScript (void)
{ m_ScriptRunning = false; }
void StartScript (void)
{ m_ScriptRunning = true; }
void Debug (bool flag,void (*printCallback)(char* s))
{
m_Debugging = flag;
m_DebugPrintCallback = printCallback;
}
#ifdef LAB_ONLY
void DebugMessage (char *s)
{
if (m_DebugPrintCallback)
m_DebugPrintCallback (s);
}
#else
void DebugMessage (char *s)
{ }
#endif
virtual void Info (void (*printCallback)(char* s));
void PreCollisionExecute(Stuff::Time till);
void DetermineExecution(Stuff::Time till);
virtual void StartExecute (void);
virtual void StopExecute (void);
virtual void Die (void);
virtual void Eject (void);
bool Dead (void) const
{ return m_Dead; }
bool CanAct() const
{
if ((m_Active == true) &&
(m_Dead == false))
{
return (true);
}
return (false);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Memory Support
//
public:
void IntegerMemory (long index, long val)
{
Verify (index >=0);
Verify (index <MW4AI::NUM_MEMORY_CELLS);
memory[index] = (float) val;
}
void RealMemory (long index, Stuff::Scalar val)
{
Verify (index >=0);
Verify (index <MW4AI::NUM_MEMORY_CELLS);
memory[index] = val;
}
long IntegerMemory (long index) const
{
Verify (index >=0);
Verify (index <MW4AI::NUM_MEMORY_CELLS);
return((long) memory[index]);
}
Stuff::Scalar RealMemory (long index) const
{
Verify (index >=0);
Verify (index <MW4AI::NUM_MEMORY_CELLS);
return(memory[index]);
}
static bool GetGlobalTrigger(int index)
{
Verify(index >= 0);
Verify(index < MW4AI::MAX_GLOBAL_TRIGGERS);
return (m_GlobalTriggers[index]);
}
static void SetGlobalTrigger(int index, bool value)
{
Verify(index >= 0);
Verify(index < MW4AI::MAX_GLOBAL_TRIGGERS);
m_GlobalTriggers[index] = value;
}
virtual void AddStatsToString(std::string& s);
virtual bool GetExtendedSensorData(std::vector<MWObject*>& sensor_data_list)
{
return (false);
}
virtual Stuff::Scalar GetLeastSquaredSensorDistance(const Stuff::Point3D& point) const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Lancemate Support
//
protected:
LancematePlug *m_Lancemate;
public:
void ConnectLancemate(LancematePlug *lancemate);
void DisconnectLancemate(void);
LancematePlug *GetLancemate (void) const
{ return m_Lancemate; }
Stuff::MString GetTalkerSuffix() const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Misc Support
//
public:
void ConnectEntity(MWObject *entity);
ResourceID interfaceResourceID;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// File Support
//
void SaveInstanceText(Stuff::Page *page);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Test Support
//
public:
void TestInstance();
};
}
#if 1
#define AI_LOGIC(string) PRECOLLISION_LOGIC("AI::" string)
#else
#define AI_LOGIC(string)
#endif
#if 1
#define MOVER_LOGIC(string) AI_LOGIC("Mover::" string)
#else
#define MOVER_LOGIC(string)
#endif
#if 1
#define COMBAT_LOGIC(string) AI_LOGIC("Combat::" string)
#else
#define COMBAT_LOGIC(string)
#endif
#if 1
#define ABL_LOGIC(string) AI_LOGIC("ABL::" string)
#else
#define ABL_LOGIC(string)
#endif
#if 1
#define PATH_LOGIC(string) AI_LOGIC("Path::" string)
#else
#define PATH_LOGIC(string)
#endif