Improved guard behaviour and randomisation

* Improved the bot guard behaviour
* Improved randomisation of bot names and chamber sequences
* Fixed bug where bots would drop hives within range of siege bases
* Fixed bug where requesting turret factories and sentry turrets from the AI commander would place a phase gate instead
This commit is contained in:
RGreenlees 2024-04-14 20:50:26 +01:00 committed by pierow
parent 7d659fb8c2
commit 9484517a15
6 changed files with 171 additions and 114 deletions

View file

@ -2978,12 +2978,15 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
case BUILD_TURRET_FACTORY:
RequiredRes = BALANCE_VAR(kTurretFactoryCost);
StructureToDeploy = STRUCTURE_MARINE_TURRETFACTORY;
break;
case BUILD_TURRET:
RequiredRes = BALANCE_VAR(kSentryCost);
StructureToDeploy = STRUCTURE_MARINE_TURRET;
break;
case BUILD_PHASEGATE:
RequiredRes = BALANCE_VAR(kPhaseGateCost);
StructureToDeploy = STRUCTURE_MARINE_PHASEGATE;
break;
default:
break;
}

View file

@ -16,7 +16,7 @@ std::unordered_map<std::string, TeamSizeDefinitions> TeamSizeMap;
bot_skill BotSkillLevels[4];
AvHMessageID ChamberSequence[3] = { ALIEN_BUILD_DEFENSE_CHAMBER, ALIEN_BUILD_MOVEMENT_CHAMBER, ALIEN_BUILD_SENSORY_CHAMBER };
std::vector<AvHMessageID> ChamberSequence;
string DefaultBotNames[MAX_PLAYERS] = { "MrRobot",
"Wall-E",
@ -173,6 +173,7 @@ void CONFIG_PopulateBotNames()
if (BotNames.size() > 2)
{
auto rng = std::default_random_engine{};
rng.seed(time(0));
std::shuffle(begin(BotNames), end(BotNames), rng);
}
@ -187,6 +188,7 @@ void CONFIG_PopulateBotNames()
if (DefaultNames.size() > 2)
{
auto rng = std::default_random_engine{};
rng.seed(time(0));
std::shuffle(begin(DefaultNames), end(DefaultNames), rng);
}
@ -255,6 +257,15 @@ void CONFIG_ParseConfigFile()
BotSkillLevels[3].alien_bot_motion_tracking_skill = 1.0f;
BotSkillLevels[3].alien_bot_view_speed = 2.0f;
ChamberSequence.clear();
ChamberSequence.push_back(ALIEN_BUILD_DEFENSE_CHAMBER);
ChamberSequence.push_back(ALIEN_BUILD_MOVEMENT_CHAMBER);
ChamberSequence.push_back(ALIEN_BUILD_SENSORY_CHAMBER);
std::srand(time(0));
auto rng = std::default_random_engine{};
rng.seed(time(0));
std::shuffle(std::begin(ChamberSequence), std::end(ChamberSequence), rng);
string BotConfigFile = string(getModDirectory()) + "/nsbots.ini";
@ -503,12 +514,6 @@ void CONFIG_ParseConfigFile()
if (!stricmp(keyChar, "ChamberSequence"))
{
AvHMessageID HiveOneTech = MESSAGE_NULL;
AvHMessageID HiveTwoTech = MESSAGE_NULL;
AvHMessageID HiveThreeTech = MESSAGE_NULL;
std::vector<AvHMessageID> AvailableTechs = { ALIEN_BUILD_DEFENSE_CHAMBER, ALIEN_BUILD_MOVEMENT_CHAMBER, ALIEN_BUILD_SENSORY_CHAMBER };
auto firstTechDelimiter = value.find("/");
if (firstTechDelimiter == std::string::npos)
@ -533,104 +538,64 @@ void CONFIG_ParseConfigFile()
const char* SecondTechChar = SecondTech.c_str();
const char* ThirdTechChar = ThirdTech.c_str();
if (!stricmp(FirstTechChar, "movement"))
if (!stricmp(FirstTechChar, "defense"))
{
HiveOneTech = ALIEN_BUILD_MOVEMENT_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER), AvailableTechs.end());
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_DEFENSE_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[0], ChamberSequence[Index]);
}
else if (!stricmp(FirstTechChar, "defense"))
else if (!stricmp(FirstTechChar, "movement"))
{
HiveOneTech = ALIEN_BUILD_DEFENSE_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER), AvailableTechs.end());
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_MOVEMENT_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[0], ChamberSequence[Index]);
}
else if (!stricmp(FirstTechChar, "sensory"))
{
HiveOneTech = ALIEN_BUILD_SENSORY_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER), AvailableTechs.end());
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_SENSORY_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[0], ChamberSequence[Index]);
}
if (!stricmp(SecondTechChar, "movement"))
if (!stricmp(SecondTechChar, "defense"))
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER) != AvailableTechs.end())
{
HiveTwoTech = ALIEN_BUILD_MOVEMENT_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER), AvailableTechs.end());
}
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_DEFENSE_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[1], ChamberSequence[Index]);
}
else if (!stricmp(SecondTechChar, "defense"))
else if (!stricmp(SecondTechChar, "movement"))
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER) != AvailableTechs.end())
{
HiveTwoTech = ALIEN_BUILD_DEFENSE_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER), AvailableTechs.end());
}
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_MOVEMENT_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[1], ChamberSequence[Index]);
}
else if (!stricmp(SecondTechChar, "sensory"))
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER) != AvailableTechs.end())
{
HiveTwoTech = ALIEN_BUILD_SENSORY_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER), AvailableTechs.end());
}
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_SENSORY_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[1], ChamberSequence[Index]);
}
if (!stricmp(ThirdTechChar, "movement"))
if (!stricmp(ThirdTechChar, "defense"))
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER) != AvailableTechs.end())
{
HiveThreeTech = ALIEN_BUILD_MOVEMENT_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER), AvailableTechs.end());
}
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_DEFENSE_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[2], ChamberSequence[Index]);
}
else if (!stricmp(ThirdTechChar, "defense"))
else if (!stricmp(ThirdTechChar, "movement"))
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER) != AvailableTechs.end())
{
HiveThreeTech = ALIEN_BUILD_DEFENSE_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER), AvailableTechs.end());
}
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_MOVEMENT_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[2], ChamberSequence[Index]);
}
else if (!stricmp(ThirdTechChar, "sensory"))
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER) != AvailableTechs.end())
{
HiveThreeTech = ALIEN_BUILD_SENSORY_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER), AvailableTechs.end());
}
auto Element = std::find(ChamberSequence.begin(), ChamberSequence.end(), ALIEN_BUILD_SENSORY_CHAMBER);
int Index = Element - ChamberSequence.begin();
std::swap(ChamberSequence[2], ChamberSequence[Index]);
}
if (HiveOneTech == MESSAGE_NULL)
{
int random = rand() % AvailableTechs.size();
HiveOneTech = AvailableTechs[random];
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), HiveOneTech), AvailableTechs.end());
}
if (HiveTwoTech == MESSAGE_NULL)
{
int random = rand() % AvailableTechs.size();
HiveTwoTech = AvailableTechs[random];
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), HiveTwoTech), AvailableTechs.end());
}
if (HiveThreeTech == MESSAGE_NULL)
{
int random = rand() % AvailableTechs.size();
HiveThreeTech = AvailableTechs[random];
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), HiveTwoTech), AvailableTechs.end());
}
ChamberSequence[0] = HiveOneTech;
ChamberSequence[1] = HiveTwoTech;
ChamberSequence[2] = HiveThreeTech;
continue;
}
}

