Commander improvements

This commit is contained in:
RGreenlees 2023-12-30 20:54:14 +00:00 committed by pierow
parent b78629738d
commit aa314842ce
9 changed files with 368 additions and 94 deletions

View file

@ -16,14 +16,27 @@ bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureTyp
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; }
CBaseEntity* NewBuilding = AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player);
string theErrorMessage;
int theCost = 0;
bool thePurchaseAllowed = pBot->Player->GetPurchaseAllowed(StructureID, theCost, &theErrorMessage);
if (!thePurchaseAllowed) { return false; }
bool theSuccess = (AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player) != NULL);
if (!theSuccess) { return false; }
pBot->Player->PayPurchaseCost(theCost);
pBot->next_commander_action_time = gpGlobals->time + 1.0f;
return NewBuilding != nullptr;
return true;
}
bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDeploy, const Vector Location)
@ -32,25 +45,38 @@ bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDe
Vector BuildLocation = Location;
string theErrorMessage;
int theCost = 0;
bool thePurchaseAllowed = pBot->Player->GetPurchaseAllowed(StructureID, theCost, &theErrorMessage);
if (!thePurchaseAllowed) { return false; }
if (!AvHSHUGetIsSiteValidForBuild(StructureID, &BuildLocation)) { return false; }
CBaseEntity* NewBuilding = AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player);
bool theSuccess = (AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player) != NULL);
if (!theSuccess) { return false; }
pBot->Player->PayPurchaseCost(theCost);
pBot->next_commander_action_time = gpGlobals->time + 0.2f;
return NewBuilding != nullptr;
return true;
}
bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToResearch, AvHMessageID Research)
{
if (FNullEnt(StructureToResearch->edict)) { return false; }
if (StructureToResearch->EntityRef->GetIsRecycling() || StructureToResearch->EntityRef->GetIsResearching()) { 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; }
int StructureIndex = ENTINDEX(StructureToResearch->edict);
if (StructureIndex < 0) { return false; }
if (!StructureToResearch->EntityRef->GetIsTechnologyAvailable(Research)) { return false; }
pBot->Player->SetSelection(StructureIndex, true);
pBot->Button |= IN_ATTACK2;
@ -72,6 +98,7 @@ bool AICOMM_UpgradeStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* Structu
break;
case STRUCTURE_MARINE_TURRETFACTORY:
UpgradeImpulse = TURRET_FACTORY_UPGRADE;
break;
default:
return false;
}
@ -376,7 +403,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
StructureFilter.DeployableTeam = TeamNumber;
StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
StructureFilter.ReachabilityTeam = TeamNumber;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
int NumInfantryPortals = AITAC_GetNumDeployablesNearLocation(CommChair->v.origin, &StructureFilter);
@ -389,7 +416,6 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
}
StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
AvHAIBuildableStructure* BaseArmoury = AITAC_FindClosestDeployableToLocation(CommChair->v.origin, &StructureFilter);
@ -405,12 +431,12 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
if (NearestInfantryPortal)
{
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestInfantryPortal->Location, UTIL_MetresToGoldSrcUnits(5.0f));
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestInfantryPortal->Location, UTIL_MetresToGoldSrcUnits(5.0f));
}
if (vIsZero(BuildLocation))
{
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
}
if (!vIsZero(BuildLocation))
@ -430,7 +456,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
if (!bAlreadyScanning)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(ALL_NAV_PROFILE), HiveUnderSiege->FloorLocation, UTIL_MetresToGoldSrcUnits(3.0f));
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), HiveUnderSiege->FloorLocation, UTIL_MetresToGoldSrcUnits(3.0f));
if (AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_SCAN, BuildLocation))
{
@ -448,7 +474,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
if (!bPhaseNearBase)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
if (AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation))
{
@ -484,7 +510,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
if (!bHasArmsLab)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
if (!vIsZero(BuildLocation))
{
@ -501,7 +527,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
if (!bHasObservatory)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
if (!vIsZero(BuildLocation))
{
@ -564,7 +590,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
if (!bHasPrototypeLab && bHasAdvArmoury)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInDonut(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), BaseArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f), UTIL_MetresToGoldSrcUnits(5.0f));
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), BaseArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f), UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
@ -775,35 +801,39 @@ const AvHAIHiveDefinition* AICOMM_GetHiveSiegeOpportunityNearestLocation(AvHAIPl
bool bPhaseGatesAvailable = AITAC_PhaseGatesAvailable(CommanderTeam);
// Only siege if we have phase gates available
if (!bPhaseGatesAvailable) { return nullptr; }
const AvHAIHiveDefinition* Result = nullptr;
float MinDist = 0.0f;
const vector<AvHAIHiveDefinition> Hives = AITAC_GetAllHives();
const vector<AvHAIHiveDefinition*> Hives = AITAC_GetAllHives();
for (auto it = Hives.begin(); it != Hives.end(); it++)
{
const AvHAIHiveDefinition* Hive = &(*it);
const AvHAIHiveDefinition* Hive = (*it);
if (it->Status == HIVE_STATUS_UNBUILT) { continue; }
if (AITAC_GetMarineEligibleToBuildSiege(CommanderTeam, Hive) == nullptr) { continue; }
if (Hive->Status == HIVE_STATUS_UNBUILT) { continue; }
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
StructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure* BuiltPhaseGate = AITAC_FindClosestDeployableToLocation(Hive->Location, &StructureFilter);
// If we have a phase gate already in place, then keep building as long as someone is there. If we don't have a phase gate, only build if there is a marine who isn't sighted by the enemy (to allow element of surprise)
if (BuiltPhaseGate)
{
int NumBuilders = AITAC_GetNumPlayersOfTeamInArea(CommanderTeam, BuiltPhaseGate->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, CommanderBot->Edict, AVH_USER3_COMMANDER_PLAYER);
if (NumBuilders == 0) { continue; }
}
else
{
if (AITAC_GetMarineEligibleToBuildSiege(CommanderTeam, Hive) == nullptr) { continue; }
}
float ThisDist = vDist2DSq(Hive->FloorLocation, SearchLocation);
@ -820,22 +850,24 @@ const AvHAIHiveDefinition* AICOMM_GetHiveSiegeOpportunityNearestLocation(AvHAIPl
const AvHAIResourceNode* AICOMM_GetNearestResourceNodeCapOpportunity(const AvHTeamNumber Team, const Vector SearchLocation)
{
vector<AvHAIResourceNode> AllNodes = AITAC_GetAllResourceNodes();
vector<AvHAIResourceNode*> AllNodes = AITAC_GetAllResourceNodes();
AvHAIResourceNode* Result = nullptr;
float MinDist = 0.0f;
for (auto it = AllNodes.begin(); it != AllNodes.end(); it++)
{
if (it->bIsOccupied) { continue; }
AvHAIResourceNode* ResNode = (*it);
if (!AITAC_AnyPlayerOnTeamWithLOS(Team, (it->Location + Vector(0.0f, 0.0f, 32.0f)), UTIL_MetresToGoldSrcUnits(5.0f))) { continue; }
if (ResNode->bIsOccupied) { continue; }
float ThisDist = vDist2DSq(it->Location, SearchLocation);
if (!AITAC_AnyPlayerOnTeamWithLOS(Team, (ResNode->Location + Vector(0.0f, 0.0f, 32.0f)), UTIL_MetresToGoldSrcUnits(5.0f))) { continue; }
float ThisDist = vDist2DSq(ResNode->Location, SearchLocation);
if (!Result || ThisDist < MinDist)
{
Result = &(*it);
Result = ResNode;
MinDist = ThisDist;
}
}
@ -855,7 +887,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
Vector SiegeLocation;
Vector SiegeLocation = ZERO_VECTOR;
AvHAIBuildableStructure* ExistingPG = nullptr;
if (AITAC_PhaseGatesAvailable(CommanderTeam))
@ -895,7 +927,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
if (FNullEnt(NearestBuilder)) { return false; }
Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(BUILDING_NAV_MESH), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
if (!ExistingPG)
{
@ -917,7 +949,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, NextBuildPosition);
}
if (ExistingTF->StructureType != STRUCTURE_MARINE_ADVARMOURY)
if (ExistingTF->StructureType != STRUCTURE_MARINE_ADVTURRETFACTORY)
{
return AICOMM_UpgradeStructure(pBot, ExistingTF);
}
@ -931,7 +963,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
{
SiegeLocation = ExistingTF->Location;
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(BUILDING_NAV_MESH), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition);
}
@ -945,8 +977,98 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
}
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege, commander_action* Action)
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure, commander_action* Action)
{
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
StructureFilter.DeployableTeam = pBot->Player->GetTeam();
StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
StructureFilter.ReachabilityTeam = pBot->Player->GetTeam();
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure* ExistingStructure = AITAC_FindClosestDeployableToLocation(HiveToSecure->FloorLocation, &StructureFilter);
AvHAIBuildableStructure* ExistingPG = nullptr;
AvHAIBuildableStructure* ExistingTF = nullptr;
Vector OutpostLocation = (ExistingStructure) ? ExistingStructure->Location : HiveToSecure->FloorLocation;
if (ExistingStructure)
{
if (ExistingStructure->StructureType == STRUCTURE_MARINE_PHASEGATE)
{
ExistingPG = ExistingStructure;
}
else
{
ExistingTF = ExistingStructure;
}
}
if (AITAC_PhaseGatesAvailable(pBot->Player->GetTeam()))
{
if (!ExistingPG)
{
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
ExistingPG = AITAC_FindClosestDeployableToLocation(OutpostLocation, &StructureFilter);
if (!ExistingPG)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation);
}
return false;
}
}
}
if (!ExistingTF)
{
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY;
ExistingTF = AITAC_FindClosestDeployableToLocation(OutpostLocation, &StructureFilter);
if (!ExistingTF)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation);
}
return false;
}
}
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRET;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
int NumTurrets = AITAC_GetNumDeployablesNearLocation(ExistingTF->Location, &StructureFilter);
if (NumTurrets < 5)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF->Location, UTIL_MetresToGoldSrcUnits(3.0f));
if (!vIsZero(BuildLocation))
{
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation);
}
return false;
}
if (!UTIL_IsStructureElectrified(ExistingTF->edict))
{
return AICOMM_ResearchTech(pBot, ExistingTF, RESEARCH_ELECTRICAL);
}
return false;
}
@ -958,7 +1080,7 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander
DeployableSearchFilter ExistingPortalFilter;
ExistingPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
ExistingPortalFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
ExistingPortalFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
ExistingPortalFilter.DeployableTeam = pBot->Player->GetTeam();
ExistingPortalFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
ExistingPortalFilter.ReachabilityTeam = pBot->Player->GetTeam();
@ -968,7 +1090,7 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander
// First see if we can place the next infantry portal next to the first one
if (ExistingInfantryPortal)
{
BuildLocation = UTIL_GetRandomPointOnNavmeshInDonut(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ExistingInfantryPortal->edict->v.origin, UTIL_MetresToGoldSrcUnits(2.0f), UTIL_MetresToGoldSrcUnits(3.0f));
BuildLocation = UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingInfantryPortal->edict->v.origin, UTIL_MetresToGoldSrcUnits(2.0f), UTIL_MetresToGoldSrcUnits(3.0f));
}
if (vIsZero(BuildLocation))
@ -997,12 +1119,12 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander
float Distance = vDist2D(NearestPointToChair, CommChair->v.origin);
float RandomDist = UTIL_MetresToGoldSrcUnits(5.0f) - Distance;
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestPointToChair, RandomDist);
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestPointToChair, RandomDist);
}
else
{
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
}
}
@ -1011,6 +1133,25 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation);
}
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot)
{
DeployableSearchFilter UnreachableFilter;
UnreachableFilter.DeployableTeam = pBot->Player->GetTeam();
UnreachableFilter.ReachabilityTeam = pBot->Player->GetTeam();
UnreachableFilter.ReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
UnreachableFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_RESEARCHING;
AvHAIBuildableStructure* UnreachableStructure = AITAC_FindClosestDeployableToLocation(AITAC_GetCommChairLocation(pBot->Player->GetTeam()), &UnreachableFilter);
// Recycle any structures which are unreachable (e.g. sunk below the map)
if (UnreachableStructure)
{
return AICOMM_RecycleStructure(pBot, UnreachableStructure);
}
return false;
}
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
{
AICOMM_CheckNewRequests(pBot);
@ -1245,6 +1386,7 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot)
if (gpGlobals->time < pBot->next_commander_action_time) { return; }
if (AICOMM_CheckForNextRecycleAction(pBot)) { return; }
if (AICOMM_CheckForNextSupportAction(pBot)) { return; }
if (AICOMM_CheckForNextBuildAction(pBot, &pBot->BuildAction)) { return; }
if (AICOMM_CheckForNextResearchAction(pBot, &pBot->ResearchAction)) { return; }
@ -1320,11 +1462,11 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
const AvHAIHiveDefinition* Result = nullptr;
float MinDist = 0.0f;
const vector<AvHAIHiveDefinition> Hives = AITAC_GetAllHives();
const vector<AvHAIHiveDefinition*> Hives = AITAC_GetAllHives();
for (auto it = Hives.begin(); it != Hives.end(); it++)
{
const AvHAIHiveDefinition* Hive = &(*it);
const AvHAIHiveDefinition* Hive = (*it);
if (Hive->Status != HIVE_STATUS_UNBUILT) { continue; }
@ -1332,7 +1474,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
if (AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, Hive->Location, UTIL_MetresToGoldSrcUnits(10.0f)) == nullptr) { continue; }
if (!AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, Hive->Location, UTIL_MetresToGoldSrcUnits(10.0f)))
if (AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, Hive->Location, UTIL_MetresToGoldSrcUnits(10.0f)))
{
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTeam = CommanderTeam;
@ -1345,7 +1487,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
AvHAIBuildableStructure* PG = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter);
bool bCanSeePG = (PG && AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(PG->edict), UTIL_MetresToGoldSrcUnits(10.0f)));
bool bCanSeePG = (!PG || AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(PG->edict), UTIL_MetresToGoldSrcUnits(10.0f)));
if (!bCanSeePG)
{
@ -1363,7 +1505,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
bNeedsElectrifying = (UTIL_StructureIsFullyBuilt(TF->edict) && !UTIL_IsStructureElectrified(TF->edict) && AITAC_DeployableExistsAtLocation(TF->Location, &StructureFilter));
}
bool bCanSeeTF = (TF && AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(TF->edict), UTIL_MetresToGoldSrcUnits(10.0f)));
bool bCanSeeTF = (!TF || AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(TF->edict), UTIL_MetresToGoldSrcUnits(10.0f)));
if (!bNeedsElectrifying && !bCanSeePG && !bCanSeeTF) { continue; }
}

