Commander + nav improvements

This commit is contained in:
RGreenlees 2024-01-31 20:52:03 +00:00 committed by pierow
parent 7dcd3ca1d8
commit eacf2b174f
10 changed files with 477 additions and 185 deletions

View file

@ -1089,6 +1089,96 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
return false;
}
bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot)
{
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
int NumDesiredWelders = 1;
int NumTeamWelders = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_WELDER);
vector<AvHAIResourceNode*> AllNodes = AITAC_GetAllResourceNodes();
for (auto it = AllNodes.begin(); it != AllNodes.end(); it++)
{
AvHAIResourceNode* ThisNode = (*it);
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisNode->TeamAReachabilityFlags : ThisNode->TeamBReachabilityFlags;
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
{
NumDesiredWelders++;
break;
}
}
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisHive->TeamAReachabilityFlags : ThisHive->TeamBReachabilityFlags;
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
{
NumDesiredWelders++;
break;
}
}
NumDesiredWelders = imini(NumDesiredWelders, (AIMGR_GetNumPlayersOnTeam(CommanderTeam) / 2));
if (NumTeamWelders < NumDesiredWelders)
{
DeployableSearchFilter ArmouryFilter;
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
ArmouryFilter.DeployableTeam = CommanderTeam;
ArmouryFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
ArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &ArmouryFilter);
if (NearestArmoury)
{
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_WELDER, DeployLocation);
return bSuccess;
}
}
// Don't drop stuff if we badly need resource nodes
if (AICOMM_ShouldCommanderPrioritiseNodes(pBot)) { return false; }
if (pBot->Player->GetResources() > (30 + BALANCE_VAR(kShotgunCost)))
{
int NumDesiredShotguns = (int)ceilf(AIMGR_GetNumPlayersOnTeam(CommanderTeam) * 0.33f);
int NumShottysInPlay = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_SHOTGUN);
if (NumShottysInPlay < NumDesiredShotguns)
{
DeployableSearchFilter ArmouryFilter;
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
ArmouryFilter.DeployableTeam = CommanderTeam;
ArmouryFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
ArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &ArmouryFilter);
if (NearestArmoury)
{
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_SHOTGUN, DeployLocation);
return bSuccess;
}
}
}
return false;
}
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot)
{
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
@ -1776,66 +1866,8 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
}
// We didn't find any requests outstanding, see if we want to pro-actively drop stuff for our team
if (!NextRequest)
{
int NumDesiredWelders = 1;
int NumTeamWelders = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_WELDER);
vector<AvHAIResourceNode*> AllNodes = AITAC_GetAllResourceNodes();
for (auto it = AllNodes.begin(); it != AllNodes.end(); it++)
{
AvHAIResourceNode* ThisNode = (*it);
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisNode->TeamAReachabilityFlags : ThisNode->TeamBReachabilityFlags;
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
{
NumDesiredWelders++;
break;
}
}
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisHive->TeamAReachabilityFlags : ThisHive->TeamBReachabilityFlags;
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
{
NumDesiredWelders++;
break;
}
}
NumDesiredWelders = imini(NumDesiredWelders, (AIMGR_GetNumPlayersOnTeam(CommanderTeam) / 2));
if (NumTeamWelders < NumDesiredWelders)
{
DeployableSearchFilter ArmouryFilter;
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
ArmouryFilter.DeployableTeam = CommanderTeam;
ArmouryFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
ArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &ArmouryFilter);
if (NearestArmoury)
{
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_WELDER, DeployLocation);
return bSuccess;
}
}
return false;
}
// We didn't find any unresponded requests outstanding
if (!NextRequest) { return false; }
edict_t* Requestor = NextRequest->Requestor;
@ -2050,6 +2082,7 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot)
if (AICOMM_CheckForNextSupportAction(pBot)) { return; }
if (AICOMM_CheckForNextBuildAction(pBot)) { return; }
if (AICOMM_CheckForNextResearchAction(pBot)) { return; }
if (AICOMM_CheckForNextSupplyAction(pBot)) { return; }
}
bool AICOMM_IsCommanderActionValid(AvHAIPlayer* pBot, commander_action* Action)

