diff --git a/main/source/mod/AIPlayers/AvHAICommander.cpp b/main/source/mod/AIPlayers/AvHAICommander.cpp index 7d7be8fe..78327ae8 100644 --- a/main/source/mod/AIPlayers/AvHAICommander.cpp +++ b/main/source/mod/AIPlayers/AvHAICommander.cpp @@ -11,15 +11,15 @@ #include "../AvHSharedUtil.h" #include "../AvHServerUtil.h" -bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureType StructureToDeploy, const Vector Location) +bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureType StructureToDeploy, const Vector Location, StructurePurpose Purpose) { + if (vIsZero(Location)) { return false; } + AvHMessageID StructureID = UTIL_StructureTypeToImpulseCommand(StructureToDeploy); Vector BuildLocation = Location; BuildLocation.z += 4.0f; - UTIL_DrawLine(INDEXENT(1), INDEXENT(1)->v.origin, Location, 10.0f); - if (!AvHSHUGetIsSiteValidForBuild(StructureID, &BuildLocation)) { return false; } string theErrorMessage; @@ -28,9 +28,16 @@ bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureTyp if (!thePurchaseAllowed) { return false; } - bool theSuccess = (AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player) != NULL); + CBaseEntity* NewStructureEntity = AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player); - if (!theSuccess) { return false; } + if (!NewStructureEntity) { return false; } + + AvHAIBuildableStructure* NewStructure = AITAC_UpdateBuildableStructure(NewStructureEntity); + + if (NewStructure) + { + NewStructure->Purpose = Purpose; + } pBot->Player->PayPurchaseCost(theCost); @@ -885,18 +892,20 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit StructureFilter.ReachabilityTeam = CommanderTeam; StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; - StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f); + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(25.0f); Vector SiegeLocation = ZERO_VECTOR; AvHAIBuildableStructure* ExistingPG = nullptr; + edict_t* NearestBuilder = nullptr; + if (AITAC_PhaseGatesAvailable(CommanderTeam)) { StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE; ExistingPG = AITAC_FindClosestDeployableToLocation(HiveToSiege->Location, &StructureFilter); - if (ExistingPG && (ExistingPG->StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) + if (ExistingPG) { SiegeLocation = ExistingPG->Location; } @@ -915,7 +924,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit } else { - edict_t* NearestBuilder = AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, HiveToSiege->Location, UTIL_MetresToGoldSrcUnits(20.0f)); + NearestBuilder = AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, HiveToSiege->Location, UTIL_MetresToGoldSrcUnits(20.0f)); if (FNullEnt(NearestBuilder)) { return false; } @@ -923,20 +932,28 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit } } - edict_t* NearestBuilder = AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, SiegeLocation, UTIL_MetresToGoldSrcUnits(20.0f)); + if (FNullEnt(NearestBuilder)) + { + NearestBuilder = AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, SiegeLocation, UTIL_MetresToGoldSrcUnits(20.0f)); + } if (FNullEnt(NearestBuilder)) { return false; } - Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); + Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); + + if (vIsZero(NextBuildPosition)) + { + NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); + } if (!ExistingPG) { - return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, NextBuildPosition); + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE); } if (!ExistingTF) { - return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, NextBuildPosition); + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE); } StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY; @@ -946,7 +963,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit if (!ExistingArmoury) { - return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, NextBuildPosition); + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE); } if (ExistingTF->StructureType != STRUCTURE_MARINE_ADVTURRETFACTORY) @@ -963,9 +980,14 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit { SiegeLocation = ExistingTF->Location; - NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); + NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); - return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition); + if (vIsZero(NextBuildPosition)) + { + NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); + } + + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE); } if (!UTIL_IsStructureElectrified(ExistingTF->edict)) @@ -1019,7 +1041,7 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini if (!vIsZero(BuildLocation)) { - return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation); + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_FORTIFY); } return false; @@ -1039,7 +1061,7 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini if (!vIsZero(BuildLocation)) { - return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation); + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_FORTIFY); } return false; @@ -1057,7 +1079,7 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini if (!vIsZero(BuildLocation)) { - return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation); + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, STRUCTURE_PURPOSE_FORTIFY); } return false; @@ -1149,6 +1171,38 @@ bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot) return AICOMM_RecycleStructure(pBot, UnreachableStructure); } + vector Hives = AITAC_GetAllHives(); + + for (auto HiveIt = Hives.begin(); HiveIt != Hives.end(); HiveIt++) + { + AvHAIHiveDefinition* Hive = (*HiveIt); + + if (Hive->Status != HIVE_STATUS_UNBUILT) { continue; } + + DeployableSearchFilter RedundantFilter; + RedundantFilter.DeployableTeam = pBot->Player->GetTeam(); + RedundantFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; + RedundantFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(25.0f); + + vector NearbyStructures = AITAC_FindAllDeployables(Hive->Location, &RedundantFilter); + + for (auto StructIt = NearbyStructures.begin(); StructIt != NearbyStructures.end(); StructIt++) + { + AvHAIBuildableStructure* Structure = (*StructIt); + + if (Structure->Purpose == STRUCTURE_PURPOSE_SIEGE) + { + // Check for the potential situation where we can siege more than one hive at a time + const AvHAIHiveDefinition* NearestHive = AITAC_GetNonEmptyHiveNearestLocation(Structure->Location); + + if (!NearestHive || vDist2DSq(NearestHive->Location, Structure->Location) > sqrf(UTIL_MetresToGoldSrcUnits(25.0f))) + { + return AICOMM_RecycleStructure(pBot, Structure); + } + } + } + } + return false; } diff --git a/main/source/mod/AIPlayers/AvHAICommander.h b/main/source/mod/AIPlayers/AvHAICommander.h index 66fa3e53..5241a547 100644 --- a/main/source/mod/AIPlayers/AvHAICommander.h +++ b/main/source/mod/AIPlayers/AvHAICommander.h @@ -12,7 +12,7 @@ #include "AvHAIConstants.h" -bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureType StructureToDeploy, const Vector Location); +bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureType StructureToDeploy, const Vector Location, StructurePurpose Purpose = STRUCTURE_PURPOSE_NONE); bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDeploy, const Vector Location); bool AICOMM_UpgradeStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToUpgrade); bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToResearch, AvHMessageID Research); diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index 069029fb..82132dfd 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -1625,22 +1625,22 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure) } } -void AITAC_UpdateBuildableStructure(CBaseEntity* Structure) +AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) { - if (!Structure || (Structure->pev->effects & EF_NODRAW) || (Structure->pev->deadflag != DEAD_NO)) { return; } + if (!Structure || (Structure->pev->effects & EF_NODRAW) || (Structure->pev->deadflag != DEAD_NO)) { return nullptr; } AvHBaseBuildable* BaseBuildable = dynamic_cast(Structure); - if (!BaseBuildable) { return; } + if (!BaseBuildable) { return nullptr; } edict_t* BuildingEdict = BaseBuildable->edict(); AvHAIDeployableStructureType StructureType = UTIL_IUSER3ToStructureType(BaseBuildable->pev->iuser3); - if (StructureType == STRUCTURE_NONE) { return; } + if (StructureType == STRUCTURE_NONE) { return nullptr; } int EntIndex = BaseBuildable->entindex(); - if (EntIndex < 0) { return; } + if (EntIndex < 0) { return nullptr; } AvHTeamNumber TeamANumber = GetGameRules()->GetTeamANumber(); AvHTeamNumber TeamBNumber = GetGameRules()->GetTeamBNumber(); @@ -1721,6 +1721,8 @@ void AITAC_UpdateBuildableStructure(CBaseEntity* Structure) BuildingMap[EntIndex].StructureStatusFlags = NewFlags; BuildingMap[EntIndex].LastSeen = StructureRefreshFrame; + return &BuildingMap[EntIndex]; + } void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure) @@ -2297,6 +2299,27 @@ const AvHAIHiveDefinition* AITAC_GetActiveHiveNearestLocation(const Vector Searc return Result; } +const AvHAIHiveDefinition* AITAC_GetNonEmptyHiveNearestLocation(const Vector SearchLocation) +{ + AvHAIHiveDefinition* Result = nullptr; + float MinDist = 0.0f; + + for (auto it = Hives.begin(); it != Hives.end(); it++) + { + if (it->Status == HIVE_STATUS_UNBUILT) { continue; } + + float ThisDist = vDist3DSq(SearchLocation, it->Location); + + if (!Result || ThisDist < MinDist) + { + Result = &(*it); + MinDist = ThisDist; + } + } + + return Result; +} + AvHAIResourceNode* AITAC_GetNearestResourceNodeToLocation(const Vector Location) { AvHAIResourceNode* Result = nullptr; diff --git a/main/source/mod/AIPlayers/AvHAITactical.h b/main/source/mod/AIPlayers/AvHAITactical.h index f2ed712d..b4c321c7 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.h +++ b/main/source/mod/AIPlayers/AvHAITactical.h @@ -32,7 +32,7 @@ void AITAC_RefreshResourceNodes(); void AITAC_UpdateMapAIData(); void AITAC_CheckNavMeshModified(); void AITAC_RefreshBuildableStructures(); -void AITAC_UpdateBuildableStructure(CBaseEntity* Structure); +AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure); void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure); void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode); void AITAC_RefreshAllResNodeReachability(); @@ -49,6 +49,7 @@ float AITAC_GetPhaseDistanceBetweenPoints(const Vector StartPoint, const Ve const AvHAIHiveDefinition* AITAC_GetHiveAtIndex(int Index); const AvHAIHiveDefinition* AITAC_GetHiveNearestLocation(const Vector SearchLocation); const AvHAIHiveDefinition* AITAC_GetActiveHiveNearestLocation(const Vector SearchLocation); +const AvHAIHiveDefinition* AITAC_GetNonEmptyHiveNearestLocation(const Vector SearchLocation); Vector AITAC_GetCommChairLocation(AvHTeamNumber Team); edict_t* AITAC_GetCommChair(AvHTeamNumber Team);