From b3a1c7a6be5f1e798860a9f41c7ccbb81b76a6f6 Mon Sep 17 00:00:00 2001 From: RGreenlees Date: Fri, 8 Mar 2024 22:36:34 +0000 Subject: [PATCH] Bot stability and mine placement improvements --- main/source/mod/AIPlayers/AvHAICommander.cpp | 2 +- main/source/mod/AIPlayers/AvHAIConstants.h | 2 +- main/source/mod/AIPlayers/AvHAIHelper.cpp | 56 ++ main/source/mod/AIPlayers/AvHAIHelper.h | 3 + main/source/mod/AIPlayers/AvHAINavigation.cpp | 539 +++++++++++------- main/source/mod/AIPlayers/AvHAINavigation.h | 4 +- main/source/mod/AIPlayers/AvHAIPlayer.cpp | 16 +- .../mod/AIPlayers/AvHAIPlayerManager.cpp | 29 +- .../source/mod/AIPlayers/AvHAIPlayerManager.h | 2 + main/source/mod/AIPlayers/AvHAITactical.cpp | 161 +++++- main/source/mod/AIPlayers/AvHAITactical.h | 5 +- main/source/mod/AIPlayers/AvHAITask.cpp | 79 ++- main/source/mod/AvHConsoleCommands.cpp | 26 +- 13 files changed, 656 insertions(+), 268 deletions(-) diff --git a/main/source/mod/AIPlayers/AvHAICommander.cpp b/main/source/mod/AIPlayers/AvHAICommander.cpp index c03fa445..cbef2a9c 100644 --- a/main/source/mod/AIPlayers/AvHAICommander.cpp +++ b/main/source/mod/AIPlayers/AvHAICommander.cpp @@ -2143,7 +2143,7 @@ bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot) { AvHAIBuildableStructure* Structure = (*SecureIt); - if (Structure->Purpose == STRUCTURE_PURPOSE_SIEGE) + if (Structure->Purpose == STRUCTURE_PURPOSE_FORTIFY) { if (Structure->StructureType == STRUCTURE_MARINE_PHASEGATE) { diff --git a/main/source/mod/AIPlayers/AvHAIConstants.h b/main/source/mod/AIPlayers/AvHAIConstants.h index 98a4705a..5956af0e 100644 --- a/main/source/mod/AIPlayers/AvHAIConstants.h +++ b/main/source/mod/AIPlayers/AvHAIConstants.h @@ -555,7 +555,7 @@ typedef struct _AVH_AI_STUCK_TRACKER typedef struct _NAV_STATUS { vector CurrentPath; // Bot's path nodes - vector::iterator CurrentPathPoint = CurrentPath.end(); + unsigned int CurrentPathPoint = 0; Vector TargetDestination = g_vecZero; // Desired destination Vector ActualMoveDestination = g_vecZero; // Actual destination on nav mesh diff --git a/main/source/mod/AIPlayers/AvHAIHelper.cpp b/main/source/mod/AIPlayers/AvHAIHelper.cpp index a2c5ccaf..16ce05ac 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIHelper.cpp @@ -457,6 +457,62 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, int r, int g, int MESSAGE_END(); } +void UTIL_DrawBox(edict_t* pEntity, Vector bMin, Vector bMax, float drawTimeSeconds) +{ + Vector LowerBottomLeftCorner = bMin; + Vector LowerTopLeftCorner = Vector(bMin.x, bMax.y, bMin.z); + Vector LowerTopRightCorner = Vector(bMax.x, bMax.y, bMin.z); + Vector LowerBottomRightCorner = Vector(bMax.x, bMin.y, bMin.z); + + Vector UpperBottomLeftCorner = Vector(bMin.x, bMin.y, bMax.z); + Vector UpperTopLeftCorner = Vector(bMin.x, bMax.y, bMax.z); + Vector UpperTopRightCorner = Vector(bMax.x, bMax.y, bMax.z); + Vector UpperBottomRightCorner = Vector(bMax.x, bMin.y, bMax.z); + + + UTIL_DrawLine(pEntity, LowerTopLeftCorner, LowerTopRightCorner, drawTimeSeconds, 255, 255, 255); + UTIL_DrawLine(pEntity, LowerTopRightCorner, LowerBottomRightCorner, drawTimeSeconds, 255, 255, 255); + UTIL_DrawLine(pEntity, LowerBottomRightCorner, LowerBottomLeftCorner, drawTimeSeconds, 255, 255, 255); + + UTIL_DrawLine(pEntity, UpperBottomLeftCorner, UpperTopLeftCorner, drawTimeSeconds, 255, 255, 255); + UTIL_DrawLine(pEntity, UpperTopLeftCorner, UpperTopRightCorner, drawTimeSeconds, 255, 255, 255); + UTIL_DrawLine(pEntity, UpperTopRightCorner, UpperBottomRightCorner, drawTimeSeconds, 255, 255, 255); + UTIL_DrawLine(pEntity, UpperBottomRightCorner, UpperBottomLeftCorner, drawTimeSeconds, 255, 255, 255); + + UTIL_DrawLine(pEntity, LowerBottomLeftCorner, UpperBottomLeftCorner, drawTimeSeconds, 255, 255, 255); + UTIL_DrawLine(pEntity, LowerTopLeftCorner, UpperTopLeftCorner, drawTimeSeconds, 255, 255, 255); + UTIL_DrawLine(pEntity, LowerTopRightCorner, UpperTopRightCorner, drawTimeSeconds, 255, 255, 255); + UTIL_DrawLine(pEntity, LowerBottomRightCorner, UpperBottomRightCorner, drawTimeSeconds, 255, 255, 255); +} + +void UTIL_DrawBox(edict_t* pEntity, Vector bMin, Vector bMax, float drawTimeSeconds, int r, int g, int b) +{ + Vector LowerBottomLeftCorner = bMin; + Vector LowerTopLeftCorner = Vector(bMin.x, bMax.y, bMin.z); + Vector LowerTopRightCorner = Vector(bMax.x, bMax.y, bMin.z); + Vector LowerBottomRightCorner = Vector(bMax.x, bMin.y, bMin.z); + + Vector UpperBottomLeftCorner = Vector(bMin.x, bMin.y, bMax.z); + Vector UpperTopLeftCorner = Vector(bMin.x, bMax.y, bMax.z); + Vector UpperTopRightCorner = Vector(bMax.x, bMax.y, bMax.z); + Vector UpperBottomRightCorner = Vector(bMax.x, bMin.y, bMax.z); + + + UTIL_DrawLine(pEntity, LowerTopLeftCorner, LowerTopRightCorner, drawTimeSeconds, r, g, b); + UTIL_DrawLine(pEntity, LowerTopRightCorner, LowerBottomRightCorner, drawTimeSeconds, r, g, b); + UTIL_DrawLine(pEntity, LowerBottomRightCorner, LowerBottomLeftCorner, drawTimeSeconds, r, g, b); + + UTIL_DrawLine(pEntity, UpperBottomLeftCorner, UpperTopLeftCorner, drawTimeSeconds, r, g, b); + UTIL_DrawLine(pEntity, UpperTopLeftCorner, UpperTopRightCorner, drawTimeSeconds, r, g, b); + UTIL_DrawLine(pEntity, UpperTopRightCorner, UpperBottomRightCorner, drawTimeSeconds, r, g, b); + UTIL_DrawLine(pEntity, UpperBottomRightCorner, UpperBottomLeftCorner, drawTimeSeconds, r, g, b); + + UTIL_DrawLine(pEntity, LowerBottomLeftCorner, UpperBottomLeftCorner, drawTimeSeconds, r, g, b); + UTIL_DrawLine(pEntity, LowerTopLeftCorner, UpperTopLeftCorner, drawTimeSeconds, r, g, b); + UTIL_DrawLine(pEntity, LowerTopRightCorner, UpperTopRightCorner, drawTimeSeconds, r, g, b); + UTIL_DrawLine(pEntity, LowerBottomRightCorner, UpperBottomRightCorner, drawTimeSeconds, r, g, b); +} + void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned char r, unsigned char g, unsigned char b, const char* string) { // higher level wrapper for hudtextparms TE_TEXTMESSAGEs. This function is meant to be called diff --git a/main/source/mod/AIPlayers/AvHAIHelper.h b/main/source/mod/AIPlayers/AvHAIHelper.h index 09480169..a9795019 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.h +++ b/main/source/mod/AIPlayers/AvHAIHelper.h @@ -50,6 +50,9 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, int r, int g, int // Draws a coloured line using RGB input, between start and end for the given player (pEntity) for given number of seconds void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSeconds, int r, int g, int b); +void UTIL_DrawBox(edict_t* pEntity, Vector bMin, Vector bMax, float drawTimeSeconds); +void UTIL_DrawBox(edict_t* pEntity, Vector bMin, Vector bMax, float drawTimeSeconds, int r, int g, int b); + void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned char r, unsigned char g, unsigned char b, const char* string); void UTIL_ClearLocalizations(); diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index 298b1207..05d1cee1 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -268,6 +268,153 @@ struct MeshProcess : public dtTileCacheMeshProcess } }; +void AIDEBUG_DrawTemporaryObstacles(float DrawTime) +{ + const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE)); + const dtTileCache* m_tileCache = UTIL_GetTileCacheForProfile(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE)); + + if (m_navMesh) + { + int NumObstacles = m_tileCache->getObstacleCount(); + + for (int i = 0; i < NumObstacles; i++) + { + const dtTileCacheObstacle* ObstacleRef = m_tileCache->getObstacle(i); + + if (!ObstacleRef || ObstacleRef->state != DT_OBSTACLE_PROCESSED) { continue; } + + int r, g, b; + + + + if (ObstacleRef->type == ObstacleType::DT_OBSTACLE_BOX) + { + switch (ObstacleRef->box.area) + { + case DT_TILECACHE_NULL_AREA: + r = 128; + g = 128; + b = 128; + break; + case DT_TILECACHE_CROUCH_AREA: + r = 128; + g = 0; + b = 0; + break; + case DT_TILECACHE_BLOCKED_AREA: + r = 255; + g = 255; + b = 0; + break; + case DT_TILECACHE_WALLCLIMB_AREA: + r = 0; + g = 128; + b = 0; + break; + case DT_TILECACHE_LADDER_AREA: + r = 0; + g = 0; + b = 255; + break; + case DT_TILECACHE_TEAM1STRUCTURE_AREA: + r = 0; + g = 0; + b = 255; + break; + case DT_TILECACHE_TEAM2STRUCTURE_AREA: + r = 0; + g = 128; + b = 0; + break; + case DT_TILECACHE_WELD_AREA: + r = 255; + g = 0; + b = 0; + break; + case DT_TILECACHE_DOOR_AREA: + r = 0; + g = 128; + b = 128; + break; + } + + Vector bMin = Vector(ObstacleRef->box.bmin[0], -ObstacleRef->box.bmin[2], ObstacleRef->box.bmin[1]); + Vector bMax = Vector(ObstacleRef->box.bmax[0], -ObstacleRef->box.bmax[2], ObstacleRef->box.bmax[1]); + + UTIL_DrawBox(INDEXENT(1), bMin, bMax, DrawTime, r, g, b); + continue; + } + + if (ObstacleRef->type == ObstacleType::DT_OBSTACLE_CYLINDER) + { + switch (ObstacleRef->cylinder.area) + { + case DT_TILECACHE_NULL_AREA: + r = 128; + g = 128; + b = 128; + break; + case DT_TILECACHE_CROUCH_AREA: + r = 128; + g = 0; + b = 0; + break; + case DT_TILECACHE_BLOCKED_AREA: + r = 255; + g = 255; + b = 0; + break; + case DT_TILECACHE_WALLCLIMB_AREA: + r = 0; + g = 128; + b = 0; + break; + case DT_TILECACHE_LADDER_AREA: + r = 0; + g = 0; + b = 255; + break; + case DT_TILECACHE_TEAM1STRUCTURE_AREA: + r = 0; + g = 0; + b = 255; + break; + case DT_TILECACHE_TEAM2STRUCTURE_AREA: + r = 0; + g = 128; + b = 0; + break; + case DT_TILECACHE_WELD_AREA: + r = 255; + g = 0; + b = 0; + break; + case DT_TILECACHE_DOOR_AREA: + r = 0; + g = 128; + b = 128; + break; + } + + float Radius = ObstacleRef->cylinder.radius; + float Height = ObstacleRef->cylinder.height; + + // The location of obstacles in Recast are at the bottom of the shape, not the centre + Vector Centre = Vector(ObstacleRef->cylinder.pos[0], -ObstacleRef->cylinder.pos[2], ObstacleRef->cylinder.pos[1] + (Height * 0.5f)); + + if (vDist2DSq(INDEXENT(1)->v.origin, Centre) > sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) { continue; } + + Vector bMin = Centre - Vector(Radius, Radius, Height * 0.5f); + Vector bMax = Centre + Vector(Radius, Radius, (Height * 0.5f)); + + UTIL_DrawBox(INDEXENT(1), bMin, bMax, DrawTime, r, g, b); + continue; + } + + } + } +} + void AIDEBUG_DrawOffMeshConnections(float DrawTime) { if (NavMeshes[REGULAR_NAV_MESH].tileCache) @@ -1964,25 +2111,26 @@ bool UTIL_PointIsReachable(const nav_profile &NavProfile, const Vector FromLocat bool HasBotReachedPathPoint(const AvHAIPlayer* pBot) { - if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { return true; } - SamplePolyFlags CurrentNavFlag = (SamplePolyFlags)pBot->BotNavInfo.CurrentPathPoint->flag; - Vector MoveFrom = pBot->BotNavInfo.CurrentPathPoint->FromLocation; - Vector MoveTo = pBot->BotNavInfo.CurrentPathPoint->Location; - float RequiredClimbHeight = pBot->BotNavInfo.CurrentPathPoint->requiredZ; + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + SamplePolyFlags CurrentNavFlag = (SamplePolyFlags)CurrentPathNode.flag; + Vector MoveFrom = CurrentPathNode.FromLocation; + Vector MoveTo = CurrentPathNode.Location; + float RequiredClimbHeight = CurrentPathNode.requiredZ; Vector NextMoveLocation = ZERO_VECTOR; SamplePolyFlags NextMoveFlag = SAMPLE_POLYFLAGS_DISABLED; - vector::iterator NextPathPoint = next(pBot->BotNavInfo.CurrentPathPoint); - - if (NextPathPoint != pBot->BotNavInfo.CurrentPath.end()) + if ((pBot->BotNavInfo.CurrentPathPoint + 1) < pBot->BotNavInfo.CurrentPath.size()) { - NextMoveLocation = NextPathPoint->Location; - NextMoveFlag = (SamplePolyFlags)NextPathPoint->flag; + bot_path_node NextPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1]; + NextMoveLocation = NextPathNode.Location; + NextMoveFlag = (SamplePolyFlags)NextPathNode.flag; } switch (CurrentNavFlag) @@ -2144,15 +2292,21 @@ bool HasBotCompletedLiftMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector M void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot) { - edict_t* BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location, pBot->BotNavInfo.CurrentPathPoint->flag, nullptr); + if (pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { return; } + + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + edict_t* BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(pBot->Edict->v.origin, CurrentPathNode.Location, CurrentPathNode.flag, nullptr); if (FNullEnt(BlockingDoorEdict)) { int NumIterations = 0; - for (auto it = next(pBot->BotNavInfo.CurrentPathPoint); it != pBot->BotNavInfo.CurrentPath.end(); it++) + for (int i = (pBot->BotNavInfo.CurrentPathPoint + 1); i < pBot->BotNavInfo.CurrentPath.size(); i++) { - BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(it->FromLocation, it->Location, SAMPLE_POLYAREA_GROUND, nullptr); + bot_path_node ThisPathNode = pBot->BotNavInfo.CurrentPath[i]; + + BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(ThisPathNode.FromLocation, ThisPathNode.Location, SAMPLE_POLYAREA_GROUND, nullptr); NumIterations++; @@ -2846,15 +3000,21 @@ DoorTrigger* UTIL_GetNearestDoorTrigger(const Vector Location, nav_door* Door, C void CheckAndHandleBreakableObstruction(AvHAIPlayer* pBot, const Vector MoveFrom, const Vector MoveTo) { - edict_t* BlockingBreakableEdict = UTIL_GetBreakableBlockingPathPoint(pBot, pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location, SAMPLE_POLYAREA_GROUND, nullptr); + if (pBot->BotNavInfo.CurrentPathPoint > pBot->BotNavInfo.CurrentPath.size()) { return; } + + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + edict_t* BlockingBreakableEdict = UTIL_GetBreakableBlockingPathPoint(pBot, pBot->Edict->v.origin, CurrentPathNode.Location, SAMPLE_POLYAREA_GROUND, nullptr); if (FNullEnt(BlockingBreakableEdict)) { int NumIterations = 0; - for (auto it = next(pBot->BotNavInfo.CurrentPathPoint); it != pBot->BotNavInfo.CurrentPath.end(); it++) + for (int i = (pBot->BotNavInfo.CurrentPathPoint + 1); i < pBot->BotNavInfo.CurrentPath.size(); i++) { - BlockingBreakableEdict = UTIL_GetBreakableBlockingPathPoint(pBot, it->FromLocation, it->Location, SAMPLE_POLYAREA_GROUND, nullptr); + bot_path_node ThisPathNode = pBot->BotNavInfo.CurrentPath[i]; + + BlockingBreakableEdict = UTIL_GetBreakableBlockingPathPoint(pBot, ThisPathNode.FromLocation, ThisPathNode.Location, SAMPLE_POLYAREA_GROUND, nullptr); NumIterations++; @@ -2916,37 +3076,35 @@ void CheckAndHandleBreakableObstruction(AvHAIPlayer* pBot, const Vector MoveFrom void NewMove(AvHAIPlayer* pBot) { - - if (pBot->BotNavInfo.CurrentPath.size() == 0) + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { return; } - vector::iterator NextPathPoint = next(pBot->BotNavInfo.CurrentPathPoint); + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; - Vector MoveFrom = pBot->BotNavInfo.CurrentPathPoint->FromLocation; - Vector MoveTo = pBot->BotNavInfo.CurrentPathPoint->Location; + Vector MoveFrom = CurrentPathNode.FromLocation; + Vector MoveTo = CurrentPathNode.Location; - SamplePolyAreas CurrentNavArea = (SamplePolyAreas)pBot->BotNavInfo.CurrentPathPoint->area; - SamplePolyFlags CurrentNavFlags = (SamplePolyFlags)pBot->BotNavInfo.CurrentPathPoint->flag; + SamplePolyAreas CurrentNavArea = (SamplePolyAreas)CurrentPathNode.area; + SamplePolyFlags CurrentNavFlags = (SamplePolyFlags)CurrentPathNode.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; - bool bAtEndOfPath = (NextPathPoint == pBot->BotNavInfo.CurrentPath.end()); - - if (!bAtEndOfPath) + if (pBot->BotNavInfo.CurrentPathPoint < pBot->BotNavInfo.CurrentPath.size() - 1) { - NextArea = NextPathPoint->area; - } + bot_path_node NextPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1]; - // Are we reaching the start of our next path point? - bool bIsNearNextPoint = (!bAtEndOfPath && (vDist2DSq(pBot->Edict->v.origin, NextPathPoint->FromLocation) <= sqrf(50.0f))); + NextArea = NextPathNode.area; - // 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; + bool bIsNearNextPoint = (vDist2DSq(pBot->Edict->v.origin, NextPathNode.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 (CurrentNavFlags) @@ -2971,16 +3129,16 @@ void NewMove(AvHAIPlayer* pBot) { if (PlayerHasWeapon(pBot->Player, WEAPON_FADE_BLINK)) { - BlinkClimbMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ); + BlinkClimbMove(pBot, MoveFrom, MoveTo, CurrentPathNode.requiredZ); } else { - WallClimbMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ); + WallClimbMove(pBot, MoveFrom, MoveTo, CurrentPathNode.requiredZ); } } break; case SAMPLE_POLYFLAGS_LADDER: - LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ, NextArea); + LadderMove(pBot, MoveFrom, MoveTo, CurrentPathNode.requiredZ, NextArea); break; case SAMPLE_POLYFLAGS_TEAM1PHASEGATE: case SAMPLE_POLYFLAGS_TEAM2PHASEGATE: @@ -3024,6 +3182,8 @@ void GroundMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin { edict_t* pEdict = pBot->Edict; + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + Vector CurrentPos = (pBot->BotNavInfo.IsOnGround) ? pBot->Edict->v.origin : pBot->CurrentFloorPosition; Vector vForward = UTIL_GetVectorNormal2D(EndPoint - CurrentPos); @@ -3080,7 +3240,7 @@ void GroundMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin float LeapDist = (IsPlayerSkulk(pBot->Edict)) ? UTIL_MetresToGoldSrcUnits(5.0f) : UTIL_MetresToGoldSrcUnits(2.0f); - if (IsPlayerFade(pBot->Edict) && pBot->BotNavInfo.CurrentPathPoint->area == SAMPLE_POLYAREA_CROUCH) + if (IsPlayerFade(pBot->Edict) && CurrentPathNode.area == SAMPLE_POLYAREA_CROUCH) { LeapDist = UTIL_MetresToGoldSrcUnits(1.0f); } @@ -3530,6 +3690,19 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin // We're not yet on the ladder + // We're swimming + if (pBot->Edict->v.flags & FL_INWATER) + { + Vector TargetPoint = UTIL_GetNearestLadderCentrePoint(pBot->Edict->v.origin); + TargetPoint.z = pBot->Edict->v.origin.z; + + pBot->desiredMovementDir = UTIL_GetVectorNormal2D(TargetPoint - pBot->Edict->v.origin); + Vector LookPoint = TargetPoint + Vector(0.0f, 0.0f, 100.0f); + + BotMoveLookAt(pBot, LookPoint); + return; + } + if (!pBot->BotNavInfo.IsOnGround && !bIsGoingUpLadder) { // We're close enough to the end that we can jump off the ladder @@ -3597,6 +3770,10 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin Vector nearestLadderPoint = UTIL_GetNearestLadderCentrePoint(pEdict); nearestLadderPoint.z = pEdict->v.origin.z; pBot->desiredMovementDir = UTIL_GetVectorNormal2D(nearestLadderPoint - pEdict->v.origin); + + Vector LookPoint = nearestLadderPoint + Vector(0.0f, 0.0f, 20.0f); + + BotMoveLookAt(pBot, LookPoint); } } @@ -3924,26 +4101,29 @@ void PhaseGateMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP bool IsBotOffPath(const AvHAIPlayer* pBot) { - if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { return true; } - SamplePolyFlags CurrentNavFlag = (SamplePolyFlags)pBot->BotNavInfo.CurrentPathPoint->flag; - Vector MoveFrom = pBot->BotNavInfo.CurrentPathPoint->FromLocation; - Vector MoveTo = pBot->BotNavInfo.CurrentPathPoint->Location; + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + SamplePolyFlags CurrentNavFlag = (SamplePolyFlags)CurrentPathNode.flag; + Vector MoveFrom = CurrentPathNode.FromLocation; + Vector MoveTo = CurrentPathNode.Location; Vector NextMoveLocation = ZERO_VECTOR; SamplePolyFlags NextMoveFlag = SAMPLE_POLYFLAGS_DISABLED; - vector::iterator NextPathPoint = next(pBot->BotNavInfo.CurrentPathPoint); - - if (NextPathPoint != pBot->BotNavInfo.CurrentPath.end()) + if (pBot->BotNavInfo.CurrentPathPoint < pBot->BotNavInfo.CurrentPath.size() - 1) { - NextMoveLocation = NextPathPoint->Location; - NextMoveFlag = (SamplePolyFlags)NextPathPoint->flag; + bot_path_node NextPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + NextMoveLocation = NextPathNode.Location; + NextMoveFlag = (SamplePolyFlags)NextPathNode.flag; } + switch (CurrentNavFlag) { case SAMPLE_POLYFLAGS_WALK: @@ -4380,19 +4560,6 @@ void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination) float DistFromDestination = vDist2DSq(pBot->Edict->v.origin, Destination); - if (CanBotLeap(pBot) && DistFromDestination > sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) - { - Vector CurrVelocity = UTIL_GetVectorNormal2D(pBot->Edict->v.velocity); - - float MoveDot = UTIL_GetDotProduct2D(CurrVelocity, vForward); - - if (MoveDot >= 0.98f) - { - BotLeap(pBot, Destination); - } - } - - if (vIsZero(pBot->LookTargetLocation)) { Vector LookTarget = Destination; @@ -5104,11 +5271,13 @@ void UTIL_UpdateBotMovementStatus(AvHAIPlayer* pBot) bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination) { - if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end() || pBot->BotNavInfo.NavProfile.bFlyingProfile) { return true; } + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size() || pBot->BotNavInfo.NavProfile.bFlyingProfile) { return true; } - Vector MoveFrom = pBot->BotNavInfo.CurrentPathPoint->FromLocation; - Vector MoveTo = pBot->BotNavInfo.CurrentPathPoint->Location; - unsigned int flag = pBot->BotNavInfo.CurrentPathPoint->flag; + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + Vector MoveFrom = CurrentPathNode.FromLocation; + Vector MoveTo = CurrentPathNode.Location; + unsigned int flag = CurrentPathNode.flag; if (flag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || flag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE || flag == SAMPLE_POLYFLAGS_TEAM1STRUCTURE || flag == SAMPLE_POLYFLAGS_TEAM2STRUCTURE) { return true; } @@ -5181,11 +5350,11 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination) { if (PlayerHasWeapon(pBot->Player, WEAPON_FADE_BLINK)) { - BlinkClimbMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ); + BlinkClimbMove(pBot, MoveFrom, MoveTo, CurrentPathNode.requiredZ); } else { - WallClimbMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ); + WallClimbMove(pBot, MoveFrom, MoveTo, CurrentPathNode.requiredZ); } } @@ -5196,7 +5365,7 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination) if (bReverseCourse) { - LadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPathPoint->requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); + LadderMove(pBot, MoveTo, MoveFrom, CurrentPathNode.requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); // We're going DOWN the ladder if (MoveTo.z > MoveFrom.z) @@ -5210,7 +5379,7 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination) else { - LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); + LadderMove(pBot, MoveFrom, MoveTo, CurrentPathNode.requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH); // We're going DOWN the ladder if (MoveFrom.z > MoveTo.z) @@ -5260,9 +5429,9 @@ void UpdateBotStuck(AvHAIPlayer* pBot) if (!pBot->BotNavInfo.StuckInfo.bPathFollowFailed) { - bool bIsFollowingPath = (pBot->BotNavInfo.CurrentPath.size() > 0 && pBot->BotNavInfo.CurrentPathPoint != pBot->BotNavInfo.CurrentPath.end()); + bool bIsFollowingPath = (pBot->BotNavInfo.CurrentPath.size() > 0 && pBot->BotNavInfo.CurrentPathPoint < pBot->BotNavInfo.CurrentPath.size()); - bool bDist3D = pBot->BotNavInfo.NavProfile.bFlyingProfile || (bIsFollowingPath && (pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_LADDER || pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_WALLCLIMB)); + bool bDist3D = pBot->BotNavInfo.NavProfile.bFlyingProfile || (bIsFollowingPath && (pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag == SAMPLE_POLYFLAGS_LADDER || pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag == SAMPLE_POLYFLAGS_WALLCLIMB)); float DistFromLastPoint = (bDist3D) ? vDist3DSq(pBot->Edict->v.origin, pBot->BotNavInfo.StuckInfo.LastBotPosition) : vDist2DSq(pBot->Edict->v.origin, pBot->BotNavInfo.StuckInfo.LastBotPosition); @@ -5638,11 +5807,10 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move BotNavInfo->TargetDestination = Destination; } - BotNavInfo->CurrentPathPoint = BotNavInfo->CurrentPath.begin(); + BotNavInfo->CurrentPathPoint = 0; } else { - BotNavInfo->CurrentPathPoint = BotNavInfo->CurrentPath.end(); pBot->BotNavInfo.StuckInfo.bPathFollowFailed = true; @@ -5727,9 +5895,9 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move } // Check to ensure BotFollowFlightPath or BotFollowPath haven't cleared the path (will happen if reached end of path) - if (BotNavInfo->CurrentPathPoint != BotNavInfo->CurrentPath.end()) + if (BotNavInfo->CurrentPathPoint < BotNavInfo->CurrentPath.size()) { - HandlePlayerAvoidance(pBot, BotNavInfo->CurrentPathPoint->Location); + HandlePlayerAvoidance(pBot, BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].Location); BotMovementInputs(pBot); } @@ -5821,18 +5989,20 @@ void SkipAheadInFlightPath(AvHAIPlayer* pBot) nav_status* BotNavInfo = &pBot->BotNavInfo; // Early exit if we don't have a path, or we're already on the last path point - if (BotNavInfo->CurrentPath.size() == 0 || BotNavInfo->CurrentPathPoint == prev(BotNavInfo->CurrentPath.end())) { return; } + if (BotNavInfo->CurrentPath.size() == 0 || BotNavInfo->CurrentPathPoint >= (pBot->BotNavInfo.CurrentPath.size() - 1)) { return; } + + vector::iterator CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint); if (UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, prev(BotNavInfo->CurrentPath.end())->Location, head_hull, false)) { - pBot->BotNavInfo.CurrentPathPoint = prev(BotNavInfo->CurrentPath.end()); + pBot->BotNavInfo.CurrentPathPoint = (BotNavInfo->CurrentPath.size() - 1); return; } // If we are currently in a low area or approaching one, don't try to skip ahead in case it screws us up - if (BotNavInfo->CurrentPathPoint->area == SAMPLE_POLYAREA_CROUCH || (next(BotNavInfo->CurrentPathPoint) != BotNavInfo->CurrentPath.end() && next(BotNavInfo->CurrentPathPoint)->area == SAMPLE_POLYAREA_CROUCH)) { return; } + if (CurrentPathPoint->area == SAMPLE_POLYAREA_CROUCH || (next(CurrentPathPoint) != BotNavInfo->CurrentPath.end() && next(CurrentPathPoint)->area == SAMPLE_POLYAREA_CROUCH)) { return; } - for (auto it = prev(BotNavInfo->CurrentPath.end()); it != next(BotNavInfo->CurrentPathPoint); it--) + for (auto it = prev(BotNavInfo->CurrentPath.end()); it != next(CurrentPathPoint); it--) { Vector NextFlightPoint = UTIL_FindHighestSuccessfulTracePoint(pBot->Edict->v.origin, it->FromLocation, it->Location, 5.0f, 50.0f, 200.0f); @@ -5841,10 +6011,13 @@ void SkipAheadInFlightPath(AvHAIPlayer* pBot) { it->FromLocation = NextFlightPoint; - pBot->BotNavInfo.CurrentPathPoint = prev(it); + pBot->BotNavInfo.CurrentPathPoint = distance(BotNavInfo->CurrentPath.begin(), prev(it)); + CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint); - pBot->BotNavInfo.CurrentPathPoint->FromLocation = pBot->Edict->v.origin; - pBot->BotNavInfo.CurrentPathPoint->Location = it->FromLocation; + CurrentPathPoint->FromLocation = pBot->Edict->v.origin; + CurrentPathPoint->Location = it->FromLocation; + + return; } @@ -5853,7 +6026,7 @@ void SkipAheadInFlightPath(AvHAIPlayer* pBot) void BotFollowFlightPath(AvHAIPlayer* pBot) { - if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { ClearBotPath(pBot); return; @@ -5862,15 +6035,17 @@ void BotFollowFlightPath(AvHAIPlayer* pBot) nav_status* BotNavInfo = &pBot->BotNavInfo; edict_t* pEdict = pBot->Edict; - Vector CurrentMoveDest = BotNavInfo->CurrentPathPoint->Location; - Vector ClosestPointToPath = vClosestPointOnLine(BotNavInfo->CurrentPathPoint->FromLocation, CurrentMoveDest, pEdict->v.origin); + vector::iterator CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint); + + Vector CurrentMoveDest = CurrentPathPoint->Location; + Vector ClosestPointToPath = vClosestPointOnLine(CurrentPathPoint->FromLocation, CurrentMoveDest, pEdict->v.origin); bool bAtOrPastDestination = vEquals(ClosestPointToPath, CurrentMoveDest, 32.0f); // If we've reached our current path point if (bAtOrPastDestination) { // End of the whole path, stop all movement - if (next(BotNavInfo->CurrentPathPoint) == BotNavInfo->CurrentPath.end()) + if (BotNavInfo->CurrentPathPoint >= (pBot->BotNavInfo.CurrentPath.size() - 1)) { ClearBotMovement(pBot); return; @@ -5878,23 +6053,25 @@ void BotFollowFlightPath(AvHAIPlayer* pBot) else { // Pick the next point in the path - BotNavInfo->CurrentPathPoint = next(BotNavInfo->CurrentPathPoint); + BotNavInfo->CurrentPathPoint++; ClearBotStuck(pBot); + CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint); } } - if (BotNavInfo->CurrentPathPoint->flag == SAMPLE_POLYFLAGS_LIFT) + if (CurrentPathPoint->flag == SAMPLE_POLYFLAGS_LIFT) { - LiftMove(pBot, BotNavInfo->CurrentPathPoint->FromLocation, CurrentMoveDest); + LiftMove(pBot, CurrentPathPoint->FromLocation, CurrentMoveDest); return; } - if (BotNavInfo->CurrentPathPoint->area != SAMPLE_POLYAREA_CROUCH && next(BotNavInfo->CurrentPathPoint) != BotNavInfo->CurrentPath.end() && next(BotNavInfo->CurrentPathPoint)->area != SAMPLE_POLYAREA_CROUCH) + if (CurrentPathPoint->area != SAMPLE_POLYAREA_CROUCH && next(CurrentPathPoint) != BotNavInfo->CurrentPath.end() && next(CurrentPathPoint)->area != SAMPLE_POLYAREA_CROUCH) { SkipAheadInFlightPath(pBot); + CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint); } - ClosestPointToPath = vClosestPointOnLine(BotNavInfo->CurrentPathPoint->FromLocation, BotNavInfo->CurrentPathPoint->Location, pEdict->v.origin); + ClosestPointToPath = vClosestPointOnLine(CurrentPathPoint->FromLocation, CurrentPathPoint->Location, pEdict->v.origin); if (vDist3DSq(pBot->Edict->v.origin, ClosestPointToPath) > sqrf(GetPlayerRadius(pBot->Edict) * 3.0f)) { @@ -5902,11 +6079,11 @@ void BotFollowFlightPath(AvHAIPlayer* pBot) return; } - CurrentMoveDest = BotNavInfo->CurrentPathPoint->Location; - Vector MoveFrom = BotNavInfo->CurrentPathPoint->FromLocation; + CurrentMoveDest = CurrentPathPoint->Location; + Vector MoveFrom = CurrentPathPoint->FromLocation; - unsigned char CurrentMoveArea = BotNavInfo->CurrentPathPoint->area; - unsigned char NextMoveArea = (next(BotNavInfo->CurrentPathPoint) != BotNavInfo->CurrentPath.end()) ? next(BotNavInfo->CurrentPathPoint)->area : CurrentMoveArea; + unsigned char CurrentMoveArea = CurrentPathPoint->area; + unsigned char NextMoveArea = (next(CurrentPathPoint) != BotNavInfo->CurrentPath.end()) ? next(CurrentPathPoint)->area : CurrentMoveArea; Vector MoveDir = UTIL_GetVectorNormal(CurrentMoveDest - MoveFrom); @@ -5957,7 +6134,7 @@ void BotFollowFlightPath(AvHAIPlayer* pBot) Vector LookLocation = CurrentMoveDest; - if (pEdict->v.origin.z < BotNavInfo->CurrentPathPoint->requiredZ) + if (pEdict->v.origin.z < CurrentPathPoint->requiredZ) { LookLocation.z += 32.0f; } @@ -5974,12 +6151,8 @@ void BotFollowFlightPath(AvHAIPlayer* pBot) void BotFollowSwimPath(AvHAIPlayer* pBot) { - if (pBot->BotNavInfo.CurrentPath.size() == 0) - { - return; - } - if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { ClearBotPath(pBot); return; @@ -5988,43 +6161,47 @@ void BotFollowSwimPath(AvHAIPlayer* pBot) nav_status* BotNavInfo = &pBot->BotNavInfo; edict_t* pEdict = pBot->Edict; + vector::iterator CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint); + // If we've reached our current path point - if (HasBotReachedPathPoint(pBot)) + if (vPointOverlaps3D(CurrentPathPoint->Location, pBot->Edict->v.absmin, pBot->Edict->v.absmax)) { ClearBotStuck(pBot); - pBot->BotNavInfo.CurrentPathPoint = next(pBot->BotNavInfo.CurrentPathPoint); + pBot->BotNavInfo.CurrentPathPoint++; // No more path points, we've reached the end of our path - if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) + if (pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { ClearBotPath(pBot); return; } else { - if (pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_WALK) + CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint); + + if (CurrentPathPoint->flag == SAMPLE_POLYFLAGS_WALK) { - pBot->BotNavInfo.CurrentPathPoint->FromLocation = pBot->CurrentFloorPosition; + CurrentPathPoint->FromLocation = pBot->Edict->v.origin; } } } - bool TargetPointIsInWater = (UTIL_PointContents(BotNavInfo->CurrentPathPoint->Location) == CONTENTS_WATER || UTIL_PointContents(BotNavInfo->CurrentPathPoint->Location) == CONTENTS_SLIME); + bool TargetPointIsInWater = (UTIL_PointContents(CurrentPathPoint->Location) == CONTENTS_WATER || UTIL_PointContents(CurrentPathPoint->Location) == CONTENTS_SLIME); - bool bHasNextPoint = (next(BotNavInfo->CurrentPathPoint) != BotNavInfo->CurrentPath.end()); - bool NextPointInWater = (bHasNextPoint) ? UTIL_PointContents(next(BotNavInfo->CurrentPathPoint)->Location) == CONTENTS_WATER : TargetPointIsInWater; + bool bHasNextPoint = (next(CurrentPathPoint) != BotNavInfo->CurrentPath.end()); + bool NextPointInWater = (bHasNextPoint) ? UTIL_PointContents(next(CurrentPathPoint)->Location) == CONTENTS_WATER : TargetPointIsInWater; - bool bShouldSurface = (bHasNextPoint && !NextPointInWater && vDist2DSq(pEdict->v.origin, next(BotNavInfo->CurrentPathPoint)->FromLocation) < sqrf(100.0f)); + bool bShouldSurface = (bHasNextPoint && !NextPointInWater && vDist2DSq(pEdict->v.origin, next(CurrentPathPoint)->FromLocation) < sqrf(100.0f)); if (TargetPointIsInWater && !bShouldSurface) { - BotMoveLookAt(pBot, BotNavInfo->CurrentPathPoint->Location); - pBot->desiredMovementDir = UTIL_GetVectorNormal2D(BotNavInfo->CurrentPathPoint->Location - pEdict->v.origin); + BotMoveLookAt(pBot, CurrentPathPoint->Location); + pBot->desiredMovementDir = UTIL_GetVectorNormal2D(CurrentPathPoint->Location - pEdict->v.origin); - unsigned char NextArea = (next(BotNavInfo->CurrentPathPoint) != BotNavInfo->CurrentPath.end()) ? next(BotNavInfo->CurrentPathPoint)->area : SAMPLE_POLYAREA_GROUND; + unsigned char NextArea = (next(CurrentPathPoint) != BotNavInfo->CurrentPath.end()) ? next(CurrentPathPoint)->area : SAMPLE_POLYAREA_GROUND; - if (BotNavInfo->CurrentPathPoint->area == SAMPLE_POLYAREA_CROUCH || (NextArea == SAMPLE_POLYAREA_CROUCH && vDist2DSq(pEdict->v.origin, next(BotNavInfo->CurrentPathPoint)->FromLocation) < sqrf(50.0f))) + if (CurrentPathPoint->area == SAMPLE_POLYAREA_CROUCH || (NextArea == SAMPLE_POLYAREA_CROUCH && vDist2DSq(pEdict->v.origin, next(CurrentPathPoint)->FromLocation) < sqrf(50.0f))) { pBot->Button |= IN_DUCK; } @@ -6039,7 +6216,7 @@ void BotFollowSwimPath(AvHAIPlayer* pBot) // If we're below the waterline by a significant amount, then swim up to surface before we move on if (WaterDiff > 5.0f) { - Vector MoveDir = UTIL_GetVectorNormal2D(BotNavInfo->CurrentPathPoint->Location - pEdict->v.origin); + Vector MoveDir = UTIL_GetVectorNormal2D(CurrentPathPoint->Location - pEdict->v.origin); pBot->desiredMovementDir = MoveDir; if (WaterDiff > 10.0f) @@ -6068,12 +6245,7 @@ void BotFollowSwimPath(AvHAIPlayer* pBot) void BotFollowPath(AvHAIPlayer* pBot) { - if (pBot->BotNavInfo.CurrentPath.size() == 0) - { - return; - } - - if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { ClearBotPath(pBot); return; @@ -6088,27 +6260,52 @@ void BotFollowPath(AvHAIPlayer* pBot) { ClearBotStuck(pBot); - pBot->BotNavInfo.CurrentPathPoint = next(pBot->BotNavInfo.CurrentPathPoint); + pBot->BotNavInfo.CurrentPathPoint++; // No more path points, we've reached the end of our path - if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) + if (pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { ClearBotPath(pBot); return; } } - if (IsPlayerStandingOnPlayer(pBot->Edict) && pBot->BotNavInfo.CurrentPathPoint->flag != SAMPLE_POLYFLAGS_WALLCLIMB && pBot->BotNavInfo.CurrentPathPoint->flag != SAMPLE_POLYFLAGS_LADDER) + bot_path_node CurrentNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + if (IsPlayerStandingOnPlayer(pBot->Edict) && CurrentNode.flag != SAMPLE_POLYFLAGS_WALLCLIMB && CurrentNode.flag != SAMPLE_POLYFLAGS_LADDER) { if (pBot->Edict->v.groundentity->v.velocity.Length2D() > 10.0f) { pBot->desiredMovementDir = UTIL_GetVectorNormal2D(-pBot->Edict->v.groundentity->v.velocity); return; } - MoveToWithoutNav(pBot, pBot->BotNavInfo.CurrentPathPoint->Location); + MoveToWithoutNav(pBot, CurrentNode.Location); return; } + vector PotentialRiders = AITAC_GetAllPlayersOfTeamInArea(pBot->Player->GetTeam(), pBot->Edict->v.origin, pBot->Edict->v.size.Length(), false, pBot->Edict, AVH_USER3_NONE); + + for (auto it = PotentialRiders.begin(); it != PotentialRiders.end(); it++) + { + if ((*it)->pev->groundentity == pBot->Edict) + { + if (vDist2DSq(pBot->Edict->v.origin, CurrentNode.FromLocation) > sqrf(GetPlayerRadius(pBot->Edict))) + { + MoveToWithoutNav(pBot, CurrentNode.FromLocation); + return; + } + else + { + if (pBot->BotNavInfo.CurrentPathPoint > 0) + { + bot_path_node PrevNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint - 1]; + MoveToWithoutNav(pBot, PrevNode.FromLocation); + return; + } + } + } + } + if (IsBotOffPath(pBot)) { pBot->BotNavInfo.StuckInfo.bPathFollowFailed = true; @@ -6118,7 +6315,7 @@ void BotFollowPath(AvHAIPlayer* pBot) pBot->BotNavInfo.StuckInfo.bPathFollowFailed = false; - Vector MoveTo = BotNavInfo->CurrentPathPoint->Location; + Vector MoveTo = CurrentNode.Location; NewMove(pBot); @@ -6186,76 +6383,6 @@ void PerformUnstuckMove(AvHAIPlayer* pBot, const Vector MoveDestination) } -bool IsBotStuck(AvHAIPlayer* pBot, const Vector MoveDestination) -{ - // If invalid move destination then bail out - if (vIsZero(MoveDestination) || vDist2DSq(pBot->Edict->v.origin, MoveDestination) < sqrf(32.0f)) - { - pBot->BotNavInfo.LastStuckCheckTime = gpGlobals->time; - return false; - } - - if (pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_LIFT || pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE) - { - pBot->BotNavInfo.LastStuckCheckTime = gpGlobals->time; - return false; - } - - // If moving to a new destination set a new distance baseline. We do not reset the stuck timer - if (MoveDestination != pBot->BotNavInfo.StuckCheckMoveLocation) - { - float CurrentDistFromDestination = vDist3DSq(pBot->Edict->v.origin, MoveDestination); - pBot->BotNavInfo.StuckCheckMoveLocation = MoveDestination; - pBot->BotNavInfo.LastDistanceFromDestination = CurrentDistFromDestination; - return false; - } - - // If first time performing a stuck check, set a baseline time and return false - if (pBot->BotNavInfo.LastStuckCheckTime == 0.0f || gpGlobals->time - pBot->BotNavInfo.LastStuckCheckTime > 3.0f) - { - pBot->BotNavInfo.LastStuckCheckTime = gpGlobals->time; - return false; - } - - // Get the delta (usually frame time) between our last stuck check time. Will be appended to the total stuck time if bot is stuck - float StuckCheckDelta = (gpGlobals->time - pBot->BotNavInfo.LastStuckCheckTime); - pBot->BotNavInfo.LastStuckCheckTime = gpGlobals->time; - - // Get distance to destination - float CurrentDistFromDestination = vDist3DSq(pBot->Edict->v.origin, MoveDestination); - - // If this is the first time we're checking if we're stuck for this location, then set a baseline distance and bail - if (pBot->BotNavInfo.LastDistanceFromDestination == 0.0f) - { - pBot->BotNavInfo.LastDistanceFromDestination = CurrentDistFromDestination; - - return false; - } - - // If we've not managed to get any closer to our destination since the last check, then we're stuck. - //Increase the total stuck time, and we're stuck if we've been unable to progress for 1 second or longer - if (CurrentDistFromDestination >= pBot->BotNavInfo.LastDistanceFromDestination) - { - pBot->BotNavInfo.TotalStuckTime += StuckCheckDelta; - - if (pBot->BotNavInfo.TotalStuckTime >= 1.0f) - { - return true; - } - } - else - { - // We managed to make some progress, we're not stuck. Set a new baseline for distance to destination - pBot->BotNavInfo.TotalStuckTime = 0.0f; - pBot->BotNavInfo.LastDistanceFromDestination = CurrentDistFromDestination; - return false; - } - - - - return false; -} - bool BotIsAtLocation(const AvHAIPlayer* pBot, const Vector Destination) { if (vIsZero(Destination) || !(pBot->Edict->v.flags & FL_ONGROUND)) { return false; } @@ -6646,11 +6773,11 @@ bool BotRecalcPath(AvHAIPlayer* pBot, const Vector Destination) if (next(pBot->BotNavInfo.CurrentPath.begin()) == pBot->BotNavInfo.CurrentPath.end() || vDist2DSq(pBot->BotNavInfo.CurrentPath.front().Location, pBot->Edict->v.origin) > sqrf(GetPlayerRadius(pBot->Player))) { - pBot->BotNavInfo.CurrentPathPoint = pBot->BotNavInfo.CurrentPath.begin(); + pBot->BotNavInfo.CurrentPathPoint = 0; } else { - pBot->BotNavInfo.CurrentPathPoint = next(pBot->BotNavInfo.CurrentPath.begin()); + pBot->BotNavInfo.CurrentPathPoint = 1; } @@ -6715,7 +6842,7 @@ float UTIL_FindZHeightForWallClimb(const Vector ClimbStart, const Vector ClimbEn void ClearBotPath(AvHAIPlayer* pBot) { pBot->BotNavInfo.CurrentPath.clear(); - pBot->BotNavInfo.CurrentPathPoint = pBot->BotNavInfo.CurrentPath.end(); + pBot->BotNavInfo.CurrentPathPoint = 0; pBot->BotNavInfo.SpecialMovementFlags = 0; @@ -6806,7 +6933,7 @@ void BotMovementInputs(AvHAIPlayer* pBot) pBot->Button |= IN_MOVELEFT; } - if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end() || pBot->BotNavInfo.CurrentPathPoint->flag != SAMPLE_POLYFLAGS_LADDER) + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size() || pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].flag != SAMPLE_POLYFLAGS_LADDER) { if (pBot->Player->IsOnLadder()) { @@ -6827,18 +6954,20 @@ void OnBotEndLadder(AvHAIPlayer* pBot) Vector UTIL_GetFurthestVisiblePointOnPath(const AvHAIPlayer* pBot) { - if (pBot->BotNavInfo.CurrentPath.size() == 0) { return g_vecZero; } + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { return g_vecZero; } - if (pBot->BotNavInfo.CurrentPathPoint == prev(pBot->BotNavInfo.CurrentPath.end())) + vector::const_iterator CurrentPathPoint = (pBot->BotNavInfo.CurrentPath.begin() + pBot->BotNavInfo.CurrentPathPoint); + + if (CurrentPathPoint == prev(pBot->BotNavInfo.CurrentPath.end())) { - Vector MoveDir = UTIL_GetVectorNormal2D(pBot->BotNavInfo.CurrentPathPoint->Location - pBot->Edict->v.origin); - return pBot->BotNavInfo.CurrentPathPoint->Location + (MoveDir * 300.0f); + Vector MoveDir = UTIL_GetVectorNormal2D(CurrentPathPoint->Location - pBot->Edict->v.origin); + return CurrentPathPoint->Location + (MoveDir * 300.0f); } - Vector FurthestVisiblePoint = pBot->BotNavInfo.CurrentPathPoint->Location; + Vector FurthestVisiblePoint = CurrentPathPoint->Location; FurthestVisiblePoint.z = pBot->CurrentEyePosition.z; - for (auto it = next(pBot->BotNavInfo.CurrentPathPoint); it != pBot->BotNavInfo.CurrentPath.end(); it++) + for (auto it = next(CurrentPathPoint); it != pBot->BotNavInfo.CurrentPath.end(); it++) { Vector CheckPoint = it->Location + Vector(0.0f, 0.0f, 32.0f); diff --git a/main/source/mod/AIPlayers/AvHAINavigation.h b/main/source/mod/AIPlayers/AvHAINavigation.h index 22a196a2..37b0f4e5 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.h +++ b/main/source/mod/AIPlayers/AvHAINavigation.h @@ -276,13 +276,11 @@ void ClearBotStuck(AvHAIPlayer* pBot); // Clears all bot movement data, including the current path, their stuck status. Effectively stops all movement the bot is performing. void ClearBotMovement(AvHAIPlayer* pBot); -// Checks if the bot has managed to make progress towards MoveDestination. Move destination should be the bot's current path point, not overall destination -bool IsBotStuck(AvHAIPlayer* pBot, const Vector MoveDestination); - // Called every bot frame (default is 60fps). Ensures the tile cache is updated after obstacles are placed bool UTIL_UpdateTileCache(); void AIDEBUG_DrawOffMeshConnections(float DrawTime); +void AIDEBUG_DrawTemporaryObstacles(float DrawTime); Vector UTIL_GetNearestPointOnNavWall(AvHAIPlayer* pBot, const float MaxRadius); Vector UTIL_GetNearestPointOnNavWall(const nav_profile& NavProfile, const Vector Location, const float MaxRadius); diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index a4d11237..2a549688 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -6725,10 +6725,12 @@ bool SkulkCombatThink(AvHAIPlayer* pBot) } else { - if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) { return true; } + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { return true; } + + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; // EVASIVE MANOEUVRES! Only do this if we're running along the floor and aren't approaching a path point (so we don't stray off the path) - if (pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_WALK && vDist2DSq(pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location) > sqrf(50.0f)) + if (CurrentPathNode.flag == SAMPLE_POLYFLAGS_WALK && vDist2DSq(pBot->Edict->v.origin, CurrentPathNode.Location) > sqrf(50.0f)) { Vector RightDir = UTIL_GetCrossProduct(pBot->desiredMovementDir, UP_VECTOR); @@ -7286,9 +7288,15 @@ bool FadeCombatThink(AvHAIPlayer* pBot) // If we're still in danger while retreating, do extra leaping to get the hell out if (TrackedEnemyRef->bHasLOS) { - if (pBot->BotNavInfo.CurrentPathPoint != pBot->BotNavInfo.CurrentPath.end() && pBot->BotNavInfo.CurrentPathPoint->flag != SAMPLE_POLYFLAGS_WALLCLIMB && pBot->BotNavInfo.CurrentPathPoint->flag != SAMPLE_POLYFLAGS_LIFT) + if (pBot->BotNavInfo.CurrentPath.size() > 0 && pBot->BotNavInfo.CurrentPathPoint < pBot->BotNavInfo.CurrentPath.size()) { - BotLeap(pBot, pBot->BotNavInfo.CurrentPathPoint->Location); + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + if (CurrentPathNode.flag != SAMPLE_POLYFLAGS_WALLCLIMB && CurrentPathNode.flag != SAMPLE_POLYFLAGS_LIFT) + { + BotLeap(pBot, CurrentPathNode.Location); + } + } } else diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp index b70fc206..cd908f71 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp @@ -635,10 +635,11 @@ void AIMGR_UpdateAIPlayers() AIDEBUG_DrawBotPath(bot); - if (bot->BotNavInfo.CurrentPath.size() > 0 && bot->BotNavInfo.CurrentPathPoint != bot->BotNavInfo.CurrentPath.end()) + if (bot->BotNavInfo.CurrentPath.size() > 0 && bot->BotNavInfo.CurrentPathPoint < bot->BotNavInfo.CurrentPath.size()) { - UTIL_DrawLine(INDEXENT(1), bot->Edict->v.origin, bot->BotNavInfo.CurrentPathPoint->FromLocation, 255, 0, 0); - UTIL_DrawLine(INDEXENT(1), bot->Edict->v.origin, bot->BotNavInfo.CurrentPathPoint->Location, 0, 128, 0); + bot_path_node CurrentPathNode = bot->BotNavInfo.CurrentPath[bot->BotNavInfo.CurrentPathPoint]; + UTIL_DrawLine(INDEXENT(1), bot->Edict->v.origin, CurrentPathNode.FromLocation, 255, 0, 0); + UTIL_DrawLine(INDEXENT(1), bot->Edict->v.origin, CurrentPathNode.Location, 0, 128, 0); } } @@ -1124,6 +1125,28 @@ vector AIMGR_GetAllAIPlayers() return Result; } +vector AIMGR_GetAllActivePlayers() +{ + vector Result; + + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + edict_t* PlayerEdict = INDEXENT(i); + + if (!FNullEnt(PlayerEdict) && !PlayerEdict->free && IsPlayerActiveInGame(PlayerEdict)) + { + AvHPlayer* PlayerRef = dynamic_cast(CBaseEntity::Instance(PlayerEdict)); + + if (PlayerRef) + { + Result.push_back(PlayerRef); + } + } + } + + return Result; +} + vector AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team) { vector Result; diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.h b/main/source/mod/AIPlayers/AvHAIPlayerManager.h index 4247e37f..f0f21cac 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.h +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.h @@ -84,6 +84,8 @@ AvHTeam* AIMGR_GetTeamRef(const AvHTeamNumber Team); vector AIMGR_GetAllAIPlayers(); // Returns all NS AI players on the requested team. Does not include third-party bots vector AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team); +// Returns all active players (i.e. not dead, commanding, spectating or in the ready room) +vector AIMGR_GetAllActivePlayers(); // Returns all players on a team which are not an internal NS bot. Will still include third party bots such as Whichbot and RCBot vector AIMGR_GetNonAIPlayersOnTeam(AvHTeamNumber Team); diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index 4e3b15b7..f754ff0b 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -1232,7 +1232,7 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) if (GetGameRules()->GetTeamA()->GetTeamType() == AVH_CLASS_TYPE_MARINE) { - bool bIsReachableMarine = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, max_player_use_reach); + bool bIsReachableMarine = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, 4.0f); if (bIsReachableMarine) { @@ -1246,7 +1246,7 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD); - bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, TeamAStart, ResNodeLocation, max_player_use_reach); + bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, TeamAStart, ResNodeLocation, 4.0f); if (bIsReachableWelder) { @@ -1260,9 +1260,9 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) } else { - bool bIsReachableSkulk = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, max_player_use_reach); - bool bIsReachableGorge = UTIL_PointIsReachable(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, max_player_use_reach); - bool bIsReachableOnos = UTIL_PointIsReachable(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, max_player_use_reach); + bool bIsReachableSkulk = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, 4.0f); + bool bIsReachableGorge = UTIL_PointIsReachable(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, 4.0f); + bool bIsReachableOnos = UTIL_PointIsReachable(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, 4.0f); if (bIsReachableSkulk) { @@ -1287,7 +1287,7 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) if (GetGameRules()->GetTeamB()->GetTeamType() == AVH_CLASS_TYPE_MARINE) { - bool bIsReachableMarine = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, max_player_use_reach); + bool bIsReachableMarine = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, 4.0f); if (bIsReachableMarine) { @@ -1301,7 +1301,7 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD); - bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, TeamBStart, ResNodeLocation, max_player_use_reach); + bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, TeamBStart, ResNodeLocation, 4.0f); if (bIsReachableWelder) { @@ -1315,9 +1315,9 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) } else { - bool bIsReachableSkulk = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, max_player_use_reach); - bool bIsReachableGorge = UTIL_PointIsReachable(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, max_player_use_reach); - bool bIsReachableOnos = UTIL_PointIsReachable(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, max_player_use_reach); + bool bIsReachableSkulk = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, 4.0f); + bool bIsReachableGorge = UTIL_PointIsReachable(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, 4.0f); + bool bIsReachableOnos = UTIL_PointIsReachable(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, 4.0f); if (bIsReachableSkulk) { @@ -3356,6 +3356,122 @@ AvHAIWeapon UTIL_GetWeaponTypeFromDroppedItem(const AvHAIDeployableItemType Item return WEAPON_INVALID; } +Vector UTIL_GetNextMinePosition2(edict_t* StructureToMine) +{ + if (FNullEnt(StructureToMine)) { return ZERO_VECTOR; } + + AvHTeamNumber StructureTeam = (AvHTeamNumber)StructureToMine->v.team; + + nav_profile MineCheckProfile = GetBaseNavProfile(MARINE_BASE_NAV_PROFILE); + MineCheckProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_BLOCKED); + MineCheckProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1STRUCTURE); + MineCheckProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM2STRUCTURE); + MineCheckProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WELD); + MineCheckProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_DOOR); + MineCheckProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_BLOCKED); + MineCheckProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM1STRUCTURE); + MineCheckProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM2STRUCTURE); + MineCheckProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD); + MineCheckProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_DOOR); + + Vector FwdVector = UTIL_GetForwardVector2D(StructureToMine->v.angles); + Vector RightVector = UTIL_GetVectorNormal2D(UTIL_GetCrossProduct(FwdVector, UP_VECTOR)); + + bool bFwd = false; + bool bRight = false; + bool bBack = false; + bool bLeft = false; + + DeployableSearchFilter MineFilter; + MineFilter.DeployableTeam = StructureTeam; + MineFilter.DeployableTypes = STRUCTURE_MARINE_DEPLOYEDMINE; + MineFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(3.0f); + + vector SurroundingMines = AITAC_FindAllDeployables(StructureToMine->v.origin, &MineFilter); + + for (auto it = SurroundingMines.begin(); it != SurroundingMines.end(); it++) + { + AvHAIBuildableStructure* ThisMine = (*it); + + Vector Dir = UTIL_GetVectorNormal2D(ThisMine->Location - StructureToMine->v.origin); + + if (UTIL_GetDotProduct2D(FwdVector, Dir) > 0.7f) + { + bFwd = true; + } + + if (UTIL_GetDotProduct2D(FwdVector, Dir) < -0.7f) + { + bBack = true; + } + + if (UTIL_GetDotProduct2D(RightVector, Dir) > 0.7f) + { + bRight = true; + } + + if (UTIL_GetDotProduct2D(RightVector, Dir) < -0.7f) + { + bLeft = true; + } + } + + float Size = fmaxf(StructureToMine->v.size.x, StructureToMine->v.size.y); + Size += 8.0f; + + if (!bFwd) + { + Vector SearchLocation = StructureToMine->v.origin + (FwdVector * Size); + + Vector BuildLocation = UTIL_ProjectPointToNavmesh(SearchLocation, MineCheckProfile); + + if (BuildLocation != ZERO_VECTOR) + { + return BuildLocation; + } + } + + if (!bBack) + { + Vector SearchLocation = StructureToMine->v.origin - (FwdVector * Size); + + Vector BuildLocation = UTIL_ProjectPointToNavmesh(SearchLocation, MineCheckProfile); + + if (BuildLocation != ZERO_VECTOR) + { + return BuildLocation; + } + } + + if (!bRight) + { + Vector SearchLocation = StructureToMine->v.origin + (RightVector * Size); + + Vector BuildLocation = UTIL_ProjectPointToNavmesh(SearchLocation, MineCheckProfile); + + if (BuildLocation != ZERO_VECTOR) + { + return BuildLocation; + } + } + + if (!bLeft) + { + Vector SearchLocation = StructureToMine->v.origin - (RightVector * Size); + + Vector BuildLocation = UTIL_ProjectPointToNavmesh(SearchLocation, MineCheckProfile); + + if (BuildLocation != ZERO_VECTOR) + { + return BuildLocation; + } + } + + Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(MineCheckProfile, StructureToMine->v.origin, Size); + + return BuildLocation; +} + Vector UTIL_GetNextMinePosition(edict_t* StructureToMine) { if (FNullEnt(StructureToMine)) { return ZERO_VECTOR; } @@ -3713,19 +3829,21 @@ bool AITAC_GetNumPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location bool AITAC_ShouldBotBeCautious(AvHAIPlayer* pBot) { - if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end()) { return false; } + if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) { return false; } - if (pBot->BotNavInfo.CurrentPathPoint->area != SAMPLE_POLYAREA_GROUND) { return false; } + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + if (CurrentPathNode.area != SAMPLE_POLYAREA_GROUND) { return false; } AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(pBot->Player->GetTeam()); if (AITAC_AnyPlayerOnTeamHasLOSToLocation(EnemyTeam, pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(50.0f), nullptr)) { return false; } - int NumEnemiesAtDestination = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, pBot->BotNavInfo.CurrentPathPoint->Location, UTIL_MetresToGoldSrcUnits(50.0f), pBot->Edict); + int NumEnemiesAtDestination = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, CurrentPathNode.Location, UTIL_MetresToGoldSrcUnits(50.0f), pBot->Edict); if (NumEnemiesAtDestination > 1) { - return (vDist2DSq(pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f))); + return (vDist2DSq(pBot->Edict->v.origin, CurrentPathNode.Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f))); } return false; @@ -4086,7 +4204,20 @@ bool AITAC_IsAlienHarasserNeeded(AvHAIPlayer* pBot) int DesiredLerks = (int)ceilf((float)NumTeamPlayers * 0.1f); int NumLerks = imaxi(AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER3, nullptr), AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_HARASS, pBot)); - return NumLerks < DesiredLerks; + if (NumLerks < DesiredLerks) + { + if (DesiredLerks > 1) { return true; } + + float LastSeenTime; + edict_t* PreviousLerk = AITAC_GetLastSeenLerkForTeam(BotTeam, LastSeenTime); + + // We only go lerk if the last lerk we had in the match was either us, or we've not had another lerk in 60 seconds + // It avoids aliens spending all their resources on evolving lerks if they keep dying + // It also means that if a human was playing lerk and died, a bot doesn't immediately take over that role, and lets the human try again if they want + return (LastSeenTime > 60.0f || PreviousLerk == pBot->Edict); + } + + return false; } bool AITAC_ShouldBotBuildHive(AvHAIPlayer* pBot, AvHAIHiveDefinition** EligibleHive) diff --git a/main/source/mod/AIPlayers/AvHAITactical.h b/main/source/mod/AIPlayers/AvHAITactical.h index 15cd5c35..e34fee7a 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.h +++ b/main/source/mod/AIPlayers/AvHAITactical.h @@ -15,10 +15,10 @@ #include "AvHAIConstants.h" // How frequently to update the global list of built structures (in seconds). 0 = every frame -static const float structure_inventory_refresh_rate = 0.0f; +static const float structure_inventory_refresh_rate = 0.2f; // How frequently to update the global list of dropped marine items (in seconds). 0 = every frame -static const float item_inventory_refresh_rate = 0.0f; +static const float item_inventory_refresh_rate = 0.2f; bool AITAC_DeployableExistsAtLocation(const Vector& Location, const DeployableSearchFilter* Filter); std::vector AITAC_FindAllDeployables(const Vector& Location, const DeployableSearchFilter* Filter); @@ -148,6 +148,7 @@ bool AITAC_MarineResearchIsAvailable(const AvHTeamNumber Team, const AvHMessageI bool AITAC_ElectricalResearchIsAvailable(edict_t* Structure); Vector UTIL_GetNextMinePosition(edict_t* StructureToMine); +Vector UTIL_GetNextMinePosition2(edict_t* StructureToMine); int UTIL_GetCostOfStructureType(AvHAIDeployableStructureType StructureType); edict_t* AITAC_GetNearestHumanAtLocation(const AvHTeamNumber Team, const Vector Location, const float MaxSearchRadius); diff --git a/main/source/mod/AIPlayers/AvHAITask.cpp b/main/source/mod/AIPlayers/AvHAITask.cpp index b75607dd..73eda79a 100644 --- a/main/source/mod/AIPlayers/AvHAITask.cpp +++ b/main/source/mod/AIPlayers/AvHAITask.cpp @@ -11,6 +11,7 @@ #include "../AvHSharedUtil.h" #include "../AvHAlienWeaponConstants.h" +#include "../AvHMarineEquipmentConstants.h" #include "../AvHGamerules.h" #include "../AvHWeldable.h" #include "../AvHTurret.h" @@ -1126,6 +1127,29 @@ void BotProgressPickupTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) void BotProgressMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) { + if (vIsZero(Task->TaskLocation)) + { + Task->TaskLocation = UTIL_GetNextMinePosition2(Task->TaskTarget); + + if (vIsZero(Task->TaskLocation)) + { + AITASK_ClearBotTask(pBot, Task); + return; + } + } + + float DistToPlaceLocation = vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation); + + if (DistToPlaceLocation < sqrf(UTIL_MetresToGoldSrcUnits(5.0f))) + { + pBot->DesiredCombatWeapon = WEAPON_MARINE_MINES; + } + else + { + MoveTo(pBot, Task->TaskLocation, MOVESTYLE_NORMAL); + return; + } + if (Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING) { return; @@ -1147,38 +1171,30 @@ void BotProgressMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) Task->ActiveBuildInfo.BuildStatus = BUILD_ATTEMPT_NONE; } - if (vIsZero(Task->TaskLocation)) - { - Task->TaskLocation = UTIL_GetNextMinePosition(Task->TaskTarget); - - if (vIsZero(Task->TaskLocation)) - { - AITASK_ClearBotTask(pBot, Task); - return; - } - } - - float DistToPlaceLocation = vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation); - - if (DistToPlaceLocation < sqrf(UTIL_MetresToGoldSrcUnits(3.0f))) - { - pBot->DesiredCombatWeapon = WEAPON_MARINE_MINES; - } - - - if (DistToPlaceLocation > sqrf(8.0f)) - { - MoveTo(pBot, Task->TaskLocation, MOVESTYLE_NORMAL); - return; - } - - BotDirectLookAt(pBot, Task->TaskLocation); + BotLookAt(pBot, Task->TaskLocation); if (GetPlayerCurrentWeapon(pBot->Player) == WEAPON_MARINE_MINES) { - float LookDot = UTIL_GetDotProduct(UTIL_GetForwardVector(pBot->Edict->v.v_angle), UTIL_GetVectorNormal(Task->TaskLocation - pBot->CurrentEyePosition)); + Vector TraceStart = pBot->Player->GetGunPosition(); + Vector TraceDir = UTIL_GetForwardVector(pBot->Edict->v.v_angle); - if (LookDot > 0.95f) + TraceResult tr; + UTIL_TraceLine(TraceStart, TraceStart + (TraceDir * kMineRange), dont_ignore_monsters, pBot->Edict->v.pContainingEntity, &tr); + + bool bTraceSuccessful = false; + + if (tr.flFraction < 1.0f) + { + if (tr.pHit != Task->TaskTarget) + { + if (vDist2DSq(tr.vecEndPos, Task->TaskLocation) < sqrf(8.0f)) + { + bTraceSuccessful = true; + } + } + } + + if (bTraceSuccessful) { pBot->Button |= IN_ATTACK; Task->ActiveBuildInfo.AttemptedLocation = Task->TaskLocation; @@ -1187,6 +1203,11 @@ void BotProgressMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) Task->ActiveBuildInfo.AttemptedStructureType = STRUCTURE_MARINE_DEPLOYEDMINE; Task->ActiveBuildInfo.NumAttempts++; } + else + { + MoveTo(pBot, Task->TaskLocation, MOVESTYLE_NORMAL); + return; + } } } @@ -3438,7 +3459,7 @@ void AITASK_SetMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict Task->TaskType = TASK_PLACE_MINE; Task->TaskTarget = Target; Task->bTaskIsUrgent = bIsUrgent; - Task->TaskLocation = UTIL_GetNextMinePosition(Target); + Task->TaskLocation = UTIL_GetNextMinePosition2(Target); Task->StructureType = STRUCTURE_MARINE_DEPLOYEDMINE; diff --git a/main/source/mod/AvHConsoleCommands.cpp b/main/source/mod/AvHConsoleCommands.cpp index aaa0db59..84812b1c 100644 --- a/main/source/mod/AvHConsoleCommands.cpp +++ b/main/source/mod/AvHConsoleCommands.cpp @@ -1456,13 +1456,29 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd ) theSuccess = true; } - else if (FStrEq(pcmd, "showteamstarts")) + else if (FStrEq(pcmd, "drawtempobstacles")) { - Vector TeamAStart = AITAC_GetTeamStartingLocation(TEAM_ONE); - Vector TeamBStart = AITAC_GetTeamStartingLocation(TEAM_TWO); + AIDEBUG_DrawTemporaryObstacles(10.0f); - UTIL_DrawLine(INDEXENT(1), theAvHPlayer->pev->origin, TeamAStart, 20.0f, 0, 0, 255); - UTIL_DrawLine(INDEXENT(1), theAvHPlayer->pev->origin, TeamBStart, 20.0f, 0, 128, 0); + theSuccess = true; + } + else if (FStrEq(pcmd, "nextmineposition")) + { + DeployableSearchFilter NearestStructureFilter; + NearestStructureFilter.DeployableTeam = theAvHPlayer->GetTeam(); + NearestStructureFilter.DeployableTypes = (STRUCTURE_MARINE_INFANTRYPORTAL | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ARMOURY); + + AvHAIBuildableStructure* Nearest = AITAC_FindClosestDeployableToLocation(theAvHPlayer->pev->origin, &NearestStructureFilter); + + if (Nearest) + { + Vector MinePosition = UTIL_GetNextMinePosition2(Nearest->edict); + + if (!vIsZero(MinePosition)) + { + UTIL_DrawLine(INDEXENT(1), INDEXENT(1)->v.origin, MinePosition, 10.0f); + } + } theSuccess = true; }