diff --git a/main/source/mod/AIPlayers/AvHAIConstants.h b/main/source/mod/AIPlayers/AvHAIConstants.h index 45528415..d4520420 100644 --- a/main/source/mod/AIPlayers/AvHAIConstants.h +++ b/main/source/mod/AIPlayers/AvHAIConstants.h @@ -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 diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index 9bf9ca96..fd1b0690 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -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]); } diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index 9253cd74..5e802723 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -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) diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp index ab17eeba..9b0a034c 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp @@ -13,7 +13,7 @@ double last_think_time = 0.0; float BotDeltaTime = 0.01666667f; -AvHAIPlayer ActiveAIPlayers[MAX_PLAYERS]; +vector 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::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 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 AIMGR_GetAllAIPlayers() { - if (Index < 0 || Index >= MAX_PLAYERS) { return nullptr; } + vector 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 AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team) +{ + vector 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() diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.h b/main/source/mod/AIPlayers/AvHAIPlayerManager.h index 1c124b3f..bbf622bc 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.h +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.h @@ -49,7 +49,8 @@ AvHAIPlayer* AIMGR_FindPlayerOnTeamWaitingBuildLink(const AvHTeamNumber Team, co AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam); -AvHAIPlayer* AIMGR_GetAIPlayerAtIndex(const int Index); +vector AIMGR_GetAllAIPlayers(); +vector AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team); void AIMGR_ClearBotData(); diff --git a/main/source/mod/AIPlayers/AvHAITask.cpp b/main/source/mod/AIPlayers/AvHAITask.cpp index 48b0f367..c1b99511 100644 --- a/main/source/mod/AIPlayers/AvHAITask.cpp +++ b/main/source/mod/AIPlayers/AvHAITask.cpp @@ -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 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 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 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 } }