View file

@ -322,7 +322,7 @@ typedef struct _BOT_GUARD_INFO
{
Vector GuardLocation = g_vecZero; // What position are we guarding?
Vector GuardStandPosition = g_vecZero; // Where the bot should stand to guard position (moves around a bit)
Vector GuardPoints[8]; // All potential areas to watch that an enemy could approach from
std::vector<Vector> GuardPoints; // All potential areas to watch that an enemy could approach from
int NumGuardPoints = 0; // How many watch areas there are for the current location
Vector GuardLookLocation = g_vecZero; // Which area are we currently watching?
float GuardStartLookTime = 0.0f; // When did we start watching the current area?

View file

@ -4711,23 +4711,25 @@ bool AITAC_ShouldBotBuildHive(AvHAIPlayer* pBot, AvHAIHiveDefinition** EligibleH
// Must be an empty hive
DeployableSearchFilter EnemyFortificationsFilter;
EnemyFortificationsFilter.DeployableTeam = EnemyTeam;
EnemyFortificationsFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
EnemyFortificationsFilter.DeployableTeam = EnemyTeam;
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{
EnemyFortificationsFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
EnemyFortificationsFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
EnemyFortificationsFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_SIEGETURRET);
EnemyFortificationsFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyFortificationsFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED; // This is important to prevent exploiting the AI. Those structures have to be built first!
}
else
{
EnemyFortificationsFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
EnemyFortificationsFilter.DeployableTypes = (STRUCTURE_ALIEN_OFFENCECHAMBER);
}
// Enemy have built some stuff, wait until it's clear before building
if (AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyFortificationsFilter)) { continue; }
// Should be clear to drop dat hive!
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisHive->FloorLocation);

