mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
- Use CUtlBuffer instead to construct the data. - Dump traverse ex nodes as well (CAI_TraverseNode).
482 lines
16 KiB
C++
482 lines
16 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "core/stdafx.h"
|
|
#include "tier0/fasttimer.h"
|
|
#include "tier1/cvar.h"
|
|
#include "tier1/cmd.h"
|
|
#include "mathlib/crc32.h"
|
|
#include "public/edict.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "game/server/ai_node.h"
|
|
#include "game/server/ai_network.h"
|
|
#include "game/server/ai_networkmanager.h"
|
|
#include <public/worldsize.h>
|
|
|
|
constexpr int AINET_SCRIPT_VERSION_NUMBER = 21;
|
|
constexpr int AINET_VERSION_NUMBER = 57;
|
|
constexpr int AINET_HEADER_SIZE = 16;
|
|
constexpr const char* AINETWORK_EXT = ".ain";
|
|
constexpr const char* AINETWORK_PATH = "maps/graphs/";
|
|
|
|
/*
|
|
==============================
|
|
CAI_NetworkBuilder::BuildFile
|
|
|
|
Build AI node graph file from
|
|
in-memory structures and write
|
|
to disk to be loaded
|
|
==============================
|
|
*/
|
|
void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
|
|
{
|
|
char szMeshPath[MAX_PATH];
|
|
char szGraphPath[MAX_PATH];
|
|
|
|
V_snprintf(szMeshPath, sizeof(szMeshPath), "%s%s_%s%s", NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, S_HULL_TYPE[E_HULL_TYPE::LARGE], NAVMESH_EXT);
|
|
V_snprintf(szGraphPath, sizeof(szGraphPath), "%s%s%s", AINETWORK_PATH, g_ServerGlobalVariables->m_pszMapName, AINETWORK_EXT);
|
|
|
|
CFastTimer masterTimer;
|
|
CFastTimer timer;
|
|
|
|
// Build from memory.
|
|
Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
|
|
Msg(eDLL_T::SERVER, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AI NETWORK GRAPH FILE CONSTRUCTION STARTED <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
|
|
Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
|
|
|
|
masterTimer.Start();
|
|
timer.Start();
|
|
|
|
FileSystem()->CreateDirHierarchy(AINETWORK_PATH, "GAME");
|
|
FileHandle_t pAIGraph = FileSystem()->Open(szGraphPath, "wb", "GAME");
|
|
if (!pAIGraph)
|
|
{
|
|
Error(eDLL_T::SERVER, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, szGraphPath);
|
|
return;
|
|
}
|
|
|
|
DevMsg(eDLL_T::SERVER, "+- Writing header...\n");
|
|
|
|
FileHandle_t pNavMesh = FileSystem()->Open(szMeshPath, "rb", "GAME");
|
|
uint32_t nNavMeshCRC = NULL;
|
|
|
|
if (!pNavMesh)
|
|
{
|
|
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network\n",
|
|
__FUNCTION__, S_HULL_TYPE[E_HULL_TYPE::LARGE]);
|
|
}
|
|
else
|
|
{
|
|
const ssize_t nLen = FileSystem()->Size(pNavMesh);
|
|
std::unique_ptr<uint8_t[]> pBuf(new uint8_t[nLen]);
|
|
|
|
FileSystem()->Read(pBuf.get(), nLen, pNavMesh);
|
|
FileSystem()->Close(pNavMesh);
|
|
|
|
nNavMeshCRC = crc32::update(NULL, pBuf.get(), nLen);
|
|
}
|
|
|
|
// Large NavMesh CRC.
|
|
DevMsg(eDLL_T::SERVER, " |-- AINet version: '%d'\n", AINET_VERSION_NUMBER);
|
|
DevMsg(eDLL_T::SERVER, " |-- Map version: '%d'\n", g_ServerGlobalVariables->m_nMapVersion);
|
|
DevMsg(eDLL_T::SERVER, " |-- NavMesh CRC: '0x%lX'\n", nNavMeshCRC);
|
|
|
|
CUtlBuffer buf;
|
|
|
|
// ---------------------------
|
|
// Save the version numbers
|
|
// ---------------------------
|
|
buf.PutInt(AINET_VERSION_NUMBER);
|
|
buf.PutInt(g_ServerGlobalVariables->m_nMapVersion);
|
|
buf.PutInt(nNavMeshCRC);
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing header. %lf seconds\n", timer.GetDuration().GetSeconds());
|
|
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, "+- Writing path nodes...\n");
|
|
|
|
// -------------------------------
|
|
// Dump all the nodes to the file
|
|
// -------------------------------
|
|
buf.PutInt(pNetwork->NumPathNodes());
|
|
int totalNumLinks = 0;
|
|
|
|
for (int node = 0; node < pNetwork->NumPathNodes(); node++)
|
|
{
|
|
const CAI_Node* aiNode = pNetwork->GetPathNode(node);
|
|
|
|
DevMsg(eDLL_T::SERVER, " |-- Copying node '#%d' from '0x%p' to '0x%zX'\n", aiNode->m_iID, aiNode, buf.TellPut());
|
|
|
|
buf.PutFloat(aiNode->GetOrigin().x);
|
|
buf.PutFloat(aiNode->GetOrigin().y);
|
|
buf.PutFloat(aiNode->GetOrigin().z);
|
|
|
|
buf.PutFloat(aiNode->GetYaw());
|
|
buf.Put(aiNode->m_flVOffset, sizeof(aiNode->m_flVOffset));
|
|
buf.PutChar((char)aiNode->GetType());
|
|
buf.PutInt(aiNode->GetInfo());
|
|
|
|
for (int j = 0; j < MAX_HULLS; j++)
|
|
{
|
|
buf.PutShort((short)aiNode->unk2[j]);
|
|
}
|
|
|
|
buf.Put(aiNode->unk3, sizeof(aiNode->unk3));
|
|
buf.PutShort(aiNode->unk6);
|
|
buf.PutShort(aiNode->unk9); // Always -1;
|
|
buf.Put(aiNode->unk11, sizeof(aiNode->unk11));
|
|
|
|
totalNumLinks += aiNode->NumLinks();
|
|
}
|
|
|
|
pNetwork->m_iNumLinks = totalNumLinks;
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing path nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumNodes);
|
|
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, "+- Writing node links...\n");
|
|
DevMsg(eDLL_T::SERVER, " |-- Cached node link count: '%d'\n", pNetwork->m_iNumLinks);
|
|
|
|
// -------------------------------
|
|
// Dump all the links to the file
|
|
// -------------------------------
|
|
int packedLinks = pNetwork->m_iNumLinks / 2;
|
|
buf.PutInt(packedLinks);
|
|
|
|
for (int node = 0; node < pNetwork->NumPathNodes(); node++)
|
|
{
|
|
const CAI_Node* aiNode = pNetwork->GetPathNode(node);
|
|
|
|
for (int link = 0; link < aiNode->NumLinks(); link++)
|
|
{
|
|
const CAI_NodeLink* nodeLink = aiNode->GetLinkByIndex(link);
|
|
|
|
// Skip links that don't originate from current node.
|
|
if (nodeLink->m_iSrcID == aiNode->m_iID)
|
|
{
|
|
DevMsg(eDLL_T::SERVER, " |-- Writing link (%hd <--> %hd) to '0x%zX'\n", nodeLink->m_iSrcID, nodeLink->m_iDestID, buf.TellPut());
|
|
|
|
buf.PutShort(nodeLink->m_iSrcID);
|
|
buf.PutShort(nodeLink->m_iDestID);
|
|
|
|
buf.PutChar(nodeLink->unk1);
|
|
buf.Put(nodeLink->m_iAcceptedMoveTypes, sizeof(nodeLink->m_iAcceptedMoveTypes));
|
|
}
|
|
}
|
|
}
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing node links. %lf seconds (%d links)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumLinks);
|
|
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, "+- Writing WC lookup table...\n");
|
|
|
|
// -------------------------------
|
|
// Dump WC lookup table
|
|
// -------------------------------
|
|
CUtlMap<int, int> wcIDs;
|
|
SetDefLessFunc(wcIDs);
|
|
bool bCheckForProblems = false;
|
|
|
|
const CAI_NetworkEditTools* const pEditOps = (*g_ppAINetworkManager)->GetEditOps();
|
|
|
|
for (int node = 0; node < pNetwork->m_iNumNodes; node++)
|
|
{
|
|
const int nIndex = pEditOps->m_pNodeIndexTable[node];
|
|
const int iPreviousNodeBinding = wcIDs.Find(nIndex);
|
|
|
|
if (iPreviousNodeBinding != wcIDs.InvalidIndex())
|
|
{
|
|
if (!bCheckForProblems)
|
|
{
|
|
DevWarning(eDLL_T::SERVER, "******* MAP CONTAINS DUPLICATE HAMMER NODE IDS! CHECK FOR PROBLEMS IN HAMMER TO CORRECT *******\n");
|
|
bCheckForProblems = true;
|
|
}
|
|
DevWarning(eDLL_T::SERVER, " AI node %d is associated with Hammer node %d, but %d is already bound to node %d\n",
|
|
node, nIndex, nIndex, wcIDs[(unsigned short)nIndex]);
|
|
}
|
|
else
|
|
{
|
|
wcIDs.Insert(nIndex, node);
|
|
}
|
|
|
|
DevMsg(eDLL_T::SERVER, " |-- Writing Hammer node (%d <--> %d) at '0x%zX'\n", nIndex, wcIDs.Element((unsigned short)nIndex), buf.TellPut());
|
|
buf.PutInt(nIndex);
|
|
}
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing hammer nodes. %lf seconds (%d indices)\n", timer.GetDuration().GetSeconds(), wcIDs.Count());
|
|
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, "+- Writing traverse ex nodes...\n");
|
|
|
|
const int traverseExNodeCount = g_pAITraverseNodes->Count();
|
|
buf.PutShort((short)traverseExNodeCount);
|
|
|
|
FOR_EACH_VEC(*g_pAITraverseNodes, i)
|
|
{
|
|
DevMsg(eDLL_T::SERVER, " |-- Writing traverse ex node '%d' at '0x%zX'\n", i, buf.TellPut());
|
|
|
|
const CAI_TraverseNode& traverseExNode = (*g_pAITraverseNodes)[i];
|
|
buf.Put(&traverseExNode.m_Quat, sizeof(Quaternion));
|
|
buf.PutInt(traverseExNode.m_Index_MAYBE);
|
|
}
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing traverse ex nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), traverseExNodeCount);
|
|
|
|
// TODO: Ideally these should be actually dumped, but they're always 0 in r2 from what i can tell.
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, " |-- Writing '%d' bytes for hull data block at '0x%zX'\n", (MAX_HULLS * 8), buf.TellPut());
|
|
for (int i = 0; i < (MAX_HULLS * 8); i++)
|
|
{
|
|
buf.PutChar('\0');
|
|
}
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing hull data. %lf seconds (%d blocks)\n", timer.GetDuration().GetSeconds(), MAX_HULLS);
|
|
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, "+- Writing path clusters...\n");
|
|
|
|
const int numClusters = g_pAIPathClusters->Count();
|
|
buf.PutInt(numClusters);
|
|
|
|
FOR_EACH_VEC(*g_pAIPathClusters, i)
|
|
{
|
|
DevMsg(eDLL_T::SERVER, " |-- Writing cluster '#%d' at '0x%zX'\n", i, buf.TellPut());
|
|
|
|
const CAI_Cluster* pathClusters = (*g_pAIPathClusters)[i];
|
|
|
|
buf.PutInt(pathClusters->m_nIndex);
|
|
buf.PutChar(pathClusters->unk1);
|
|
|
|
buf.PutFloat(pathClusters->GetOrigin().x);
|
|
buf.PutFloat(pathClusters->GetOrigin().y);
|
|
buf.PutFloat(pathClusters->GetOrigin().z);
|
|
|
|
const int unkVec0Size = pathClusters->unkVec0.Count();
|
|
buf.PutInt(unkVec0Size);
|
|
|
|
FOR_EACH_VEC(pathClusters->unkVec0, j)
|
|
{
|
|
short unkShort = static_cast<short>(pathClusters->unkVec0[j]);
|
|
buf.PutShort(unkShort);
|
|
}
|
|
|
|
const int unkVec1Size = pathClusters->unkVec1.Count();
|
|
buf.PutInt(unkVec1Size);
|
|
|
|
FOR_EACH_VEC(pathClusters->unkVec1, j)
|
|
{
|
|
short unkShort = static_cast<short>(pathClusters->unkVec1[j]);
|
|
buf.PutShort(unkShort);
|
|
}
|
|
|
|
buf.PutChar(pathClusters->unk5);
|
|
}
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing path clusters. %lf seconds (%d clusters)\n", timer.GetDuration().GetSeconds(), numClusters);
|
|
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, "+- Writing cluster links...\n");
|
|
|
|
const int numClusterLinks = g_pAIClusterLinks->Count();
|
|
buf.PutInt(numClusterLinks);
|
|
|
|
FOR_EACH_VEC(*g_pAIClusterLinks, i)
|
|
{
|
|
// Disk and memory structs are literally identical here so just directly write.
|
|
const CAI_ClusterLink* clusterLink = (*g_pAIClusterLinks)[i];
|
|
|
|
DevMsg(eDLL_T::SERVER, " |-- Writing link (%hd <--> %hd) to '0x%zX'\n", clusterLink->m_iSrcID, clusterLink->m_iDestID, buf.TellPut());
|
|
|
|
buf.PutShort(clusterLink->m_iSrcID);
|
|
buf.PutShort(clusterLink->m_iDestID);
|
|
|
|
buf.PutInt(clusterLink->unk2);
|
|
buf.PutChar(clusterLink->flags);
|
|
|
|
buf.PutChar(clusterLink->unkFlags4);
|
|
buf.PutChar(clusterLink->unkFlags5);
|
|
}
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing cluster links. %lf seconds (%d links)\n", timer.GetDuration().GetSeconds(), numClusterLinks);
|
|
|
|
// This is always set to '-1'. Likely a field for maintaining compatibility.
|
|
buf.PutInt(pNetwork->unk5);
|
|
|
|
// AIN v57 and above only (not present in r1, static array in r2, pointer to dynamic array in r5).
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, "+- Writing script nodes...\n");
|
|
|
|
buf.PutInt(pNetwork->m_iNumScriptNodes);
|
|
for (int i = 0; i < pNetwork->m_iNumScriptNodes; i++)
|
|
{
|
|
// Disk and memory structs for script nodes are identical.
|
|
DevMsg(eDLL_T::SERVER, " |-- Writing script node '#%d' at '0x%zX'\n", i, buf.TellPut());
|
|
buf.Put(&pNetwork->m_ScriptNode[i], sizeof(CAI_ScriptNode));
|
|
}
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing script nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumScriptNodes);
|
|
|
|
timer.Start();
|
|
DevMsg(eDLL_T::SERVER, "+- Writing hint data...\n");
|
|
|
|
buf.PutInt(pNetwork->m_iNumHints);
|
|
for (int i = 0; i < pNetwork->m_iNumHints; i++)
|
|
{
|
|
DevMsg(eDLL_T::SERVER, " |-- Writing hint data '#%d' at '0x%zX'\n", i, buf.TellPut());
|
|
buf.PutShort(pNetwork->m_Hints[i]);
|
|
}
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing hint data. %lf seconds (%d hints)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumHints);
|
|
|
|
FileSystem()->Write(buf.Base(), buf.TellPut(), pAIGraph);
|
|
FileSystem()->Close(pAIGraph);
|
|
|
|
masterTimer.End();
|
|
Msg(eDLL_T::SERVER, "...done writing AI node graph. %lf seconds\n", masterTimer.GetDuration().GetSeconds());
|
|
Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
|
|
Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
CAI_NetworkManager::LoadNetworkGraph
|
|
|
|
Load network from the disk
|
|
and validate status
|
|
==============================
|
|
*/
|
|
void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pManager, CUtlBuffer* pBuffer, const char* szAIGraphFile)
|
|
{
|
|
bool bNavMeshAvailable = true;
|
|
|
|
char szMeshPath[MAX_PATH];
|
|
char szGraphPath[MAX_PATH];
|
|
|
|
V_snprintf(szMeshPath, sizeof(szMeshPath), "%s%s_%s%s", NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, S_HULL_TYPE[E_HULL_TYPE::LARGE], NAVMESH_EXT);
|
|
V_snprintf(szGraphPath, sizeof(szGraphPath), "%s%s%s", AINETWORK_PATH, g_ServerGlobalVariables->m_pszMapName, AINETWORK_EXT);
|
|
|
|
int nAiNetVersion = NULL;
|
|
int nAiMapVersion = NULL;
|
|
|
|
uint32_t nAiGraphHash = NULL;
|
|
uint32_t nNavMeshHash = NULL;
|
|
|
|
FileHandle_t pNavMesh = FileSystem()->Open(szMeshPath, "rb", "GAME");
|
|
if (!pNavMesh)
|
|
{
|
|
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network\n", __FUNCTION__, S_HULL_TYPE[E_HULL_TYPE::LARGE]);
|
|
bNavMeshAvailable = false;
|
|
}
|
|
else
|
|
{
|
|
const ssize_t nLen = FileSystem()->Size(pNavMesh);
|
|
std::unique_ptr<uint8_t[]> pBuf(new uint8_t[nLen]);
|
|
|
|
FileSystem()->Read(pBuf.get(), nLen, pNavMesh);
|
|
FileSystem()->Close(pNavMesh);
|
|
|
|
nNavMeshHash = crc32::update(NULL, pBuf.get(), nLen);
|
|
}
|
|
|
|
FileHandle_t pAIGraph = FileSystem()->Open(szGraphPath, "rb", "GAME");
|
|
if (!pAIGraph)
|
|
{
|
|
Error(eDLL_T::SERVER, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__, szGraphPath);
|
|
LoadNetworkGraphEx(pManager, pBuffer, szAIGraphFile);
|
|
|
|
return;
|
|
}
|
|
|
|
if (FileSystem()->Size(pAIGraph) >= AINET_HEADER_SIZE)
|
|
{
|
|
FileSystem()->Read(&nAiNetVersion, sizeof(int), pAIGraph);
|
|
FileSystem()->Read(&nAiMapVersion, sizeof(int), pAIGraph);
|
|
FileSystem()->Read(&nAiGraphHash, sizeof(int), pAIGraph);
|
|
|
|
if (nAiNetVersion > AINET_VERSION_NUMBER)
|
|
{
|
|
Warning(eDLL_T::SERVER, "AI node graph '%s' is unsupported (net version: '%d' expected: '%d')\n",
|
|
szGraphPath, nAiNetVersion, AINET_VERSION_NUMBER);
|
|
}
|
|
else if (nAiMapVersion != g_ServerGlobalVariables->m_nMapVersion)
|
|
{
|
|
Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (map version: '%d' expected: '%d')\n",
|
|
szGraphPath, nAiMapVersion, g_ServerGlobalVariables->m_nMapVersion);
|
|
}
|
|
else if (bNavMeshAvailable && nAiGraphHash != nNavMeshHash)
|
|
{
|
|
Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (checksum: '%x' expected: '%x')\n",
|
|
szGraphPath, nAiGraphHash, nNavMeshHash);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(eDLL_T::SERVER, NO_ERROR, "%s - AI node graph '%s' is corrupt\n", __FUNCTION__, szGraphPath);
|
|
}
|
|
|
|
FileSystem()->Close(pAIGraph);
|
|
LoadNetworkGraphEx(pManager, pBuffer, szAIGraphFile);
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
CAI_NetworkManager::LoadNetworkGraphEx
|
|
|
|
Load network
|
|
(internal)
|
|
==============================
|
|
*/
|
|
void CAI_NetworkManager::LoadNetworkGraphEx(CAI_NetworkManager* pManager, CUtlBuffer* pBuffer, const char* szAIGraphFile)
|
|
{
|
|
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
|
|
CAI_NetworkManager__LoadNetworkGraph(pManager, pBuffer, szAIGraphFile);
|
|
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
|
|
CAI_NetworkManager__LoadNetworkGraph(pManager, pBuffer, szAIGraphFile);
|
|
#endif
|
|
|
|
if (ai_ainDumpOnLoad->GetBool())
|
|
{
|
|
Msg(eDLL_T::SERVER, "Dumping AI Network '%s'\n", szAIGraphFile);
|
|
CAI_NetworkBuilder::SaveNetworkGraph(pManager->m_pNetwork);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
CAI_NetworkBuilder::Build
|
|
|
|
builds network in-memory
|
|
during level load
|
|
==============================
|
|
*/
|
|
void CAI_NetworkBuilder::Build(CAI_NetworkBuilder* pBuilder, CAI_Network* pAINetwork)
|
|
{
|
|
CAI_NetworkBuilder__Build(pBuilder, pAINetwork);
|
|
CAI_NetworkBuilder::SaveNetworkGraph(pAINetwork);
|
|
}
|
|
|
|
void VAI_NetworkManager::Attach() const
|
|
{
|
|
DetourAttach((LPVOID*)&CAI_NetworkManager__LoadNetworkGraph, &CAI_NetworkManager::LoadNetworkGraph);
|
|
DetourAttach((LPVOID*)&CAI_NetworkBuilder__Build, &CAI_NetworkBuilder::Build);
|
|
}
|
|
|
|
void VAI_NetworkManager::Detach() const
|
|
{
|
|
DetourDetach((LPVOID*)&CAI_NetworkManager__LoadNetworkGraph, &CAI_NetworkManager::LoadNetworkGraph);
|
|
DetourDetach((LPVOID*)&CAI_NetworkBuilder__Build, &CAI_NetworkBuilder::Build);
|
|
}
|