From 4102dabe05dec1815796939c2a47e8e024fb93e6 Mon Sep 17 00:00:00 2001 From: RGreenlees Date: Tue, 25 Jun 2024 17:36:13 +0100 Subject: [PATCH] Further bot improvements * Player-requested structures are properly integrated into the base system to prevent duplicates and better anticipate player desires * Improved bot path finding for moving targets, hopefully they get caught less often on railings and stuff when pursuing a moving target * When bots teleport (e.g. phase gate, but any kind of teleport), they clear their stored unstuck locations so they don't try and backtrack when attempting to get unstuck --- main/source/mod/AvHAICommander.cpp | 147 ++++++++++++++++++++++++++-- main/source/mod/AvHAINavigation.cpp | 59 +++++++++-- main/source/mod/AvHAIPlayer.cpp | 33 +++---- main/source/mod/AvHAIPlayer.h | 2 + 4 files changed, 205 insertions(+), 36 deletions(-) diff --git a/main/source/mod/AvHAICommander.cpp b/main/source/mod/AvHAICommander.cpp index 39432c86..7c6cd053 100644 --- a/main/source/mod/AvHAICommander.cpp +++ b/main/source/mod/AvHAICommander.cpp @@ -3143,6 +3143,88 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot) Vector IdealDeployLocation = Requestor->v.origin + (UTIL_GetForwardVector2D(Requestor->v.angles) * ProjectDistance); Vector ProjectedDeployLocation = AdjustPointForPathfinding(IdealDeployLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE)); + AvHAIMarineBase* BaseToDeployIn = nullptr; + float MinDist = 0.0f; + + for (auto it = pBot->Bases.begin(); it != pBot->Bases.end(); it++) + { + float DistFromBase = vDist2DSq(Requestor->v.origin, it->BaseLocation); + + if (it->BaseType == MARINE_BASE_MAINBASE && DistFromBase < sqrf(UTIL_MetresToGoldSrcUnits(20.0f))) + { + BaseToDeployIn = &(*it); + break; + } + + float DesiredDist = (it->BaseType == MARINE_BASE_GUARDPOST) ? BALANCE_VAR(kTurretFactoryBuildDistance) : UTIL_MetresToGoldSrcUnits(15.0f); + + if (DistFromBase < sqrf(DesiredDist)) + { + if (!BaseToDeployIn || DistFromBase < MinDist) + { + BaseToDeployIn = &(*it); + MinDist = DistFromBase; + } + } + } + + // We've requested a building away from any existing bases the commander is building + if (!BaseToDeployIn && (NextRequest->RequestType == BUILD_PHASEGATE || NextRequest->RequestType == BUILD_TURRET_FACTORY)) + { + + const AvHAIHiveDefinition* NearestHive = AITAC_GetHiveNearestLocation(Requestor->v.origin); + float DesiredDistance = (NearestHive->Status != HIVE_STATUS_UNBUILT) ? sqrf(BALANCE_VAR(kSiegeTurretRange)) : sqrf(UTIL_MetresToGoldSrcUnits(10.0f)); + float DistFromHive = vDist2DSq(NearestHive->Location, Requestor->v.origin); + + if (NearestHive && DistFromHive < DesiredDistance) + { + AvHAIMarineBase* ExistingBase = nullptr; + + for (auto it = pBot->Bases.begin(); it != pBot->Bases.end(); it++) + { + if (!it->bIsActive || !it->bRecycleBase) { continue; } + + if (NearestHive->Status != HIVE_STATUS_UNBUILT) + { + if (it->BaseType == MARINE_BASE_SIEGE && vEquals2D(it->SiegeTarget, NearestHive->Location)) + { + if (!it->bBaseInitialised) + { + BaseToDeployIn = &(*it); + BaseToDeployIn->BaseLocation = Requestor->v.origin; + break; + } + } + } + else + { + if (it->BaseType == MARINE_BASE_OUTPOST && vDist2DSq(it->BaseLocation, NearestHive->FloorLocation) < UTIL_MetresToGoldSrcUnits(15.0f)) + { + if (!it->bBaseInitialised) + { + BaseToDeployIn = &(*it); + break; + } + } + } + + } + + if (!BaseToDeployIn && NearestHive) + { + MarineBaseType NewBaseType = (NearestHive->Status != HIVE_STATUS_UNBUILT) ? MARINE_BASE_SIEGE : MARINE_BASE_OUTPOST; + + BaseToDeployIn = AICOMM_AddNewBase(pBot, Requestor->v.origin, NewBaseType); + } + } + else + { + BaseToDeployIn = AICOMM_AddNewBase(pBot, Requestor->v.origin, MARINE_BASE_GUARDPOST); + } + + + } + if (!vIsZero(ProjectedDeployLocation)) { if (NextRequest->RequestType == BUILD_INFANTRYPORTAL) @@ -3195,9 +3277,20 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot) } } - AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, StructureToDeploy, ProjectedDeployLocation, STRUCTURE_PURPOSE_GENERAL, true); + bool bSuccess = false; - if (DeployedStructure) + if (BaseToDeployIn) + { + bSuccess = AICOMM_AddStructureToBase(pBot, StructureToDeploy, ProjectedDeployLocation, BaseToDeployIn); + } + else + { + AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, StructureToDeploy, ProjectedDeployLocation, STRUCTURE_PURPOSE_GENERAL, true); + + bSuccess = DeployedStructure != nullptr; + } + + if (bSuccess) { NextRequest->bResponded = true; return true; @@ -3208,9 +3301,21 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot) if (!vIsZero(DeployLocation)) { - AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_GENERAL); + bool bSuccess = false; - if (DeployedStructure) + if (BaseToDeployIn) + { + bSuccess = AICOMM_AddStructureToBase(pBot, StructureToDeploy, DeployLocation, BaseToDeployIn); + } + else + { + AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_GENERAL, true); + + bSuccess = DeployedStructure != nullptr; + } + + + if (bSuccess) { NextRequest->bResponded = true; return true; @@ -3221,9 +3326,21 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot) if (!vIsZero(DeployLocation)) { - AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_GENERAL); + bool bSuccess = false; - if (DeployedStructure) + if (BaseToDeployIn) + { + bSuccess = AICOMM_AddStructureToBase(pBot, StructureToDeploy, DeployLocation, BaseToDeployIn); + } + else + { + AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_GENERAL, true); + + bSuccess = DeployedStructure != nullptr; + } + + + if (bSuccess) { NextRequest->bResponded = true; return true; @@ -3744,6 +3861,8 @@ void AICOMM_UpdateGuardpostStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base) } } + Base->bRecycleBase = false; + Base->bIsActive = true; // Check how far we are into building this outpost. If we have a TF and at least 3 sentries then we can consider it "established" // even if it isn't finished yet @@ -4040,7 +4159,11 @@ void AICOMM_DeployBases(AvHAIPlayer* pBot) if (!AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyStuffFilter)) { - AICOMM_AddNewBase(pBot, ThisHive->FloorLocation, MARINE_BASE_OUTPOST); + Vector ProjectedPoint = UTIL_ProjectPointToNavmesh(ThisHive->FloorLocation, Vector(500.0f, 500.0f, 500.0f), GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE)); + if (!vIsZero(ProjectedPoint)) + { + AICOMM_AddNewBase(pBot, ProjectedPoint, MARINE_BASE_OUTPOST); + } } } } @@ -4090,7 +4213,12 @@ void AICOMM_DeployBases(AvHAIPlayer* pBot) if (!AITAC_DeployableExistsAtLocation(ThisPlayer->pev->origin, &EnemyStuffFilter)) { - AICOMM_AddNewBase(pBot, ThisPlayer->pev->origin, MARINE_BASE_SIEGE); + // Make sure that our siege guy is somewhere we can build + Vector ProjectedPoint = UTIL_ProjectPointToNavmesh(ThisPlayer->pev->origin, Vector(100.0f, 100.0f, 100.0f), GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE)); + if (!vIsZero(ProjectedPoint)) + { + AICOMM_AddNewBase(pBot, ProjectedPoint, MARINE_BASE_SIEGE); + } } } } @@ -4162,7 +4290,6 @@ bool AICOMM_GetRelocationMessage(Vector RelocationPoint, char* MessageBuffer) sprintf(MessageBuffer, "Get ready to relocate"); return true; } - int MsgIndex = irandrange(0, 2); @@ -4829,6 +4956,8 @@ bool AICOMM_BuildOutBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut) return AICOMM_BuildOutOutpost(pBot, BaseToBuildOut); case MARINE_BASE_MAINBASE: return AICOMM_BuildOutMainBase(pBot, BaseToBuildOut); + case MARINE_BASE_GUARDPOST: + return AICOMM_BuildOutGuardPost(pBot, BaseToBuildOut); default: return false; } diff --git a/main/source/mod/AvHAINavigation.cpp b/main/source/mod/AvHAINavigation.cpp index 13772f78..a3ed6d8a 100644 --- a/main/source/mod/AvHAINavigation.cpp +++ b/main/source/mod/AvHAINavigation.cpp @@ -1166,6 +1166,7 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.5f); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_WALLCLIMB, 1.0f); + BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED); @@ -1199,6 +1200,7 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f); BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f); BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.0f); + BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f); BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f); BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL); BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED); @@ -1215,6 +1217,7 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f); BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f); BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.5f); + BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f); BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f); BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL); BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED); @@ -2085,17 +2088,48 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle, } Vector FromLocation = pBot->CurrentFloorPosition; + Vector FromFloorLocation = FromLocation; - // Add a slight bias towards trying to move forward if on a railing or other narrow bit of navigable terrain - // rather than potentially dropping back off it the wrong way - Vector GeneralDir = UTIL_GetVectorNormal2D(ToLocation - pBot->CurrentFloorPosition); - Vector CheckLocation = FromLocation + (GeneralDir * 16.0f); - - Vector FromFloorLocation = AdjustPointForPathfinding(CheckLocation); - - if (vIsZero(FromFloorLocation)) + // If the bot currently has a path, then let's calculate the navigation from the "from" point rather than our exact position right now + if (pBot->BotNavInfo.CurrentPathPoint < pBot->BotNavInfo.CurrentPath.size()) { - FromFloorLocation = AdjustPointForPathfinding(FromLocation); + bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; + + if (CurrentPathNode.flag == SAMPLE_POLYFLAGS_WALK) + { + bool bFromReachable = UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, CurrentPathNode.Location); + bool bToReachable = UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, CurrentPathNode.FromLocation); + if (bFromReachable && bToReachable) + { + FromFloorLocation = pBot->CurrentFloorPosition; + } + else if (bFromReachable) + { + FromFloorLocation = CurrentPathNode.FromLocation; + } + else + { + FromFloorLocation = CurrentPathNode.Location; + } + } + else + { + FromFloorLocation = CurrentPathNode.FromLocation; + } + } + else + { + // Add a slight bias towards trying to move forward if on a railing or other narrow bit of navigable terrain + // rather than potentially dropping back off it the wrong way + Vector GeneralDir = UTIL_GetVectorNormal2D(ToLocation - pBot->CurrentFloorPosition); + Vector CheckLocation = FromLocation + (GeneralDir * 16.0f); + + Vector FromFloorLocation = AdjustPointForPathfinding(CheckLocation); + + if (vIsZero(FromFloorLocation)) + { + FromFloorLocation = AdjustPointForPathfinding(FromLocation); + } } nav_door* LiftReference = UTIL_GetLiftReferenceByEdict(pBot->Edict->v.groundentity); @@ -6602,10 +6636,12 @@ bool NAV_MergeAndUpdatePath(AvHAIPlayer* pBot, std::vector& NewPa return true; } + // Start with the bot's current path point std::vector::iterator OldPathStart = (pBot->BotNavInfo.CurrentPath.begin() + pBot->BotNavInfo.CurrentPathPoint); std::vector::iterator OldPathEnd; std::vector::iterator NewPathStart; + // We skip ahead in the path until we reach the first non-walk node in our CURRENT path for (OldPathEnd = OldPathStart; OldPathEnd != pBot->BotNavInfo.CurrentPath.end(); OldPathEnd++) { if (OldPathEnd->flag != SAMPLE_POLYFLAGS_WALK) @@ -6614,11 +6650,13 @@ bool NAV_MergeAndUpdatePath(AvHAIPlayer* pBot, std::vector& NewPa } } + // Our path is all walk, so we will return false which will basically cause us to cancel this path completely and adopt a new one if (OldPathEnd == pBot->BotNavInfo.CurrentPath.end()) { return false; } + // We have reached the next non-walk node in our CURRENT path, now we find the next non-walk path in a prospective NEW path for (NewPathStart = NewPath.begin(); NewPathStart != NewPath.end(); NewPathStart++) { if (NewPathStart->flag != SAMPLE_POLYFLAGS_WALK) @@ -6627,16 +6665,19 @@ bool NAV_MergeAndUpdatePath(AvHAIPlayer* pBot, std::vector& NewPa } } + // New path is all walk, just embrace it and forget the old path if (NewPathStart == NewPath.end()) { return false; } + // The upcoming non-walk node in our current path and the upcoming non-walk node in our new path are different: we have a different path entirely so just get the new path instead if (OldPathEnd->flag != NewPathStart->flag || !vEquals(OldPathEnd->FromLocation, NewPathStart->FromLocation, 16.0f) || !vEquals(OldPathEnd->Location, NewPathStart->Location, 16.0f)) { return false; } + // Now we truncate the current path at the non-walk node, and append the new path from that point OldPathEnd = next(OldPathEnd); NewPathStart = next(NewPathStart); diff --git a/main/source/mod/AvHAIPlayer.cpp b/main/source/mod/AvHAIPlayer.cpp index fa741368..aaa6675e 100644 --- a/main/source/mod/AvHAIPlayer.cpp +++ b/main/source/mod/AvHAIPlayer.cpp @@ -1714,7 +1714,14 @@ void StartNewBotFrame(AvHAIPlayer* pBot) ClearBotInputs(pBot); pBot->CurrentEyePosition = GetPlayerEyePosition(pEdict); - pBot->CurrentFloorPosition = UTIL_GetEntityGroundLocation(pEdict); + Vector NewFloorPosition = UTIL_GetEntityGroundLocation(pEdict); + + if (vDist2DSq(NewFloorPosition, pBot->CurrentFloorPosition) > sqrf(UTIL_MetresToGoldSrcUnits(3.0f))) + { + OnBotTeleport(pBot); + } + + pBot->CurrentFloorPosition = NewFloorPosition; if (vDist3DSq(pBot->BotNavInfo.LastNavMeshCheckPosition, pBot->CurrentFloorPosition) > sqrf(16.0f)) { @@ -1887,23 +1894,7 @@ void EndBotFrame(AvHAIPlayer* pBot) void CustomThink(AvHAIPlayer* pBot) { - if (!IsPlayerAlien(pBot->Edict)) { return; } - - int Enemy = BotGetNextEnemyTarget(pBot); - - if (Enemy > -1) - { - AlienCombatThink(pBot); - } - else - { - edict_t* CommChair = AITAC_GetCommChair(AIMGR_GetEnemyTeam(pBot->Player->GetTeam())); - - if (!FNullEnt(CommChair)) - { - MoveTo(pBot, CommChair->v.origin, MOVESTYLE_NORMAL); - } - } + MoveTo(pBot, UTIL_GetFloorUnderEntity(INDEXENT(1)), MOVESTYLE_NORMAL); } @@ -8896,4 +8887,10 @@ void DEBUG_PrintBotDebugInfo(edict_t* OutputPlayer, AvHAIPlayer* pBot) DEBUG_PrintTaskInfo(OutputPlayer, pBot); DEBUG_PrintCombatInfo(OutputPlayer, pBot); +} + +void OnBotTeleport(AvHAIPlayer* pBot) +{ + ClearBotStuck(pBot); + ClearBotStuckMovement(pBot); } \ No newline at end of file diff --git a/main/source/mod/AvHAIPlayer.h b/main/source/mod/AvHAIPlayer.h index 9233d151..53210ee9 100644 --- a/main/source/mod/AvHAIPlayer.h +++ b/main/source/mod/AvHAIPlayer.h @@ -159,6 +159,8 @@ void AIPlayerHearEnemy(AvHAIPlayer* pBot, edict_t* HeardEnemy, float SoundVolume int BotGetNextEnemyTarget(AvHAIPlayer* pBot); +void OnBotTeleport(AvHAIPlayer* pBot); + AvHMessageID AlienGetDesiredUpgrade(AvHAIPlayer* pBot, HiveTechStatus DesiredTech); AvHAICombatStrategy GetBotCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);