View file

@ -1192,7 +1192,7 @@ void BotProgressPickupTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
AvHAIWeapon CurrentPrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
if (CurrentPrimaryWeapon != WEAPON_NONE && CurrentPrimaryWeapon != UTIL_GetWeaponTypeFromEdict(Task->TaskTarget))
if (CurrentPrimaryWeapon != WEAPON_INVALID && CurrentPrimaryWeapon != UTIL_GetWeaponTypeFromEdict(Task->TaskTarget))
{
if (GetPlayerCurrentWeapon(pBot->Player) != CurrentPrimaryWeapon)
{
@ -2821,9 +2821,9 @@ void BotGuardLocation(AvHAIPlayer* pBot, const Vector GuardLocation)
{
float DistFromGuardLocation = vDist2DSq(pBot->Edict->v.origin, GuardLocation);
if (DistFromGuardLocation > sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
if (DistFromGuardLocation > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
{
memset(&pBot->GuardInfo, 0, sizeof(AvHAIGuardInfo));
UTIL_ClearGuardInfo(pBot);
MoveTo(pBot, GuardLocation, MOVESTYLE_NORMAL, UTIL_MetresToGoldSrcUnits(10.0f));
return;
}
@ -2836,15 +2836,38 @@ void BotGuardLocation(AvHAIPlayer* pBot, const Vector GuardLocation)
if (gpGlobals->time > pBot->GuardInfo.ThisGuardLookTime)
{
if (pBot->GuardInfo.NumGuardPoints > 0)
if (pBot->GuardInfo.GuardPoints.size() > 0)
{
int NewGuardLookIndex = irandrange(0, (pBot->GuardInfo.NumGuardPoints - 1));
if (pBot->GuardInfo.GuardPoints.size() == 1)
{
pBot->GuardInfo.GuardLookLocation = (*pBot->GuardInfo.GuardPoints.begin());
}
else
{
Vector NewLookPoint = pBot->GuardInfo.GuardLookLocation;
int HighestScore = 0.0f;
for (auto it = pBot->GuardInfo.GuardPoints.begin(); it != pBot->GuardInfo.GuardPoints.end(); it++)
{
if (vEquals((*it), pBot->GuardInfo.GuardLookLocation)) { continue; }
float thisScore = frandrange(0.01f, 1.0f);
if (thisScore > HighestScore)
{
NewLookPoint = (*it);
HighestScore = thisScore;
}
}
pBot->GuardInfo.GuardLookLocation = NewLookPoint;
}
pBot->GuardInfo.GuardLookLocation = pBot->GuardInfo.GuardPoints[NewGuardLookIndex];
}
else
{
pBot->GuardInfo.GuardLookLocation = UTIL_GetRandomPointOnNavmeshInRadius(BaseNavProfiles[SKULK_BASE_NAV_PROFILE], pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
pBot->GuardInfo.GuardLookLocation = UTIL_GetRandomPointOnNavmeshInRadius(BaseNavProfiles[SKULK_BASE_NAV_PROFILE], pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(3.0f));
pBot->GuardInfo.GuardLookLocation.z = pBot->CurrentEyePosition.z;
}
@ -2854,7 +2877,7 @@ void BotGuardLocation(AvHAIPlayer* pBot, const Vector GuardLocation)
if (gpGlobals->time > pBot->GuardInfo.ThisGuardStandTime)
{
pBot->GuardInfo.GuardStandPosition = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, GuardLocation, UTIL_MetresToGoldSrcUnits(5.0f));
pBot->GuardInfo.GuardStandPosition = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, GuardLocation, UTIL_MetresToGoldSrcUnits(3.0f));
pBot->GuardInfo.ThisGuardStandTime = gpGlobals->time + frandrange(5.0f, 10.0f);
}
@ -2878,7 +2901,14 @@ void BotGuardLocation(AvHAIPlayer* pBot, const Vector GuardLocation)
void UTIL_ClearGuardInfo(AvHAIPlayer* pBot)
{
memset(&pBot->GuardInfo, 0, sizeof(AvHAIGuardInfo));
pBot->GuardInfo.GuardLocation = ZERO_VECTOR;
pBot->GuardInfo.GuardLookLocation = ZERO_VECTOR;
pBot->GuardInfo.GuardPoints.clear();
pBot->GuardInfo.GuardStandPosition = ZERO_VECTOR;
pBot->GuardInfo.GuardStartLookTime = 0.0f;
pBot->GuardInfo.GuardStartStandTime = 0.0f;
pBot->GuardInfo.ThisGuardLookTime = 0.0f;
pBot->GuardInfo.ThisGuardStandTime = 0.0f;
}
void AITASK_GenerateGuardWatchPoints(AvHAIPlayer* pBot, const Vector& GuardLocation)
@ -2903,51 +2933,107 @@ void AITASK_GenerateGuardWatchPoints(AvHAIPlayer* pBot, const Vector& GuardLocat
{
const AvHAIHiveDefinition* ThisHive = (*it);
if (UTIL_QuickTrace(pEdict, GuardLocation, ThisHive->Location)) { continue; }
if (UTIL_QuickTrace(pEdict, GuardLocation + Vector(0.0f, 0.0f, 10.0f), ThisHive->Location) || vDist2DSq(GuardLocation, ThisHive->Location) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) { continue; }
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, ThisHive->FloorLocation, GuardLocation, path, 500.0f);
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, GuardLocation, ThisHive->FloorLocation, path, 500.0f);
if (dtStatusSucceed(SearchResult) && path.size() > 0)
{
Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location);
Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f);
Vector FurthestPoint = UTIL_GetFurthestVisiblePointOnPath(GuardLocation + Vector(0.0f, 0.0f, 64.0f), path, true);
FurthestPoint.z += 64.0f;
ProspectiveNewGuardLoc.z = prev(prev(path.end()))->Location.z;
Vector LookDir = UTIL_GetVectorNormal(FurthestPoint - GuardLocation);
pBot->GuardInfo.GuardPoints[pBot->GuardInfo.NumGuardPoints++] = ProspectiveNewGuardLoc;
bool bShouldAdd = true;
for (auto it = pBot->GuardInfo.GuardPoints.begin(); it != pBot->GuardInfo.GuardPoints.end(); it++)
{
Vector ThisLookDir = UTIL_GetVectorNormal((*it) - GuardLocation);
if (UTIL_GetDotProduct(ThisLookDir, LookDir) > 0.8f)
{
bShouldAdd = false;
break;
}
}
if (bShouldAdd)
{
pBot->GuardInfo.GuardPoints.push_back(FurthestPoint);
}
}
}
if (AIMGR_GetEnemyTeamType(pBot->Player->GetTeam()) == AVH_CLASS_TYPE_MARINE)
{
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, AITAC_GetTeamStartingLocation(EnemyTeam), GuardLocation, path, 500.0f);
if (dtStatusSucceed(SearchResult) && path.size() > 0)
if (!UTIL_QuickTrace(nullptr, GuardLocation, AITAC_GetTeamStartingLocation(EnemyTeam)))
{
Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location);
Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f);
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, GuardLocation, AITAC_GetTeamStartingLocation(EnemyTeam), path, 500.0f);
ProspectiveNewGuardLoc.z = prev(prev(path.end()))->Location.z;
if (dtStatusSucceed(SearchResult) && path.size() > 0)
{
Vector FurthestPoint = UTIL_GetFurthestVisiblePointOnPath(GuardLocation + Vector(0.0f, 0.0f, 64.0f), path, true);
FurthestPoint.z += 64.0f;
pBot->GuardInfo.GuardPoints[pBot->GuardInfo.NumGuardPoints++] = ProspectiveNewGuardLoc;
Vector LookDir = UTIL_GetVectorNormal(FurthestPoint - GuardLocation);
bool bShouldAdd = true;
for (auto it = pBot->GuardInfo.GuardPoints.begin(); it != pBot->GuardInfo.GuardPoints.end(); it++)
{
Vector ThisLookDir = UTIL_GetVectorNormal((*it) - GuardLocation);
if (UTIL_GetDotProduct(ThisLookDir, LookDir) > 0.8f)
{
bShouldAdd = false;
break;
}
}
if (bShouldAdd)
{
pBot->GuardInfo.GuardPoints.push_back(FurthestPoint);
}
}
}
}
if (AIMGR_GetTeamType(pBot->Player->GetTeam()) == AVH_CLASS_TYPE_MARINE)
{
if (vDist2DSq(GuardLocation, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam())) > sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
if (!UTIL_QuickTrace(pEdict, GuardLocation, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam())))
{
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), GuardLocation, path, 500.0f);
if (dtStatusSucceed(SearchResult) && path.size() > 0)
if (vDist2DSq(GuardLocation, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam())) > sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
{
Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location);
Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f);
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, GuardLocation, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), path, 500.0f);
ProspectiveNewGuardLoc.z = prev(prev(path.end()))->Location.z;
if (dtStatusSucceed(SearchResult) && path.size() > 0)
{
Vector FurthestPoint = UTIL_GetFurthestVisiblePointOnPath(GuardLocation + Vector(0.0f, 0.0f, 64.0f), path, true);
FurthestPoint.z += 64.0f;
pBot->GuardInfo.GuardPoints[pBot->GuardInfo.NumGuardPoints++] = ProspectiveNewGuardLoc;
Vector LookDir = UTIL_GetVectorNormal(FurthestPoint - GuardLocation);
bool bShouldAdd = true;
for (auto it = pBot->GuardInfo.GuardPoints.begin(); it != pBot->GuardInfo.GuardPoints.end(); it++)
{
Vector ThisLookDir = UTIL_GetVectorNormal((*it) - GuardLocation);
if (UTIL_GetDotProduct(ThisLookDir, LookDir) > 0.8f)
{
bShouldAdd = false;
break;
}
}
if (bShouldAdd)
{
pBot->GuardInfo.GuardPoints.push_back(FurthestPoint);
}
}
}
}
}

View file

@ -118,4 +118,5 @@ void BotAlienHealTarget(AvHAIPlayer* pBot, edict_t* HealTarget);
void RegisterBotAlienBuildAttempt(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, Vector PlacementLocation, AvHAIDeployableStructureType DesiredStructure);
#endif