mirror of
https://github.com/ENSL/NS.git
synced 2025-04-20 16:30:56 +00:00
Implemented fully dynamic off-mesh connections
Phase gates now use connections rather than custom path finding. Much more performant.
This commit is contained in:
parent
46efcdaeda
commit
82ea559a7a
8 changed files with 419 additions and 182 deletions
|
@ -13,6 +13,7 @@ static const float commander_action_cooldown = 1.0f;
|
|||
static const float min_request_spam_time = 5.0f;
|
||||
|
||||
constexpr auto MAX_AI_PATH_SIZE = 512; // Maximum number of points allowed in a path (this should be enough for any sized map)
|
||||
static const int MAX_NAV_MESHES = 8; // Max number of nav meshes allowed. Currently 3 are used (one for building placement, one for the onos, and a regular one for everyone else)
|
||||
|
||||
// NS weapon types. Each number refers to the GoldSrc weapon index
|
||||
typedef enum
|
||||
|
@ -168,6 +169,15 @@ typedef enum _STRUCTUREPURPOSE
|
|||
|
||||
} StructurePurpose;
|
||||
|
||||
typedef struct _OFF_MESH_CONN
|
||||
{
|
||||
int MeshConnectionIndex = -1;
|
||||
unsigned short ConnectionFlags = 0;
|
||||
Vector FromLocation = g_vecZero;
|
||||
Vector ToLocation = g_vecZero;
|
||||
edict_t* TargetObject = nullptr;
|
||||
} AvHAIOffMeshConnection;
|
||||
|
||||
// Data structure used to track resource nodes in the map
|
||||
typedef struct _RESOURCE_NODE
|
||||
{
|
||||
|
@ -192,7 +202,7 @@ typedef struct _HIVE_DEFINITION_T
|
|||
AvHMessageID TechStatus = MESSAGE_NULL; // What tech (if any) is assigned to this hive right now
|
||||
bool bIsUnderAttack = false; // Is the hive currently under attack? Becomes false if not taken damage for more than 10 seconds
|
||||
AvHAIResourceNode* HiveResNodeRef = nullptr; // Which resource node (indexes into ResourceNodes array) belongs to this hive?
|
||||
unsigned int ObstacleRefs[8]; // When in progress or built, will place an obstacle so bots don't try to walk through it
|
||||
unsigned int ObstacleRefs[MAX_NAV_MESHES]; // When in progress or built, will place an obstacle so bots don't try to walk through it
|
||||
float NextFloorLocationCheck = 0.0f; // When should the closest navigable point to the hive be calculated? Used to delay the check after a hive is built
|
||||
AvHTeamNumber OwningTeam = TEAM_IND; // Which team owns this hive currently (TEAM_IND if empty)
|
||||
|
||||
|
@ -254,7 +264,8 @@ typedef struct _AVH_AI_BUILDABLE_STRUCTURE
|
|||
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE;
|
||||
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE;
|
||||
int LastSeen = 0; // Which refresh cycle was this last seen on? Used to determine if the building has been removed from play
|
||||
unsigned int ObstacleRefs[8]; // References to this structure's obstacles across each nav mesh
|
||||
unsigned int ObstacleRefs[MAX_NAV_MESHES]; // References to this structure's obstacles across each nav mesh
|
||||
vector<AvHAIOffMeshConnection> OffMeshConnections; // References to any off-mesh connections this structure is associated with
|
||||
Vector LastSuccessfulCommanderLocation = g_vecZero; // Tracks the last commander view location where it successfully placed or selected the building
|
||||
Vector LastSuccessfulCommanderAngle = g_vecZero; // Tracks the last commander input angle ("click" location) used to successfully place or select building
|
||||
StructurePurpose Purpose = STRUCTURE_PURPOSE_NONE;
|
||||
|
|
|
@ -272,7 +272,8 @@ void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot)
|
|||
case SAMPLE_POLYFLAGS_BLOCKED:
|
||||
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 128, 128, 128);
|
||||
break;
|
||||
case SAMPLE_POLYFLAGS_PHASEGATE:
|
||||
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
|
||||
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
|
||||
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 255, 128, 128);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "../AvHWeldable.h"
|
||||
#include "../AvHServerUtil.h"
|
||||
#include "../AvHGamerules.h"
|
||||
|
||||
#include "../../dlls/triggers.h"
|
||||
|
||||
|
@ -104,13 +105,13 @@ struct NavMeshTileHeader
|
|||
|
||||
struct OffMeshConnectionDef
|
||||
{
|
||||
bool bIsActive = false;
|
||||
unsigned int UserID = 0;
|
||||
float spos[3] = { 0.0f, 0.0f, 0.0f };
|
||||
float epos[3] = { 0.0f, 0.0f, 0.0f };
|
||||
bool bBiDir = false;
|
||||
float Rad = 0.0f;
|
||||
char Area = 0;
|
||||
short Flag = 0;
|
||||
unsigned char Area = 0;
|
||||
unsigned short Flag = 0;
|
||||
};
|
||||
|
||||
struct FastLZCompressor : public dtTileCacheCompressor
|
||||
|
@ -192,8 +193,14 @@ struct MeshProcess : public dtTileCacheMeshProcess
|
|||
unsigned short OffMeshFlags[MAX_OFFMESH_CONNS];
|
||||
unsigned int OffMeshIDs[MAX_OFFMESH_CONNS];
|
||||
|
||||
bool bNavDataDirty = false;
|
||||
|
||||
OffMeshConnectionDef ConnectionDefinitions[MAX_OFFMESH_CONNS];
|
||||
|
||||
vector<OffMeshConnectionDef> OffMeshConnections;
|
||||
|
||||
unsigned int NextUserID = 0;
|
||||
|
||||
inline MeshProcess()
|
||||
{}
|
||||
|
||||
|
@ -202,57 +209,113 @@ struct MeshProcess : public dtTileCacheMeshProcess
|
|||
|
||||
}
|
||||
|
||||
int AddOffMeshConnectionDef(Vector Start, Vector End, unsigned char area, unsigned short flag, bool bBiDirectional)
|
||||
void AddOffMeshConnectionDef(Vector Start, Vector End, unsigned char area, unsigned short flag, bool bBiDirectional, AvHAIOffMeshConnection* ConnectionRef)
|
||||
{
|
||||
float spos[3] = { Start.x, Start.z, -Start.y };
|
||||
float epos[3] = { End.x, End.z, -End.y };
|
||||
OffMeshConnectionDef NewDefinition;
|
||||
NewDefinition.Area = area;
|
||||
NewDefinition.bBiDir = bBiDirectional;
|
||||
NewDefinition.spos[0] = Start.x;
|
||||
NewDefinition.spos[1] = Start.z;
|
||||
NewDefinition.spos[2] = -Start.y;
|
||||
NewDefinition.epos[0] = End.x;
|
||||
NewDefinition.epos[1] = End.z;
|
||||
NewDefinition.epos[2] = -End.y;
|
||||
NewDefinition.Flag = flag;
|
||||
NewDefinition.Rad = 18.0f;
|
||||
NewDefinition.UserID = NextUserID;
|
||||
|
||||
if (NumOffMeshConns >= MAX_OFFMESH_CONNS) return -1;
|
||||
float* v = &OffMeshVerts[NumOffMeshConns * 3 * 2];
|
||||
OffMeshRads[NumOffMeshConns] = 18.0f;
|
||||
OffMeshDirs[NumOffMeshConns] = bBiDirectional;
|
||||
OffMeshAreas[NumOffMeshConns] = area;
|
||||
OffMeshFlags[NumOffMeshConns] = flag;
|
||||
OffMeshIDs[NumOffMeshConns] = 1000 + NumOffMeshConns;
|
||||
dtVcopy(&v[0], spos);
|
||||
dtVcopy(&v[3], epos);
|
||||
NumOffMeshConns++;
|
||||
return NumOffMeshConns - 1;
|
||||
}
|
||||
|
||||
void RemoveOffMeshConnectionDef(int Index)
|
||||
{
|
||||
if (Index > -1 && Index < MAX_OFFMESH_CONNS)
|
||||
if (ConnectionRef)
|
||||
{
|
||||
NumOffMeshConns--;
|
||||
float* src = &OffMeshVerts[NumOffMeshConns * 3 * 2];
|
||||
float* dst = &OffMeshVerts[Index * 3 * 2];
|
||||
dtVcopy(&dst[0], &src[0]);
|
||||
dtVcopy(&dst[3], &src[3]);
|
||||
OffMeshRads[Index] = OffMeshRads[NumOffMeshConns];
|
||||
OffMeshDirs[Index] = OffMeshDirs[NumOffMeshConns];
|
||||
OffMeshAreas[Index] = OffMeshAreas[NumOffMeshConns];
|
||||
OffMeshFlags[Index] = OffMeshFlags[NumOffMeshConns];
|
||||
ConnectionRef->MeshConnectionIndex = NextUserID;
|
||||
}
|
||||
|
||||
NextUserID++;
|
||||
|
||||
OffMeshConnections.push_back(NewDefinition);
|
||||
|
||||
bNavDataDirty = true;
|
||||
};
|
||||
|
||||
void RemoveOffMeshConnectionDef(int UserID)
|
||||
{
|
||||
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end();)
|
||||
{
|
||||
if (it->UserID == UserID)
|
||||
{
|
||||
it = OffMeshConnections.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
bNavDataDirty = true;
|
||||
}
|
||||
|
||||
void GetOffMeshConnectionPoints(int Index, Vector& OutStartLoc, Vector& OutEndLoc)
|
||||
void UpdateOffMeshData()
|
||||
{
|
||||
int CurrIndex = 0;
|
||||
int VertIndex = 0;
|
||||
|
||||
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
|
||||
{
|
||||
OffMeshVerts[VertIndex++] = it->spos[0];
|
||||
OffMeshVerts[VertIndex++] = it->spos[1];
|
||||
OffMeshVerts[VertIndex++] = it->spos[2];
|
||||
OffMeshVerts[VertIndex++] = it->epos[0];
|
||||
OffMeshVerts[VertIndex++] = it->epos[1];
|
||||
OffMeshVerts[VertIndex++] = it->epos[2];
|
||||
|
||||
OffMeshRads[CurrIndex] = it->Rad;
|
||||
OffMeshDirs[CurrIndex] = it->bBiDir;
|
||||
OffMeshAreas[CurrIndex] = it->Area;
|
||||
OffMeshFlags[CurrIndex] = it->Flag;
|
||||
OffMeshIDs[CurrIndex] = it->UserID;
|
||||
|
||||
CurrIndex++;
|
||||
}
|
||||
|
||||
NumOffMeshConns = OffMeshConnections.size();
|
||||
|
||||
bNavDataDirty = false;
|
||||
}
|
||||
|
||||
void PopulateOffMeshConnectionVector()
|
||||
{
|
||||
OffMeshConnections.clear();
|
||||
|
||||
for (int i = 0; i < NumOffMeshConns; i++)
|
||||
{
|
||||
float* v = &OffMeshVerts[i*3*2];
|
||||
Vector StartPos = Vector(v[0], -v[2], v[1]);
|
||||
Vector EndPos = Vector(v[3], -v[5], v[4]);
|
||||
AddOffMeshConnectionDef(StartPos, EndPos, OffMeshAreas[i], OffMeshFlags[i], OffMeshDirs[i], nullptr);
|
||||
}
|
||||
|
||||
bNavDataDirty = false;
|
||||
}
|
||||
|
||||
void GetOffMeshConnectionPoints(int UserID, Vector& OutStartLoc, Vector& OutEndLoc)
|
||||
{
|
||||
OutStartLoc = ZERO_VECTOR;
|
||||
OutEndLoc = ZERO_VECTOR;
|
||||
|
||||
if (Index > -1 && Index < MAX_OFFMESH_CONNS)
|
||||
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
|
||||
{
|
||||
float* src = &OffMeshVerts[Index * 3 * 2];
|
||||
if (it->UserID == UserID)
|
||||
{
|
||||
OutStartLoc.x = it->spos[0];
|
||||
OutStartLoc.y = -it->spos[2];
|
||||
OutStartLoc.z = it->spos[1];
|
||||
|
||||
OutStartLoc.x = src[0];
|
||||
OutStartLoc.y = -src[2];
|
||||
OutStartLoc.z = src[1];
|
||||
OutEndLoc.x = it->epos[0];
|
||||
OutEndLoc.y = -it->epos[2];
|
||||
OutEndLoc.z = it->epos[1];
|
||||
|
||||
OutEndLoc.x = src[3];
|
||||
OutEndLoc.y = -src[5];
|
||||
OutEndLoc.z = src[4];
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawAllConnections(float DrawTime)
|
||||
|
@ -260,12 +323,12 @@ struct MeshProcess : public dtTileCacheMeshProcess
|
|||
Vector StartLine = ZERO_VECTOR;
|
||||
Vector EndLine = ZERO_VECTOR;
|
||||
|
||||
for (int i = 0; i < NumOffMeshConns; i++)
|
||||
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
|
||||
{
|
||||
Vector StartLine = Vector(OffMeshVerts[i * 6], -OffMeshVerts[(i * 6) + 2], OffMeshVerts[(i * 6) + 1]);
|
||||
Vector EndLine = Vector(OffMeshVerts[(i * 6) + 3], -OffMeshVerts[(i * 6) + 5], OffMeshVerts[(i * 6) + 4]);
|
||||
Vector StartLine = Vector(it->spos[0], -it->spos[2], it->spos[1]);
|
||||
Vector EndLine = Vector(it->epos[0], -it->epos[2], it->epos[1]);
|
||||
|
||||
switch (OffMeshFlags[i])
|
||||
switch (it->Flag)
|
||||
{
|
||||
case SAMPLE_POLYFLAGS_WALK:
|
||||
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 255, 255);
|
||||
|
@ -282,7 +345,8 @@ struct MeshProcess : public dtTileCacheMeshProcess
|
|||
case SAMPLE_POLYFLAGS_LADDER:
|
||||
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 0, 255);
|
||||
break;
|
||||
case SAMPLE_POLYFLAGS_PHASEGATE:
|
||||
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
|
||||
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
|
||||
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 128, 128);
|
||||
break;
|
||||
default:
|
||||
|
@ -345,6 +409,11 @@ struct MeshProcess : public dtTileCacheMeshProcess
|
|||
}
|
||||
}
|
||||
|
||||
if (bNavDataDirty)
|
||||
{
|
||||
UpdateOffMeshData();
|
||||
}
|
||||
|
||||
params->offMeshConAreas = OffMeshAreas;
|
||||
params->offMeshConCount = NumOffMeshConns;
|
||||
params->offMeshConDir = OffMeshDirs;
|
||||
|
@ -800,6 +869,8 @@ bool LoadNavMesh(const char* mapname)
|
|||
fseek(savedFile, header.OffMeshConVertsOffset, SEEK_SET);
|
||||
ReadResult = fread(m_tmproc->OffMeshVerts, header.OffMeshConVertsLength, 1, savedFile);
|
||||
|
||||
m_tmproc->PopulateOffMeshConnectionVector();
|
||||
|
||||
// TODO: Need to pass all off mesh connection verts, areas, flags etc as arrays to m_tmproc. Needs to be exported from recast as such
|
||||
|
||||
status = NavMeshes[REGULAR_NAV_MESH].tileCache->init(&header.regularCacheParams, m_talloc, m_tcomp, m_tmproc);
|
||||
|
@ -1015,7 +1086,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 1.0f);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_WALLCLIMB, 1.0f);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_PHASEGATE);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_DUCKJUMP);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD);
|
||||
|
||||
|
@ -1030,7 +1102,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setIncludeFlags(0xFFFF);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WALLCLIMB);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_PHASEGATE);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_DUCKJUMP);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD);
|
||||
|
||||
|
@ -1045,7 +1118,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_WALLCLIMB, 1.0f);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setIncludeFlags(0xFFFF);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_PHASEGATE);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD);
|
||||
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].NavMeshIndex = REGULAR_NAV_MESH;
|
||||
|
@ -1059,7 +1133,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_WALLCLIMB, 1.0f);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setIncludeFlags(0xFFFF);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_PHASEGATE);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD);
|
||||
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].NavMeshIndex = ONOS_NAV_MESH;
|
||||
|
@ -1073,7 +1148,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setIncludeFlags(0xFFFF);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WALLCLIMB);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_PHASEGATE);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_NOONOS);
|
||||
|
||||
|
@ -1318,85 +1394,6 @@ static float frand()
|
|||
return (float)rand() / (float)RAND_MAX;
|
||||
}
|
||||
|
||||
dtStatus FindPhaseGatePathToPoint(const nav_profile& NavProfile, Vector FromLocation, Vector ToLocation, bot_path_node* path, int* pathSize, float MaxAcceptableDistance)
|
||||
{
|
||||
*pathSize = 0;
|
||||
|
||||
const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(NavProfile);
|
||||
const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(NavProfile);
|
||||
const dtQueryFilter* m_navFilter = &NavProfile.Filters;
|
||||
|
||||
if (!m_navQuery || vIsZero(FromLocation) || vIsZero(ToLocation))
|
||||
{
|
||||
return DT_FAILURE;
|
||||
}
|
||||
|
||||
bot_path_node PathToPhaseStart[MAX_AI_PATH_SIZE];
|
||||
memset(PathToPhaseStart, 0, sizeof(PathToPhaseStart));
|
||||
int PhaseStartPathSize = 0;
|
||||
|
||||
bot_path_node PathToFinalDestination[MAX_AI_PATH_SIZE];
|
||||
memset(PathToFinalDestination, 0, sizeof(PathToFinalDestination));
|
||||
int PhaseEndPathSize = 0;
|
||||
|
||||
DeployableSearchFilter PhaseGateSearch;
|
||||
PhaseGateSearch.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
PhaseGateSearch.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
PhaseGateSearch.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
PhaseGateSearch.ReachabilityFlags = AI_REACHABILITY_MARINE;
|
||||
|
||||
AvHAIBuildableStructure* StartPhaseGate = AITAC_FindClosestDeployableToLocation(FromLocation, &PhaseGateSearch);
|
||||
AvHAIBuildableStructure* EndPhaseGate = AITAC_FindClosestDeployableToLocation(ToLocation, &PhaseGateSearch);
|
||||
|
||||
if (!StartPhaseGate || !EndPhaseGate || (StartPhaseGate == EndPhaseGate)) { return DT_FAILURE; }
|
||||
|
||||
float TotalDist = vDist2DSq(FromLocation, StartPhaseGate->edict->v.origin) + vDist2DSq(EndPhaseGate->edict->v.origin, ToLocation);
|
||||
|
||||
if (TotalDist > vDist2DSq(FromLocation, ToLocation)) { return DT_FAILURE; }
|
||||
|
||||
dtStatus RouteToFirstPhaseGate = FindPathClosestToPoint(NavProfile, FromLocation, StartPhaseGate->edict->v.origin, PathToPhaseStart, &PhaseStartPathSize, max_ai_use_reach);
|
||||
|
||||
if (dtStatusFailed(RouteToFirstPhaseGate))
|
||||
{
|
||||
return DT_FAILURE;
|
||||
}
|
||||
|
||||
dtStatus RouteToFinalPoint = FindPathClosestToPoint(NavProfile, EndPhaseGate->edict->v.origin, ToLocation, PathToFinalDestination, &PhaseEndPathSize, MaxAcceptableDistance);
|
||||
|
||||
if (dtStatusFailed(RouteToFinalPoint))
|
||||
{
|
||||
return DT_FAILURE;
|
||||
}
|
||||
|
||||
// Now we join together the path to the starting phase gate and the path from the phase destination to the end, and add the phase itself in the middle
|
||||
|
||||
int CurrPathIndex = 0;
|
||||
|
||||
for (int i = 0; i < PhaseStartPathSize; i++)
|
||||
{
|
||||
memcpy(&path[CurrPathIndex++], &PathToPhaseStart[i], sizeof(bot_path_node));
|
||||
}
|
||||
|
||||
// Add a node to inform the bot they have to use the phase gate
|
||||
path[CurrPathIndex].Location = EndPhaseGate->edict->v.origin + Vector(0.0f, 0.0f, 10.0f);
|
||||
path[CurrPathIndex].area = SAMPLE_POLYAREA_GROUND;
|
||||
path[CurrPathIndex].flag = SAMPLE_POLYFLAGS_PHASEGATE;
|
||||
path[CurrPathIndex].poly = UTIL_GetNearestPolyRefForEntity(EndPhaseGate->edict);
|
||||
path[CurrPathIndex].requiredZ = EndPhaseGate->edict->v.origin.z;
|
||||
|
||||
CurrPathIndex++;
|
||||
|
||||
// Append the path from the destination phase to the end
|
||||
for (int i = 1; i < PhaseEndPathSize; i++)
|
||||
{
|
||||
memcpy(&path[CurrPathIndex++], &PathToFinalDestination[i], sizeof(bot_path_node));
|
||||
}
|
||||
|
||||
*pathSize = CurrPathIndex;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
// Special path finding that takes flight movement into account
|
||||
dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocation, Vector ToLocation, bot_path_node* path, int* pathSize, float MaxAcceptableDistance)
|
||||
{
|
||||
|
@ -1836,22 +1833,6 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
|
|||
return DT_FAILURE;
|
||||
}
|
||||
|
||||
DeployableSearchFilter PGFilter;
|
||||
PGFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
PGFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
PGFilter.bConsiderPhaseDistance = false;
|
||||
|
||||
if (IsPlayerMarine(pBot->Edict) && AITAC_GetNumDeployablesNearLocation(ZERO_VECTOR, &PGFilter) > 1)
|
||||
{
|
||||
dtStatus PhaseStatus = FindPhaseGatePathToPoint(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, ToLocation, path, pathSize, MaxAcceptableDistance);
|
||||
|
||||
if (dtStatusSucceed(PhaseStatus))
|
||||
{
|
||||
pBot->BotNavInfo.CurrentPathPoint = 1;
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
float pStartPos[3] = { FromLocation.x, FromLocation.z, -FromLocation.y };
|
||||
float pEndPos[3] = { ToLocation.x, ToLocation.z, -ToLocation.y };
|
||||
|
||||
|
@ -2085,11 +2066,11 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
|
||||
SamplePolyFlags CurrentNavFlag = (SamplePolyFlags)pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag;
|
||||
Vector CurrentMoveDest = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location;
|
||||
Vector PrevMoveDest = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1].Location;
|
||||
Vector PrevMoveDest = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].FromLocation;
|
||||
|
||||
bool bIsAtFinalPathPoint = (pBot->BotNavInfo.CurrentPathPoint == (pBot->BotNavInfo.PathSize - 1));
|
||||
|
||||
Vector ClosestPointToPath = vClosestPointOnLine2D(pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1].Location, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location, pEdict->v.origin);
|
||||
Vector ClosestPointToPath = vClosestPointOnLine2D(PrevMoveDest, CurrentMoveDest, pEdict->v.origin);
|
||||
|
||||
bool bDestIsDirectlyReachable = UTIL_PointIsDirectlyReachable(CurrentPos, CurrentMoveDest);
|
||||
bool bAtOrPastDestination = vEquals2D(ClosestPointToPath, CurrentMoveDest, 1.0f) && bDestIsDirectlyReachable;
|
||||
|
@ -2145,6 +2126,9 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
{
|
||||
return (fabs(pBot->CollisionHullBottomLocation.z - CurrentMoveDest.z) < 50.0f);
|
||||
}
|
||||
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
|
||||
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
|
||||
return (vDist2DSq(pBot->CurrentFloorPosition, CurrentMoveDest) < sqrf(32.0f));
|
||||
default:
|
||||
return (bAtOrPastDestination && UTIL_QuickTrace(pEdict, pEdict->v.origin, CurrentMoveDest));
|
||||
}
|
||||
|
@ -2961,7 +2945,8 @@ void NewMove(AvHAIPlayer* pBot)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case SAMPLE_POLYFLAGS_PHASEGATE:
|
||||
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
|
||||
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
|
||||
PhaseGateMove(pBot, MoveFrom, MoveTo);
|
||||
break;
|
||||
default:
|
||||
|
@ -3583,7 +3568,7 @@ bool IsBotOffPath(const AvHAIPlayer* pBot)
|
|||
|
||||
|
||||
// If we're trying to use a phase gate, then we're fine as long as there is a phase gate within reach at the start and end teleport points
|
||||
if (pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag == SAMPLE_POLYFLAGS_PHASEGATE)
|
||||
if (pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE)
|
||||
{
|
||||
DeployableSearchFilter PGFilter;
|
||||
PGFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
|
@ -4705,7 +4690,7 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
|
|||
}
|
||||
}
|
||||
|
||||
if (flag == SAMPLE_POLYFLAGS_PHASEGATE)
|
||||
if (flag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || flag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -4842,6 +4827,17 @@ void MarineUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle)
|
|||
}
|
||||
}
|
||||
|
||||
SamplePolyFlags ExcludePhaseGateFlag = (pBot->Player->GetTeam() == GetGameRules()->GetTeamANumber()) ? SAMPLE_POLYFLAGS_TEAM2PHASEGATE : SAMPLE_POLYFLAGS_TEAM1PHASEGATE;
|
||||
SamplePolyFlags IncludePhaseGateFlag = (ExcludePhaseGateFlag & SAMPLE_POLYFLAGS_TEAM1PHASEGATE) ? SAMPLE_POLYFLAGS_TEAM2PHASEGATE : SAMPLE_POLYFLAGS_TEAM1PHASEGATE;
|
||||
|
||||
if (!(NavProfile->Filters.getExcludeFlags() & ExcludePhaseGateFlag))
|
||||
{
|
||||
pBot->BotNavInfo.bNavProfileChanged = true;
|
||||
|
||||
NavProfile->Filters.removeExcludeFlags(IncludePhaseGateFlag);
|
||||
NavProfile->Filters.addExcludeFlags(ExcludePhaseGateFlag);
|
||||
}
|
||||
|
||||
if (MoveStyle == pBot->BotNavInfo.PreviousMoveStyle) { return; }
|
||||
|
||||
pBot->BotNavInfo.PreviousMoveStyle = MoveStyle;
|
||||
|
@ -5517,7 +5513,7 @@ void BotFollowPath(AvHAIPlayer* pBot)
|
|||
|
||||
Vector TargetMoveLocation = BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].Location;
|
||||
|
||||
bool bIsUsingPhaseGate = (BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag == SAMPLE_POLYFLAGS_PHASEGATE);
|
||||
bool bIsUsingPhaseGate = (BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
|
||||
|
||||
bool bIsJumping = (BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag == SAMPLE_POLYFLAGS_JUMP);
|
||||
|
||||
|
@ -7175,35 +7171,61 @@ unsigned char UTIL_GetNextBotCurrentPathArea(AvHAIPlayer* pBot)
|
|||
return pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].area;
|
||||
}
|
||||
|
||||
int UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned char flags, bool bBiDirectional)
|
||||
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* NewConnectionDef)
|
||||
{
|
||||
Vector ConnStart, ConnEnd;
|
||||
|
||||
TraceResult hit;
|
||||
UTIL_TraceLine(StartLoc + Vector(0.0f, 0.0f, 5.0f), StartLoc - Vector(0.0f, 0.0f, 100.0f), ignore_monsters, ignore_glass, nullptr, &hit);
|
||||
|
||||
ConnStart = (hit.flFraction < 1.0f) ? hit.vecEndPos : StartLoc;
|
||||
|
||||
UTIL_TraceLine(EndLoc + Vector(0.0f, 0.0f, 5.0f), EndLoc - Vector(0.0f, 0.0f, 100.0f), ignore_monsters, ignore_glass, nullptr, &hit);
|
||||
|
||||
ConnEnd = (hit.flFraction < 1.0f) ? hit.vecEndPos : EndLoc;
|
||||
|
||||
bool bMeshModified = false;
|
||||
|
||||
if (NavMeshes[REGULAR_NAV_MESH].tileCache)
|
||||
{
|
||||
NewConnectionDef->MeshConnectionIndex = -1;
|
||||
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[REGULAR_NAV_MESH].tileCache->getMeshProcess();
|
||||
|
||||
if (m_tmproc)
|
||||
{
|
||||
return m_tmproc->AddOffMeshConnectionDef(StartLoc, EndLoc, area, flags, bBiDirectional);
|
||||
UTIL_OnOffMeshConnectionModified(StartLoc, EndLoc);
|
||||
m_tmproc->AddOffMeshConnectionDef(ConnStart, ConnEnd, area, flags, bBiDirectional, NewConnectionDef);
|
||||
if (NewConnectionDef->MeshConnectionIndex > -1) { bMeshModified = true; }
|
||||
}
|
||||
}
|
||||
|
||||
if (bMeshModified)
|
||||
{
|
||||
UTIL_OnOffMeshConnectionModified(ConnStart, ConnEnd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void UTIL_RemoveOffMeshConnection(int ConnectionIndex)
|
||||
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* NewConnectionDef)
|
||||
{
|
||||
Vector StartLoc, EndLoc;
|
||||
if (NewConnectionDef->MeshConnectionIndex < 0) { return; }
|
||||
|
||||
Vector StartLoc, EndLoc;
|
||||
|
||||
if (NavMeshes[REGULAR_NAV_MESH].tileCache)
|
||||
{
|
||||
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[REGULAR_NAV_MESH].tileCache->getMeshProcess();
|
||||
|
||||
if (m_tmproc)
|
||||
{
|
||||
m_tmproc->GetOffMeshConnectionPoints(ConnectionIndex, StartLoc, EndLoc);
|
||||
m_tmproc->RemoveOffMeshConnectionDef(ConnectionIndex);
|
||||
m_tmproc->GetOffMeshConnectionPoints(NewConnectionDef->MeshConnectionIndex, StartLoc, EndLoc);
|
||||
m_tmproc->RemoveOffMeshConnectionDef(NewConnectionDef->MeshConnectionIndex);
|
||||
NewConnectionDef->MeshConnectionIndex = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NewConnectionDef->MeshConnectionIndex = -1;
|
||||
|
||||
UTIL_OnOffMeshConnectionModified(StartLoc, EndLoc);
|
||||
}
|
||||
|
||||
|
|
|
@ -56,11 +56,12 @@ enum SamplePolyFlags
|
|||
SAMPLE_POLYFLAGS_JUMP = 1 << 5, // Requires a regular jump to traverse
|
||||
SAMPLE_POLYFLAGS_DUCKJUMP = 1 << 6, // Requires a duck-jump to traverse
|
||||
SAMPLE_POLYFLAGS_NOONOS = 1 << 7, // This movement is not allowed by onos
|
||||
SAMPLE_POLYFLAGS_PHASEGATE = 1 << 8, // Requires using a phase gate to traverse
|
||||
SAMPLE_POLYFLAGS_TEAM1STRUCTURE = 1 << 9, // A team 1 structure is in the way that cannot be jumped over. Impassable to team 1 players
|
||||
SAMPLE_POLYFLAGS_TEAM2STRUCTURE = 1 << 10, // A team 2 structure is in the way that cannot be jumped over. Impassable to team 2 players
|
||||
SAMPLE_POLYFLAGS_WELD = 1 << 11, // Requires a welder to get through here
|
||||
SAMPLE_POLYFLAGS_DOOR = 1 << 12, // Requires a welder to get through here
|
||||
SAMPLE_POLYFLAGS_TEAM1PHASEGATE = 1 << 8, // Requires using a phase gate to traverse (team 1 only)
|
||||
SAMPLE_POLYFLAGS_TEAM2PHASEGATE = 1 << 9, // Requires using a phase gate to traverse (team 2 only)
|
||||
SAMPLE_POLYFLAGS_TEAM1STRUCTURE = 1 << 10, // A team 1 structure is in the way that cannot be jumped over. Impassable to team 1 players (assume cannot teamkill own structures)
|
||||
SAMPLE_POLYFLAGS_TEAM2STRUCTURE = 1 << 11, // A team 2 structure is in the way that cannot be jumped over. Impassable to team 2 players (assume cannot teamkill own structures)
|
||||
SAMPLE_POLYFLAGS_WELD = 1 << 12, // Requires a welder to get through here
|
||||
SAMPLE_POLYFLAGS_DOOR = 1 << 13, // Requires a welder to get through here
|
||||
|
||||
SAMPLE_POLYFLAGS_DISABLED = 1 << 15, // Disabled, not usable by anyone
|
||||
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
|
||||
|
@ -92,7 +93,7 @@ typedef struct _NAV_DOOR
|
|||
{
|
||||
CBaseToggle* DoorEntity = nullptr;
|
||||
edict_t* DoorEdict = nullptr; // Reference to the func_door
|
||||
unsigned int ObstacleRefs[32][8]; // Dynamic obstacle ref. Used to add/remove the obstacle as the door is opened/closed
|
||||
unsigned int ObstacleRefs[32][MAX_NAV_MESHES]; // Dynamic obstacle ref. Used to add/remove the obstacle as the door is opened/closed
|
||||
int NumObstacles = 0;
|
||||
vector<DoorTrigger> TriggerEnts; // Reference to the trigger edicts (e.g. func_trigger, func_button etc.)
|
||||
DoorActivationType ActivationType = DOOR_NONE; // How the door should be opened
|
||||
|
@ -103,7 +104,7 @@ typedef struct _NAV_DOOR
|
|||
typedef struct _NAV_WELDABLE
|
||||
{
|
||||
edict_t* WeldableEdict = nullptr;
|
||||
unsigned int ObstacleRefs[32][8];
|
||||
unsigned int ObstacleRefs[32][MAX_NAV_MESHES];
|
||||
int NumObstacles = 0;
|
||||
} nav_weldable;
|
||||
|
||||
|
@ -133,7 +134,6 @@ static const int TILECACHESET_VERSION = 1;
|
|||
static const float pExtents[3] = { 400.0f, 50.0f, 400.0f }; // Default extents (in GoldSrc units) to find the nearest spot on the nav mesh
|
||||
static const float pReachableExtents[3] = { max_ai_use_reach, max_ai_use_reach, max_ai_use_reach }; // Extents (in GoldSrc units) to determine if something is on the nav mesh
|
||||
|
||||
static const int MAX_NAV_MESHES = 8; // Max number of nav meshes allowed. Currently 3 are used (one for building placement, one for the onos, and a regular one for everyone else)
|
||||
static const int MAX_NAV_PROFILES = 16; // Max number of possible nav profiles. Currently 9 are used (see top of this header file)
|
||||
|
||||
static const int REGULAR_NAV_MESH = 0;
|
||||
|
@ -293,8 +293,8 @@ void UTIL_RemoveTemporaryObstacle(unsigned int ObstacleRef);
|
|||
|
||||
void UTIL_RemoveTemporaryObstacles(unsigned int* ObstacleRefs);
|
||||
|
||||
int UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned char flags, bool bBiDirectional);
|
||||
void UTIL_RemoveOffMeshConnection(int ConnectionIndex);
|
||||
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* NewConnectionDef);
|
||||
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* NewConnectionDef);
|
||||
void UTIL_OnOffMeshConnectionModified(Vector StartLoc, Vector EndLoc);
|
||||
|
||||
|
||||
|
@ -332,9 +332,6 @@ void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination);
|
|||
// Check if there are any players in our way and try to move around them. If we can't, then back up to let them through
|
||||
void HandlePlayerAvoidance(AvHAIPlayer* pBot, const Vector MoveDestination);
|
||||
|
||||
// Special path finding that takes the presence of phase gates into account
|
||||
dtStatus FindPhaseGatePathToPoint(const nav_profile& NavProfile, Vector FromLocation, Vector ToLocation, bot_path_node* path, int* pathSize, float MaxAcceptableDistance);
|
||||
|
||||
// Special path finding that takes the presence of phase gates into account
|
||||
dtStatus FindFlightPathToPoint(const nav_profile& NavProfile, Vector FromLocation, Vector ToLocation, bot_path_node* path, int* pathSize, float MaxAcceptableDistance);
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ float AIStartedTime = 0.0f; // Used to give 5-second grace period before adding
|
|||
|
||||
extern int m_spriteTexture;
|
||||
|
||||
Vector DebugVector1 = ZERO_VECTOR;
|
||||
Vector DebugVector2 = ZERO_VECTOR;
|
||||
|
||||
string BotNames[MAX_PLAYERS] = { "MrRobot",
|
||||
"Wall-E",
|
||||
"BeepBoop",
|
||||
|
@ -739,4 +742,4 @@ void AIMGR_UpdateAIMapData()
|
|||
void AIMGR_BotPrecache()
|
||||
{
|
||||
m_spriteTexture = PRECACHE_MODEL("sprites/zbeam6.spr");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,86 @@ extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles
|
|||
|
||||
bool bNavMeshModified = false;
|
||||
|
||||
std::vector<AvHAIBuildableStructure*> AITAC_FindAllDeployables(const Vector& Location, const DeployableSearchFilter* Filter)
|
||||
{
|
||||
std::vector<AvHAIBuildableStructure*> Result;
|
||||
|
||||
AvHTeamNumber TeamA = GetGameRules()->GetTeamANumber();
|
||||
AvHTeamNumber TeamB = GetGameRules()->GetTeamBNumber();
|
||||
|
||||
float CurrMinDist = 0.0f;
|
||||
|
||||
float MinDistSq = sqrf(Filter->MinSearchRadius);
|
||||
float MaxDistSq = sqrf(Filter->MaxSearchRadius);
|
||||
|
||||
bool bUseMinDist = MinDistSq > 0.1f;
|
||||
bool bUseMaxDist = MaxDistSq > 0.1f;
|
||||
|
||||
if (Filter->DeployableTeam == TeamA || Filter->DeployableTeam == TEAM_IND)
|
||||
{
|
||||
for (auto& it : TeamAStructureMap)
|
||||
{
|
||||
if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; }
|
||||
if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; }
|
||||
|
||||
if (Filter->ReachabilityFlags != AI_REACHABILITY_NONE)
|
||||
{
|
||||
unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags);
|
||||
|
||||
if (Filter->ReachabilityTeam != TEAM_IND)
|
||||
{
|
||||
StructureReachabilityFlags = (Filter->ReachabilityTeam == TeamA) ? it.second.TeamAReachabilityFlags : it.second.TeamBReachabilityFlags;
|
||||
}
|
||||
|
||||
if (!(StructureReachabilityFlags & Filter->ReachabilityFlags)) { continue; }
|
||||
}
|
||||
|
||||
if (it.second.StructureType & Filter->DeployableTypes)
|
||||
{
|
||||
float DistSq = (Filter->bConsiderPhaseDistance) ? sqrf(AITAC_GetPhaseDistanceBetweenPoints(it.second.Location, Location)) : vDist2DSq(it.second.Location, Location);
|
||||
|
||||
if ((!bUseMinDist || DistSq >= MinDistSq) && (!bUseMaxDist || DistSq <= MaxDistSq))
|
||||
{
|
||||
Result.push_back(&it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Filter->DeployableTeam == TeamB || Filter->DeployableTeam == TEAM_IND)
|
||||
{
|
||||
for (auto& it : TeamBStructureMap)
|
||||
{
|
||||
if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; }
|
||||
if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; }
|
||||
|
||||
if (Filter->ReachabilityFlags != AI_REACHABILITY_NONE)
|
||||
{
|
||||
unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags);
|
||||
|
||||
if (Filter->ReachabilityTeam != TEAM_IND)
|
||||
{
|
||||
StructureReachabilityFlags = (Filter->ReachabilityTeam == TeamA) ? it.second.TeamAReachabilityFlags : it.second.TeamBReachabilityFlags;
|
||||
}
|
||||
|
||||
if (!(StructureReachabilityFlags & Filter->ReachabilityFlags)) { continue; }
|
||||
}
|
||||
|
||||
if (it.second.StructureType & Filter->DeployableTypes)
|
||||
{
|
||||
float DistSq = (Filter->bConsiderPhaseDistance) ? sqrf(AITAC_GetPhaseDistanceBetweenPoints(it.second.Location, Location)) : vDist2DSq(it.second.Location, Location);
|
||||
|
||||
if ((!bUseMinDist || DistSq >= MinDistSq) && (!bUseMaxDist || DistSq <= MaxDistSq))
|
||||
{
|
||||
Result.push_back(&it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool AITAC_DeployableExistsAtLocation(const Vector& Location, const DeployableSearchFilter* Filter)
|
||||
{
|
||||
AvHTeamNumber TeamA = GetGameRules()->GetTeamANumber();
|
||||
|
@ -1019,6 +1099,7 @@ void AITAC_RefreshBuildableStructures()
|
|||
{
|
||||
if (it->second.LastSeen < StructureRefreshFrame)
|
||||
{
|
||||
AITAC_OnStructureDestroyed(&it->second);
|
||||
UTIL_RemoveTemporaryObstacles(it->second.ObstacleRefs);
|
||||
it = TeamAStructureMap.erase(it);
|
||||
}
|
||||
|
@ -1037,6 +1118,7 @@ void AITAC_RefreshBuildableStructures()
|
|||
{
|
||||
if (it->second.LastSeen < StructureRefreshFrame)
|
||||
{
|
||||
AITAC_OnStructureDestroyed(&it->second);
|
||||
UTIL_RemoveTemporaryObstacles(it->second.ObstacleRefs);
|
||||
it = TeamBStructureMap.erase(it);
|
||||
}
|
||||
|
@ -1347,6 +1429,10 @@ void AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
|
|||
BuildingMap[EntIndex].edict = BuildingEdict;
|
||||
BuildingMap[EntIndex].StructureType = StructureType;
|
||||
|
||||
BuildingMap[EntIndex].OffMeshConnections.clear();
|
||||
|
||||
memset(&BuildingMap[EntIndex].ObstacleRefs, 0, sizeof(BuildingMap[EntIndex].ObstacleRefs));
|
||||
|
||||
bool bShouldCollide = UTIL_ShouldStructureCollide(StructureType);
|
||||
|
||||
if (bShouldCollide)
|
||||
|
@ -1372,26 +1458,33 @@ void AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
|
|||
BuildingMap[EntIndex].Location = BaseBuildable->pev->origin;
|
||||
}
|
||||
|
||||
BuildingMap[EntIndex].StructureStatusFlags = STRUCTURE_STATUS_NONE;
|
||||
unsigned int NewFlags = STRUCTURE_STATUS_NONE;
|
||||
|
||||
if (BaseBuildable->GetIsBuilt())
|
||||
{
|
||||
BuildingMap[EntIndex].StructureStatusFlags |= STRUCTURE_STATUS_COMPLETED;
|
||||
if (!(BuildingMap[EntIndex].StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) {
|
||||
AITAC_OnStructureCompleted(&BuildingMap[EntIndex]);
|
||||
}
|
||||
NewFlags |= STRUCTURE_STATUS_COMPLETED;
|
||||
}
|
||||
|
||||
if (UTIL_IsStructureElectrified(BuildingEdict))
|
||||
{
|
||||
BuildingMap[EntIndex].StructureStatusFlags |= STRUCTURE_STATUS_ELECTRIFIED;
|
||||
NewFlags |= STRUCTURE_STATUS_ELECTRIFIED;
|
||||
}
|
||||
|
||||
if (BuildingEdict->v.iuser4 & MASK_PARASITED)
|
||||
{
|
||||
BuildingMap[EntIndex].StructureStatusFlags |= STRUCTURE_STATUS_PARASITED;
|
||||
NewFlags |= STRUCTURE_STATUS_PARASITED;
|
||||
}
|
||||
|
||||
if (BaseBuildable->GetIsRecycling())
|
||||
{
|
||||
BuildingMap[EntIndex].StructureStatusFlags |= STRUCTURE_STATUS_RECYCLING;
|
||||
if (!(BuildingMap[EntIndex].StructureStatusFlags & STRUCTURE_STATUS_RECYCLING))
|
||||
{
|
||||
AITAC_OnStructureBeginRecycling(&BuildingMap[EntIndex]);
|
||||
}
|
||||
NewFlags |= STRUCTURE_STATUS_RECYCLING;
|
||||
}
|
||||
|
||||
float NewHealthPercent = (BuildingEdict->v.health / BuildingEdict->v.max_health);
|
||||
|
@ -1405,9 +1498,10 @@ void AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
|
|||
|
||||
if (gpGlobals->time - BuildingMap[EntIndex].lastDamagedTime < 10.0f)
|
||||
{
|
||||
BuildingMap[EntIndex].StructureStatusFlags |= STRUCTURE_STATUS_UNDERATTACK;
|
||||
NewFlags |= STRUCTURE_STATUS_UNDERATTACK;
|
||||
}
|
||||
|
||||
BuildingMap[EntIndex].StructureStatusFlags = NewFlags;
|
||||
BuildingMap[EntIndex].LastSeen = StructureRefreshFrame;
|
||||
|
||||
}
|
||||
|
@ -1446,6 +1540,105 @@ void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure)
|
|||
|
||||
}
|
||||
|
||||
void AITAC_OnStructureCompleted(AvHAIBuildableStructure* NewStructure)
|
||||
{
|
||||
if (NewStructure->StructureType == STRUCTURE_MARINE_PHASEGATE)
|
||||
{
|
||||
DeployableSearchFilter Filter;
|
||||
Filter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
Filter.DeployableTeam = (AvHTeamNumber)NewStructure->edict->v.team;
|
||||
Filter.ReachabilityFlags = AI_REACHABILITY_NONE;
|
||||
Filter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
|
||||
// Get all other completed phase gates for this team and add bidirectional connections to them
|
||||
std::vector<AvHAIBuildableStructure*> OtherPhaseGates = AITAC_FindAllDeployables(NewStructure->Location, &Filter);
|
||||
|
||||
SamplePolyFlags NewFlag = ((AvHTeamNumber)NewStructure->edict->v.team == GetGameRules()->GetTeamANumber()) ? SAMPLE_POLYFLAGS_TEAM1PHASEGATE : SAMPLE_POLYFLAGS_TEAM2PHASEGATE;
|
||||
|
||||
for (auto pg = OtherPhaseGates.begin(); pg != OtherPhaseGates.end(); pg++)
|
||||
{
|
||||
// Don't add off-mesh connections to ourselves!
|
||||
if ((*pg) == NewStructure) { continue; }
|
||||
|
||||
AvHAIBuildableStructure* OtherPhaseGate = (*pg);
|
||||
|
||||
AvHAIOffMeshConnection NewConnection;
|
||||
NewConnection.FromLocation = NewStructure->Location;
|
||||
NewConnection.ToLocation = OtherPhaseGate->Location;
|
||||
NewConnection.ConnectionFlags = NewFlag;
|
||||
NewConnection.TargetObject = OtherPhaseGate->edict;
|
||||
NewConnection.MeshConnectionIndex = -1;
|
||||
|
||||
UTIL_AddOffMeshConnection(NewStructure->Location, OtherPhaseGate->Location, SAMPLE_POLYAREA_GROUND, NewFlag, true, &NewConnection);
|
||||
|
||||
NewStructure->OffMeshConnections.push_back(NewConnection);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AITAC_RemovePhaseGateConnections(AvHAIBuildableStructure* SourceGate, AvHAIBuildableStructure* TargetGate)
|
||||
{
|
||||
if (!SourceGate || !TargetGate) { return; }
|
||||
|
||||
for (auto it = SourceGate->OffMeshConnections.begin(); it != SourceGate->OffMeshConnections.end();)
|
||||
{
|
||||
if (it->TargetObject == TargetGate->edict)
|
||||
{
|
||||
UTIL_RemoveOffMeshConnections(&(*it));
|
||||
it = SourceGate->OffMeshConnections.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AITAC_OnStructureBeginRecycling(AvHAIBuildableStructure* RecyclingStructure)
|
||||
{
|
||||
// For phase gates, treat them like they've been destroyed
|
||||
if (RecyclingStructure->StructureType == STRUCTURE_MARINE_PHASEGATE)
|
||||
{
|
||||
AITAC_OnStructureDestroyed(RecyclingStructure);
|
||||
}
|
||||
}
|
||||
|
||||
void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure)
|
||||
{
|
||||
if (DestroyedStructure->StructureType == STRUCTURE_MARINE_PHASEGATE)
|
||||
{
|
||||
// Eliminate all connections from this phase gate
|
||||
for (auto it = DestroyedStructure->OffMeshConnections.begin(); it != DestroyedStructure->OffMeshConnections.begin();)
|
||||
{
|
||||
UTIL_RemoveOffMeshConnections(&(*it));
|
||||
|
||||
it = DestroyedStructure->OffMeshConnections.erase(it);
|
||||
}
|
||||
|
||||
DestroyedStructure->OffMeshConnections.clear();
|
||||
|
||||
DeployableSearchFilter Filter;
|
||||
Filter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
Filter.DeployableTeam = (AvHTeamNumber)DestroyedStructure->edict->v.team;
|
||||
Filter.ReachabilityFlags = AI_REACHABILITY_NONE;
|
||||
Filter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
|
||||
// Get all other completed phase gates for this team and remove any connections going to this structure
|
||||
std::vector<AvHAIBuildableStructure*> OtherPhaseGates = AITAC_FindAllDeployables(DestroyedStructure->Location, &Filter);
|
||||
|
||||
for (auto it = OtherPhaseGates.begin(); it != OtherPhaseGates.end(); it++)
|
||||
{
|
||||
// Don't check for off-mesh connections from ourselves!
|
||||
if ((*it) == DestroyedStructure) { continue; }
|
||||
|
||||
AvHAIBuildableStructure* OtherPhaseGate = (*it);
|
||||
|
||||
AITAC_RemovePhaseGateConnections(OtherPhaseGate, DestroyedStructure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AITAC_LinkAlienStructureToTask(AvHAIPlayer* pBot, AvHAIBuildableStructure* NewStructure)
|
||||
{
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ static const float structure_inventory_refresh_rate = 0.2f;
|
|||
static const float item_inventory_refresh_rate = 0.1f;
|
||||
|
||||
bool AITAC_DeployableExistsAtLocation(const Vector& Location, const DeployableSearchFilter* Filter);
|
||||
std::vector<AvHAIBuildableStructure*> AITAC_FindAllDeployables(const Vector& Location, const DeployableSearchFilter* Filter);
|
||||
AvHAIBuildableStructure* AITAC_FindClosestDeployableToLocation(const Vector& Location, const DeployableSearchFilter* Filter);
|
||||
AvHAIBuildableStructure* AITAC_GetDeployableRefFromEdict(const edict_t* Structure);
|
||||
AvHAIBuildableStructure* AITAC_GetNearestDeployableDirectlyReachable(AvHAIPlayer* pBot, const Vector Location, const DeployableSearchFilter* Filter);
|
||||
|
@ -34,6 +35,9 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structu
|
|||
void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode);
|
||||
void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item);
|
||||
void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure);
|
||||
void AITAC_OnStructureCompleted(AvHAIBuildableStructure* NewStructure);
|
||||
void AITAC_OnStructureBeginRecycling(AvHAIBuildableStructure* RecyclingStructure);
|
||||
void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure);
|
||||
void AITAC_LinkDeployedItemToAction(AvHAIPlayer* CommanderBot, const AvHAIDroppedItem* NewItem);
|
||||
void AITAC_LinkAlienStructureToTask(AvHAIPlayer* pBot, AvHAIBuildableStructure* NewStructure);
|
||||
|
||||
|
|
|
@ -1414,6 +1414,12 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
|
|||
theSuccess = true;
|
||||
}
|
||||
}
|
||||
else if (FStrEq(pcmd, "drawoffmesh"))
|
||||
{
|
||||
AIDEBUG_DrawOffMeshConnections(10.0f);
|
||||
|
||||
theSuccess = true;
|
||||
}
|
||||
else if (FStrEq(pcmd, "tracedoor"))
|
||||
{
|
||||
Vector TraceStart = GetPlayerEyePosition(theAvHPlayer->edict()); // origin + pev->view_ofs
|
||||
|
|
Loading…
Reference in a new issue