Commander supply improvements

This commit is contained in:
RGreenlees 2024-02-06 14:55:55 +00:00 committed by pierow
parent a747ed0187
commit 09a0206b9c
6 changed files with 160 additions and 75 deletions

View File

@ -76,7 +76,7 @@ bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDe
bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToResearch, AvHMessageID Research)
{
if (!StructureToResearch || FNullEnt(StructureToResearch->edict)) { return false; }
if (!StructureToResearch || FNullEnt(StructureToResearch->edict) || !StructureToResearch->EntityRef) { return false; }
// Don't do anything if the structure is being recycled, or we DON'T want to recycle but the structure is already busy
if (StructureToResearch->EntityRef->GetIsRecycling() || (Research != BUILD_RECYCLE && StructureToResearch->EntityRef->GetIsResearching())) { return false; }
@ -132,6 +132,8 @@ bool AICOMM_UpgradeStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* Structu
bool AICOMM_RecycleStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToRecycle)
{
if (!StructureToRecycle || StructureToRecycle->StructureType == STRUCTURE_MARINE_DEPLOYEDMINE) { return false; }
return AICOMM_ResearchTech(pBot, StructureToRecycle, BUILD_RECYCLE);
}
@ -902,8 +904,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (NumInfantryPortals < 2)
{
AICOMM_BuildInfantryPortal(pBot, CommChair);
return true;
bool bSuccess = AICOMM_BuildInfantryPortal(pBot, CommChair);
return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kInfantryPortalCost) + 5);
}
StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY;
@ -932,8 +934,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (!vIsZero(BuildLocation))
{
AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation);
return true;
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation);
return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kArmoryCost) + 5);
}
}
@ -967,8 +969,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (!vIsZero(BuildLocation))
{
AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation);
return true;
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation);
return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kPhaseGateCost) + 5);
}
}
}
@ -977,21 +979,19 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (CappableNode)
{
AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_RESTOWER, CappableNode->Location);
return true;
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_RESTOWER, CappableNode->Location);
return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kResourceTowerCost) + 5);
}
const AvHAIHiveDefinition* HiveToSecure = AICOMM_GetEmptyHiveOpportunityNearestLocation(pBot, AITAC_GetCommChairLocation(TeamNumber));
if (HiveToSecure)
{
if (AICOMM_PerformNextSecureHiveAction(pBot, HiveToSecure))
{
return true;
}
bool bSuccess = AICOMM_PerformNextSecureHiveAction(pBot, HiveToSecure);
return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kTurretFactoryCost) + 5);
}
if (pBot->Player->GetResources() < 30) { return false; }
if (AICOMM_ShouldCommanderPrioritiseNodes(pBot) && pBot->Player->GetResources() < 30) { return false; }
StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMSLAB;
StructureFilter.MaxSearchRadius = 0.0f;
@ -1004,10 +1004,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (!vIsZero(BuildLocation))
{
if (AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMSLAB, BuildLocation))
{
return true;
}
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMSLAB, BuildLocation);
return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kArmsLabCost) + 5);
}
}
@ -1021,10 +1019,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (!vIsZero(BuildLocation))
{
if (AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_OBSERVATORY, BuildLocation))
{
return true;
}
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_OBSERVATORY, BuildLocation);
return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kObservatoryCost) + 5);
}
}
@ -1037,10 +1033,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (HiveToSiege)
{
if (AICOMM_PerformNextSiegeHiveAction(pBot, HiveToSiege))
{
return true;
}
bool bSuccess = AICOMM_PerformNextSiegeHiveAction(pBot, HiveToSiege);
return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kTurretFactoryCost) + 5);
}
StructureFilter.DeployableTypes = STRUCTURE_MARINE_ADVARMOURY;
@ -1178,10 +1172,9 @@ bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot)
int NumDesiredWelders = 1;
if (!AICOMM_ShouldCommanderPrioritiseNodes(pBot))
if (!AICOMM_ShouldCommanderPrioritiseNodes(pBot) && pBot->Player->GetResources() >= 20)
{
NumDesiredWelders = (int)ceilf((float)AIMGR_GetNumPlayersOnTeam(CommanderTeam) * 0.3f);
return false;
}
int NumTeamWelders = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_WELDER);
@ -2021,6 +2014,7 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair)
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot)
{
DeployableSearchFilter UnreachableFilter;
UnreachableFilter.DeployableTypes = (SEARCH_ALL_STRUCTURES & ~(STRUCTURE_MARINE_DEPLOYEDMINE));
UnreachableFilter.DeployableTeam = pBot->Player->GetTeam();
UnreachableFilter.ReachabilityTeam = pBot->Player->GetTeam();
UnreachableFilter.ReachabilityFlags = AI_REACHABILITY_UNREACHABLE;

View File

@ -1597,8 +1597,6 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
pBot->BotNavInfo.LastNavMeshCheckPosition = pBot->CurrentFloorPosition;
}
UpdateBotStuck(pBot);
pBot->LookTargetLocation = ZERO_VECTOR;
pBot->MoveLookLocation = ZERO_VECTOR;
pBot->LookTarget = nullptr;
@ -1668,6 +1666,18 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
}
void EndBotFrame(AvHAIPlayer* pBot)
{
UpdateBotStuck(pBot);
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_INVALID) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_INVALID && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
{
BotSwitchToWeapon(pBot, DesiredWeapon);
}
}
void CustomThink(AvHAIPlayer* pBot)
{
if (IsPlayerMarine(pBot->Player))
@ -1699,13 +1709,6 @@ void CustomThink(AvHAIPlayer* pBot)
BotProgressTask(pBot, &pBot->PrimaryBotTask);
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_INVALID) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_NONE && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
{
BotSwitchToWeapon(pBot, DesiredWeapon);
}
return;
}
@ -1723,13 +1726,6 @@ void CustomThink(AvHAIPlayer* pBot)
MarineCombatThink(pBot);
}
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_INVALID) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_INVALID && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
{
BotSwitchToWeapon(pBot, DesiredWeapon);
}
return;
}
@ -1756,13 +1752,6 @@ void CustomThink(AvHAIPlayer* pBot)
AlienCombatThink(pBot);
}
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_INVALID) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_INVALID && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
{
BotSwitchToWeapon(pBot, DesiredWeapon);
}
}
void DroneThink(AvHAIPlayer* pBot)
@ -1783,12 +1772,6 @@ void DroneThink(AvHAIPlayer* pBot)
AIDEBUG_DrawBotPath(pBot);
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_NONE) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_NONE && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
{
BotSwitchToWeapon(pBot, DesiredWeapon);
}
}
void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole)
@ -1847,7 +1830,7 @@ bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot)
AvHTeamNumber BotTeamNumber = pBot->Player->GetTeam();
AvHTeam* BotTeam = GetGameRules()->GetTeam(BotTeamNumber);
// Don't go commander if we're not an alien. You never know with the way I structure my logic...
// Don't go commander if we're an alien. You never know with the way I structure my logic...
if (!BotTeam || BotTeam->GetTeamType() != AVH_CLASS_TYPE_MARINE) { return false; }
// Don't go commander if we're only supposed to command when there aren't any humans and we have one
@ -3049,13 +3032,26 @@ void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
Vector CommChairLocation = AITAC_GetCommChairLocation(BotTeam);
// Always built IPs first, so we don't end up getting wiped right at the start
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
StructureFilter.DeployableTeam = BotTeam;
StructureFilter.ReachabilityTeam = BotTeam;
StructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
StructureFilter.ExcludeStatusFlags = (STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_COMPLETED);
AvHAIBuildableStructure* UnbuiltIP = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &StructureFilter);
if (UnbuiltIP)
{
AITASK_SetBuildTask(pBot, Task, UnbuiltIP->edict, true);
return;
}
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
StructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
if (AITAC_GetNumDeployablesNearLocation(CommChairLocation, &StructureFilter) < 2)
{
@ -3428,6 +3424,35 @@ void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
}
}
}
if (!PlayerHasWeapon(pBot->Player, WEAPON_MARINE_MINES))
{
AvHAIDroppedItem* NearbyWeapon = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_MINES, BotTeam, pBot->BotNavInfo.NavProfile.ReachabilityFlag, 0.0f, UTIL_MetresToGoldSrcUnits(10.0f), true);
if (NearbyWeapon)
{
vector<AvHPlayer*> NearbyPlayers = AITAC_GetAllPlayersOfTeamInArea(BotTeam, NearbyWeapon->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
bool bHumanNearby = false;
for (auto it = NearbyPlayers.begin(); it != NearbyPlayers.end(); it++)
{
AvHPlayer* ThisPlayer = (*it);
edict_t* PlayerEdict = ThisPlayer->edict();
if (IsPlayerActiveInGame(PlayerEdict) && !PlayerHasWeapon(ThisPlayer, WEAPON_MARINE_MINES) && !IsPlayerBot(PlayerEdict))
{
bHumanNearby = true;
break;
}
}
if (!bHumanNearby)
{
AITASK_SetPickupTask(pBot, Task, NearbyWeapon->edict, vDist2DSq(pBot->Edict->v.origin, NearbyWeapon->Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)));
return;
}
}
}
}
void AIPlayerRequestHealth(AvHAIPlayer* pBot)
@ -3456,7 +3481,7 @@ void AIPlayerRequestOrder(AvHAIPlayer* pBot)
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
// Don't pick a new build target if we're already near one
// If we're building, finish that before doing anything else
if (Task->TaskType == TASK_BUILD && vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
{
return;
@ -3585,21 +3610,47 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (Task->TaskType == TASK_PLACE_MINE) { return; }
DeployableSearchFilter MineableStructures;
MineableStructures.DeployableTypes = (STRUCTURE_MARINE_INFANTRYPORTAL, STRUCTURE_MARINE_PHASEGATE, STRUCTURE_MARINE_TURRETFACTORY, STRUCTURE_MARINE_ADVTURRETFACTORY);
MineableStructures.DeployableTypes = (STRUCTURE_MARINE_INFANTRYPORTAL | STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
MineableStructures.DeployableTeam = BotTeam;
MineableStructures.ReachabilityTeam = BotTeam;
MineableStructures.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
MineableStructures.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
MineableStructures.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
MineableStructures.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
MineableStructures.bConsiderPhaseDistance = true;
vector<AvHAIBuildableStructure*> AllMineableStructures = AITAC_FindAllDeployables(AITAC_GetTeamStartingLocation(BotTeam), &MineableStructures);
AvHAIBuildableStructure* StructureToMine = nullptr;
DeployableSearchFilter MineFilter;
MineFilter.DeployableTypes = STRUCTURE_MARINE_DEPLOYEDMINE;
MineFilter.DeployableTeam = BotTeam;
MineFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(3.0f);
float FarDist = 0.0f;
for (auto it = AllMineableStructures.begin(); it != AllMineableStructures.end(); it++)
{
AvHAIBuildableStructure* ThisStructure = (*it);
int NumMines = AITAC_GetNumDeployablesNearLocation(ThisStructure->Location, &MineFilter);
if (NumMines < 4)
{
float ThisDist = AITAC_GetPhaseDistanceBetweenPoints(ThisStructure->Location, AITAC_GetTeamStartingLocation(BotTeam));
if (!StructureToMine || ThisDist > FarDist)
{
StructureToMine = ThisStructure;
FarDist = ThisDist;
}
}
}
if (StructureToMine)
{
AITASK_SetMineStructureTask(pBot, Task, StructureToMine->edict, true);
}
}
}
@ -3752,13 +3803,6 @@ void AIPlayerThink(AvHAIPlayer* pBot)
AIPlayerDMThink(pBot);
break;
}
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_NONE) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_NONE && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
{
BotSwitchToWeapon(pBot, DesiredWeapon);
}
}
void TestNavThink(AvHAIPlayer* pBot)
@ -4357,7 +4401,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
return;
}
if (pBot->Player->GetResources() >= BALANCE_VAR(kOnosCost))
if (!IsPlayerOnos(pBot->Edict) && pBot->Player->GetResources() >= BALANCE_VAR(kOnosCost))
{
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
@ -4373,7 +4417,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
}
}
if (pBot->Player->GetResources() >= BALANCE_VAR(kFadeCost))
if (!IsPlayerFade(pBot->Edict) && 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);
@ -4704,7 +4748,20 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
}
DeployableSearchFilter EnemyInfPortalFilter;
EnemyInfPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
EnemyInfPortalFilter.DeployableTeam = EnemyTeam;
EnemyInfPortalFilter.ReachabilityTeam = BotTeam;
EnemyInfPortalFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyInfPortalFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyInfPortalFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure* EnemyInfPortal = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &EnemyInfPortalFilter);
if (EnemyInfPortal)
{
AITASK_SetAttackTask(pBot, Task, EnemyInfPortal->edict, false);
return;
}
// TODO: Attack enemy hive/base
edict_t* EnemyChair = AITAC_GetCommChair(EnemyTeam);
@ -5081,6 +5138,10 @@ void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
float ThisDist = vDist2D(pBot->Edict->v.origin, ThisStructure->edict->v.origin);
int NumAttackers = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, ThisStructure->Location, UTIL_MetresToGoldSrcUnits(15.0f), nullptr);
if (NumAttackers == 0) { continue; }
int NumExistingDefenders = AITAC_GetNumPlayersOfTeamInArea(BotTeam, ThisStructure->Location, ThisDist - 10.0f, false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
if (NumExistingDefenders < 2)

View File

@ -54,6 +54,7 @@ void UpdateBotChat(AvHAIPlayer* pBot);
void ClearBotInputs(AvHAIPlayer* pBot);
void StartNewBotFrame(AvHAIPlayer* pBot);
void EndBotFrame(AvHAIPlayer* pBot);
void AIPlayerThink(AvHAIPlayer* pBot);
// Think routine for regular NS game mode

View File

@ -597,7 +597,9 @@ void AIMGR_UpdateAIPlayers()
UpdateBotChat(bot);
DroneThink(bot);
AIPlayerThink(bot);
EndBotFrame(bot);
BotUpdateDesiredViewRotation(bot);
}

