Replaced arrays with vectors for simpler code

This commit is contained in:
RGreenlees 2023-10-26 13:30:52 +01:00 committed by pierow
parent 82ea559a7a
commit e64cb51067
6 changed files with 325 additions and 234 deletions

View file

@ -438,10 +438,9 @@ typedef struct _NAV_STATUS
Vector ActualMoveDestination = g_vecZero; // Actual destination on nav mesh
Vector PathDestination = g_vecZero; // Where the path is currently headed to
Vector LastNavMeshCheckPosition = g_vecZero;
Vector LastNavMeshPosition = g_vecZero; // Tracks the last place the bot was on the nav mesh. Useful if accidentally straying off it
Vector LastPathFollowPosition = g_vecZero; // Tracks the last point where the bot was happily following a path
int PathSize = 0; // How many path nodes the bot's current path has
int CurrentPathPoint = 0; // Which point in the path the bot is on
int CurrentMoveType = MOVETYPE_NONE; // Tracks the edict's current movement type

View file

@ -1397,6 +1397,24 @@ static float frand()
// 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)
{
TraceResult directHit;
UTIL_TraceHull(FromLocation + Vector(0.0f, 0.0f, 5.0f), ToLocation, ignore_monsters, head_hull, nullptr, &directHit);
if (directHit.flFraction >= 1.0f)
{
path[0].FromLocation = FromLocation;
path[0].Location = ToLocation;
path[0].area = SAMPLE_POLYAREA_GROUND;
path[0].flag = SAMPLE_POLYFLAGS_WALK;
path[0].poly = 0;
path[0].requiredZ = ToLocation.z;
*pathSize = 1;
return DT_SUCCESS;
}
const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(NavProfile);
const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(NavProfile);
const dtQueryFilter* m_navFilter = &NavProfile.Filters;
@ -1406,14 +1424,31 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
return DT_FAILURE;
}
float pStartPos[3] = { FromLocation.x, FromLocation.z, -FromLocation.y };
float pEndPos[3] = { ToLocation.x, ToLocation.z, -ToLocation.y };
Vector ToFloorLocation = ToLocation;
Vector FromFloorLocation = ToLocation;
UTIL_TraceHull(ToLocation, ToLocation - Vector(0.0f, 0.0f, 1000.0f), ignore_monsters, head_hull, nullptr, &directHit);
if (directHit.flFraction < 1.0f)
{
ToFloorLocation = directHit.vecEndPos;
}
UTIL_TraceHull(FromLocation, FromLocation - Vector(0.0f, 0.0f, 1000.0f), ignore_monsters, head_hull, nullptr, &directHit);
if (directHit.flFraction < 1.0f)
{
FromFloorLocation = directHit.vecEndPos;
}
float pStartPos[3] = { FromFloorLocation.x, FromFloorLocation.z, -FromFloorLocation.y };
float pEndPos[3] = { ToFloorLocation.x, ToFloorLocation.z, -ToFloorLocation.y };
dtStatus status;
dtPolyRef StartPoly;
float StartNearest[3];
dtPolyRef EndPoly;
float EndNearest[3];
dtPolyRef StartPoly = 0;
float StartNearest[3] = { 0.0f, 0.0f, 0.0f };
dtPolyRef EndPoly = 0;
float EndNearest[3] = { 0.0f, 0.0f, 0.0f };
dtPolyRef PolyPath[MAX_PATH_POLY];
dtPolyRef StraightPolyPath[MAX_AI_PATH_SIZE];
int nPathCount = 0;
@ -1424,7 +1459,7 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
// find the start polygon
status = m_navQuery->findNearestPoly(pStartPos, pExtents, m_navFilter, &StartPoly, StartNearest);
if ((status & DT_FAILURE) || (status & DT_STATUS_DETAIL_MASK))
if (!StartPoly || (status & DT_FAILURE) || (status & DT_STATUS_DETAIL_MASK))
{
//BotSay(pBot, "findNearestPoly start failed!");
return (status & DT_STATUS_DETAIL_MASK); // couldn't find a polygon
@ -1432,7 +1467,7 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
// find the end polygon
status = m_navQuery->findNearestPoly(pEndPos, pExtents, m_navFilter, &EndPoly, EndNearest);
if ((status & DT_FAILURE) || (status & DT_STATUS_DETAIL_MASK))
if (!EndPoly || (status & DT_FAILURE) || (status & DT_STATUS_DETAIL_MASK))
{
//BotSay(pBot, "findNearestPoly end failed!");
return (status & DT_STATUS_DETAIL_MASK); // couldn't find a polygon
@ -1447,14 +1482,29 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
m_navQuery->closestPointOnPoly(PolyPath[nPathCount - 1], EndNearest, epos, 0);
if (dtVdistSqr(EndNearest, epos) > sqrf(MaxAcceptableDistance))
float DistToEndSq = dtVdist(EndNearest, epos);
if (DistToEndSq > MaxAcceptableDistance)
{
return DT_FAILURE;
}
else
{
dtVcopy(EndNearest, epos);
// For flight paths, if we can directly reach the end point from where our path finding got to, then see if we can fly directly to our desired target
Vector StartTrace = Vector(EndNearest[0], -EndNearest[2], EndNearest[1]);
StartTrace.z += 20.0f;
TraceResult hit;
UTIL_TraceHull(StartTrace, ToLocation, ignore_monsters, head_hull, nullptr, &hit);
float DesiredDist = vDist3D(StartTrace, ToLocation);
float ActualDist = DesiredDist * hit.flFraction;
// We couldn't reach our end goal
if (DesiredDist - ActualDist > MaxAcceptableDistance)
{
return DT_FAILURE;
}
}
dtVcopy(EndNearest, epos);
}
status = m_navQuery->findStraightPath(StartNearest, EndNearest, PolyPath, nPathCount, StraightPath, straightPathFlags, StraightPolyPath, &nVertCount, MAX_AI_PATH_SIZE, DT_STRAIGHTPATH_AREA_CROSSINGS);
@ -1607,6 +1657,15 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
CurrentPathPoint++;
}
path[CurrentPathPoint].requiredZ = ToLocation.z;
path[CurrentPathPoint].Location = ToLocation;
path[CurrentPathPoint].area = SAMPLE_POLYAREA_GROUND;
path[CurrentPathPoint].flag = SAMPLE_POLYFLAGS_WALLCLIMB;
path[CurrentPathPoint].FromLocation = path[CurrentPathPoint - 1].Location;
path[CurrentPathPoint].poly = 0;
NumPathPoints++;
*pathSize = NumPathPoints;
return DT_SUCCESS;
@ -1660,6 +1719,11 @@ Vector UTIL_FindHighestSuccessfulTracePoint(const Vector TraceFrom, const Vector
dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, bot_path_node* path, int* pathSize, float MaxAcceptableDistance)
{
if (NavProfile.bFlyingProfile)
{
return FindFlightPathToPoint(NavProfile, FromLocation, ToLocation, path, pathSize, MaxAcceptableDistance);
}
const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(NavProfile);
const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(NavProfile);
const dtQueryFilter* m_navFilter = &NavProfile.Filters;
@ -1822,6 +1886,11 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
{
if (!pBot) { return DT_FAILURE; }
if (pBot->BotNavInfo.NavProfile.bFlyingProfile)
{
return FindFlightPathToPoint(pBot->BotNavInfo.NavProfile, FromLocation, ToLocation, path, pathSize, MaxAcceptableDistance);
}
const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(pBot->BotNavInfo.NavProfile);
const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(pBot->BotNavInfo.NavProfile);
const dtQueryFilter* m_navFilter = &pBot->BotNavInfo.NavProfile.Filters;
@ -2873,41 +2942,30 @@ void NewMove(AvHAIPlayer* pBot)
return;
}
Vector MoveFrom = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].FromLocation;
Vector MoveTo = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location;
SamplePolyAreas CurrentNavArea = (SamplePolyAreas)pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].area;
unsigned short CurrentNavFlags = (SamplePolyFlags)pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag;
SamplePolyFlags CurrentNavFlag = (SamplePolyFlags)CurrentNavFlags;
SamplePolyFlags CurrentNavFlags = (SamplePolyFlags)pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag;
// Used to anticipate if we're about to enter a crouch area so we can start crouching early
unsigned char NextArea = SAMPLE_POLYAREA_GROUND;
unsigned char NextFlag = SAMPLE_POLYFLAGS_WALK;
if (pBot->BotNavInfo.CurrentPathPoint < (pBot->BotNavInfo.PathSize - 1))
{
NextArea = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].area;
NextFlag = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].flag;
}
bool bIsNearNextPoint = (pBot->BotNavInfo.CurrentPathPoint < (pBot->BotNavInfo.PathSize - 1)) && vDist2DSq(pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].FromLocation) <= sqrf(50.0f);
Vector MoveFrom = g_vecZero;
if (pBot->BotNavInfo.CurrentPathPoint > 0)
{
MoveFrom = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1].Location;
}
else
{
MoveFrom = pBot->Edict->v.origin;
}
Vector MoveTo = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location;
// Are we reaching the start of our next path point?
bool bIsNearNextPoint = (pBot->BotNavInfo.CurrentPathPoint < (pBot->BotNavInfo.PathSize - 1)) && (vDist2DSq(pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].FromLocation) <= sqrf(50.0f));
// Start crouching early if we're about to enter a crouch path point
if (CanPlayerCrouch(pBot->Edict) && (CurrentNavArea == SAMPLE_POLYAREA_CROUCH || (NextArea == SAMPLE_POLYAREA_CROUCH && bIsNearNextPoint)))
{
pBot->Button |= IN_DUCK;
}
switch (CurrentNavFlag)
switch (CurrentNavFlags)
{
case SAMPLE_POLYFLAGS_WALK:
GroundMove(pBot, MoveFrom, MoveTo);
@ -2981,12 +3039,6 @@ void GroundMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
{
edict_t* pEdict = pBot->Edict;
if (pBot->BotNavInfo.IsOnGround)
{
pBot->BotNavInfo.LastNavMeshPosition = pBot->CurrentFloorPosition;
pBot->BotNavInfo.LastPathFollowPosition = pBot->CurrentFloorPosition;
}
Vector CurrentPos = (pBot->BotNavInfo.IsOnGround) ? pBot->Edict->v.origin : pBot->CurrentFloorPosition;
Vector vForward = UTIL_GetVectorNormal2D(EndPoint - CurrentPos);
@ -3069,12 +3121,15 @@ void GroundMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(pBot->desiredMovementDir);
Vector HeadLocation = GetPlayerTopOfCollisionHull(pEdict, false);
// Crouch if we have something in our way at head height
if (!UTIL_QuickTrace(pBot->Edict, HeadLocation, (HeadLocation + (pBot->desiredMovementDir * 50.0f))))
if (CanPlayerCrouch(pEdict))
{
pBot->Button |= IN_DUCK;
Vector HeadLocation = GetPlayerTopOfCollisionHull(pEdict, false);
// Crouch if we have something in our way at head height
if (!UTIL_QuickTrace(pBot->Edict, HeadLocation, (HeadLocation + (pBot->desiredMovementDir * 50.0f))))
{
pBot->Button |= IN_DUCK;
}
}
}
@ -5104,23 +5159,23 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
BotNavInfo->PathDestination = Destination;
Vector ValidNavmeshPoint = UTIL_ProjectPointToNavmesh(Destination, Vector(max_ai_use_reach, max_ai_use_reach, max_ai_use_reach), pBot->BotNavInfo.NavProfile);
// Destination is not on the nav mesh, so we can't get close enough
if (vIsZero(ValidNavmeshPoint))
{
sprintf(pBot->PathStatus, "Could not project destination to navmesh");
return false;
}
dtStatus PathFindingStatus = DT_FAILURE;
if (bIsFlyingProfile)
{
PathFindingStatus = FindFlightPathToPoint(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, ValidNavmeshPoint, BotNavInfo->CurrentPath, &BotNavInfo->PathSize, MaxAcceptableDist);
PathFindingStatus = FindFlightPathToPoint(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, Destination, BotNavInfo->CurrentPath, &BotNavInfo->PathSize, MaxAcceptableDist);
}
else
{
Vector ValidNavmeshPoint = UTIL_ProjectPointToNavmesh(Destination, Vector(max_ai_use_reach, max_ai_use_reach, max_ai_use_reach), pBot->BotNavInfo.NavProfile);
// Destination is not on the nav mesh, so we can't get close enough
if (vIsZero(ValidNavmeshPoint))
{
sprintf(pBot->PathStatus, "Could not project destination to navmesh");
return false;
}
PathFindingStatus = FindPathClosestToPoint(pBot, pBot->BotNavInfo.MoveStyle, pBot->CurrentFloorPosition, ValidNavmeshPoint, BotNavInfo->CurrentPath, &BotNavInfo->PathSize, MaxAcceptableDist);
}
@ -5308,33 +5363,18 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
nav_status* BotNavInfo = &pBot->BotNavInfo;
edict_t* pEdict = pBot->Edict;
Vector CurrentMoveDest = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location;
Vector MoveFrom = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].FromLocation;
Vector MoveTo = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location;
Vector NextPoint = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].Location;
unsigned char CurrentMoveArea = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].area;
unsigned char NextMoveArea = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].area;
Vector ClosestPointToPath = vClosestPointOnLine(MoveFrom, MoveTo, pEdict->v.origin);
bool bAtOrPastDestination = vEquals(ClosestPointToPath, MoveTo, 32.0f);
//if ((CurrentMoveArea == SAMPLE_POLYAREA_GROUND || CurrentMoveArea == SAMPLE_POLYAREA_CROUCH) && !UTIL_QuickTrace(pBot->Edict, pBot->Edict->v.origin, CurrentMoveDest))
//{
// BotRecalcPath(pBot, BotNavInfo->ActualMoveDestination);
// return;
//}
Vector ClosestPointToPath = vClosestPointOnLine(pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1].Location, CurrentMoveDest, pEdict->v.origin);
bool bAtOrPastDestination = vEquals(ClosestPointToPath, CurrentMoveDest, 32.0f);
Vector MoveFrom = g_vecZero;
if (BotNavInfo->CurrentPathPoint > 0)
{
MoveFrom = BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint - 1].Location;
}
else
{
MoveFrom = pBot->Edict->v.origin;
}
// If we've reached our current path point
// If we've reached our current path point or can directly reach the next point
if (bAtOrPastDestination || UTIL_QuickHullTrace(pEdict, pEdict->v.origin, NextPoint, head_hull))
{
// End of the whole path, stop all movement
@ -5354,19 +5394,20 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
BotNavInfo->CurrentPathPoint = GetNextDirectFlightPath(pBot);
}
pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1].Location = pBot->Edict->v.origin;
pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].FromLocation = pBot->Edict->v.origin;
CurrentMoveDest = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location;
MoveFrom = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].FromLocation;
MoveTo = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location;
NextPoint = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].Location;
ClosestPointToPath = vClosestPointOnLine(pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1].Location, CurrentMoveDest, pEdict->v.origin);
MoveFrom = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1].Location;
CurrentMoveArea = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].area;
NextMoveArea = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].area;
ClearBotStuck(pBot);
}
}
Vector MoveDir = UTIL_GetVectorNormal(NextPoint - pBot->Edict->v.origin);
Vector MoveDir = UTIL_GetVectorNormal(MoveTo - pEdict->v.origin);
Vector ObstacleCheck = pBot->Edict->v.origin + (MoveDir * 32.0f);
@ -5376,8 +5417,7 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
return;
}
if (IsBotStuck(pBot, CurrentMoveDest))
if (IsBotStuck(pBot, MoveTo))
{
if (BotNavInfo->TotalStuckTime > 3.0f)
{
@ -5388,9 +5428,9 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
float Velocity = vSize2DSq(pBot->Edict->v.velocity);
bool bMustHugGround = (pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].area == SAMPLE_POLYAREA_CROUCH || pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].area == SAMPLE_POLYAREA_CROUCH);
bool bMustHugGround = (CurrentMoveArea == SAMPLE_POLYAREA_CROUCH || NextMoveArea == SAMPLE_POLYAREA_CROUCH);
if (!bMustHugGround || pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1].Location.z <= pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location.z)
if (!bMustHugGround || MoveFrom.z <= MoveTo.z)
{
if (Velocity < sqrf(500.f))
{
@ -5416,11 +5456,9 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
}
}
pBot->desiredMovementDir = UTIL_GetForwardVector2D(pBot->Edict->v.v_angle);
Vector LookLocation = CurrentMoveDest;
Vector LookLocation = MoveTo;
if (ClosestPointToPath.z - pBot->Edict->v.origin.z > 8.0f)
@ -5431,29 +5469,27 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
{
// Crouch areas need the lerk to stick close to the ground to avoid missing the crouch entry point
bool bIsFinalPoint = pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.PathSize - 1;
bool bIsFinalPoint = pBot->BotNavInfo.CurrentPathPoint == (pBot->BotNavInfo.PathSize - 1);
if (bMustHugGround || bIsFinalPoint)
{
if (pBot->Edict->v.origin.z - CurrentMoveDest.z > 8.0f)
if (pBot->Edict->v.origin.z - MoveTo.z > 8.0f)
{
LookLocation.z -= 50.0f;
}
}
else
{
if (UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, CurrentMoveDest + Vector(0.0f, 0.0f, 50.0f)))
if (UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, MoveTo + Vector(0.0f, 0.0f, 50.0f)))
{
LookLocation.z += 50.0f;
}
}
}
BotMoveLookAt(pBot, LookLocation);
CheckAndHandleBreakableObstruction(pBot, MoveFrom, CurrentMoveDest);
CheckAndHandleBreakableObstruction(pBot, MoveFrom, MoveTo);
CheckAndHandleDoorObstruction(pBot);
}
@ -5468,8 +5504,6 @@ void BotFollowPath(AvHAIPlayer* pBot)
nav_status* BotNavInfo = &pBot->BotNavInfo;
edict_t* pEdict = pBot->Edict;
SamplePolyAreas CurrentMoveArea = (SamplePolyAreas)BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].area;
// If we've reached our current path point
if (HasBotReachedPathPoint(pBot))
{
@ -5511,20 +5545,15 @@ void BotFollowPath(AvHAIPlayer* pBot)
}
}
Vector TargetMoveLocation = BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].Location;
Vector MoveTo = BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].Location;
bool bIsUsingPhaseGate = (BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
unsigned short CurrentFlag = BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag;
bool bIsJumping = (BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag == SAMPLE_POLYFLAGS_JUMP);
if (bIsJumping)
{
bool thing = true;
}
bool bIsUsingPhaseGate = (CurrentFlag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || CurrentFlag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
NewMove(pBot);
if (!bIsUsingPhaseGate && IsBotStuck(pBot, TargetMoveLocation))
if (!bIsUsingPhaseGate && IsBotStuck(pBot, MoveTo))
{
if (BotNavInfo->TotalStuckTime > 3.0f)
{
@ -5540,13 +5569,11 @@ void BotFollowPath(AvHAIPlayer* pBot)
if (!IsPlayerClimbingWall(pBot->Edict) && !IsPlayerOnLadder(pBot->Edict))
{
PerformUnstuckMove(pBot, TargetMoveLocation);
PerformUnstuckMove(pBot, MoveTo);
return;
}
}
}
void PerformUnstuckMove(AvHAIPlayer* pBot, const Vector MoveDestination)
@ -5698,7 +5725,7 @@ Vector UTIL_ProjectPointToNavmesh(const Vector Location)
dtStatus success = m_navQuery->findNearestPoly(pCheckLoc, Extents, m_navFilter, &FoundPoly, NavNearest);
if (dtStatusSucceed(success))
if (FoundPoly > 0 && dtStatusSucceed(success))
{
return Vector(NavNearest[0], -NavNearest[2], NavNearest[1]);
}
@ -5729,7 +5756,7 @@ Vector UTIL_ProjectPointToNavmesh(const Vector Location, const Vector Extents)
dtStatus success = m_navQuery->findNearestPoly(pCheckLoc, extents, m_navFilter, &FoundPoly, NavNearest);
if (dtStatusSucceed(success))
if (FoundPoly > 0 && dtStatusSucceed(success))
{
return Vector(NavNearest[0], -NavNearest[2], NavNearest[1]);
}
@ -5754,7 +5781,7 @@ Vector UTIL_ProjectPointToNavmesh(const Vector Location, const nav_profile &NavP
dtStatus success = m_navQuery->findNearestPoly(pCheckLoc, pExtents, m_navFilter, &FoundPoly, NavNearest);
if (dtStatusSucceed(success))
if (FoundPoly > 0 && dtStatusSucceed(success))
{
return Vector(NavNearest[0], -NavNearest[2], NavNearest[1]);
}
@ -5781,7 +5808,7 @@ Vector UTIL_ProjectPointToNavmesh(const Vector Location, const Vector Extents, c
dtStatus success = m_navQuery->findNearestPoly(pCheckLoc, fExtents, m_navFilter, &FoundPoly, NavNearest);
if (dtStatusSucceed(success))
if (FoundPoly > 0 && dtStatusSucceed(success))
{
return Vector(NavNearest[0], -NavNearest[2], NavNearest[1]);
}

View file

@ -1420,7 +1420,19 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
ClearBotInputs(pBot);
pBot->CurrentEyePosition = GetPlayerEyePosition(pEdict);
pBot->CurrentFloorPosition = UTIL_GetEntityGroundLocation(pEdict);
if (vDist3DSq(pBot->BotNavInfo.LastNavMeshCheckPosition, pBot->CurrentFloorPosition) > sqrf(16.0f))
{
if (UTIL_PointIsReachable(pBot->BotNavInfo.NavProfile, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), pBot->CurrentFloorPosition, 16.0f))
{
pBot->BotNavInfo.LastNavMeshPosition = pBot->CurrentFloorPosition;
}
pBot->BotNavInfo.LastNavMeshCheckPosition = pBot->CurrentFloorPosition;
}
pBot->LookTargetLocation = ZERO_VECTOR;
pBot->MoveLookLocation = ZERO_VECTOR;
pBot->LookTarget = nullptr;
@ -1465,6 +1477,8 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
UpdateCommanderOrders(pBot);
}
UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->BotNavInfo.LastNavMeshPosition);
}
void DroneThink(AvHAIPlayer* pBot)

