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
This commit is contained in:
RGreenlees 2024-06-25 17:36:13 +01:00 committed by pierow
parent 11512d9d28
commit 4102dabe05
4 changed files with 205 additions and 36 deletions

View file

@ -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;
}

View file

@ -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<bot_path_node>& NewPa
return true;
}
// Start with the bot's current path point
std::vector<bot_path_node>::iterator OldPathStart = (pBot->BotNavInfo.CurrentPath.begin() + pBot->BotNavInfo.CurrentPathPoint);
std::vector<bot_path_node>::iterator OldPathEnd;
std::vector<bot_path_node>::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<bot_path_node>& 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<bot_path_node>& 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);

View file

@ -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);
}

View file

@ -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);