mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Server: refactor AI detour implementation
The current implementation was mixing several types with each other, making it hard to determine what a hull and what a navmesh type is. Code has been refactored to use the new types and names introduced in commit e638cc9323c7b74279f0274591264efc53c29d4d.
This commit is contained in:
parent
d2cf0ebac6
commit
568265a330
@ -71,7 +71,6 @@ add_sources( SOURCE_GROUP "AI"
|
||||
"server/ai_networkmanager.cpp"
|
||||
"server/ai_networkmanager.h"
|
||||
"server/ai_node.h"
|
||||
"server/ai_hull.h"
|
||||
"server/ai_utility.cpp"
|
||||
"server/ai_utility.h"
|
||||
"server/detour_impl.h"
|
||||
@ -137,6 +136,12 @@ add_sources( SOURCE_GROUP "Public"
|
||||
"${ENGINE_SOURCE_DIR}/public/iserverunknown.h"
|
||||
)
|
||||
|
||||
add_sources( SOURCE_GROUP "Public/AI"
|
||||
"${ENGINE_SOURCE_DIR}/public/game/server/ai_agent.h"
|
||||
"${ENGINE_SOURCE_DIR}/public/game/server/ai_hull.h"
|
||||
"${ENGINE_SOURCE_DIR}/public/game/server/ai_navmesh.h"
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
if( ${PROJECT_NAME} STREQUAL "client_static" )
|
||||
|
@ -1,27 +0,0 @@
|
||||
//=============================================================================//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef AI_HULL_H
|
||||
#define AI_HULL_H
|
||||
|
||||
//=========================================================
|
||||
// Link Properties. These hulls must correspond to the hulls
|
||||
// in AI_Hull.cpp!
|
||||
//=========================================================
|
||||
enum Hull_t
|
||||
{
|
||||
HULL_HUMAN,
|
||||
HULL_MEDIUM,
|
||||
HULL_FLYING_VEHICLE,
|
||||
HULL_SMALL,
|
||||
HULL_TITAN,
|
||||
HULL_GOLIATH,
|
||||
HULL_PROWLER,
|
||||
//--------------------------------------------
|
||||
NUM_HULLS, // Confirmed, see [r5apex_ds + CB2765]
|
||||
HULL_NONE = -1 // No Hull (appears after num hulls as we don't want to count it)
|
||||
};
|
||||
|
||||
#endif // AI_HULL_H
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "game/server/ai_node.h"
|
||||
#include "game/server/ai_navmesh.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CAI_Network
|
||||
@ -31,8 +32,8 @@ public:
|
||||
int m_iNumLinks; // +0x0008
|
||||
int m_nUnk0;
|
||||
|
||||
CAI_HullData m_HullData[MAX_HULLS];
|
||||
int m_iNumZones[MAX_HULLS]; // +0x0088
|
||||
CAI_HullData m_HullData[NAVMESH_COUNT];
|
||||
int m_iNumZones[NAVMESH_COUNT];// +0x0088
|
||||
|
||||
// unk8 on disk
|
||||
int unk5; // +0x009C
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "game/server/ai_node.h"
|
||||
#include "game/server/ai_network.h"
|
||||
#include "game/server/ai_networkmanager.h"
|
||||
#include "game/server/ai_navmesh.h"
|
||||
#include <public/worldsize.h>
|
||||
|
||||
constexpr int AINET_SCRIPT_VERSION_NUMBER = 21;
|
||||
@ -39,7 +40,7 @@ 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(szMeshPath, sizeof(szMeshPath), "%s%s_%s%s", NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, NavMesh_GetNameForType(NAVMESH_LARGE), NAVMESH_EXT);
|
||||
V_snprintf(szGraphPath, sizeof(szGraphPath), "%s%s%s", AINETWORK_PATH, g_ServerGlobalVariables->m_pszMapName, AINETWORK_EXT);
|
||||
|
||||
CFastTimer masterTimer;
|
||||
@ -104,7 +105,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
|
||||
buf.PutChar((char)aiNode->GetType());
|
||||
buf.PutInt(aiNode->GetInfo());
|
||||
|
||||
for (int j = 0; j < MAX_HULLS; j++)
|
||||
for (int j = 0; j < NAVMESH_COUNT; j++)
|
||||
{
|
||||
buf.PutShort((short)aiNode->unk2[j]);
|
||||
}
|
||||
@ -225,7 +226,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
|
||||
// Dump the hull data blocks
|
||||
// -------------------------------
|
||||
|
||||
for (int i = 0; i < MAX_HULLS; i++)
|
||||
for (int i = 0; i < NAVMESH_COUNT; i++)
|
||||
{
|
||||
const CAI_HullData& hullData = pNetwork->m_HullData[i];
|
||||
const int numHullZones = pNetwork->m_iNumZones[i];
|
||||
@ -240,7 +241,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
|
||||
}
|
||||
|
||||
timer.End();
|
||||
Msg(eDLL_T::SERVER, "...done writing hull data blocks. %lf seconds (%d blocks)\n", timer.GetDuration().GetSeconds(), MAX_HULLS);
|
||||
Msg(eDLL_T::SERVER, "...done writing hull data blocks. %lf seconds (%d blocks)\n", timer.GetDuration().GetSeconds(), NAVMESH_COUNT);
|
||||
|
||||
|
||||
timer.Start();
|
||||
@ -371,7 +372,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
|
||||
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]);
|
||||
__FUNCTION__, NavMesh_GetNameForType(NAVMESH_LARGE));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -418,7 +419,7 @@ void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pManager, CUtlBuff
|
||||
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(szMeshPath, sizeof(szMeshPath), "%s%s_%s%s", NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, NavMesh_GetNameForType(NAVMESH_LARGE), NAVMESH_EXT);
|
||||
V_snprintf(szGraphPath, sizeof(szGraphPath), "%s%s%s", AINETWORK_PATH, g_ServerGlobalVariables->m_pszMapName, AINETWORK_EXT);
|
||||
|
||||
int nAiNetVersion = NULL;
|
||||
@ -432,7 +433,7 @@ void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pManager, CUtlBuff
|
||||
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]);
|
||||
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network\n", __FUNCTION__, NavMesh_GetNameForType(NAVMESH_LARGE));
|
||||
bNavMeshAvailable = false;
|
||||
}
|
||||
else
|
||||
|
@ -6,7 +6,7 @@
|
||||
#pragma once
|
||||
#include "mathlib/vector.h"
|
||||
#include "mathlib/bitvec.h"
|
||||
constexpr int MAX_HULLS = 5;
|
||||
#include "game/server/ai_navmesh.h"
|
||||
|
||||
constexpr int NOT_CACHED = -2; // Returned if data not in cache
|
||||
constexpr int NO_NODE = -1; // Returned when no node meets the qualification
|
||||
@ -31,7 +31,7 @@ struct CAI_NodeLink
|
||||
{
|
||||
short m_iSrcID;
|
||||
short m_iDestID;
|
||||
byte m_iAcceptedMoveTypes[MAX_HULLS];
|
||||
byte m_iAcceptedMoveTypes[NAVMESH_COUNT];
|
||||
byte m_LinkInfo;
|
||||
char unk1; // maps => unk0 on disk
|
||||
char unk2[5];
|
||||
@ -58,20 +58,20 @@ public:
|
||||
int SetInfo(int info) { return m_eNodeInfo = info; }
|
||||
int GetInfo() const { return m_eNodeInfo; }
|
||||
|
||||
int m_iID; // ID for this node
|
||||
Vector3D m_vOrigin; // location of this node in space
|
||||
float m_flVOffset[MAX_HULLS]; // vertical offset for each hull type, assuming ground node, 0 otherwise
|
||||
float m_flYaw; // NPC on this node should face this yaw to face the hint, or climb a ladder
|
||||
int m_iID; // ID for this node
|
||||
Vector3D m_vOrigin; // location of this node in space
|
||||
float m_flVOffset[NAVMESH_COUNT]; // vertical offset for each hull type, assuming ground node, 0 otherwise
|
||||
float m_flYaw; // NPC on this node should face this yaw to face the hint, or climb a ladder
|
||||
|
||||
NodeType_e m_eNodeType; // The type of node; always 2 in buildainfile.
|
||||
int m_eNodeInfo; // bits that tell us more about this nodes
|
||||
NodeType_e m_eNodeType; // The type of node; always 2 in buildainfile.
|
||||
int m_eNodeInfo; // bits that tell us more about this nodes
|
||||
|
||||
int unk2[MAX_HULLS]; // Maps directly to unk2 in disk struct, despite being ints rather than shorts
|
||||
int unk2[NAVMESH_COUNT]; // Maps directly to unk2 in disk struct, despite being ints rather than shorts
|
||||
|
||||
// View server.dll+393672 for context
|
||||
char unk3[MAX_HULLS]; // Should map to unk3 on disk
|
||||
char pad[3]; // Aligns next bytes
|
||||
float unk4[MAX_HULLS]; // I have no clue, calculated using some kind float function magic
|
||||
char unk3[NAVMESH_COUNT]; // Should map to unk3 on disk
|
||||
char pad[3]; // Aligns next bytes
|
||||
float unk4[NAVMESH_COUNT]; // I have no clue, calculated using some kind float function magic
|
||||
|
||||
CUtlVector<CAI_NodeLink*> m_Links;
|
||||
short unk6; // Should match up to unk4 on disk
|
||||
|
@ -16,69 +16,51 @@
|
||||
|
||||
static ConVar navmesh_always_reachable("navmesh_always_reachable", "0", FCVAR_DEVELOPMENTONLY, "Marks goal poly from agent poly as reachable regardless of table data ( !slower! )");
|
||||
|
||||
inline uint32_t g_HullMasks[10] = // Hull mask table [r5apex_ds.exe + 131a2f8].
|
||||
{
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0xfffffffb, 0xfffffffa, 0xfffffff9, 0xfffffff8, 0x00040200
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: gets the navmesh by hull from global array [small, med_short, medium, large, extra_large]
|
||||
// input : hull -
|
||||
// Purpose: gets the navmesh by type from global array [small, med_short, medium, large, extra_large]
|
||||
// input : navMeshType -
|
||||
// Output : pointer to navmesh
|
||||
//-----------------------------------------------------------------------------
|
||||
dtNavMesh* GetNavMeshForHull(int hullSize)
|
||||
dtNavMesh* Detour_GetNavMeshByType(const NavMeshType_e navMeshType)
|
||||
{
|
||||
Assert(hullSize >= NULL && hullSize < MAX_HULLS); // Programmer error.
|
||||
return g_pNavMesh[hullSize];
|
||||
Assert(navMeshType >= NULL && navMeshType < NAVMESH_COUNT); // Programmer error.
|
||||
return g_pNavMesh[navMeshType];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: gets the navmesh by hull from global array [small, med_short, medium, large, extra_large]
|
||||
// input : hull -
|
||||
// Output : pointer to navmesh
|
||||
// Purpose: free's the navmesh by type from global array [small, med_short, medium, large, extra_large]
|
||||
// input : navMeshType -
|
||||
//-----------------------------------------------------------------------------
|
||||
void ClearNavMeshForHull(int hullSize)
|
||||
void Detour_FreeNavMeshByType(const NavMeshType_e navMeshType)
|
||||
{
|
||||
Assert(hullSize >= NULL && hullSize < MAX_HULLS); // Programmer error.
|
||||
dtNavMesh* nav = g_pNavMesh[hullSize];
|
||||
Assert(navMeshType >= NULL && navMeshType < NAVMESH_COUNT); // Programmer error.
|
||||
dtNavMesh* const nav = g_pNavMesh[navMeshType];
|
||||
|
||||
if (nav) // Only free if NavMesh for hull is loaded.
|
||||
if (nav) // Only free if NavMesh for type is loaded.
|
||||
{
|
||||
// Frees tiles, polys, tris, anything dynamically
|
||||
// allocated for this navmesh, and the navmesh itself.
|
||||
v_Detour_FreeNavMesh(nav);
|
||||
free(nav);
|
||||
|
||||
g_pNavMesh[hullSize] = nullptr;
|
||||
g_pNavMesh[navMeshType] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: gets hull mask by id
|
||||
// input : hullId -
|
||||
// Output : hull mask
|
||||
//-----------------------------------------------------------------------------
|
||||
uint32_t GetHullMaskById(int hullId)
|
||||
{
|
||||
Assert(hullId >= NULL && hullId < SDK_ARRAYSIZE(g_HullMasks)); // Programmer error.
|
||||
return (hullId + g_HullMasks[hullId]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: determines whether goal poly is reachable from agent poly
|
||||
// input : *nav -
|
||||
// fromRef -
|
||||
// goalRef -
|
||||
// hull_type -
|
||||
// animType -
|
||||
// Output : value if reachable, false otherwise
|
||||
//-----------------------------------------------------------------------------
|
||||
uint8_t IsGoalPolyReachable(dtNavMesh* nav, dtPolyRef fromRef, dtPolyRef goalRef, int hullId)
|
||||
uint8_t Detour_IsGoalPolyReachable(dtNavMesh* nav, dtPolyRef fromRef, dtPolyRef goalRef, TraverseAnimType_e animType)
|
||||
{
|
||||
if (navmesh_always_reachable.GetBool())
|
||||
return true;
|
||||
|
||||
return dtNavMesh__isPolyReachable(nav, fromRef, goalRef, hullId);
|
||||
return dtNavMesh__isPolyReachable(nav, fromRef, goalRef, animType);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -95,9 +77,9 @@ void Detour_LevelInit()
|
||||
//-----------------------------------------------------------------------------
|
||||
void Detour_LevelShutdown()
|
||||
{
|
||||
for (int i = 0; i < MAX_HULLS; i++)
|
||||
for (int i = 0; i < NAVMESH_COUNT; i++)
|
||||
{
|
||||
ClearNavMeshForHull(i);
|
||||
Detour_FreeNavMeshByType(NavMeshType_e(i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,20 +90,21 @@ void Detour_LevelShutdown()
|
||||
bool Detour_IsLoaded()
|
||||
{
|
||||
int ret = 0;
|
||||
for (int i = 0; i < MAX_HULLS; i++)
|
||||
for (int i = 0; i < NAVMESH_COUNT; i++)
|
||||
{
|
||||
const dtNavMesh* nav = GetNavMeshForHull(i);
|
||||
const dtNavMesh* nav = Detour_GetNavMeshByType(NavMeshType_e(i));
|
||||
if (!nav) // Failed to load...
|
||||
{
|
||||
Warning(eDLL_T::SERVER, "NavMesh '%s%s_%s%s' not loaded\n",
|
||||
NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, S_HULL_TYPE[i], NAVMESH_EXT);
|
||||
NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName,
|
||||
NavMesh_GetNameForType(NavMeshType_e(i)), NAVMESH_EXT);
|
||||
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
|
||||
Assert(ret <= MAX_HULLS);
|
||||
return (ret != MAX_HULLS);
|
||||
Assert(ret <= NAVMESH_COUNT);
|
||||
return (ret != NAVMESH_COUNT);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -177,6 +160,6 @@ static ConCommand navmesh_hotswap("navmesh_hotswap", Detour_HotSwap_f, "Hot swap
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void VRecast::Detour(const bool bAttach) const
|
||||
{
|
||||
DetourSetup(&dtNavMesh__isPolyReachable, &IsGoalPolyReachable, bAttach);
|
||||
DetourSetup(&dtNavMesh__isPolyReachable, &Detour_IsGoalPolyReachable, bAttach);
|
||||
DetourSetup(&v_Detour_LevelInit, &Detour_LevelInit, bAttach);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "thirdparty/recast/Detour/Include/DetourStatus.h"
|
||||
#include "thirdparty/recast/Detour/Include/DetourNavMesh.h"
|
||||
#include "thirdparty/recast/Detour/Include/DetourNavMeshQuery.h"
|
||||
#include "game/server/ai_navmesh.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// RUNTIME: DETOUR
|
||||
@ -10,35 +11,16 @@ inline void(*v_Detour_LevelInit)(void);
|
||||
inline void(*v_Detour_FreeNavMesh)(dtNavMesh* mesh);
|
||||
inline dtStatus(*dtNavMesh__Init)(dtNavMesh* thisptr, unsigned char* data, int flags);
|
||||
inline dtStatus(*dtNavMesh__addTile)(dtNavMesh* thisptr, unsigned char* data, dtMeshHeader* header, int dataSize, int flags, dtTileRef lastRef);
|
||||
inline uint8_t(*dtNavMesh__isPolyReachable)(dtNavMesh* thisptr, dtPolyRef poly_1, dtPolyRef poly_2, int hull_type);
|
||||
inline uint8_t(*dtNavMesh__isPolyReachable)(dtNavMesh* thisptr, dtPolyRef poly_1, dtPolyRef poly_2, TraverseAnimType_e animType);
|
||||
|
||||
|
||||
constexpr const char* NAVMESH_PATH = "maps/navmesh/";
|
||||
constexpr const char* NAVMESH_EXT = ".nm";
|
||||
|
||||
static const char* S_HULL_TYPE[5] =
|
||||
{
|
||||
"small",
|
||||
"med_short",
|
||||
"medium",
|
||||
"large",
|
||||
"extra_large"
|
||||
};
|
||||
|
||||
enum E_HULL_TYPE
|
||||
{
|
||||
SMALL = 0,
|
||||
MED_SHORT,
|
||||
MEDIUM,
|
||||
LARGE,
|
||||
EXTRA_LARGE
|
||||
};
|
||||
|
||||
inline dtNavMesh** g_pNavMesh = nullptr;
|
||||
inline dtNavMeshQuery* g_pNavMeshQuery = nullptr;
|
||||
|
||||
dtNavMesh* GetNavMeshForHull(int hullSize);
|
||||
uint32_t GetHullMaskById(int hullId);
|
||||
dtNavMesh* Detour_GetNavMeshByType(const NavMeshType_e navMeshType);
|
||||
|
||||
void Detour_LevelInit();
|
||||
void Detour_LevelShutdown();
|
||||
@ -54,7 +36,7 @@ class VRecast : public IDetour
|
||||
LogFunAdr("dtNavMesh::Init", dtNavMesh__Init);
|
||||
LogFunAdr("dtNavMesh::addTile", dtNavMesh__addTile);
|
||||
LogFunAdr("dtNavMesh::isPolyReachable", dtNavMesh__isPolyReachable);
|
||||
LogVarAdr("g_pNavMesh[ MAX_HULLS ]", g_pNavMesh);
|
||||
LogVarAdr("g_pNavMesh[ NavMeshType_e::NAVMESH_COUNT ]", g_pNavMesh);
|
||||
LogVarAdr("g_pNavMeshQuery", g_pNavMeshQuery);
|
||||
}
|
||||
virtual void GetFun(void) const
|
||||
|
@ -187,7 +187,7 @@ void CAI_Utility::DrawNavMeshBVTree(
|
||||
const bool bDepthBuffer) const
|
||||
{
|
||||
if (!pMesh)
|
||||
pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt());
|
||||
pMesh = Detour_GetNavMeshByType(NavMeshType_e(navmesh_debug_type.GetInt()));
|
||||
if (!pMesh)
|
||||
return; // NavMesh for hull not loaded.
|
||||
|
||||
@ -248,7 +248,7 @@ void CAI_Utility::DrawNavMeshPortals(const dtNavMesh* pMesh,
|
||||
const bool bDepthBuffer) const
|
||||
{
|
||||
if (!pMesh)
|
||||
pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt());
|
||||
pMesh = Detour_GetNavMeshByType(NavMeshType_e(navmesh_debug_type.GetInt()));
|
||||
if (!pMesh)
|
||||
return; // NavMesh for hull not loaded.
|
||||
|
||||
@ -368,7 +368,7 @@ void CAI_Utility::DrawNavMeshPolys(const dtNavMesh* pMesh,
|
||||
const bool bDepthBuffer) const
|
||||
{
|
||||
if (!pMesh)
|
||||
pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt());
|
||||
pMesh = Detour_GetNavMeshByType(NavMeshType_e(navmesh_debug_type.GetInt()));
|
||||
if (!pMesh)
|
||||
return; // NavMesh for hull not loaded.
|
||||
|
||||
@ -455,7 +455,7 @@ void CAI_Utility::DrawNavMeshPolyBoundaries(const dtNavMesh* pMesh,
|
||||
Color col{ 20, 140, 255, 255 };
|
||||
|
||||
if (!pMesh)
|
||||
pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt());
|
||||
pMesh = Detour_GetNavMeshByType(NavMeshType_e(navmesh_debug_type.GetInt()));
|
||||
if (!pMesh)
|
||||
return; // NavMesh for hull not loaded.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user