View file

@ -35,10 +35,13 @@ bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* O
void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Order);
void AICOMM_ClearAction(commander_action* Action);
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot);
void AICOMM_SetDropHealthAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
void AICOMM_SetDropAmmoAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
void AICOMM_SetDeployStructureAction(AvHAIPlayer* pBot, commander_action* Action, AvHAIDeployableStructureType StructureToBuild, const Vector Location, bool bIsUrgent);

View file

@ -10,7 +10,7 @@
#include "../AvHEntities.h"
static const float commander_action_cooldown = 1.0f;
static const float min_request_spam_time = 5.0f;
static const float min_request_spam_time = 10.0f;
constexpr auto MAX_AI_PATH_SIZE = 512; // Maximum number of points allowed in a path (this should be enough for any sized map)
static const int MAX_NAV_MESHES = 8; // Max number of nav meshes allowed. Currently 3 are used (one for building placement, one for the onos, and a regular one for everyone else)

View file

@ -1653,7 +1653,7 @@ dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector From
{
NextPathNode.Location = hit.vecEndPos;
if (CurrFlags != SAMPLE_POLYFLAGS_JUMP)
if (CurrFlags != SAMPLE_POLYFLAGS_JUMP && CurrFlags != SAMPLE_POLYFLAGS_WALLCLIMB)
{
NextPathNode.Location.z += 20.0f;
}
@ -1664,7 +1664,8 @@ dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector From
if (CurrFlags == SAMPLE_POLYFLAGS_WALLCLIMB || CurrFlags == SAMPLE_POLYFLAGS_LADDER)
{
float NewRequiredZ = UTIL_FindZHeightForWallClimb(path.back().Location, NextPathNode.Location, head_hull);
NextPathNode.requiredZ = fmaxf(NewRequiredZ, NextPathNode.Location.z);
//NextPathNode.requiredZ = fmaxf(NewRequiredZ, NextPathNode.Location.z);
NextPathNode.requiredZ = NewRequiredZ;
if (CurrFlags == SAMPLE_POLYFLAGS_LADDER)
{
@ -1813,7 +1814,7 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
NextPathNode.Location = AdjustPointForPathfinding(NextPathNode.Location);
if (CurrFlags != SAMPLE_POLYFLAGS_JUMP || NextPathNode.FromLocation.z > NextPathNode.Location.z)
if ((CurrFlags != SAMPLE_POLYFLAGS_JUMP && CurrFlags != SAMPLE_POLYFLAGS_WALLCLIMB) || NextPathNode.FromLocation.z > NextPathNode.Location.z)
{
NextPathNode.Location.z += 17.0f;
@ -1951,6 +1952,11 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
return true;
}
if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end())
{
return true;
}
Vector CurrentPos = (pBot->BotNavInfo.IsOnGround) ? pBot->Edict->v.origin : pBot->CurrentFloorPosition;
edict_t* pEdict = pBot->Edict;
@ -1976,6 +1982,7 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
switch (CurrentNavFlag)
{
case SAMPLE_POLYFLAGS_WALK:
{
if (!bIsAtFinalPathPoint)
{
return (bAtOrPastDestination || (vDist2D(pEdict->v.origin, MoveTo) <= 8.0f && (fabs(pBot->CurrentFloorPosition.z - MoveTo.z) < 50.0f)));
@ -1984,12 +1991,14 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
{
return ((vDist2D(pEdict->v.origin, MoveTo) < playerRadius && bDestIsDirectlyReachable) || bAtOrPastDestination);
}
}
case SAMPLE_POLYFLAGS_BLOCKED:
case SAMPLE_POLYFLAGS_TEAM1STRUCTURE:
case SAMPLE_POLYFLAGS_TEAM2STRUCTURE:
return bAtOrPastDestination;
case SAMPLE_POLYFLAGS_FALL:
case SAMPLE_POLYFLAGS_JUMP:
{
if (!bIsAtFinalPathPoint)
{
Vector thisMoveDir = UTIL_GetVectorNormal2D(MoveTo - MoveFrom);
@ -2010,9 +2019,11 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
{
return (vDist2D(pEdict->v.origin, MoveTo) <= playerRadius && (pEdict->v.origin.z - MoveTo.z) < 50.0f && pBot->BotNavInfo.IsOnGround);
}
}
case SAMPLE_POLYFLAGS_WALLCLIMB:
return (bAtOrPastDestination && fabs(pBot->CollisionHullTopLocation.z - MoveTo.z) < fabs(MoveFrom.z - MoveTo.z));
return bAtOrPastDestination && fabs(pEdict->v.origin.z - MoveTo.z) < 50.0f;
case SAMPLE_POLYFLAGS_LADDER:
{
if (MoveTo.z > MoveFrom.z)
{
return ((BotPoly == DestinationPoly) && UTIL_QuickTrace(pEdict, pEdict->v.origin, MoveTo));
@ -2021,6 +2032,7 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
{
return (fabs(pBot->CollisionHullBottomLocation.z - MoveTo.z) < 50.0f);
}
}
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
return (vDist2DSq(pBot->CurrentFloorPosition, MoveTo) < sqrf(32.0f));
@ -4045,6 +4057,7 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
if (VelocityDot > 0.7f)
{
// This was causing issues in tight areas, rethink this
//BotJump(pBot);
}
}
@ -5745,6 +5758,12 @@ void BotFollowSwimPath(AvHAIPlayer* pBot)
return;
}
if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end())
{
ClearBotPath(pBot);
return;
}
nav_status* BotNavInfo = &pBot->BotNavInfo;
edict_t* pEdict = pBot->Edict;
@ -5826,6 +5845,12 @@ void BotFollowPath(AvHAIPlayer* pBot)
return;
}
if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end())
{
ClearBotPath(pBot);
return;
}
nav_status* BotNavInfo = &pBot->BotNavInfo;
edict_t* pEdict = pBot->Edict;
@ -8136,6 +8161,8 @@ void NAV_SetBreakMovementTask(AvHAIPlayer* pBot, edict_t* EntityToBreak, DoorTri
void NAV_SetWeldMovementTask(AvHAIPlayer* pBot, edict_t* EntityToWeld, DoorTrigger* TriggerToActivate)
{
if (IsPlayerAlien(pBot->Edict)) { return; }
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_WELD && MoveTask->TaskTarget == EntityToWeld) { return; }

View file

@ -2497,6 +2497,7 @@ void AIPlayerNSMarineThink(AvHAIPlayer* pBot)
AIPlayerSetPrimaryMarineTask(pBot, &pBot->PrimaryBotTask);
AIPlayerSetSecondaryMarineTask(pBot, &pBot->SecondaryBotTask);
AIPlayerSetWantsAndNeedsMarineTask(pBot, &pBot->WantsAndNeedsTask);
}
pBot->CurrentTask = AIPlayerGetNextTask(pBot);
@ -3162,6 +3163,107 @@ void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask*
}
void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (Task->TaskType == TASK_RESUPPLY || Task->TaskType == TASK_GET_HEALTH || Task->TaskType == TASK_GET_AMMO) { return; }
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
bool bNeedsHealth = pBot->Edict->v.health < (pBot->Edict->v.max_health * 0.9f);
bool bNeedsAmmo = UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < (UTIL_GetPlayerPrimaryMaxAmmoReserve(pBot->Player) * 0.9f);
if (!bNeedsHealth && !bNeedsAmmo)
{
AITASK_ClearBotTask(pBot, Task);
return;
}
bool bTaskIsUrgent = (pBot->Edict->v.health < (pBot->Edict->v.max_health * 0.7f)) || (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < UTIL_GetPlayerPrimaryWeaponMaxClipSize(pBot->Player));
float SearchRadius = (bTaskIsUrgent) ? UTIL_MetresToGoldSrcUnits(5.0f) : UTIL_MetresToGoldSrcUnits(10.0f);
AvHAIDroppedItem* NearestHealthPack = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_HEALTHPACK, BotTeam, pBot->BotNavInfo.NavProfile.ReachabilityFlag, 0.0f, SearchRadius, false);
AvHAIDroppedItem* NearestAmmoPack = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_AMMO, BotTeam, pBot->BotNavInfo.NavProfile.ReachabilityFlag, 0.0f, SearchRadius, false);
if (bNeedsHealth && NearestHealthPack)
{
AITASK_SetPickupTask(pBot, Task, NearestHealthPack->edict, bTaskIsUrgent);
return;
}
if (bNeedsAmmo && NearestAmmoPack)
{
AITASK_SetPickupTask(pBot, Task, NearestAmmoPack->edict, bTaskIsUrgent);
return;
}
DeployableSearchFilter NearestArmouryFilter;
NearestArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
NearestArmouryFilter.DeployableTeam = pBot->Player->GetTeam();
NearestArmouryFilter.ReachabilityTeam = pBot->Player->GetTeam();
NearestArmouryFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
NearestArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
NearestArmouryFilter.MaxSearchRadius = (bTaskIsUrgent) ? UTIL_MetresToGoldSrcUnits(20.0f) : UTIL_MetresToGoldSrcUnits(5.0f);
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &NearestArmouryFilter);
// If we don't really need health or ammo and there isn't a handy armoury nearby, just get on with things
if (!NearestArmoury && !bTaskIsUrgent)
{
AITASK_ClearBotTask(pBot, Task);
return;
}
// We really need some health or ammo, either hit the armoury, or ask for a resupply
if (NearestArmoury)
{
Task->TaskType = TASK_RESUPPLY;
Task->bTaskIsUrgent = true;
Task->TaskLocation = NearestArmoury->Location;
Task->TaskTarget = NearestArmoury->edict;
return;
}
else
{
AITASK_ClearBotTask(pBot, Task);
if (bNeedsHealth)
{
AIPlayerRequestHealth(pBot);
return;
}
if (bNeedsAmmo)
{
AIPlayerRequestAmmo(pBot);
return;
}
}
}
void AIPlayerRequestHealth(AvHAIPlayer* pBot)
{
if (gpGlobals->time - pBot->LastRequestTime < min_request_spam_time) { return; }
pBot->Impulse = SAYING_4;
pBot->LastRequestTime = gpGlobals->time;
}
void AIPlayerRequestAmmo(AvHAIPlayer* pBot)
{
if (gpGlobals->time - pBot->LastRequestTime < min_request_spam_time) { return; }
pBot->Impulse = SAYING_5;
pBot->LastRequestTime = gpGlobals->time;
}
void AIPlayerRequestOrder(AvHAIPlayer* pBot)
{
if (gpGlobals->time - pBot->LastRequestTime < min_request_spam_time) { return; }
pBot->Impulse = SAYING_6;
pBot->LastRequestTime = gpGlobals->time;
}
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
// Find any nearby unbuilt structures
@ -3205,8 +3307,51 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
bool AIPlayerMustFinishCurrentTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (!Task || Task->TaskType == TASK_NONE) { return false; }
if (!AITASK_IsTaskStillValid(pBot, Task)) { return false; }
if (IsPlayerGorge(pBot->Edict))
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING) { return true; }
// If we're already capping a node, are at the node and there is an unfinished tower on there, then finish the job and don't move on yet
if (Task->TaskType == TASK_CAP_RESNODE)
{
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
if (ResNodeIndex && ResNodeIndex->OwningTeam == BotTeam)
{
if (!FNullEnt(ResNodeIndex->ActiveTowerEntity) && !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
{
return true;
}
}
}
}
return false;
}
void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
{
if (!pBot->CurrentTask) { pBot->CurrentTask = &pBot->PrimaryBotTask; }
if (pBot->CurrentEnemy > -1)
{
if (AlienCombatThink(pBot)) { return; }
}
if (AIPlayerMustFinishCurrentTask(pBot, pBot->CurrentTask))
{
BotProgressTask(pBot, pBot->CurrentTask);
return;
}
UpdateAIAlienPlayerNSRole(pBot);
AvHAIHiveDefinition* HiveToBuild = nullptr;
@ -3217,8 +3362,6 @@ void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
return;
}
if (!pBot->CurrentTask) { pBot->CurrentTask = &pBot->PrimaryBotTask; }
if (gpGlobals->time >= pBot->BotNextTaskEvaluationTime)
{
pBot->BotNextTaskEvaluationTime = gpGlobals->time + frandrange(0.2f, 0.5f);
@ -3231,11 +3374,6 @@ void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
pBot->CurrentTask = AIPlayerGetNextTask(pBot);
if (pBot->CurrentEnemy > -1)
{
if (AlienCombatThink(pBot)) { return; }
}
if (pBot->LastCombatTime > 5.0f)
{
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_DEFENCE) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_DEFENCE))
@ -3594,7 +3732,7 @@ void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
DeployableSearchFilter ExistingReinforcementFilter;
ExistingReinforcementFilter.DeployableTeam = BotTeam;
ExistingReinforcementFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
ExistingReinforcementFilter.DeployableTypes = SEARCH_ALL_ALIEN_STRUCTURES;
ExistingReinforcementFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
vector<AvHAIBuildableStructure*> AllReinforcingStructures = AITAC_FindAllDeployables(ThisHive->FloorLocation, &ExistingReinforcementFilter);
@ -3713,6 +3851,7 @@ void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
if (!FNullEnt(TowerToReinforce))
{
AITASK_SetReinforceStructureTask(pBot, Task, TowerToReinforce, false);
return;
}
}
@ -3723,6 +3862,23 @@ void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
AvHAIResourceNode* NodeToCap = nullptr;
// If we're already capping a node, are at the node and there is an unfinished tower on there, then finish the job and don't move on yet
if (Task->TaskType == TASK_CAP_RESNODE)
{
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
if (ResNodeIndex && ResNodeIndex->OwningTeam == BotTeam)
{
if (!FNullEnt(ResNodeIndex->ActiveTowerEntity) && !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
{
if (vDist2DSq(pBot->Edict->v.origin, ResNodeIndex->Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
{
return;
}
}
}
}
float ResourcesRequired = BALANCE_VAR(kResourceTowerCost);
if (!IsPlayerGorge(pBot->Edict))
@ -3898,6 +4054,46 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
if (IsPlayerGorge(pBot->Edict) && gpGlobals->time - pBot->LastCombatTime > 5.0f)
{
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_ONE);
return;
}
if (pBot->Player->GetResources() >= BALANCE_VAR(kOnosCost))
{
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
if (NumOnos < 2)
{
const AvHAIHiveDefinition* NearestHive = AITAC_GetNearestTeamHive(BotTeam, pBot->Edict->v.origin, true);
if (NearestHive)
{
AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEntity->edict(), ALIEN_LIFEFORM_FIVE, true);
return;
}
}
}
if (pBot->Player->GetResources() >= BALANCE_VAR(kFadeCost))
{
int NumFades = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER4, pBot->Edict);
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
if (NumFades < 2 || NumOnos >= 2)
{
const AvHAIHiveDefinition* NearestHive = AITAC_GetNearestTeamHive(BotTeam, pBot->Edict->v.origin, true);
if (NearestHive)
{
AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEntity->edict(), ALIEN_LIFEFORM_FOUR, true);
return;
}
}
}
Vector EnemyBaseLocation = AITAC_GetTeamStartingLocation(EnemyTeam);
AvHAIHiveDefinition* HiveToGuard = nullptr;
@ -3923,7 +4119,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
EnemyStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
if (IsPlayerSkulk(pBot->Edict) || IsPlayerLerk(pBot->Edict))
{
EnemyStuffFilter.ExcludeStatusFlags |= STRUCTURE_STATUS_RECYCLING;
EnemyStuffFilter.ExcludeStatusFlags |= STRUCTURE_STATUS_ELECTRIFIED;
}
}
else
@ -3931,6 +4127,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
EnemyStuffFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
}
bool bShouldGuardEmptyHive = pBot->Player->GetUser3() < AVH_USER3_ALIEN_PLAYER3;
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
@ -3951,6 +4149,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
}
else
{
if (!bShouldGuardEmptyHive) { continue; }
DeployableSearchFilter FriendlyStuffFilter;
FriendlyStuffFilter.DeployableTeam = BotTeam;
@ -3959,6 +4159,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
if (AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &FriendlyStuffFilter)) { continue; }
if (AITAC_GetNumPlayersOfTeamAndClassInArea(BotTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(20.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2) == 0) { continue; }
bool bNeedsExtraGuards = true;
int NumGuards = 0;
@ -3999,126 +4201,75 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
MaxGuardDist = ThisDist;
}
}
else
{
// The purpose of this is to ensure we only guard one empty hive at a time, otherwise all the assault bots will be sitting around in empty hives and not pressuring marines
// If we have an empty hive already being guarded, then this bool will ensure the bot doesn't go guard an empty hive even if there are 2
bShouldGuardEmptyHive = false;
}
}
}
// Favour attacking an enemy outpost over guarding an empty hive if fade or onos
if (pBot->Player->GetUser3() > AVH_USER3_ALIEN_PLAYER3)
if (bShouldGuardEmptyHive && HiveToGuard)
{
if (HiveToSecure)
{
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
vector<AvHAIBuildableStructure*> AllEnemyThings = AITAC_FindAllDeployables(HiveToSecure->FloorLocation, &EnemyStuffFilter);
AvHAIBuildableStructure* StructureToAttack = nullptr;
for (auto it = AllEnemyThings.begin(); it != AllEnemyThings.end(); it++)
{
AvHAIBuildableStructure* ThisStructure = (*it);
// First prioritise phase gates or alien OCs
if (ThisStructure->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
{
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
{
StructureToAttack = ThisStructure;
continue;
}
}
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)) { continue; }
// Then prioritise turret factories
if (ThisStructure->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)
{
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
{
StructureToAttack = ThisStructure;
continue;
}
}
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)) { continue; }
// Then target any other structures
if (!StructureToAttack || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
{
StructureToAttack = ThisStructure;
}
}
if (StructureToAttack)
{
AITASK_SetAttackTask(pBot, Task, StructureToAttack->edict, false);
return;
}
}
else if (HiveToGuard)
{
Task->TaskType = TASK_GUARD;
Task->TaskLocation = HiveToGuard->FloorLocation;
Task->TaskTarget = HiveToGuard->HiveEntity->edict();
return;
}
Task->TaskType = TASK_GUARD;
Task->TaskLocation = HiveToGuard->FloorLocation;
Task->TaskTarget = HiveToGuard->HiveEntity->edict();
return;
}
// We're a skulk or lerk
else
else if (HiveToSecure)
{
if (HiveToGuard)
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
// Don't attack electrified structures as skulk
if (pBot->Player->GetUser3() < AVH_USER3_ALIEN_PLAYER4)
{
Task->TaskType = TASK_GUARD;
Task->TaskLocation = HiveToGuard->FloorLocation;
Task->TaskTarget = HiveToGuard->HiveEntity->edict();
return;
}
else if (HiveToSecure)
{
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
EnemyStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_ELECTRIFIED;
vector<AvHAIBuildableStructure*> AllEnemyThings = AITAC_FindAllDeployables(HiveToSecure->FloorLocation, &EnemyStuffFilter);
}
AvHAIBuildableStructure* StructureToAttack = nullptr;
vector<AvHAIBuildableStructure*> AllEnemyThings = AITAC_FindAllDeployables(HiveToSecure->FloorLocation, &EnemyStuffFilter);
for (auto it = AllEnemyThings.begin(); it != AllEnemyThings.end(); it++)
AvHAIBuildableStructure* StructureToAttack = nullptr;
for (auto it = AllEnemyThings.begin(); it != AllEnemyThings.end(); it++)
{
AvHAIBuildableStructure* ThisStructure = (*it);
// First prioritise phase gates or alien OCs
if (ThisStructure->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
{
AvHAIBuildableStructure* ThisStructure = (*it);
// First prioritise phase gates or alien OCs
if (ThisStructure->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
{
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
{
StructureToAttack = ThisStructure;
continue;
}
}
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)) { continue; }
// Then prioritise turret factories
if (ThisStructure->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)
{
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
{
StructureToAttack = ThisStructure;
continue;
}
}
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)) { continue; }
// Then target any other structures
if (!StructureToAttack || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
{
StructureToAttack = ThisStructure;
continue;
}
}
if (StructureToAttack)
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)) { continue; }
// Then prioritise turret factories
if (ThisStructure->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)
{
AITASK_SetAttackTask(pBot, Task, StructureToAttack->edict, false);
return;
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
{
StructureToAttack = ThisStructure;
continue;
}
}
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)) { continue; }
// Then target any other structures
if (!StructureToAttack || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
{
StructureToAttack = ThisStructure;
}
}
if (StructureToAttack)
{
AITASK_SetAttackTask(pBot, Task, StructureToAttack->edict, false);
return;
}
}