View file

@ -13,7 +13,7 @@
double last_think_time = 0.0;
float BotDeltaTime = 0.01666667f;
AvHAIPlayer ActiveAIPlayers[MAX_PLAYERS];
vector<AvHAIPlayer> ActiveAIPlayers;
extern cvar_t avh_botautomode;
extern cvar_t avh_botsenabled;
@ -69,6 +69,19 @@ string BotNames[MAX_PLAYERS] = { "MrRobot",
void AIMGR_UpdateAIPlayerCounts()
{
for (auto BotIt = ActiveAIPlayers.begin(); BotIt != ActiveAIPlayers.end();)
{
// If bot has been kicked from the server then remove from active AI player list
if (FNullEnt(BotIt->Edict) || BotIt->Edict->free || !BotIt->Player)
{
BotIt = ActiveAIPlayers.erase(BotIt);
}
else
{
BotIt++;
}
}
// Don't add or remove bots too quickly, otherwise it can cause lag or even overflows
if (gpGlobals->time - LastAIPlayerCountUpdate < 0.2f) { return; }
@ -94,11 +107,6 @@ void AIMGR_UpdateAIPlayerCounts()
return;
}
if (avh_botautomode.value == 0) // Manual mode: do nothing, server can manually add/remove as they want
{
return;
}
if (avh_botautomode.value == 1) // Balance only: bots will only be added and removed to ensure teams remain balanced
{
AIMGR_UpdateTeamBalance();
@ -110,6 +118,9 @@ void AIMGR_UpdateAIPlayerCounts()
AIMGR_UpdateFillTeams();
return;
}
// Assume manual mode: do nothing, host can manually add/remove as they wish via sv_addaiplayer
return;
}
void AIMGR_UpdateTeamBalance()
@ -255,14 +266,14 @@ void AIMGR_RemoveAIPlayerFromTeam(int Team)
// resources tied up in them or are commanding, which could cause big disruption to the team they're leaving
int MinValue = 0; // Track the least valuable bot on the desired team.
int IndexToKick = -1; // Current bot to be kicked
vector<AvHAIPlayer>::iterator ItemToRemove = ActiveAIPlayers.end(); // Current bot to be kicked
for (int i = 0; i < MAX_PLAYERS; i++)
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
{
// Don't kick if the slot is empty, or the bot in that slot isn't on the right team
if (!ActiveAIPlayers[i].Player || ActiveAIPlayers[i].Player->GetTeam() != DesiredTeam) { continue; }
if (it->Player->GetTeam() != DesiredTeam) { continue; }
AvHPlayer* theAIPlayer = ActiveAIPlayers[i].Player;
AvHPlayer* theAIPlayer = it->Player;
float BotValue = theAIPlayer->GetResources();
@ -305,18 +316,19 @@ void AIMGR_RemoveAIPlayerFromTeam(int Team)
break;
}
if (IndexToKick < 0 || BotValue < MinValue)
if (ItemToRemove == ActiveAIPlayers.end() || BotValue < MinValue)
{
IndexToKick = i;
ItemToRemove = it;
MinValue = BotValue;
}
}
if (IndexToKick > -1)
if (ItemToRemove != ActiveAIPlayers.end())
{
ActiveAIPlayers[IndexToKick].Player->Kick();
ItemToRemove->Player->Kick();
memset(&ActiveAIPlayers[IndexToKick], 0, sizeof(AvHAIPlayer));
ActiveAIPlayers.erase(ItemToRemove);
}
}
@ -332,22 +344,6 @@ void AIMGR_AddAIPlayerToTeam(int Team)
return;
}
for (int i = 0; i < gpGlobals->maxClients; i++)
{
if (!ActiveAIPlayers[i].Player)
{
NewBotIndex = i;
break;
}
}
if (NewBotIndex < 0)
{
ALERT(at_console, "Bot limit reached, cannot add more\n");
return;
}
if (!NavmeshLoaded())
{
CONFIG_ParseConfigFile();
@ -360,12 +356,30 @@ void AIMGR_AddAIPlayerToTeam(int Team)
}
}
if (ActiveAIPlayers.size() >= gpGlobals->maxClients)
{
ALERT(at_console, "Bot limit reached, cannot add more\n");
return;
}
if (AIMGR_GetNumAIPlayers() == 0)
{
// Initialise the name index to a random number so we don't always get the same bot names
BotNameIndex = RANDOM_LONG(0, 31);
}
// Retrieve the current bot name and then cycle the index so the names are always unique
// Slap a [BOT] tag too so players know they're not human
string NewName = CONFIG_GetBotPrefix() + BotNames[BotNameIndex];
BotEnt = (*g_engfuncs.pfnCreateFakeClient)(NewName.c_str());
if (FNullEnt(BotEnt))
{
ALERT(at_console, "Failed to create AI player: server is full\n");
return;
}
AvHTeamNumber DesiredTeam = TEAM_IND;
AvHTeamNumber teamA = GetGameRules()->GetTeamANumber();
@ -376,12 +390,6 @@ void AIMGR_AddAIPlayerToTeam(int Team)
DesiredTeam = (Team == 1) ? teamA : teamB;
}
// Retrieve the current bot name and then cycle the index so the names are always unique
// Slap a [BOT] tag too so players know they're not human
string NewName = CONFIG_GetBotPrefix() + BotNames[BotNameIndex];
BotEnt = (*g_engfuncs.pfnCreateFakeClient)(NewName.c_str());
BotNameIndex++;
if (BotNameIndex > 31)
@ -389,12 +397,6 @@ void AIMGR_AddAIPlayerToTeam(int Team)
BotNameIndex = 0;
}
if (!BotEnt)
{
ALERT(at_console, "Failed to create AI player: server is full\n");
return;
}
char ptr[128]; // allocate space for message from ClientConnect
int clientIndex;
@ -431,6 +433,17 @@ void AIMGR_AddAIPlayerToTeam(int Team)
if (theNewAIPlayer)
{
AvHAIPlayer NewAIPlayer;
NewAIPlayer.Player = theNewAIPlayer;
NewAIPlayer.Edict = BotEnt;
NewAIPlayer.Team = theNewAIPlayer->GetTeam();
const bot_skill BotSkillSettings = CONFIG_GetGlobalBotSkillLevel();
memcpy(&NewAIPlayer.BotSkillSettings, &BotSkillSettings, sizeof(bot_skill));
ActiveAIPlayers.push_back(NewAIPlayer);
if (DesiredTeam != TEAM_IND)
{
ALERT(at_console, "Adding AI Player to team: %d\n", (int)Team);
@ -441,16 +454,6 @@ void AIMGR_AddAIPlayerToTeam(int Team)
ALERT(at_console, "Auto-assigning AI Player to team\n");
GetGameRules()->AutoAssignPlayer(theNewAIPlayer);
}
ActiveAIPlayers[NewBotIndex].Player = theNewAIPlayer;
ActiveAIPlayers[NewBotIndex].Edict = BotEnt;
ActiveAIPlayers[NewBotIndex].Team = theNewAIPlayer->GetTeam();
AvHAIPlayer* NewBotRef = &ActiveAIPlayers[NewBotIndex];
const bot_skill BotSkillSettings = CONFIG_GetGlobalBotSkillLevel();
memcpy(&NewBotRef->BotSkillSettings, &BotSkillSettings, sizeof(bot_skill));
}
else
{
@ -497,11 +500,16 @@ void AIMGR_UpdateAIPlayers()
float FrameDelta = CurrTime - PrevTime;
float ThinkDelta = CurrTime - LastThinkTime;
for (int bot_index = 0; bot_index < gpGlobals->maxClients; bot_index++)
for (auto BotIt = ActiveAIPlayers.begin(); BotIt != ActiveAIPlayers.end();)
{
if (!ActiveAIPlayers[bot_index].Player) { continue; } // Slot isn't filled
// If bot has been kicked from the server then remove from active AI player list
if (FNullEnt(BotIt->Edict) || BotIt->Edict->free || !BotIt->Player)
{
BotIt = ActiveAIPlayers.erase(BotIt);
continue;
}
AvHAIPlayer* bot = &ActiveAIPlayers[bot_index];
AvHAIPlayer* bot = &(*BotIt);
BotUpdateViewRotation(bot, FrameDelta);
@ -548,7 +556,9 @@ void AIMGR_UpdateAIPlayers()
bot->SideMove, bot->UpMove, bot->Button, bot->Impulse, adjustedmsec);
LastThinkTime = gpGlobals->time;
}
}
BotIt++;
}
PrevTime = CurrTime;
@ -562,26 +572,16 @@ float AIMGR_GetBotDeltaTime()
int AIMGR_GetNumAIPlayers()
{
int Result = 0;
for (int i = 0; i < MAX_PLAYERS; i++)
{
if (ActiveAIPlayers[i].Player != nullptr)
{
Result++;
}
}
return Result;
return ActiveAIPlayers.size();
}
int AIMGR_GetNumAIPlayersOnTeam(AvHTeamNumber Team)
{
int Result = 0;
for (int i = 0; i < MAX_PLAYERS; i++)
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
{
if (ActiveAIPlayers[i].Player != nullptr && ActiveAIPlayers[i].Team == Team)
if (it->Player->GetTeam() == Team)
{
Result++;
}
@ -592,9 +592,9 @@ int AIMGR_GetNumAIPlayersOnTeam(AvHTeamNumber Team)
int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team)
{
for (int i = 0; i < MAX_PLAYERS; i++)
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
{
if (ActiveAIPlayers[i].Player != nullptr && ActiveAIPlayers[i].Team == Team)
if (it->Player->GetTeam() == Team)
{
return true;
}
@ -605,13 +605,16 @@ int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team)
void AIMGR_RemoveBotsInReadyRoom()
{
for (int i = 0; i < MAX_PLAYERS; i++)
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end();)
{
if (ActiveAIPlayers[i].Player != nullptr && ActiveAIPlayers[i].Player->GetInReadyRoom())
if (it->Player->GetInReadyRoom())
{
ActiveAIPlayers[i].Player->Kick();
memset(&ActiveAIPlayers[i], 0, sizeof(AvHAIPlayer));
it->Player->Kick();
it = ActiveAIPlayers.erase(it);
}
else
{
it++;
}
}
}
@ -638,7 +641,18 @@ void AIMGR_ResetRound()
void AIMGR_ClearBotData()
{
memset(&ActiveAIPlayers, 0, sizeof(ActiveAIPlayers));
// We shouldn't have any bots in the server when this is called, but this ensures no bots end up "orphans" and no longer tracked by the system
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end();)
{
if (!FNullEnt(it->Edict) && it->Player)
{
it->Player->Kick();
}
it = ActiveAIPlayers.erase(it);
}
ActiveAIPlayers.clear();
}
void AIMGR_NewMap()
@ -671,11 +685,11 @@ AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team)
if (!ActiveCommander) { return nullptr; }
for (int i = 0; i < MAX_PLAYERS; i++)
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
{
if (ActiveAIPlayers[i].Player == ActiveCommander)
if (it->Player == ActiveCommander)
{
return &ActiveAIPlayers[i];
return &(*it);
}
}
@ -684,33 +698,34 @@ AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team)
AvHAIPlayer* AIMGR_FindPlayerOnTeamWaitingBuildLink(const AvHTeamNumber Team, const AvHAIDeployableStructureType NewStructure, const Vector BuildLocation)
{
for (int i = 0; i < MAX_PLAYERS; i++)
vector<AvHAIPlayer*> TeamPlayers = AIMGR_GetAIPlayersOnTeam(Team);
for (auto it = TeamPlayers.begin(); it != TeamPlayers.end(); it++)
{
if (ActiveAIPlayers[i].Player != nullptr && ActiveAIPlayers[i].Player->GetTeam() == Team)
AvHAIPlayer* AIPlayer = (*it);
if (AIPlayer->PrimaryBotTask.bIsWaitingForBuildLink && AIPlayer->PrimaryBotTask.StructureType == NewStructure)
{
if (ActiveAIPlayers[i].PrimaryBotTask.bIsWaitingForBuildLink && ActiveAIPlayers[i].PrimaryBotTask.StructureType == NewStructure)
if (vDist2DSq(BuildLocation, AIPlayer->PrimaryBotTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
{
if (vDist2DSq(BuildLocation, ActiveAIPlayers[i].PrimaryBotTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
{
return &ActiveAIPlayers[i];
}
return AIPlayer;
}
if (ActiveAIPlayers[i].SecondaryBotTask.bIsWaitingForBuildLink && ActiveAIPlayers[i].SecondaryBotTask.StructureType == NewStructure)
{
if (vDist2DSq(BuildLocation, ActiveAIPlayers[i].SecondaryBotTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
{
return &ActiveAIPlayers[i];
}
}
}
if (ActiveAIPlayers[i].WantsAndNeedsTask.bIsWaitingForBuildLink && ActiveAIPlayers[i].WantsAndNeedsTask.StructureType == NewStructure)
if (AIPlayer->SecondaryBotTask.bIsWaitingForBuildLink && AIPlayer->SecondaryBotTask.StructureType == NewStructure)
{
if (vDist2DSq(BuildLocation, AIPlayer->SecondaryBotTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
{
if (vDist2DSq(BuildLocation, ActiveAIPlayers[i].WantsAndNeedsTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
{
return &ActiveAIPlayers[i];
}
return AIPlayer;
}
}
if (AIPlayer->WantsAndNeedsTask.bIsWaitingForBuildLink && AIPlayer->WantsAndNeedsTask.StructureType == NewStructure)
{
if (vDist2DSq(BuildLocation, AIPlayer->WantsAndNeedsTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
{
return AIPlayer;
}
}
}
@ -726,11 +741,39 @@ AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam)
return (FriendlyTeam == TeamANumber) ? TeamBNumber : TeamANumber;
}
AvHAIPlayer* AIMGR_GetAIPlayerAtIndex(const int Index)
vector<AvHAIPlayer*> AIMGR_GetAllAIPlayers()
{
if (Index < 0 || Index >= MAX_PLAYERS) { return nullptr; }
vector<AvHAIPlayer*> Result;
return &ActiveAIPlayers[Index];
Result.clear();
for (auto BotIt = ActiveAIPlayers.begin(); BotIt != ActiveAIPlayers.end(); BotIt++)
{
if (FNullEnt(BotIt->Edict)) { continue; }
Result.push_back(&(*BotIt));
}
return Result;
}
vector<AvHAIPlayer*> AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team)
{
vector<AvHAIPlayer*> Result;
Result.clear();
for (auto BotIt = ActiveAIPlayers.begin(); BotIt != ActiveAIPlayers.end(); BotIt++)
{
if (FNullEnt(BotIt->Edict)) { continue; }
if (BotIt->Player->GetTeam() == Team)
{
Result.push_back(&(*BotIt));
}
}
return Result;
}
void AIMGR_UpdateAIMapData()

View file

@ -49,7 +49,8 @@ AvHAIPlayer* AIMGR_FindPlayerOnTeamWaitingBuildLink(const AvHTeamNumber Team, co
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam);
AvHAIPlayer* AIMGR_GetAIPlayerAtIndex(const int Index);
vector<AvHAIPlayer*> AIMGR_GetAllAIPlayers();
vector<AvHAIPlayer*> AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team);
void AIMGR_ClearBotData();

View file

@ -2542,11 +2542,13 @@ void AITASK_GenerateGuardWatchPoints(AvHAIPlayer* pBot, const Vector& GuardLocat
bool BotWithBuildTaskExists(AvHTeamNumber Team, AvHAIDeployableStructureType StructureType)
{
for (int i = 0; i < MAX_PLAYERS; i++)
{
AvHAIPlayer* Bot = AIMGR_GetAIPlayerAtIndex(i);
vector<AvHAIPlayer*> AIPlayers = AIMGR_GetAIPlayersOnTeam(Team);
if (!Bot || FNullEnt(Bot->Edict) || Bot->Player->GetTeam() != Team || !IsPlayerActiveInGame(Bot->Edict)) { continue; }
for (auto it = AIPlayers.begin(); it != AIPlayers.end(); it++)
{
AvHAIPlayer* Bot = (*it);
if (!IsPlayerActiveInGame(Bot->Edict)) { continue; }
if ((Bot->PrimaryBotTask.TaskType == TASK_BUILD && Bot->PrimaryBotTask.StructureType == StructureType) || (Bot->SecondaryBotTask.TaskType == TASK_BUILD && Bot->SecondaryBotTask.StructureType == StructureType))
{
@ -2559,11 +2561,13 @@ bool BotWithBuildTaskExists(AvHTeamNumber Team, AvHAIDeployableStructureType Str
AvHAIPlayer* GetFirstBotWithBuildTask(AvHTeamNumber Team, AvHAIDeployableStructureType StructureType, edict_t* IgnorePlayer)
{
for (int i = 0; i < MAX_PLAYERS; i++)
{
AvHAIPlayer* Bot = AIMGR_GetAIPlayerAtIndex(i);
vector<AvHAIPlayer*> AIPlayers = AIMGR_GetAIPlayersOnTeam(Team);
if (!Bot || FNullEnt(Bot->Edict) || Bot->Player->GetTeam() != Team || !IsPlayerActiveInGame(Bot->Edict)) { continue; }
for (auto it = AIPlayers.begin(); it != AIPlayers.end(); it++)
{
AvHAIPlayer* Bot = (*it);
if (!IsPlayerActiveInGame(Bot->Edict)) { continue; }
bool bPrimaryIsBuildTask = (Bot->PrimaryBotTask.TaskType == TASK_BUILD || Bot->PrimaryBotTask.TaskType == TASK_REINFORCE_STRUCTURE);
bool bSecondaryIsBuildTask = (Bot->SecondaryBotTask.TaskType == TASK_BUILD || Bot->SecondaryBotTask.TaskType == TASK_REINFORCE_STRUCTURE);
@ -2572,6 +2576,7 @@ AvHAIPlayer* GetFirstBotWithBuildTask(AvHTeamNumber Team, AvHAIDeployableStructu
{
return Bot;
}
}
return nullptr;
@ -2579,11 +2584,13 @@ AvHAIPlayer* GetFirstBotWithBuildTask(AvHTeamNumber Team, AvHAIDeployableStructu
AvHAIPlayer* GetFirstBotWithReinforceTask(AvHTeamNumber Team, edict_t* ReinforceStructure, edict_t* IgnorePlayer)
{
for (int i = 0; i < MAX_PLAYERS; i++)
{
AvHAIPlayer* Bot = AIMGR_GetAIPlayerAtIndex(i);
vector<AvHAIPlayer*> AIPlayers = AIMGR_GetAIPlayersOnTeam(Team);
if (!Bot || FNullEnt(Bot->Edict) || Bot->Player->GetTeam() != Team || !IsPlayerActiveInGame(Bot->Edict)) { continue; }
for (auto it = AIPlayers.begin(); it != AIPlayers.end(); it++)
{
AvHAIPlayer* Bot = (*it);
if (!IsPlayerActiveInGame(Bot->Edict)) { continue; }
if ((Bot->PrimaryBotTask.TaskType == TASK_REINFORCE_STRUCTURE && Bot->PrimaryBotTask.TaskTarget == ReinforceStructure) || (Bot->SecondaryBotTask.TaskType == TASK_REINFORCE_STRUCTURE && Bot->SecondaryBotTask.TaskTarget == ReinforceStructure))
{
@ -2808,7 +2815,7 @@ void AITASK_SetMoveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const Vector L
Task->TaskType = TASK_MOVE;
Task->TaskLocation = MoveLocation;
Task->bTaskIsUrgent = bIsUrgent;
Task->TaskLength = 60.0f; // Set a maximum time to reach destination. Helps avoid bots getting permanently stuck
Task->TaskLength = 120.0f; // Set a maximum time to reach destination. Helps avoid bots getting permanently stuck
}
}