From 09a0206b9c0256910131c68ec2528e36fb47194d Mon Sep 17 00:00:00 2001 From: RGreenlees Date: Tue, 6 Feb 2024 14:55:55 +0000 Subject: [PATCH] Commander supply improvements --- main/source/mod/AIPlayers/AvHAICommander.cpp | 50 +++--- main/source/mod/AIPlayers/AvHAIPlayer.cpp | 147 +++++++++++++----- main/source/mod/AIPlayers/AvHAIPlayer.h | 1 + .../mod/AIPlayers/AvHAIPlayerManager.cpp | 4 +- main/source/mod/AIPlayers/AvHAITactical.cpp | 4 +- main/source/mod/AIPlayers/AvHAITask.cpp | 29 +++- 6 files changed, 160 insertions(+), 75 deletions(-) diff --git a/main/source/mod/AIPlayers/AvHAICommander.cpp b/main/source/mod/AIPlayers/AvHAICommander.cpp index fe31522e..dbea9093 100644 --- a/main/source/mod/AIPlayers/AvHAICommander.cpp +++ b/main/source/mod/AIPlayers/AvHAICommander.cpp @@ -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; diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index 7ec3706d..2823defd 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -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 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 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) diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.h b/main/source/mod/AIPlayers/AvHAIPlayer.h index 61e24881..1533cf15 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.h +++ b/main/source/mod/AIPlayers/AvHAIPlayer.h @@ -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 diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp index 2e69a5f6..491b5c4d 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp @@ -597,7 +597,9 @@ void AIMGR_UpdateAIPlayers() UpdateBotChat(bot); - DroneThink(bot); + AIPlayerThink(bot); + + EndBotFrame(bot); BotUpdateDesiredViewRotation(bot); } diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index faa8276e..474e2681 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -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]); } diff --git a/main/source/mod/AIPlayers/AvHAITask.cpp b/main/source/mod/AIPlayers/AvHAITask.cpp index 9f5b5916..c321dfbf 100644 --- a/main/source/mod/AIPlayers/AvHAITask.cpp +++ b/main/source/mod/AIPlayers/AvHAITask.cpp @@ -1672,10 +1672,35 @@ void BotProgressAttackTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) void BotProgressDefendTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) { - BotProgressGuardTask(pBot, Task); if (!FNullEnt(Task->TaskTarget)) { + vector 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)