View file

@ -27,6 +27,7 @@ bool AICOMM_IssueSecureResNodeOrder(AvHAIPlayer* pBot, edict_t* Recipient, const
void AICOMM_ClearAction(commander_action* Action);
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action);
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot, commander_action* Action);
void AICOMM_SetDropHealthAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
void AICOMM_SetDropAmmoAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
@ -39,7 +40,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander_action* Action);
bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege, commander_action* Action);
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege, commander_action* Action);
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure, commander_action* Action);
ai_commander_request* AICOMM_GetExistingRequestForPlayer(AvHAIPlayer* pBot, edict_t* Requestor);
void AICOMM_CheckNewRequests(AvHAIPlayer* pBot);

View file

@ -89,6 +89,7 @@ typedef enum _AI_REACHABILITY_STATUS
AI_REACHABILITY_GORGE = 1u << 2,
AI_REACHABILITY_ONOS = 1u << 3,
AI_REACHABILITY_WELDER = 1u << 4,
AI_REACHABILITY_UNREACHABLE = 1u << 5,
AI_REACHABILITY_ALL = -1
} AvHAIReachabilityStatus;
@ -179,6 +180,12 @@ typedef struct _OFF_MESH_CONN
edict_t* TargetObject = nullptr;
} AvHAIOffMeshConnection;
typedef struct _STRUCTURE_OBSTACLE
{
unsigned int NavMeshIndex = 0;
unsigned int ObstacleRef = 0;
} AvHAITempObstacle;
// Data structure used to track resource nodes in the map
typedef struct _RESOURCE_NODE
{
@ -266,7 +273,7 @@ typedef struct _AVH_AI_BUILDABLE_STRUCTURE
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE;
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE;
int LastSeen = 0; // Which refresh cycle was this last seen on? Used to determine if the building has been removed from play
unsigned int ObstacleRefs[MAX_NAV_MESHES]; // References to this structure's obstacles across each nav mesh
vector< AvHAITempObstacle> Obstacles;
vector<AvHAIOffMeshConnection> OffMeshConnections; // References to any off-mesh connections this structure is associated with
Vector LastSuccessfulCommanderLocation = g_vecZero; // Tracks the last commander view location where it successfully placed or selected the building
Vector LastSuccessfulCommanderAngle = g_vecZero; // Tracks the last commander input angle ("click" location) used to successfully place or select building

View file

@ -418,31 +418,97 @@ Vector UTIL_GetNearestPointOnNavWall(const nav_profile &NavProfile, const Vector
return g_vecZero;
}
unsigned int UTIL_AddTemporaryObstacle(const Vector Location, float Radius, float Height, int area)
unsigned int UTIL_AddTemporaryObstacle(unsigned int NavMeshIndex, const Vector Location, float Radius, float Height, int area)
{
unsigned int ObstacleNum = 0;
float Pos[3] = { Location.x, Location.z - (Height * 0.5f), -Location.y };
for (int i = 0; i < MAX_NAV_MESHES; i++)
if (NavMeshes[NavMeshIndex].tileCache)
{
if (NavMeshes[i].tileCache)
float Pos[3] = { Location.x, Location.z - (Height * 0.5f), -Location.y };
dtObstacleRef ObsRef = 0;
NavMeshes[NavMeshIndex].tileCache->addObstacle(Pos, Radius, Height, area, &ObsRef);
ObstacleNum = (unsigned int)ObsRef;
if (ObstacleNum > 0)
{
dtObstacleRef ObsRef = 0;
NavMeshes[i].tileCache->addObstacle(Pos, Radius, Height, area, &ObsRef);
ObstacleNum = (unsigned int)ObsRef;
if (area == DT_TILECACHE_NULL_AREA || area == DT_TILECACHE_WELD_AREA)
{
bNavMeshModified = true;
}
bNavMeshModified = true;
}
}
return ObstacleNum;
}
void UTIL_AddStructureTemporaryObstacles(AvHAIBuildableStructure* Structure)
{
bool bCollideWithPlayers = UTIL_ShouldStructureCollide(Structure->StructureType);
float Radius = UTIL_GetStructureRadiusForObstruction(Structure->StructureType);
// Not all structures collide with players (e.g. phase gate)
if (bCollideWithPlayers)
{
unsigned int area = UTIL_GetAreaForObstruction(Structure->StructureType, Structure->edict);
// We add an obstacle for the building nav mesh below
for (int i = 0; i < BUILDING_NAV_MESH; i++)
{
unsigned int NewObstacleRef = UTIL_AddTemporaryObstacle(i, UTIL_GetCentreOfEntity(Structure->edict), Radius, 100.0f, area);
if (NewObstacleRef > 0)
{
AvHAITempObstacle NewObstacle;
NewObstacle.NavMeshIndex = i;
NewObstacle.ObstacleRef = NewObstacleRef;
Structure->Obstacles.push_back(NewObstacle);
}
}
}
// Always cut a hole in the building nav mesh so we don't try to place anything on top of this structure in future
unsigned int NewObstacleRef = UTIL_AddTemporaryObstacle(BUILDING_NAV_MESH, UTIL_GetCentreOfEntity(Structure->edict), Radius * 1.5f, 100.0f, DT_TILECACHE_NULL_AREA);
if (NewObstacleRef > 0)
{
AvHAITempObstacle NewObstacle;
NewObstacle.NavMeshIndex = BUILDING_NAV_MESH;
NewObstacle.ObstacleRef = NewObstacleRef;
Structure->Obstacles.push_back(NewObstacle);
}
}
void UTIL_RemoveStructureTemporaryObstacles(AvHAIBuildableStructure* Structure)
{
for (auto it = Structure->Obstacles.begin(); it != Structure->Obstacles.end();)
{
int NavMeshIndex = it->NavMeshIndex;
if (NavMeshes[NavMeshIndex].tileCache)
{
const dtTileCacheObstacle* ObstacleToRemove = NavMeshes[NavMeshIndex].tileCache->getObstacleByRef((dtObstacleRef)it->ObstacleRef);
if (ObstacleToRemove)
{
dtStatus RemovalStatus = NavMeshes[NavMeshIndex].tileCache->removeObstacle((dtObstacleRef)it->ObstacleRef);
if (dtStatusSucceed(RemovalStatus))
{
bNavMeshModified = true;
}
}
}
it = Structure->Obstacles.erase(it);
}
}
void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Height, int area, unsigned int* ObstacleRefArray)
{
unsigned int ObstacleNum = 0;
@ -460,7 +526,7 @@ void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Heigh
ObstacleRefArray[i] = (unsigned int)ObsRef;
if (area == DT_TILECACHE_NULL_AREA || area == DT_TILECACHE_WELD_AREA)
if (ObstacleNum > 0)
{
bNavMeshModified = true;
}

View file

@ -154,9 +154,9 @@ static const float pReachableExtents[3] = { max_ai_use_reach, max_ai_use_reach,
static const int MAX_NAV_PROFILES = 16; // Max number of possible nav profiles. Currently 9 are used (see top of this header file)
static const int REGULAR_NAV_MESH = 0;
static const int ONOS_NAV_MESH = 1;
static const int BUILDING_NAV_MESH = 2;
static const int REGULAR_NAV_MESH = 0; // Nav mesh used by all players except Onos and the AI commander
static const int ONOS_NAV_MESH = 1; // Nav mesh used by Onos (due to larger hitbox)
static const int BUILDING_NAV_MESH = 2; // Nav mesh used by commander for building placement. Must be the last nav mesh index (see UTIL_AddStructureTemporaryObstacles)
static const int DT_AREA_NULL = 0; // Represents a null area on the nav mesh. Not traversable and considered not on the nav mesh
static const int DT_AREA_BLOCKED = 3; // Area occupied by an obstruction (e.g. building). Not traversable, but considered to be on the nav mesh
@ -302,9 +302,10 @@ Vector UTIL_GetNearestPointOnNavWall(const nav_profile& NavProfile, const Vector
An example use case is to place an obstacle of area type SAMPLE_POLYAREA_OBSTRUCTION to mark where buildings are.
Using DT_AREA_NULL will effectively cut a hole in the nav mesh, meaning it's no longer considered a valid mesh position.
*/
unsigned int UTIL_AddTemporaryObstacle(const Vector Location, float Radius, float Height, int area);
unsigned int UTIL_AddTemporaryObstacle(unsigned int NavMeshIndex, const Vector Location, float Radius, float Height, int area);
void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Height, int area, unsigned int* ObstacleRefArray);
void UTIL_AddStructureTemporaryObstacles(AvHAIBuildableStructure* Structure);
void UTIL_RemoveStructureTemporaryObstacles(AvHAIBuildableStructure* Structure);
unsigned int UTIL_AddTemporaryBoxObstacle(Vector bMin, Vector bMax, int area);

View file

@ -1597,6 +1597,9 @@ void BotStopCommanderMode(AvHAIPlayer* pBot)
if (IsPlayerCommander(pBot->Edict))
{
CLIENT_COMMAND(pBot->Edict, "stopcommandermode");
pBot->Player->SetUser3(AVH_USER3_MARINE_PLAYER);
// Cheesy way to make sure player class change is sent to everyone
pBot->Player->EffectivePlayerClassChanged();
}
}

View file

@ -811,8 +811,8 @@ void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item)
if (!bOnNavMesh)
{
Item->TeamAReachabilityFlags = AI_REACHABILITY_NONE;
Item->TeamBReachabilityFlags = AI_REACHABILITY_NONE;
Item->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
Item->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
return;
}
@ -838,6 +838,10 @@ void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item)
{
Item->TeamAReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
Item->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
@ -863,6 +867,10 @@ void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item)
{
Item->TeamBReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
Item->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
}
@ -896,8 +904,8 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
if (!bOnNavMesh)
{
ResNode->TeamAReachabilityFlags = AI_REACHABILITY_NONE;
ResNode->TeamBReachabilityFlags = AI_REACHABILITY_NONE;
ResNode->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
ResNode->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
return;
}
@ -926,6 +934,10 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
{
ResNode->TeamAReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
ResNode->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
else
@ -948,6 +960,11 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
{
ResNode->TeamAReachabilityFlags |= AI_REACHABILITY_ONOS;
}
if (ResNode->TeamAReachabilityFlags == AI_REACHABILITY_NONE)
{
ResNode->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
if (GetGameRules()->GetTeamB()->GetTeamType() == AVH_CLASS_TYPE_MARINE)
@ -972,6 +989,10 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
{
ResNode->TeamBReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
ResNode->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
else
@ -994,6 +1015,11 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
{
ResNode->TeamBReachabilityFlags |= AI_REACHABILITY_ONOS;
}
if (ResNode->TeamBReachabilityFlags == AI_REACHABILITY_NONE)
{
ResNode->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
@ -1274,7 +1300,6 @@ void AITAC_RefreshBuildableStructures()
if (it->second.LastSeen < StructureRefreshFrame)
{
AITAC_OnStructureDestroyed(&it->second);
UTIL_RemoveTemporaryObstacles(it->second.ObstacleRefs);
it = TeamAStructureMap.erase(it);
}
else
@ -1293,7 +1318,6 @@ void AITAC_RefreshBuildableStructures()
if (it->second.LastSeen < StructureRefreshFrame)
{
AITAC_OnStructureDestroyed(&it->second);
UTIL_RemoveTemporaryObstacles(it->second.ObstacleRefs);
it = TeamBStructureMap.erase(it);
}
else
@ -1476,12 +1500,15 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure)
Structure->bReachabilityMarkedDirty = false;
Structure->TeamAReachabilityFlags = AI_REACHABILITY_NONE;
Structure->TeamBReachabilityFlags = AI_REACHABILITY_NONE;
bool bIsOnNavMesh = UTIL_PointIsOnNavmesh(BaseNavProfiles[MARINE_BASE_NAV_PROFILE], UTIL_GetEntityGroundLocation(Structure->edict), Vector(max_player_use_reach, max_player_use_reach, max_player_use_reach));
if (!bIsOnNavMesh)
{
Structure->TeamAReachabilityFlags = AI_REACHABILITY_NONE;
Structure->TeamBReachabilityFlags = AI_REACHABILITY_NONE;
Structure->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
Structure->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
return;
}
@ -1508,6 +1535,10 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure)
{
Structure->TeamAReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
Structure->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
else
@ -1530,6 +1561,11 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure)
{
Structure->TeamAReachabilityFlags |= AI_REACHABILITY_ONOS;
}
if (Structure->TeamAReachabilityFlags == AI_REACHABILITY_NONE)
{
Structure->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
if (GetGameRules()->GetTeamB()->GetTeamType() == AVH_CLASS_TYPE_MARINE)
@ -1555,6 +1591,10 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure)
{
Structure->TeamBReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
Structure->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
else
@ -1577,6 +1617,11 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure)
{
Structure->TeamBReachabilityFlags |= AI_REACHABILITY_ONOS;
}
if (Structure->TeamBReachabilityFlags == AI_REACHABILITY_NONE)
{
Structure->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
@ -1602,29 +1647,16 @@ void AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
std::unordered_map<int, AvHAIBuildableStructure>& BuildingMap = (BaseBuildable->GetTeamNumber() == TeamANumber) ? TeamAStructureMap : TeamBStructureMap;
// This is the first time we've seen this structure, so it must be new
if (BuildingMap[EntIndex].LastSeen == 0)
{
BuildingMap[EntIndex].EntityRef = BaseBuildable;
BuildingMap[EntIndex].edict = BuildingEdict;
BuildingMap[EntIndex].OffMeshConnections.clear();
BuildingMap[EntIndex].Obstacles.clear();
memset(&BuildingMap[EntIndex].ObstacleRefs, 0, sizeof(BuildingMap[EntIndex].ObstacleRefs));
bool bShouldCollide = UTIL_ShouldStructureCollide(StructureType);
if (bShouldCollide)
{
unsigned int area = UTIL_GetAreaForObstruction(StructureType, BuildingEdict);
float Radius = UTIL_GetStructureRadiusForObstruction(StructureType);
UTIL_AddTemporaryObstacles(UTIL_GetCentreOfEntity(BuildingMap[EntIndex].edict), Radius, 100.0f, area, BuildingMap[EntIndex].ObstacleRefs);
}
else
{
memset(BuildingMap[EntIndex].ObstacleRefs, 0, sizeof(unsigned int) * MAX_NAV_MESHES);
}
BuildingMap[EntIndex].Location = g_vecZero;
BuildingMap[EntIndex].Location = g_vecZero; // We set this just below after calculating reachability
AITAC_OnStructureCreated(&BuildingMap[EntIndex]);
}
@ -1695,8 +1727,12 @@ void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure)
{
if (!GetGameRules()->GetGameStarted()) { return; }
UTIL_AddStructureTemporaryObstacles(NewStructure);
AvHTeamNumber StructureTeam = NewStructure->EntityRef->GetTeamNumber();
AITAC_RefreshReachabilityForStructure(NewStructure);
if (StructureTeam == TEAM_IND) { return; }
AvHTeam* Team = GetGameRules()->GetTeam(StructureTeam);
@ -1791,6 +1827,8 @@ void AITAC_OnStructureBeginRecycling(AvHAIBuildableStructure* RecyclingStructure
void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure)
{
UTIL_RemoveStructureTemporaryObstacles(DestroyedStructure);
if (DestroyedStructure->StructureType == STRUCTURE_MARINE_PHASEGATE)
{
// Eliminate all connections from this phase gate
@ -3050,14 +3088,28 @@ vector<AvHPlayer*> AITAC_GetAllPlayersOnTeam(AvHTeamNumber Team)
return Result;
}
const vector<AvHAIResourceNode>& AITAC_GetAllResourceNodes()
const vector<AvHAIResourceNode*> AITAC_GetAllResourceNodes()
{
return ResourceNodes;
vector<AvHAIResourceNode*> Results;
for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++)
{
Results.push_back(&(*it));
}
return Results;
}
const vector<AvHAIHiveDefinition>& AITAC_GetAllHives()
const vector<AvHAIHiveDefinition*> AITAC_GetAllHives()
{
return Hives;
vector<AvHAIHiveDefinition*> Results;
for (auto it = Hives.begin(); it != Hives.end(); it++)
{
Results.push_back(&(*it));
}
return Results;
}
bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius)

View file

@ -145,8 +145,8 @@ edict_t* AITAC_GetMarineEligibleToBuildSiege(AvHTeamNumber Team, const AvHAIHive
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeam(AvHTeamNumber Team);
edict_t* AITAC_GetNearestHiddenPlayerInLocation(AvHTeamNumber Team, const Vector Location, const float MaxRadius);
const vector<AvHAIResourceNode>& AITAC_GetAllResourceNodes();
const vector<AvHAIHiveDefinition>& AITAC_GetAllHives();
const vector<AvHAIResourceNode*> AITAC_GetAllResourceNodes();
const vector<AvHAIHiveDefinition*> AITAC_GetAllHives();
bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius);

View file

@ -487,6 +487,8 @@ public:
bool GetHasSeenATeam();
bool GetPurchaseAllowed(AvHMessageID inUpgrade, int& outCost, string* outErrorMessage = NULL) const;
void GiveOrderToSelection(AvHOrder& inOrder);
private:
void AcquireOverwatchTarget();
@ -505,7 +507,7 @@ private:
bool QueryEnemySighted(CBaseEntity* inEntity);
bool GetHasActiveAlienWeaponWithImpulse(AvHMessageID inMessageID) const;
bool GetRandomGameStartedTick(float inApproximateFrameRate);
bool GetPurchaseAllowed(AvHMessageID inUpgrade, int& outCost, string* outErrorMessage = NULL) const;
int GetRelevantWeight(void) const;
int GetRelevantWeightForWeapon(AvHBasePlayerWeapon* inWeapon) const;
void GetSpeeds(int& outBaseSpeed, int& outUnemcumberedSpeed) const;