View file

@ -69,6 +69,7 @@ void TestNavThink(AvHAIPlayer* pBot);
void DroneThink(AvHAIPlayer* pBot);
void CustomThink(AvHAIPlayer* pBot);
bool AIPlayerMustFinishCurrentTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
AvHAIPlayerTask* AIPlayerGetNextTask(AvHAIPlayer* pBot);
void AIPlayerSetPrimaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
@ -77,6 +78,7 @@ void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetPrimaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
@ -96,6 +98,10 @@ void UpdateCommanderOrders(AvHAIPlayer* pBot);
void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination);
void AIPlayerReceiveBuildOrder(AvHAIPlayer* pBot, edict_t* BuildTarget);
void AIPlayerRequestHealth(AvHAIPlayer* pBot);
void AIPlayerRequestAmmo(AvHAIPlayer* pBot);
void AIPlayerRequestOrder(AvHAIPlayer* pBot);
void BotStopCommanderMode(AvHAIPlayer* pBot);
void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole);

View file

@ -236,6 +236,8 @@ void AIMGR_UpdateFillTeams()
if (TeamSizeA < NumDesiredTeamA && TeamSizeA <= TeamSizeB)
{
// Don't add a bot if we have any stuck in the ready room, wait for teams to resolve themselves
if (AIMGR_GetNumAIPlayersOnTeam(TEAM_IND) > 0) { return; }
AIMGR_AddAIPlayerToTeam(1);
return;
}
@ -251,6 +253,8 @@ void AIMGR_UpdateFillTeams()
if (TeamSizeB < NumDesiredTeamB && TeamSizeB <= TeamSizeA)
{
// Don't add a bot if we have any stuck in the ready room, wait for teams to resolve themselves
if (AIMGR_GetNumAIPlayersOnTeam(TEAM_IND) > 0) { return; }
AIMGR_AddAIPlayerToTeam(2);
return;
}
@ -849,6 +853,8 @@ void AIMGR_NewMap()
{
if (avh_botsenabled.value == 0) { return; } // Do nothing if we're not using bots
ActiveAIPlayers.clear();
AIStartedTime = gpGlobals->time;
ALERT(at_console, "AI Manager New Map\n");

View file

@ -3267,6 +3267,21 @@ int AITAC_GetNumHives()
return Hives.size();
}
int AITAC_GetNumTeamHives(AvHTeamNumber Team, bool bFullyCompletedOnly)
{
int Result = 0;
for (auto it = Hives.begin(); it != Hives.end(); it++)
{
if (it->OwningTeam == Team && (!bFullyCompletedOnly || it->Status == HIVE_STATUS_BUILT))
{
Result++;
}
}
return Result;
}
AvHMessageID UTIL_StructureTypeToImpulseCommand(const AvHAIDeployableStructureType StructureType)
{
switch (StructureType)
@ -3716,6 +3731,28 @@ const vector<AvHAIHiveDefinition*> AITAC_GetAllHives()
return Results;
}
const AvHAIHiveDefinition* AITAC_GetNearestTeamHive(AvHTeamNumber Team, const Vector SearchLocation, bool bFullyBuiltOnly)
{
AvHAIHiveDefinition* Result = nullptr;
float MinDist = 0.0f;
for (auto it = Hives.begin(); it != Hives.end(); it++)
{
if (it->OwningTeam == Team && (!bFullyBuiltOnly || it->Status == HIVE_STATUS_BUILT))
{
float ThisDist = vDist2DSq(it->FloorLocation, SearchLocation);
if (!Result || ThisDist < MinDist)
{
Result = &(*it);
MinDist = ThisDist;
}
}
}
return Result;
}
const vector<AvHAIHiveDefinition*> AITAC_GetAllTeamHives(AvHTeamNumber Team, bool bFullyBuiltOnly)
{
vector<AvHAIHiveDefinition*> Results;
@ -3760,6 +3797,16 @@ bool AITAC_IsAlienHarasserNeeded(AvHAIPlayer* pBot)
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
if (pBot->BotRole == BOT_ROLE_ASSAULT)
{
int NumFades = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER4, pBot->Edict);
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
int NumTeamHives = AITAC_GetNumTeamHives(BotTeam, false);
if (NumFades == 0 || (NumOnos == 0 && NumTeamHives > 1)) { return false; }
}
int NumTeamPlayers = AIMGR_GetNumPlayersOnTeam(BotTeam);
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));
@ -3906,6 +3953,19 @@ bool AITAC_IsAlienCapperNeeded(AvHAIPlayer* pBot)
if (ThisNode->OwningTeam == EnemyTeam) { NumEnemyNodes++; }
}
// If we are currently an assault bot and we are almost able to go fade, only sacrifice that if we are truly desperate and have fades already on the team
if (pBot->BotRole == BOT_ROLE_ASSAULT)
{
if (pBot->Player->GetResources() > BALANCE_VAR(kFadeCost) * 0.8f)
{
if (NumOwnedNodes >= 3) { return false; }
int NumFades = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER4, pBot->Edict);
if (NumFades < 1) { return false; }
}
}
int NumNodesLeft = NumEligibleNodes - NumOwnedNodes;
if (NumNodesLeft == 0) { return false; }
@ -3997,9 +4057,17 @@ bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot)
float ResNodeOwnership = AITAC_GetTeamResNodeOwnership(BotTeam, true);
// Don't lose all those resources!
if (IsPlayerLerk(pBot->Edict) || IsPlayerFade(pBot->Edict) || IsPlayerOnos(pBot->Edict))
// Don't lose all those resources if we're a higher lifeform!
if (pBot->Player->GetUser3() > AVH_USER3_ALIEN_PLAYER2)
{
if (IsPlayerLerk(pBot->Edict)) { return false; }
int NumFades = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER4, pBot->Edict);
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
// Don't downgrade to gorge if we're the only fade or onos on the team
if ((IsPlayerFade(pBot->Edict) && NumFades == 0) || (IsPlayerOnos(pBot->Edict) && NumOnos == 0)) { return false; }
// Only waste those resources if we have loads of res nodes and can easily replenish the lost resources
if (pBot->Player->GetResources() < 75 || ResNodeOwnership < 0.6f)
{

View file

@ -70,6 +70,7 @@ AvHAIDroppedItem* AITAC_GetDroppedItemRefFromEdict(edict_t* ItemEdict);
Vector AITAC_GetFloorLocationForHive(const AvHAIHiveDefinition* Hive);
int AITAC_GetNumHives();
int AITAC_GetNumTeamHives(AvHTeamNumber Team, bool bFullyCompletedOnly);
void AITAC_OnNavMeshModified();
@ -163,6 +164,7 @@ const vector<AvHAIResourceNode*> AITAC_GetAllResourceNodes();
const vector<AvHAIResourceNode*> AITAC_GetAllReachableResourceNodes(AvHTeamNumber Team);
const vector<AvHAIHiveDefinition*> AITAC_GetAllHives();
const vector<AvHAIHiveDefinition*> AITAC_GetAllTeamHives(AvHTeamNumber Team, bool bFullyBuiltOnly);
const AvHAIHiveDefinition* AITAC_GetNearestTeamHive(AvHTeamNumber Team, const Vector SearchLocation, bool bFullyBuiltOnly);
bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius);

View file

@ -1842,11 +1842,7 @@ void AlienProgressGetHealthTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
BotLookAt(pBot, Task->TaskTarget);
if (gpGlobals->time - pBot->LastRequestTime > min_request_spam_time)
{
pBot->Impulse = SAYING_4;
pBot->LastRequestTime = gpGlobals->time;
}
AIPlayerRequestHealth(pBot);
}
}
}