View File

@ -1923,8 +1923,8 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
BuildingMap[EntIndex].healthPercent = 1.0f;
BuildingMap[EntIndex].EntityRef = nullptr;
BuildingMap[EntIndex].StructureStatusFlags = STRUCTURE_STATUS_COMPLETED;
BuildingMap[EntIndex].TeamAReachabilityFlags = AI_REACHABILITY_ALL;
BuildingMap[EntIndex].TeamBReachabilityFlags = AI_REACHABILITY_ALL;
BuildingMap[EntIndex].TeamAReachabilityFlags = (AI_REACHABILITY_ALL & ~(AI_REACHABILITY_UNREACHABLE));
BuildingMap[EntIndex].TeamBReachabilityFlags = (AI_REACHABILITY_ALL & ~(AI_REACHABILITY_UNREACHABLE));
AITAC_OnStructureCreated(&BuildingMap[EntIndex]);
}

View File

@ -1672,10 +1672,35 @@ void BotProgressAttackTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
void BotProgressDefendTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
BotProgressGuardTask(pBot, Task);
if (!FNullEnt(Task->TaskTarget))
{
vector<AvHPlayer*> Attackers = AITAC_GetAllPlayersOnTeamWithLOS(AIMGR_GetEnemyTeam(pBot->Player->GetTeam()), Task->TaskTarget->v.origin, UTIL_MetresToGoldSrcUnits(15.0f), nullptr);
edict_t* NearestAttacker = nullptr;
float MinDist = 0.0f;
for (auto it = Attackers.begin(); it != Attackers.end(); it++)
{
AvHPlayer* ThisPlayer = (*it);
edict_t* PlayerEdict = ThisPlayer->edict();
if (FNullEnt(PlayerEdict)) { continue; }
float ThisDist = vDist2DSq(pBot->Edict->v.origin, PlayerEdict->v.origin);
if (FNullEnt(NearestAttacker) || ThisDist < MinDist)
{
NearestAttacker = PlayerEdict;
MinDist = ThisDist;
}
}
if (!FNullEnt(NearestAttacker))
{
MoveTo(pBot, UTIL_GetEntityGroundLocation(NearestAttacker), MOVESTYLE_NORMAL);
return;
}
AvHAIBuildableStructure* StructureRef = AITAC_GetDeployableRefFromEdict(Task->TaskTarget);
if (!StructureRef) { return; }
@ -1689,6 +1714,8 @@ void BotProgressDefendTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
}
}
BotProgressGuardTask(pBot, Task);
}
void BotProgressTakeCommandTask(AvHAIPlayer* pBot)